Skip to content

Плагин валидатора

Плагин валидатора отвечает за подписание и производство блоков. Он запускает выделенный цикл таймера с интервалом 250 мс в собственном потоке ОС, выполняет ряд проверок безопасности при каждом тике и вызывает database::generate_block() при выполнении всех условий.

Источник: plugins/validator/validator.cpp


Зависимости

chain::plugin, p2p::p2p_plugin, snapshot::snapshot_plugin

Конфигурация

Производство блоков

ПараметрПо умолчаниюОписание
validator / -wИмя/имена аккаунтов-валидаторов; может повторяться
private-keyWIF-ключ(и) для подписи; может повторяться
emergency-private-keyWIF-ключ для экстренного консенсуса; автоматически добавляет CHAIN_EMERGENCY_VALIDATOR_ACCOUNT в набор валидаторов
enable-stale-productionfalseОбход проверок участия и синхронизации (только для тестовой сети / восстановления сети)
required-participation3300Минимальное участие валидаторов в базисных пунктах (3300 = 33%)
fork-collision-timeout-blocks21Количество последовательных отсрочек при коллизии форков перед принудительным производством (один полный раунд валидаторов)

Синхронизация NTP

ПараметрПо умолчаниюОписание
ntp-serverpool.ntp.org, time.google.com, time.cloudflare.comNTP-серверы; может повторяться
ntp-request-interval900Нормальный интервал синхронизации в секундах
ntp-retry-interval300Интервал повторной попытки при отсутствии ответа NTP
ntp-round-trip-threshold150Отбрасывать ответы NTP с временем roundtrip > N мс
ntp-history-size5Окно скользящего среднего для сглаживания дельты NTP

Отладка

ПараметрПо умолчаниюОписание
debug-block-productionfalseВключить подробное отладочное логирование в базе данных цепочки

Таймер производства

Цикл производства работает на выделенном production_io_service_ и собственном потоке ОС — полностью отдельно от общего io_service AppBase/P2P. Это предотвращает задержку 250-мс обратного вызова таймера из-за P2P-активности (отключение пиров, TLS-рукопожатия, опустошение очереди отправки).

Выравнивание тика:

тик таймера каждые 250 мс по границам 250 мс настенных часов
минимальная задержка: 50 мс (для поглощения джиттера ОС)

Упреждение: now = ntp_time + 250 мс — сдвигает решение о производстве вперёд так, чтобы тик на T_slot - 250 мс совпадал точно с границей слота:

Слот в T=6.000 с:
  Тик в T=5.750 → now=6.000 → слот совпал → производство при lag=0 мс
  Тик в T=6.000 → now=6.250 → lag=250 мс → ещё в пределах порога 500 мс

Пропуск при задержке: После результата lag тот же слот повторно срабатывал бы при каждом тике в оставшееся время 3-секундного интервала. Защита пропускает до следующей границы слота, чтобы цикл освобождал CPU вместо холостого вращения.


maybe_produce_block() — последовательность проверок безопасности

Следующие проверки выполняются по порядку при каждом тике, где slot > 0.

ПроверкаРезультат при отказе
1Шлюз синхронизации DLT (только DLT-режим): chain().is_syncing() = false, или узел является экстренным мастеромnot_synced
2Шлюз паузы снапшота: snapshot().is_snapshot_in_progress() = falsenot_synced
3Шлюз нагона P2P: p2p().is_catching_up_after_pause() = falsenot_synced
4Трёхсостояние безопасности HF12 (см. ниже)not_synced / low_participation
5slot = db.get_slot_at_time(now) > 0not_time_yet
6Запланированный валидатор входит в наш настроенный наборnot_my_turn
7Слот ещё не заполнен (scheduled_time > head_block_time)not_time_yet
8signing_key валидатора в блокчейне ненулевойnot_my_turn
9Приватный ключ для signing_key загруженno_private_key
10До HF12: участие ≥ порогаlow_participation
11`scheduled_time - now
12Проверка коллизии форков (см. ниже)fork_collision
13Вторая проверка паузы снапшота (окно гонки)not_time_yet
14db.generate_block() + p2p().broadcast_block()produced

Трёхсостояние безопасности HF12 (проверка №4)

Активен экстренный консенсус:

  • Экстренный мастер (имеет emergency-private-key + «committee» в расписании): продолжает безусловно.
  • Ведомый: требует get_slot_time(1) >= now (цепочка не устарела) перед производством.

Нормальный режим (HF12+):

  • Участие ≥ 33%: здоровая сеть; проверка синхронизации через get_slot_time(1).
  • Участие < 33%: ослабленная сеть; применяется порог участия vs required-participation.
  • enable-stale-production=true: обходит обе проверки участия и синхронизации.

До HF12: Простая проверка синхронизации через get_slot_time(1).

Разрешение коллизии форков (проверка №12)

При наличии конкурирующего блока на head_block_num + 1:

  1. Сравнение по весу голосов (HF12+): compare_fork_branches() вычисляет суммарные SHARES, делегированные каждой ветви. Если наша ветвь тяжелее — продолжить и удалить конкурирующий блок. При равенстве или меньшем весе — отложить.
  2. Таймаут зависшей головы: После fork-collision-timeout-blocks последовательных отсрочек (по умолчанию 21 = 63 секунды) конкурирующий блок удаляется и производство возобновляется. Это обрабатывает «мёртвые» блоки форка от отключённых пиров.

Экстренный режим: Любой конкурирующий блок вызывает отсрочку; путь взвешивания голосов не используется.


Обнаружение minority fork

Перед каждой попыткой производства (после проверок безопасности HF12) плагин просматривает последние 21 блок в fork_db. Если все 21 были произведены собственными настроенными валидаторами узла — узел изолирован на minority fork.

  • Действие по умолчанию: Вызов p2p().resync_from_lib() — откат блоков к LIB, сброс fork DB, повторная инициация синхронизации P2P, переподключение к начальным узлам. Возвращает minority_fork.
  • С enable-stale-production=true: Запись предупреждения, продолжение производства.
  • Пропускается при: Активном экстренном консенсусе (блоки комитета всегда соответствовали бы нашему настроенному набору). В экстренном режиме вместо него используется специфичная для DLT проверка изоляции ведомого.

Обнаружение остановки NTP

Если get_slot_at_time(now) возвращает 0 (NTP отстаёт от времени цепочки), счётчик _slot_zero_streak увеличивается:

СерияВремяДействие
3~750 мсПредупреждение
10~2,5 сПринудительная повторная синхронизация NTP
60~15 сПредупреждение о длительной остановке
120~30 сКритическая ошибка

Счётчик сбрасывается при любом ненулевом результате слота.


Watchdog производства

Если узел хотя бы раз производил блок и should_be_producing = true (определяется из активного состояния цепочки: участие ≥ 33% или активен экстренный консенсус с нашим ключом), но ни одного блока не было произведено в течение:

  • Экстренный мастер: 60 секунд
  • Обычный валидатор: 180 секунд

Watchdog срабатывает каждые 30 секунд и записывает диагностику. При выполнении условий восстановления (голова продвигалась в последние 30 с, не синхронизируется, есть соединения с пирами, ненулевые ключи подписи в блокчейне) он принудительно сбрасывает блокирующие условия:

  1. Сбрасывает флаг _minority_fork_recovering.
  2. Вызывает p2p().clear_catchup_flag() — сбрасывает флаг нагона P2P после паузы.
  3. Вызывает chain().clear_syncing() — сбрасывает флаг синхронизации цепочки.

Производство автоматически возобновляется при следующем тике.


on_block_applied() — обработчик сигнала

Подключён к database::applied_block. Запускается для каждого входящего блока.

Обнаружение пропущенного слота

Когда block_num > prev_num + 1 (разрыв в потоке блоков), обработчик определяет, был ли наш валидатор запланирован для любого из пропущенных слотов, и записывает полное диагностическое состояние (флаги производства, смещение NTP, статус синхронизации, статус ключа подписи, время следующего слота).

Обнаружение перехвата слота (DLT-режим экстренного консенсуса)

При активном экстренном консенсусе экстренный мастер может обнулить ключ подписи нашего валидатора и производить блоки комитета в наших запланированных слотах. Обработчик отслеживает это через _slot_hijack_count. Сбрасывается, когда один из наших валидаторов производит блок.


Публичный API

is_witness_scheduled_soon()

Возвращает true, если локально управляемый валидатор запланирован для производства в следующих 4 слотах (~12 секунд). Плагин снапшота вызывает это перед планированием снапшота, чтобы отложить его при неминуемом производстве.

is_emergency_master()

Возвращает true, когда:

  1. Настроен emergency-private-key (CHAIN_EMERGENCY_VALIDATOR_ACCOUNT в _validators).
  2. Аккаунт «committee» находится в текущем расписании валидаторов.

Только узлы, где выполняются оба условия, должны производить блоки в одиночку в экстренном режиме; остальные являются ведомыми и должны сначала синхронизироваться.

is_emergency_key_configured()

Возвращает true, если настроен emergency-private-key, независимо от текущего расписания. Используется в сообщениях приветствия P2P (поле has_emergency_key).

get_production_diagnostics()

Возвращает компактную диагностическую строку:

validator[skip_flags=0x0 catching_up=0 head=#79881136 last_prod=45s_ago minority_rcv=0 slot_hijacks=0]

Включается в логи стагнации P2P FORWARD, когда узел завис без пиров впереди.


Ключевые инварианты

  1. Никогда не производить в DLT-режиме при синхронизации — создаёт блоки на устаревшей голове, вызывая осцилляцию форков.
  2. Никогда не производить во время создания снапшота — взаимная блокировка записи.
  3. Никогда не производить, если слот уже заполнен — создаёт мини-форк.
  4. Экстренный мастер должен всегда производить — он единственный производитель блоков; ожидание вызовет дедлок.
  5. Ведомые должны синхронизироваться перед производством в экстренном режиме — производство на устаревшей голове = minority fork.
  6. Участие < 33% останавливает производство — защита от сетевого раздела (переопределяемая).
  7. 21 последовательный блок от собственных валидаторов → откат к LIB — восстановление после minority fork.
  8. Все чтения базы данных свежие — кэширование состояния отсутствует; экстренный режим может активироваться/деактивироваться на каждом блоке.

Устранение неполадок

СимптомЧто проверить
Логи not_syncedАктивна синхронизация DLT или создание снапшота — ждать; watchdog сбросит автоматически при зависании
Повторное not_time_yetNTP отстаёт от времени цепочки; проверьте предупреждения _slot_zero_streak и смещение NTP
not_my_turn на нашем слотеКлюч подписи обнулён в блокчейне; отправьте validator_update_operation для восстановления
no_private_keyВ конфиге отсутствует private-key для ключа подписи, зарегистрированного в блокчейне
low_participationУчастие сети < 33%; проверьте подключение к пирам или установите enable-stale-production=true
fork_collisionКонкурирующий блок на следующей высоте; ждать разрешения по весу голосов или таймаута 21 отсрочки
minority_forkИзолирован; плагин автоматически пересинхронизируется с LIB
Watchdog срабатывает повторноФлаг синхронизации или нагона завис; watchdog сбросит автоматически при продвижении головы
Логи SLOT-HIJACKЭкстренный мастер обнулил наш ключ; восстановите через validator_update_operation

См. также: Validator Guard, Fair-DPOS, Экстренный консенсус, Обработка блоков.