P2P синхронизация¶
Как работает P2P сеть и синхронизация блоков в ComputeChain.
Архитектура P2P¶
Компоненты¶
- P2P Node: Управляет соединениями с пирами
- Protocol: Формат сообщений (handshake, блоки, транзакции)
- Sync Engine: Логика синхронизации блоков
- Mempool: Распространение транзакций
Протокол сообщений¶
Типы сообщений¶
class P2PMessageType(str, Enum):
HANDSHAKE = "handshake"
NEW_BLOCK = "new_block"
NEW_TX = "new_tx"
GET_BLOCKS = "get_blocks" # Запрос синхронизации
BLOCKS_RESPONSE = "blocks_response" # Ответ с блоками
Handshake¶
При подключении:
HandshakePayload(
node_id="node-123",
p2p_port=9000,
protocol_version=1,
network="devnet",
best_height=15,
best_hash="0x1234...",
genesis_hash="0xabcd..."
)
Проверки:
networkдолжен совпадатьgenesis_hashдолжен совпадатьprotocol_versionдолжен быть совместим
Новый блок¶
Распространение:
Процесс:
- Валидатор производит блок
- Блок подписывается
- Блок рассылается всем пирам
- Пиры проверяют и добавляют блок
Новая транзакция¶
Распространение:
Процесс:
- Транзакция попадает в мемпул
- Рассылается всем пирам
- Пиры добавляют в свой мемпул
- Валидатор включает в блок
Синхронизация блоков¶
Режимы синхронизации¶
SYNCING:
- Нода отстаёт от сети
- Не производит новые блоки
- Запрашивает блоки у пиров
SYNCED:
- Нода синхронизирована
- Может производить блоки (если валидатор)
- Принимает новые блоки от пиров
Процесс синхронизации¶
Шаг 1: Определение отставания
local_height = chain.height
peer_best_height = handshake.best_height
if peer_best_height > local_height:
# Нужна синхронизация
sync_state = SyncState.SYNCING
Шаг 2: Запрос блоков
Шаг 3: Получение блоков
BlocksResponsePayload(
blocks=[
{"header": {...}, "txs": [...]},
{"header": {...}, "txs": [...]},
...
]
)
Шаг 4: Применение блоков
for block_data in blocks:
block = Block.model_validate(block_data)
if chain.add_block(block):
# Блок добавлен успешно
continue
else:
# Ошибка валидации
break
Batch синхронизация¶
Оптимизация:
- Блоки запрашиваются пачками (например, по 100)
- Уменьшает количество сообщений
- Ускоряет синхронизацию
Пример:
# Запрос блоков пачками
batch_size = 100
for start in range(local_height + 1, peer_best_height, batch_size):
end = min(start + batch_size - 1, peer_best_height)
blocks = request_blocks(start, end)
apply_blocks(blocks)
Fork Resolution¶
Обнаружение форка¶
Признаки:
prev_hashблока не совпадает с локальнымlast_hash- Обнаружена более длинная валидная цепь
Процесс:
if block.prev_hash != chain.last_hash:
# Возможен форк
if len(peer_chain) > len(local_chain):
# Откат локальной цепи
rollback_to_height(fork_height)
# Загрузка правильной ветки
load_blocks_from_peer(peer_chain)
Rollback¶
Что происходит:
- Определение точки форка
- Откат локальных блоков до точки форка
- Откат стейта до точки форка
- Загрузка правильной ветки от пира
- Применение блоков правильной ветки
Пример:
Локальная цепь: [0] -> [1] -> [2] -> [3] -> [4]
Правильная цепь: [0] -> [1] -> [2'] -> [3'] -> [4'] -> [5']
Форк на высоте 2:
1. Откат блоков 2, 3, 4
2. Загрузка блоков 2', 3', 4', 5'
3. Применение блоков
Управление пирами¶
Добавление пиров¶
При запуске:
Автоматически:
- При handshake новый пир добавляется в список
- Сохраняется в
peers.json
Сохранение пиров¶
Файл: peers.json
Формат:
Обновление:
- При подключении нового пира
- При отключении пира (периодическая очистка)
- При завершении ноды (сохранение текущего списка)
Переподключение¶
Автоматическое:
- При запуске нода пытается подключиться к сохранённым пирам
- При отключении пира нода пытается переподключиться
- Экспоненциальная задержка между попытками
Примеры¶
Пример 1: Синхронизация новой ноды¶
1. Нода запускается с genesis
2. Подключается к пиру (best_height=100)
3. Определяет отставание: local=0, peer=100
4. Запрашивает блоки 1-100 пачками по 50
5. Применяет блоки последовательно
6. После синхронизации переходит в режим SYNCED
Пример 2: Обнаружение форка¶
1. Нода получает блок с prev_hash != last_hash
2. Запрашивает цепь у пира
3. Обнаруживает более длинную цепь
4. Откатывает локальные блоки
5. Загружает правильную ветку
6. Продолжает синхронизацию
Troubleshooting¶
Проблема: Нода не синхронизируется¶
Причины:
- Нет подключений к пирам
- Пиры недоступны
- Firewall блокирует соединения
Решение:
# Проверить подключения
cat .node_a/peers.json
# Проверить доступность пира
telnet 192.168.1.100 9000
# Добавить пиры вручную
./run_node.py --datadir .node_a run --peers 192.168.1.100:9000
Проблема: Постоянные форки¶
Причины:
- Неправильное время (time drift)
- Разные genesis
- Баги в консенсусе
Решение:
# Синхронизировать время
sudo ntpdate -s time.nist.gov
# Проверить genesis
cat .node_a/genesis.json | jq '.header'
# Пересобрать стейт
./run_node.py --datadir .node_a run --rebuild-state
Следующие шаги¶
- Run Local — Запуск локальной ноды
- Configuration — Настройка параметров
- CLI Guide — Использование CLI