DHT в торрент-обмене: от теории к своей k-v сети
в стиле “любопытный факт”
16 глав о том, как работает DHT, зачем он нужен, и как построить на нём децентрализованный discovery-слой для P2P-синхронизации файлов.
1. DHT в торрент-обмене: важность этой штуки 9 из 10
DHT (Distributed Hash Table) в BitTorrent — это реализация Kademlia, которая позволяет пирам находить друг друга без центрального трекера.
| С DHT | Без DHT |
|---|---|
| Поиск пиров без трекера | Полная зависимость от трекера |
| Magnet-ссылки работают из коробки | Magnet не работает без трекера |
| Рой живёт, даже если трекер упал | Трекер лёг = рой мёртв |
| Нельзя заблокировать централизованно | DNS-блокировка убивает всё |
| 20 миллионов узлов в сети | — |
Единственная причина не ставить 10 — в частных/закрытых роях DHT намеренно отключают (флаг private), и холодный старт (первый пир без трекера) всё ещё представляет проблему.
Оценка: 9/10.
2. Есть другие реализации DHT, кроме Kademlia
Kademlia — самая известная, но не единственная:
| DHT | Авторы | Ключевая идея | Где используется |
|---|---|---|---|
| Chord | MIT (Stoica, 2001) | Кольцо: каждый узел знает O(log N) соседей. Поиск за O(log N) прыжков. | Ранние P2P-проекты |
| Pastry | Microsoft/Rice (2001) | Префиксная маршрутизация. Узлы с общим префиксом IP — ближе. | PAST, Scribe |
| CAN | UC Berkeley (2001) | Узлы отвечают за зоны в d-мерном пространстве. | Исследования |
| Tapestry | UC Berkeley (2001) | Как Pastry, но с locality-aware маршрутизацией. | OceanStore |
| Mainline DHT | BitTorrent (2005) | Вариант Kademlia с XOR-метрикой, адаптированный под торренты. | Все торрент-клиенты |
| S/Kademlia | (2007) | Kademlia + защита от Sybil-атак. | Безопасные системы |
| Coral | NYU (2004) | DHT с кластеризацией. | Распределённый CDN |
Всё это в основном академическая чепуха , кроме Pastry , который очень крутой, но к сожалению не популярен - не успел.
3. Mainline DHT — что доступно через API торрент-клиентов
DHT внутри торрент-клиентов (Transmission, qBittorrent, Deluge) — чёрный ящик. Через стандартный API доступен минимум:
| Операция | Transmission | qBittorrent | Deluge |
|---|---|---|---|
| Включить/выключить DHT | session-set |
setPreferences |
❌ |
| Количество DHT-узлов | session-stats |
transfer/info |
get_session_status |
| Добавить magnet-ссылку | torrent-add |
torrents/add |
add_torrent_url |
| DHT port (firewall check) | session-set |
setPreferences |
❌ |
Ни один клиент не даёт прямого доступа к dht_put() / dht_get() — DHT используется только для поиска пиров.
4. Можно ли использовать DHT «не по назначению»?
Magnet-ссылка как «публикация в DHT»:
1 | Peer A (push): создаёт .torrent → magnet:?xt=urn:btih:INFO_HASH |
Работает только если оба пира добавили торрент. DHT внутри клиента автоматически находит пиров по info_hash.
DHT как health check:
1 | func Test() error { |
Полезно: проверяем что DHT жив и файрвол не блокирует UDP.
5. BEP-44: произвольные ключ-значения в DHT
BEP-44 — расширение Mainline DHT, позволяющее хранить произвольные данные:
Это расширение очень даже полезно , само по себе.
| Режим | Ключ | Размер значения | TTL |
|---|---|---|---|
| Immutable | SHA1(данных) — 20 байт | ≤ 1000 байт | 2 часа |
| Mutable | Ed25519 публичный ключ — 32 байта | ≤ 1000 байт | 2 часа (можно переиздавать) |
Mutable items поддерживают:
- Salt (до 64 байт) — чтобы иметь несколько записей на одном ключе
- Sequence number — монотонный счётчик, защита от повторов
- Ed25519 подпись — проверка подлинности
Именно BEP-44 позволяет построить децентрализованный discovery-слой: кладём в DHT magnet-ссылку, подписанную приватным ключом, и любой пир может её найти и проверить подпись.
6. Открытые исходники: Transmission, qBittorrent, Deluge
Все три торрент-клиента — open source, но с разными DHT-движками:
| Клиент | Лицензия | Язык | DHT-движок |
|---|---|---|---|
| Transmission | MIT / GPLv2 | C | Собственный (libtransmission) |
| qBittorrent | GPLv2+ | C++ (Qt) | libtorrent (rasterbar) |
| Deluge | GPLv3 | Python | libtorrent (rasterbar) |
qBittorrent и Deluge используют общий libtorrent, который уже реализует BEP-44 — dht_put_item() и dht_get_item() — но не выставляет их через Web API.
7. Форк клиента для доступа к BEP-44 через API
Можно добавить в Web API qBittorrent:
1 | POST /api/v2/dht/put |
Внутри — обёртка над готовыми функциями libtorrent. Transmission для этого хуже: его libtransmission не реализует BEP-44, пришлось бы писать с нуля.
Это реально полезно будет.
8. Инфраструктура DHT: bootstrap-узлы, размер сети, кто держит
Bootstrap-узлы (точки входа в DHT)
| Провайдер | Запись | Кто держит |
|---|---|---|
| libtorrent (rasterbar) | dht.libtorrent.org:25401 |
Arvid Norberg |
| Transmission | dht.transmissionbt.com:6881 |
Команда Transmission |
| BitTorrent Inc. | router.bittorrent.com:6881 |
Rainberry (ex BitTorrent Inc.) |
| uTorrent | router.utorrent.com:6881 |
Rainberry |
Итого: ~10 публичных bootstrap-узлов от 2-3 организаций.
Важно: bootstrap нужен только при первом запуске. После того как узел набрал таблицу маршрутизации (8-32 узла), он сам её поддерживает.
Размер сети
| Метрика | Значение |
|---|---|
| Всего узлов | 15-25 миллионов одновременно |
| С публичным IP | ~10-15% (1.5-3.5M) |
| За NAT | ~85-90% |
| Гео-распределение | 200+ стран |
Для сравнения: IPFS DHT — 50-100 тыс. узлов. Mainline DHT на 2 порядка больше (в 100+ раз больше).
9. NAT-типы узлов и их распределение
| NAT-тип | % узлов | Прямое соединение | UDP hole punching |
|---|---|---|---|
| No NAT / Full Cone (публичный IP) | ~12% | ✅ | ✅ |
| Restricted Cone | ~15% | 🟡 после исходящего | ✅ |
| Port-Restricted Cone | ~20% | 🟡 сложнее | 🟡 иногда |
| Symmetric NAT | ~40% | ❌ | ❌ |
| UDP-blocked / CGNAT | ~13% | ❌ | ❌ |
Ключевой вывод: ~47% узлов могут установить прямое P2P-соединение. Остальные 53% — только через relay или никак.
10. Ограничения DHT: размер, TTL, rate limit
BEP-44 накладывает жёсткие ограничения:
| Параметр | Значение |
|---|---|
| Размер значения | ≤ 1000 байт |
| TTL | 2 часа (потом удаляется) |
| Rate limit | Зависит от узла, обычно ~10 запросов/сек с одного IP |
Что происходит при нарушении:
- Запись > 1000 байт → молча отбрасывается
- Flood (слишком частые put) → временный бан IP на узле
- Ответ без подписи (mutable) → не проходит проверку, отбрасывается
Вывод: 1000 байт достаточно для magnet-ссылки (200 байт) или JSON-манифеста (300 байт). Данные передаются через сам торрент, в DHT кладём только указатели.
т.е., в принципе, этого может хватить для наладки каналов связи без серверов и точек отказа.
11. Алгоритмы DHT для своего k-v store
Готовые Go-реализации Kademlia:
| Библиотека | Плюсы | Минусы |
|---|---|---|
| go-libp2p-kad-dht | NAT traversal (AutoNAT, Relay, Hole punching), изолированная сеть, зрелая (IPFS) | Тянет libp2p-стек зависимостей |
| anacrolix/dht | Mainline-совместима (20M узлов), чистый Go | Документация скудная |
| OpenDHT | C++, REST API, шифрование | CGo, сложнее сборка |
Для sync-folders можно использовать форк qBittorrent (быстрый доступ к Mainline DHT), или свою DHT-сеть на go-libp2p-kad-dht.
12. Сравнение Kademlia / Chord / Pastry / CAN — потребительский взгляд
| Критерий | Kademlia | Chord | Pastry | CAN |
|---|---|---|---|---|
| Живые реализации | ✅ Десятки | 🟡 1-2 | 🟡 FreePastry (Java) | ❌ Нет |
| NAT traversal | ✅ Встроен | ❌ | ❌ | ❌ |
| Churn (узлы приходят/уходят) | 🟢 Самовосстанавливается | 🟡 Нужен stabilization | 🟡 Нужен repair | 🔴 Дыры в пространстве |
| Скорость поиска | O(log N), ~4-6 прыжков | O(log N) | O(log N) | O(d·N^(1/d)) |
| Гео-близость | ❌ XOR игнорирует топологию | ❌ | ✅ Префиксная маршрутизация | 🟡 |
| Bootstrap | Просто: любой IP | Нужен successor | Нужны соседи | Нужны соседи |
Итоговый ranking:
| # | Алгоритм | Оценка | Для кого |
|---|---|---|---|
| 1 | Kademlia | 9/10 | Для всех. Единственный с живыми реализациями и NAT traversal |
| 2 | Pastry | 5/10 | Если нужна гео-близость и готов писать реализацию сам |
| 3 | Chord | 4/10 | Если нужна математическая строгость, а не надёжность |
| 4 | CAN | 1/10 | Для курсовой, не для продакшена |
13. NAT traversal в Kademlia: что встроено и эффективность
Оригинальная статья Kademlia (2002) про NAT ничего не знает. Современные реализации добавили:
| Реализация | Механизм | Как работает |
|---|---|---|
| go-libp2p-kad-dht | AutoNAT + Circuit Relay v2 + Hole punching | Определяет NAT-тип → пробивает через relay → прямой канал |
| anacrolix/dht (Mainline) | UDP hole punching | NAT открывает «дырку» при исходящем запросе |
| OpenDHT | UPnP/NAT-PMP + ICE-lite | Просит роутер открыть порт |
Эффективность по типам NAT:
| NAT-тип | Hole punching | UPnP | Circuit Relay | Вердикт |
|---|---|---|---|---|
| Full Cone (12%) | ✅ | ✅ | — | 🟢 Всегда |
| Restricted Cone (15%) | ✅ | ✅ | — | 🟢 Работает |
| Port-Restricted (20%) | 🟡 | ✅ | — | 🟡 Иногда |
| Symmetric (40%) | ❌ | 🟡 | ✅ | 🔴 Только relay |
| CGNAT (13%) | ❌ | ❌ | ✅ | 🔴 Только relay |
14. Надёжность когда почти все за NAT
DHT ищет не один узел, а много. При k=20 репликах:
1 | Из 20 ближайших узлов в среднем: |
Защитные механизмы:
- Репликация на k узлов (по умолчанию k=8-20)
- Параллельные запросы (α=3, ждём первого ответа)
- Republish каждый час (данные переезжают на новых соседей)
- Кеширование по пути (популярные ключи размножаются далеко за k)
Реальный бенчмарк (Mainline DHT): 94% ключей находятся за < 1 сек, 99% — за < 5 сек.
15. Как DHT выбирает узлы для репликации
Стандартный Kademlia: k ближайших по XOR-расстоянию. Проблема: XOR не учитывает NAT-тип или географию.
Улучшения:
| Реализация | Стратегия |
|---|---|
| Mainline DHT | k=8 + агрессивное кеширование по пути поиска |
| go-libp2p-kad-dht | k=20 + приоритет «стабильным» узлам (дольше в сети) |
| IPFS | k=20 + Reprovider каждые 12 часов |
| OpenDHT | k=8 + разнообразие по /16 подсетям |
Идеальная стратегия для своей сети:
- k узлов из разных /16 подсетей (защита от потери ЦОДа)
- Приоритет узлам с публичным IP
- Исключать узлы за symmetric NAT если есть альтернативы
16. План: torrent-транспорт для sync-folders
Три компонента:
- Форк qBittorrent — добавить BEP-44 API (
dht/put,dht/get) поверх libtorrent - sync-folders DHT-утилита — команды
dht-put(ждёт k реплик),dht-get,dht-watch - TorrentTransport — один .torrent на папку, DHT для discovery, торрент для передачи
Ключевая идея:
- Ed25519-ключ как «странный неподбираемый идентификатор» проекта
- Подпись приватным ключом гарантирует подлинность манифеста
- BEP-44 mutable item хранит magnet-ссылку последней версии
- Торрент-клиент автоматически использует Mainline DHT для поиска пиров
Подробный план реализации: планы/draft/torrent-transport.md
Если реализовать все три плана - гибкость p2p будет максимальной.