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 в экстренном режиме приведёт к сбою
stale_sync_check_task()Если голова мастера продвигается → сбросить таймер, пропустить восстановление; если голова последователя застряла → разрешить восстановление
handle_block() (DLT, режим синхронизации, разрыв 0–2)Обрабатывается как нормальный (не синхронизация) для предотвращения нарушения цикла производства
Обнаружение зависшей синхронизации снимковТа же логика, что и проверка зависшей синхронизации

Защита 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Полностью пропускать в экстренном режиме
9stale_sync_check_taskПропустить если голова мастера продвигается; разрешить если последователь застрял
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, Разрешение форков, Узел-валидатор, Защита валидатора.