| # DHT Protocol Specification | |
| ## 1. Общие положения | |
| * DHT-протокол предназначен для обмена информацией о пирах между агентами. | |
| * Используется **DID** (Decentralized Identifier) как уникальный идентификатор агента. | |
| * Для проверки подлинности применяется криптоподпись (публичный/приватный ключ). | |
| * Для защиты от спама/флуда используется **Proof-of-Work (PoW)**. | |
| * Каждый агент может иметь несколько сетевых интерфейсов (адресов). | |
| * У агента может быть только **одна устойчивая пара DID + pubkey**. | |
| --- | |
| ## 2. Интерфейсы | |
| Формат интерфейса: | |
| ```json | |
| { | |
| "addr": "tcp://1.2.3.4:4000", | |
| "nonce": 123456, | |
| "pow_hash": "abcd1234...", | |
| "difficulty": 22, | |
| "datetime": "2025-09-14T21:00:00Z", | |
| "type": "internet" | |
| } | |
| ``` | |
| ### Поддерживаемые протоколы | |
| * `tcp://` | |
| * `udp://` | |
| ### Поле `type` (опционально) | |
| * `localhost` — адреса локальной машины. | |
| * `lan:[маска_подсети]` — локальная сеть, пример: `lan:192.168.10.0`. | |
| (Один агент может иметь несколько сетевых интерфейсов и, соответственно, несколько LAN-сегментов.) | |
| * `internet` — обычное TCP/UDP-подключение через глобальную сеть. | |
| * `yggdrasil` — узел доступен через Yggdrasil overlay. | |
| * `i2p` — узел доступен через I2P. | |
| ### Правила | |
| * Если `port = 0` → интерфейс считается **отключённым**. | |
| * Корректный интерфейс с более новой датой заменяет аналогичный старый (после проверки PoW). | |
| * При обмене рекомендуется **не передавать локальные интерфейсы** в Интернет (исключение: Yggdrasil и I2P). | |
| --- | |
| ## 3. Proof-of-Work (PoW) | |
| * Каждый интерфейс сопровождается PoW. | |
| * Сложность PoW должна быть выбрана так, чтобы генерация занимала **несколько минут** (операция нечастая). | |
| * Поля: | |
| * `nonce` — число, подобранное агентом. | |
| * `pow_hash` — хэш значения (`DID + addr + datetime + nonce`). | |
| * `difficulty` — число ведущих нулей (или иное условие). | |
| --- | |
| ## 3.1 Формализация PoW и подписи | |
| ### Канонический вход для PoW | |
| ``` | |
| pow_input_string = DID + " -- " + addr + " -- " + datetime + " -- " + nonce_string | |
| ``` | |
| * Все строки кодируются в UTF-8. | |
| * Хеш: `pow_hash = sha256(pow_input_string_bytes)`, hex lower-case (64 символа). | |
| * `difficulty` = число ведущих нулевых hex-символов. | |
| ### Подпись сообщения | |
| * Подписывается всё сообщение (JSON), кроме поля `signature`. | |
| * Сериализация: JSON с отсортированными ключами, без пробелов: | |
| ```python | |
| serialized = json.dumps(obj, separators=(",", ":"), sort_keys=True, ensure_ascii=False).encode("utf-8") | |
| ``` | |
| * Алгоритм: **Ed25519** (рекомендуется). | |
| * Подпись хранится в поле: | |
| ```json | |
| "signature": "BASE64URL(...)", | |
| "sig_algo": "ed25519" | |
| ``` | |
| ### Верификация | |
| 1. Проверить подпись сообщения по `pubkey`. | |
| 2. Для каждого `address` вычислить PoW и проверить `difficulty`. | |
| 3. Некорректные адреса игнорировать, сообщение в целом может оставаться валидным. | |
| --- | |
| ## 4. Сообщения | |
| ### 4.1 DISCOVERY | |
| Используется для объявления себя в локальной сети. | |
| ```json | |
| { | |
| "type": "DISCOVERY", | |
| "id": "did:example:123", | |
| "name": "Agent_X", | |
| "pubkey": "base58...", | |
| "addresses": [ | |
| { | |
| "addr": "tcp://1.2.3.4:4000", | |
| "nonce": 123456, | |
| "pow_hash": "0000abf39d...", | |
| "difficulty": 22, | |
| "datetime": "2025-09-14T21:00:00Z", | |
| "type": "internet" | |
| } | |
| ], | |
| "signature": "BASE64URL(...)", | |
| "sig_algo": "ed25519" | |
| } | |
| ```` | |
| --- | |
| ### 4.2 PEER\_EXCHANGE\_REQUEST / RESPONSE | |
| Запрос известных пиров: | |
| ```json | |
| { | |
| "type": "PEER_EXCHANGE_REQUEST", | |
| "id": "did:example:123", | |
| "name": "Agent_X", | |
| "addresses": [ | |
| { | |
| "addr": "udp://1.2.3.4:4010", | |
| "nonce": 987654, | |
| "pow_hash": "0000123def...", | |
| "difficulty": 22, | |
| "datetime": "2025-09-14T21:05:00Z", | |
| "type": "lan:192.168.1.0" | |
| } | |
| ], | |
| "signature": "BASE64URL(...)", | |
| "sig_algo": "ed25519" | |
| } | |
| ``` | |
| Ответ содержит список пиров (каждый с DID, pubkey и адресами, у которых тоже должен быть валидный PoW): | |
| ```json | |
| [ | |
| { | |
| "id": "did:example:456", | |
| "name": "Agent_Y", | |
| "pubkey": "base58...", | |
| "addresses": [ | |
| { | |
| "addr": "tcp://5.6.7.8:4020", | |
| "nonce": 22222, | |
| "pow_hash": "0000a1b2c3...", | |
| "difficulty": 22, | |
| "datetime": "2025-09-14T21:10:00Z", | |
| "type": "internet" | |
| } | |
| ], | |
| "signature": "BASE64URL(...)", | |
| "sig_algo": "ed25519" | |
| } | |
| ] | |
| ``` | |
| --- | |
| ## 5. Правила валидации адресов и пиров | |
| * Каждый `address` в DISCOVERY и PEER_EXCHANGE должен содержать валидный PoW: | |
| * `pow_hash = sha256(DID + " -- " + addr + " -- " + datetime + " -- " + nonce_string)` | |
| * `difficulty` соответствует локальной политике (например, 22 ведущих нуля). | |
| * Если PoW некорректен → адрес игнорируется. | |
| * `datetime` фиксируется при генерации PoW и не должен изменяться. | |
| * Если приходит новый адрес с той же парой `(DID + addr)` и более свежим `datetime` → он заменяет старый. | |
| * **Разные pubkey для одного DID** → принимается **первый**, остальные игнорируются. | |
| * **Адрес подписан чужим ключом** → запись отклоняется. | |
| * **Несколько интерфейсов** → сохраняются все, кроме явных дубликатов. | |
| > Примечание: строка `DID + " -- " + addr + " -- " + datetime + " -- " + nonce_string` всегда кодируется в UTF-8, | |
| > а `nonce_string` преобразуется к десятичной строке перед конкатенацией. | |
| --- | |
| ## 6. Безопасность | |
| * Для всех сообщений требуется **подпись отправителя** (в будущем: обязательная проверка). | |
| * Сообщения без подписи или с невалидным PoW могут игнорироваться. | |
| * В перспективе можно добавить шифрование трафика (например, на уровне TCP/TLS или QUIC). | |