|
# 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). |
|
|