Ztorrent/TODO.md

126 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ZTorrent — TODO & Контекст улучшений
> Цель: довести ztorrent до уровня qBittorrent по функциональности и скорости.
> Ведётся автоматически — отражает актуальное состояние реализации.
---
## Архитектура проекта
```
internal/
torrent/
engine.go — основной движок: загрузка торрента, воркеры, координация
peerwire.go — BitTorrent wire protocol: handshake, download piece, messages
piecescheduler.go — выбор кусков (rarest-first), endgame mode
debug.go — debugf helper
tracker/
tracker.go — HTTP + UDP announce
torrentfile/
torrentfile.go — парсинг .torrent (bencode)
app/
controller.go — тонкая обёртка над Engine для UI
ui/
ui.go — Bubble Tea TUI (welcome, loading, dashboard)
styles.go — цветовая палитра
```
---
## P0 — Критичные ✅ ГОТОВО
- [x] **Resume download**`createOrOpenPartFile()` без O_TRUNC + SHA-1 верификация
- [x] **Keep-alive**`keepaliveLoop()` горутина, 90с, `msgCancel=8`
- [x] **Endgame mode**`pieceDone` broadcast + `SendCancel()` при дублировании
## P1 — Производительность ✅ ГОТОВО
- [x] **Adaptive pipeline** — depth 464, пересчёт каждые 4 блока по RTT+BW
- [x] **IPv6 compact peers**`parsePeers6()` в tracker.go
- [x] **Rate limiting**`rateLimitedConn`, `SetDownloadLimit`/`SetUploadLimit`
- [x] **Bitmap resume data**`.part.bitmap` JSON, `asyncSaveBitmap()` в heartbeat
## Баги ✅ ПОФИКШЕНО
- [x] **`announce` пустой** — BEP 12 fallback: берём первый URL из `announce-list`
> `torrentfile.go` → `primaryAnnounce` fallback в `Open()`
- [x] **`peerBackoff` O(n)** → O(1) через `peerIdx map[string]int`
> Добавлен `Engine.peerIdx`, `peerByKey()` helper
> Все peer-методы: `setPeerState`, `setPeerError`, `peerBackoff`, `recordPieceComplete`
- [x] **Скорость без EWMA** → сглаживание α=0.3 в `updateSpeedLocked()`
> `downloadSpeed = 0.3*sample + 0.7*prev`
- [x] **Reannounce блокирует heartbeat** → запускается в отдельной горутине
> `heartbeatTicker.C` → `go func() { refreshPeersFromTrackers() }()`
> Таймаут уменьшен с 20с до 15с
## Фича ✅ РЕАЛИЗОВАНО
- [x] **Incoming connections** — TCP listener на :6881
> `listenIncoming()` + `handleIncoming()` в engine.go
> Handshake верификация (info_hash), регистрация в `peerIdx`
> Входящие пиры подхватываются `startWorkers` на следующем heartbeat тике
---
## P2 — Долгосрочные (не начато)
- [x] **DHT (BEP 5)** — Kademlia DHT для поиска пиров без трекера
> Новый пакет `internal/dht/` — реализовано!
- [x] **Магнет-ссылки (BEP 9)**`magnet:?xt=...`, ut_metadata extension
> Новый пакет `internal/magnet/` — средняя сложность
- [x] **Seeding / upload (tit-for-tat)** — раздача после завершения
> `peerwire.go` → обработка `msgRequest`, отправка `msgPiece`
- [x] **PEX (BEP 11)** — Peer Exchange между пирами
> Extension handshake + `ut_pex` message
---
## Технические решения
### peerIdx — O(1) peer lookup
```
Engine.peers []PeerStatus // слайс со всеми пирами
Engine.peerIdx map[string]int // "host:port" → индекс в слайсе
buildPeerIdx(peers) // строится при LoadTorrent и reannounce
peerByKey(key) *PeerStatus // O(1) под e.mu
```
### EWMA сглаживание скорости
```
updateSpeedLocked():
sample = delta_bytes / elapsed_sec
speed = 0.3*sample + 0.7*prev_speed // α=0.3
```
### Incoming connections flow
```
runDownload()
└── go listenIncoming(ctx, tf)
└── net.Listen(":6881")
└── for { conn = Accept() → go handleIncoming(conn) }
├── ReadFull → verify wireProtocol + infoHash
├── Write reply handshake
└── register peer in peerIdx
→ подхватывается startWorkers на следующем heartbeat
```
### Resume flow
```
createOrOpenPartFile():
├── если .part существует + .bitmap → verifyPieces() (быстро)
├── если .part существует, нет bitmap → verifyAllPieces() (SHA-1 scan)
└── если .part нет → создаём новый, Truncate(size)
heartbeat:
asyncSaveBitmap() → .part.bitmap
завершение:
os.Remove(.part) + os.Remove(.part.bitmap)
```