Запуск верификатора
Верификатор это read-only нода, которая воспроизводит каждый блок от producer на своей копии состояния. Если state root или block hash не совпадают, блок отклоняется, нода перестаёт обслуживать запросы. Пользователи и кошельки подключаются к RPC верификатора, а не напрямую к producer.
Статья про практическую сторону: конфигурация, запуск, что проверяется и что происходит при отказе. Криптографические детали state roots и block hash описаны в разделе State roots.
Конфигурация
Заголовок раздела «Конфигурация»Верификатору нужны два изменения в конфиге по сравнению с producer:
config :chain, mode: :verifier, upstream_node: :"producer@10.0.0.1"mode: :verifier меняет набор компонентов, которые стартуют при загрузке:
| Компонент | Producer | Верификатор |
|---|---|---|
| Genesis-инициализация | Да | Нет (получает от upstream) |
| Block producer | Да | Нет |
| Transaction pool | Да | Нет |
| Verifier syncer | Нет | Да |
| RPC (eth_, /wallet/) | Внутренний | Публичный |
| BlockFeed (история блоков) | Да | Да |
| BlockStage (live broadcast) | Да | Да |
Верификатору нужна своя база данных. Общего хранилища с producer нет.
Порядок запуска
Заголовок раздела «Порядок запуска»При первом старте с пустой базой:
- Syncer подключается к upstream-ноде через Erlang distribution.
- Подписывается на live block feed, затем начинает догонять с блока 0.
- Для genesis (блок 0) верификатор независимо создаёт те же начальные аккаунты и вычисляет ожидаемый state root. Сравнивает с genesis-блоком, полученным от upstream. Если совпадают, genesis фиксируется локально.
- Для каждого следующего блока верификатор воспроизводит все транзакции, пересчитывает state root и block hash, фиксирует блок только при совпадении обоих.
- После догона верификатор обрабатывает live-блоки по мере их поступления. Блоки, уже зафиксированные во время catch-up, пропускаются по номеру.
Если upstream недоступен при старте, syncer повторяет попытку каждые 5 секунд.
Что проверяет верификатор
Заголовок раздела «Что проверяет верификатор»Для каждого блока (включая genesis) верификатор независимо проверяет:
| Проверка | Что ловит |
|---|---|
| state_root | Producer записал состояние, не следующее из транзакций. Охватывает балансы, HTLC swaps, регистрации precompile. |
| transactions_root | Producer подменил, добавил или удалил транзакции из блока. |
| block_hash | Любое поле в header блока изменено после построения. |
| parent_hash | Блок некорректно продолжает предыдущий. Обнаружение форков. |
| block_number | Пропуски в последовательности (пропущенные блоки). |
| chain_id | Cross-chain replay (транзакция подписана для другой сети). |
| Восстановление sender | Для Ethereum-транзакций sender заново выводится из подписи. Для Tron-транзакций подпись перепроверяется против заявленного sender. |
| Genesis invariants | Timestamp и transactions root genesis совпадают с каноническими константами. Предотвращает подделку genesis. |
| Bridge cross-chain check | Каждая строка bridge_mints независимо проверяется по finalized Ethereum-стейту через JSON-RPC. Для строк bridge_lock адрес receiverOn2D из Ethereum-события Locked сравнивается с получателем HTLC на 2D. Полная таблица проверок в статье Мост. |
Расхождение в state_root, transactions_root или block_hash означает нарушение консенсуса. Верификатор останавливается и перестаёт обслуживать запросы. Операционные ошибки (upstream временно недоступен, пропуск в блоках) запускают повторный catch-up.
Режим верификатора и RPC
Заголовок раздела «Режим верификатора и RPC»Верификатор отклоняет RPC-вызовы, изменяющие состояние:
eth_sendRawTransactionвозвращает код ошибки-32601/wallet/broadcasttransactionвозвращает Tron-ошибкуOTHER_ERROR(код 20)
Все read-only методы работают штатно: eth_getBalance, eth_getTransactionReceipt, eth_getBlockByNumber, /wallet/getaccount и др. Кошельки и explorers могут подключаться к верификатору без изменений.
Цепочки верификаторов
Заголовок раздела «Цепочки верификаторов»Каждый верификатор ретранслирует проверенные блоки на своём block feed. Второй верификатор может подписаться на него вместо producer:
# Верификатор B подключается к Верификатору A, не к producerconfig :chain, mode: :verifier, upstream_node: :"verifier_a@10.0.0.2"Каждый верификатор воспроизводит каждый блок независимо, откуда бы он его ни получил. Модель безопасности одна: проверь, потом обслуживай.
Producer ──▶ Верификатор A ──▶ Верификатор C ▶ Верификатор B ──▶ Верификатор DСетевые требования
Заголовок раздела «Сетевые требования»Producer и верификатор связаны через Erlang distribution. Два порта, оба за файрволом:
- EPMD (4369 по умолчанию): Erlang Port Mapper Daemon.
- Distribution port (настраивается в
vm.argsилиRELEASE_DISTRIBUTION): канал данных, шифрование TLS.
У producer нет публичных HTTP-портов. Весь пользовательский трафик идёт через верификатор.
Пользователи ──▶ Верификатор (порт 4000, публичный) ──▶ Producer (только Erlang dist, файрвол)Сценарии отказа
Заголовок раздела «Сценарии отказа»| Сценарий | Поведение верификатора |
|---|---|
| Upstream недоступен при старте | Повторная попытка catch-up каждые 5 секунд |
| Upstream отключился в процессе | Live-события прекращаются; reconnect и catch-up при восстановлении |
| Пропуск блоков (gap) | Автоматический catch-up через upstream BlockFeed |
| Расхождение state_root | Остановка. Критический алерт в логах. RPC перестаёт отвечать. |
| Расхождение block_hash | Остановка. Критический алерт в логах. RPC перестаёт отвечать. |
| Nil raw-данные транзакции | Транзакция записывается как неуспешная (status 0), без краша |
Остановленный верификатор требует ручного разбора. Расхождение означает, что либо producer скомпрометирован, либо в executor есть баг детерминизма. В обоих случаях нужен человек.