Skip to content

Режим экстренного консенсуса

Режим экстренного консенсуса (введён в HF12) активируется автоматически, когда сеть простаивает в течение 1 часа. Специальный валидатор "committee" берёт на себя производство блоков для поддержания непрерывности цепочки до тех пор, пока реальные валидаторы не восстановят свои ключи подписи.


Ключевые константы

КонстантаЗначениеЗначение
CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC3600 сВремя простоя до активации
CHAIN_EMERGENCY_VALIDATOR_ACCOUNT"committee"Аккаунт экстренного производителя блоков
CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEYVIZ75CR...Детерминированный ключ подписи для экстренного режима
CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS21Последовательные блоки реальных валидаторов для выхода
CHAIN_IRREVERSIBLE_THRESHOLD75%Доля слотов расписания для выхода
CHAIN_MAX_VALIDATORS21Максимальное количество слотов валидаторов

Поля состояния в dynamic_global_property_object

ПолеПо умолчаниюЗначение
emergency_consensus_activefalseАктивен экстренный режим
emergency_consensus_start_block0Номер блока при активации

Активация

update_global_dynamic_data() выполняется при каждом применённом блоке и проверяет:

  1. Ворота HF12 — пропустить, если харфорк ещё не активирован.
  2. Уже активен — пропустить, если экстренный режим уже включён.
  3. LIB-блок доступен в block log — пропустить, если LIB-блок отсутствует в block log (DLT-узлы после восстановления из снимка имеют пустой block log; отсутствие LIB-блока вычислило бы миллионы устаревших секунд и запустило бы ложный дедлок активации).
  4. Таймаут — вычислить seconds_since_lib = current_block.timestamp − lib_block.timestamp. Если < 3600, пропустить.

Все проверки используют только временны́е метки, встроенные в блоки — никаких системных часов, никаких флагов пропуска. Это гарантирует идентичную детерминированную активацию на каждом узле, включая воспроизведения.

Последовательность активации

Когда пересекается порог таймаута:

  1. Установить dgp.emergency_consensus_active = true и dgp.emergency_consensus_start_block = block_num.
  2. Создать или обновить объект валидатора "committee":
    • signing_key = CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY
    • props = current_median_props
    • Голоса за харфорки установлены на текущую применённую версию (нейтральный голосователь).
  3. Отключить ВСЕХ реальных валидаторов: установить signing_key = zero, сбросить penalty_percent = 0, current_run = 0.
  4. Удалить все объекты witness_penalty_expire.
  5. Переопределить расписание валидаторов: все CHAIN_MAX_VALIDATORS слотов → "committee".
  6. Уведомить fork DB: _fork_db.set_emergency_mode(true) (включает детерминированное разрешение хэш-ничьих).
  7. Лог: "EMERGENCY CONSENSUS MODE activated at block #N. No blocks for X seconds since LIB Y."

Экстренная работа

Производство блоков — мастер vs. последователь

Узлы с настроенным emergency-private-key в config.ini являются экстренными мастерами; все остальные узлы — последователи.

ini
# Только для экстренного мастер-узла
emergency-private-key = 5Jzzz...   # приватный ключ CHAIN_EMERGENCY_VALIDATOR_ACCOUNT
РольПроверка DLT-синхронизацииПроверка minority forkПроизводство
МастерОбходится (привело бы к дедлоку)Пропускается (ожидается, что все блоки будут "нашими")Производит блоки для всех слотов
ПоследовательНормальная21-блочная проверка (1 полный раунд)Стандартное производство для собственных запланированных слотов

Детерминированное разрешение ничьих в fork DB

В экстренном режиме несколько мастеров (географическая избыточность) могут производить конкурирующие блоки на одной высоте с одним ключом. Порядок прибытия варьируется в зависимости от P2P-топологии.

fork_database::_push_block() разрешает ничьи:

item->num == _head->num И _emergency_consensus_active:
    item->id < _head->id  →  _head = item  (побеждает меньший хэш блока)
    иначе                 →  оставить текущую _head

Все узлы сходятся на одной вершине цепочки независимо от того, какой блок они увидели первым.

Продвижение LIB

update_last_irreversible_block() вычисляет LIB нормально, но ограничивает его значением HEAD − 1 в экстренном режиме. Без этого ограничения, поскольку все 21 слот принадлежат "committee" и committee.last_supported_block_num == HEAD, вычисление nth_element возвращает HEAD — что зафиксировало бы сессию отмены текущего блока в середине применения, вызывая необратимое повреждение.

После 3 блоков committee (CHAIN_IRREVERSIBLE_SUPPORT_MIN_RUN) LIB продвигается с каждым блоком, сохраняя окно fork DB небольшим.

Гибридное расписание валидаторов

update_witness_schedule() строит гибридное расписание в каждом раунде:

  • Слоты, где signing_key реального валидатора ненулевой: сохранить реального валидатора.
  • Пустые или нулевые слоты: заполнить "committee".

Это позволяет реальным валидаторам возвращаться постепенно. Каждый раз, когда реальный валидатор восстанавливает свой ключ подписи через validator_update_operation, следующее перестроение расписания включает его.

Committee исключается из подсчёта версий харфорков и вычисления медианных свойств цепочки (он копирует текущую медиану, поэтому учёт его на каждый слот исказил бы медиану).


Деактивация

Каждое перестроение расписания проверяет условие выхода:

real_witness_slots >= CHAIN_MAX_VALIDATORS × 75%

При 21 валидаторе: 21 × 0.75 = 15.75 → 15 слотов реальных валидаторов требуется.

При выполнении условия:

  1. dgp.emergency_consensus_active = false.
  2. _fork_db.set_emergency_mode(false).
  3. Лог: "EMERGENCY CONSENSUS MODE deactivated at block #N. R real validators active.".

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


Восстановление при запуске

database::open() проверяет расписание валидаторов после воспроизведения. Если какой-либо слот пустой (строка ""), экстренный режим был активен при остановке узла:

  1. Если emergency_consensus_active ещё не установлен → установить и установить флаг экстренного режима fork DB.
  2. Заполнить все слоты "committee".
  3. Лог: "schedule repaired, all N slots set to committee".

Это гарантирует, что расписание всегда согласовано после некорректного завершения во время экстренного режима.


Интеграция с Validator Guard

Плагин validator_guard продолжает работу во время экстренного режима и фактически становится более критичным:

  • Реальные валидаторы отключаются (ключ подписи обнуляется) при активации.
  • Защита валидатора автоматически транслирует validator_update_operation для восстановления ключа подписи каждого валидатора, как только на цепочке обнаруживается нулевой ключ.
  • Защита enable-stale-production в validator guard не блокирует восстановление ключей в экстренном режиме ("Экстренный консенсус обрабатывает собственное восстановление и восстановление ключей может по-прежнему требоваться").
  • Как только 15 валидаторов восстанавливают свои ключи, срабатывает условие выхода.

См. Защита валидатора.


Защиты P2P-взаимодействия

Несколько P2P-защитных механизмов знают об экстренном режиме:

МеханизмПоведение в экстренном режиме
resync_from_lib()Полностью пропускается — извлечение блоков вблизи LIB в экстренном режиме приведёт к сбою
check_wedge_watchdog()Отдельной ветки под экстренный режим нет. Взводится только когда голова заморожена далеко ниже подтверждённой вершины сети (вторая по высоте голова среди установленных пиров), любое продвижение головы сбрасывает таймер. Только лог, если не выставлен auto-resync-on-wedge = true — тогда подтверждённый клин завершает ноду
handle_block() (DLT, режим синхронизации, разрыв 0–2)Обрабатывается как нормальный (не синхронизация) для предотвращения нарушения цикла производства
Обнаружение зависшей синхронизации снимков (enable-stalled-sync-detection, по умолчанию выключено)Именно здесь и живёт разделение мастер/последователь. По таймауту читается emergency_consensus_active; если мы — экстренный мастер (держим экстренный ключ и комитет в расписании), соло-производство блоков нормально, и восстановление пропускается. Застрявшему последователю восстановление разрешено

Защита resync_from_lib() наиболее критична: в экстренном режиме LIB близок к HEAD. Извлечение блоков до LIB и сброс fork DB привели бы к тому, что блоки пиров из реальной сети связываются с повторно заполненным LIB, запуская переключение форка, извлечение ниже зафиксированного LIB и либо сбой, либо повреждение состояния.


Валидация блоков — ослабленное соответствие слот-валидатор

verify_signing_witness() обычно утверждает, что производитель блока точно совпадает с запланированным валидатором. В экстренном режиме:

Если block.validator != scheduled_witness:
    dlog("Emergency mode: accepting block from BW at slot scheduled for SW")
    → принять в любом случае (подпись по-прежнему проверяется по signing_key block.validator)

Это позволяет экстренным мастерам производить блоки, даже если несколько слотов в ожидающем расписании всё ещё назначены реальным валидаторам.


Совместимость со снимками

Поля экстренного состояния включены в снимки с совместимыми по умолчанию значениями:

  • emergency_consensus_active отсутствует в снимке → по умолчанию false.
  • emergency_consensus_start_block отсутствует → по умолчанию 0.

Снимки, созданные во время активного экстренного режима, корректно сохраняют состояние; снимки, созданные до HF12, импортируются как неэкстренные.


Сводка защитных механизмов

РасположениеМеханизм
1update_global_dynamic_dataАктивировать только при HF12 + не активен + LIB-блок доступен
2update_witness_scheduleГибридное переопределение + проверка выхода при ≥75% реальных валидаторов
3update_last_irreversible_blockОграничить LIB значением HEAD − 1 в экстренном режиме
4verify_signing_witnessОслабить соответствие слот-валидатор
5fork_db._push_blockДетерминированное разрешение хэш-ничьих
6maybe_produce_block (мастер)Обходить синхронизацию, устаревание, участие; пропускать minority fork
7maybe_produce_block (последователь)Сначала синхронизироваться; 21-блочная проверка изоляции
8resync_from_libПолностью пропускать в экстренном режиме
9check_wedge_watchdogВетки под экстренный режим нет; от ложного взвода защищают подтверждение вершины и сброс при продвижении головы
10handle_blockПочти догнавшие блоки обрабатываются как нормальные в DLT-экстренном режиме
11database::openИсправление расписания при запуске
12validator_guardНе подавлять восстановление ключей в экстренном режиме
13snapshot importСовместимая обработка полей
14update_witness_scheduleИсключать committee из подсчёта версий харфорков
15update_median_witness_propsИсключать committee из вычисления медианы

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

  1. Детерминированная активация — использует только временны́е метки в блоках; идентична на каждом узле и при каждом воспроизведении.
  2. Безопасность DLT-снимка — пропускает активацию если LIB-блок отсутствует в block log.
  3. Неизменность экстренного форкаresync_from_lib() отказывается выполняться в экстренном режиме.
  4. Различие мастер/последователь — только узлы с --emergency-private-key являются мастерами.
  5. Сходимость fork DB — детерминированное разрешение хэш-ничьих гарантирует, что все узлы выбирают один блок.
  6. Безопасность LIB — ограничен значением HEAD − 1 для сохранения защиты отмены.
  7. Нейтральное голосование committee — committee голосует за текущую применённую версию харфорка, копирует медианные props.

См. также: Fair-DPOS, Разрешение форков, Узел-валидатор, Защита валидатора.