Перейти к содержимому

Запуск верификатора

Верификатор это read-only нода, которая воспроизводит каждый блок от producer на своей копии состояния. Если state root или block hash не совпадают, блок отклоняется, нода перестаёт обслуживать запросы. Пользователи и кошельки подключаются к RPC верификатора, а не напрямую к producer.

Статья про практическую сторону: конфигурация, запуск, что проверяется и что происходит при отказе. Криптографические детали state roots и block hash описаны в разделе State roots.

Верификатору нужны два изменения в конфиге по сравнению с producer:

config/runtime.exs
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 нет.

При первом старте с пустой базой:

  1. Syncer подключается к upstream-ноде через Erlang distribution.
  2. Подписывается на live block feed, затем начинает догонять с блока 0.
  3. Для genesis (блок 0) верификатор независимо создаёт те же начальные аккаунты и вычисляет ожидаемый state root. Сравнивает с genesis-блоком, полученным от upstream. Если совпадают, genesis фиксируется локально.
  4. Для каждого следующего блока верификатор воспроизводит все транзакции, пересчитывает state root и block hash, фиксирует блок только при совпадении обоих.
  5. После догона верификатор обрабатывает live-блоки по мере их поступления. Блоки, уже зафиксированные во время catch-up, пропускаются по номеру.

Если upstream недоступен при старте, syncer повторяет попытку каждые 5 секунд.

Для каждого блока (включая genesis) верификатор независимо проверяет:

ПроверкаЧто ловит
state_rootProducer записал состояние, не следующее из транзакций. Охватывает балансы, HTLC swaps, регистрации precompile.
transactions_rootProducer подменил, добавил или удалил транзакции из блока.
block_hashЛюбое поле в header блока изменено после построения.
parent_hashБлок некорректно продолжает предыдущий. Обнаружение форков.
block_numberПропуски в последовательности (пропущенные блоки).
chain_idCross-chain replay (транзакция подписана для другой сети).
Восстановление senderДля Ethereum-транзакций sender заново выводится из подписи. Для Tron-транзакций подпись перепроверяется против заявленного sender.
Genesis invariantsTimestamp и 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-вызовы, изменяющие состояние:

  • 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, не к producer
config :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 есть баг детерминизма. В обоих случаях нужен человек.