Запуск верификатора
Верификатор (verifier) — это узел только для чтения, который воспроизводит каждый блок, полученный от производителя, на собственной локальной копии состояния. Если корень состояния (state root) или хеш блока (block hash) не совпадают, блок отклоняется, и узел прекращает обслуживание запросов. Отдельный верификатор предоставляет RPC только для чтения. Кошельки отправляют подписанные транзакции через публичный RPC POA для записи; верификатор такие запросы не пересылает.
В этой статье рассматривается практическая сторона вопроса: конфигурация, процесс запуска, выполняемые проверки и поведение при сбоях. Криптографические детали state roots и block hash подробно описаны в разделе State roots.
Конфигурация
Заголовок раздела «Конфигурация»Для работы верификатора требуются два изменения в конфигурации по сравнению с производителем:
config :chain, mode: :verifier,Параметр mode: :verifier изменяет набор компонентов, которые запускаются при старте узла:
| Компонент | Производитель (Producer) | Верификатор (Verifier) |
|---|---|---|
| Инициализация генезиса | Да | Нет (получает от upstream) |
| Производитель блоков | Да | Нет |
| Пул транзакций | Да | Нет |
| Синхронизатор верификатора | Нет | Да |
RPC (eth_*, /wallet/*) | Чтение и отправка транзакций | Только чтение |
| BlockFeed (история блоков) | Да | Да |
| BlockStage (рассылка новых блоков) | Да | Да |
Верификатору требуется собственная база данных. Общего хранилища с производителем не существует.
Порядок запуска
Заголовок раздела «Порядок запуска»При первом запуске с пустой базой данных процесс выглядит следующим образом:
- Синхронизатор (syncer) подключается к upstream-узлу через Erlang distribution.
- Он запрашивает у upstream-узла текущий указатель (tip), затем скачивает блоки пакетами, начиная со своего последнего известного блока, и проверяет каждый из них (догоняет цепь).
- Как только синхронизация завершена, верификатор подписывается на поток новых блоков (block feed).
- Для каждого последующего нового блока верификатор воспроизводит все транзакции, пересчитывает state root и block hash, и фиксирует блок только при совпадении обоих значений.
- Блоки, которые уже были зафиксированы на этапе catch-up, пропускаются по номеру.
Если upstream-узел недоступен при запуске, синхронизатор повторяет попытку подключения каждые 5 секунд.
Что проверяет верификатор
Заголовок раздела «Что проверяет верификатор»Для каждого блока (включая генезис) верификатор независимо проверяет следующие параметры:
| Проверка | Что предотвращает |
|---|---|
| state_root | Производитель записал состояние, которое не соответствует результатам выполнения транзакций. Охватывает балансы, HTLC-свопы, регистрации прекомпайлов и bridge_mints. |
| transactions_root | Производитель подменил, добавил или удалил транзакции из блока. |
| block_hash | Любое поле в заголовке блока было изменено после его формирования. |
| parent_hash | Блок некорректно ссылается на предыдущий. Это позволяет обнаруживать форки (разветвления цепи). |
| block_number | Наличие пропусков в последовательности (пропущенные блоки). |
| timestamp блока | Блок, помеченный временем раньше своего родителя. Timestamp-метки монотонны: производитель подтягивает их вперёд при обратном скачке часов, поэтому регрессия timestamp означает расходящийся или скомпрометированный upstream. |
| chain_id | Повторное исполнение в другой сети (транзакция была подписана для другой сети). |
| Восстановление отправителя | Для Ethereum-транзакций адрес отправителя заново вычисляется из подписи. Для Tron-транзакций подпись перепроверяется на соответствие заявленному отправителю. |
| Инварианты генезиса | Timestamp и transactions root генезис-блока должны совпадать с каноническими константами. Это предотвращает подделку генезиса. |
| Cross-chain проверка моста | Каждая строка bridge_mints независимо проверяется по финализированному состоянию 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).
Все методы чтения работают в штатном режиме: eth_getBalance, eth_getTransactionReceipt, eth_getBlockByNumber, /wallet/getaccount и другие. Кошельки и обозреватели блоков (explorers) могут подключаться к верификатору для чтения проверенных данных.
Цепочки верификаторов
Заголовок раздела «Цепочки верификаторов»Каждый верификатор ретранслирует проверенные блоки через свой собственный block feed. Второй верификатор может подписаться на этот поток вместо потока производителя:
# Верификатор B подключается к Верификатору A, а не к производителюconfig :chain, mode: :verifier,Каждый верификатор воспроизводит каждый блок самостоятельно, независимо от источника его получения. Модель безопасности остаётся неизменной: сначала проверь, потом обслуживай.
Producer ──▶ Верификатор A ──▶ Верификатор C ▶ Верификатор B ──▶ Верификатор DСетевые требования
Заголовок раздела «Сетевые требования»Производитель и верификатор взаимодействуют через Erlang distribution. Используются два порта, оба должны быть защищены файрволом:
- EPMD (по умолчанию порт 4369): Erlang Port Mapper Daemon.
- Distribution port (настраивается через
inet_dist_listen_minиinet_dist_listen_maxвvm.argsили конфигурации ядра): канал передачи данных; настройте TLS для его защиты. Переменная окруженияRELEASE_DISTRIBUTIONуправляет режимом дистрибуции (name/sname/none), а не портом.
Публичный edge направляет подписанные записи в API записи производителя. Отдельный верификатор обслуживает чтение; Erlang distribution и admin API остаются закрытыми. Настройте маршруты по роли узла.
В аргументах VM релиза задайте -proto_dist inet_tls (или inet6_tls) и -ssl_dist_optfile с сертификатами, доверенными центрами сертификации и проверкой сертификата другой стороны. TLS должен быть настроен на всех участвующих узлах. Следуйте инструкции Erlang по TLS distribution и проверьте шифрование и аутентификацию соединения до подключения узла к другим участникам. Порты EPMD, distribution и администрирования оставьте закрытыми.
Сценарии отказов
Заголовок раздела «Сценарии отказов»| Сценарий | Поведение верификатора |
|---|---|
| Upstream недоступен при запуске | Повторная попытка catch-up каждые 5 секунд. |
| Upstream-узел отключился в процессе работы | Новые события прекращают поступать; повторное подключение и синхронизация истории при восстановлении связи. |
| Пропуск блоков (gap) | Автоматический catch-up через BlockFeed upstream-узла. |
Расхождение state_root | Остановка. В логи записывается критическое предупреждение. RPC перестает отвечать. |
Расхождение block_hash | Остановка. В логи записывается критическое предупреждение. RPC перестает отвечать. |
| Отсутствие (nil) сырых данных транзакции в блоке | Транзакция записывается как неуспешная (status 0), без падения узла. |
| Повторное исполнение сделало бы баланс отрицательным или дробным | Блок не фиксируется. Верификатор перезапускается и останавливается на этом блоке, пока оператор не вмешается. Это расхождение с производителем. |
Остановленный верификатор требует ручного вмешательства и анализа. Расхождение означает, что либо производитель скомпрометирован, либо в исполнителе присутствует баг детерминизма. В обоих случаях необходимо участие инженера.
Производитель применяет то же правило для балансов. Если исполнение блока сделало бы баланс отрицательным или дробным, он ничего не фиксирует, прекращает выпуск блоков и сообщает о неисправности, пока оператор его не перезапустит.