Режим экстренного консенсуса
Режим экстренного консенсуса (введён в HF12) активируется автоматически, когда сеть простаивает в течение 1 часа. Специальный валидатор "committee" берёт на себя производство блоков для поддержания непрерывности цепочки до тех пор, пока реальные валидаторы не восстановят свои ключи подписи.
Ключевые константы
| Константа | Значение | Значение |
|---|---|---|
CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC | 3600 с | Время простоя до активации |
CHAIN_EMERGENCY_VALIDATOR_ACCOUNT | "committee" | Аккаунт экстренного производителя блоков |
CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY | VIZ75CR... | Детерминированный ключ подписи для экстренного режима |
CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS | 21 | Последовательные блоки реальных валидаторов для выхода |
CHAIN_IRREVERSIBLE_THRESHOLD | 75% | Доля слотов расписания для выхода |
CHAIN_MAX_VALIDATORS | 21 | Максимальное количество слотов валидаторов |
Поля состояния в dynamic_global_property_object
| Поле | По умолчанию | Значение |
|---|---|---|
emergency_consensus_active | false | Активен экстренный режим |
emergency_consensus_start_block | 0 | Номер блока при активации |
Активация
update_global_dynamic_data() выполняется при каждом применённом блоке и проверяет:
- Ворота HF12 — пропустить, если харфорк ещё не активирован.
- Уже активен — пропустить, если экстренный режим уже включён.
- LIB-блок доступен в block log — пропустить, если LIB-блок отсутствует в block log (DLT-узлы после восстановления из снимка имеют пустой block log; отсутствие LIB-блока вычислило бы миллионы устаревших секунд и запустило бы ложный дедлок активации).
- Таймаут — вычислить
seconds_since_lib = current_block.timestamp − lib_block.timestamp. Если< 3600, пропустить.
Все проверки используют только временны́е метки, встроенные в блоки — никаких системных часов, никаких флагов пропуска. Это гарантирует идентичную детерминированную активацию на каждом узле, включая воспроизведения.
Последовательность активации
Когда пересекается порог таймаута:
- Установить
dgp.emergency_consensus_active = trueиdgp.emergency_consensus_start_block = block_num. - Создать или обновить объект валидатора "committee":
signing_key = CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEYprops = current_median_props- Голоса за харфорки установлены на текущую применённую версию (нейтральный голосователь).
- Отключить ВСЕХ реальных валидаторов: установить
signing_key = zero, сброситьpenalty_percent = 0,current_run = 0. - Удалить все объекты
witness_penalty_expire. - Переопределить расписание валидаторов: все
CHAIN_MAX_VALIDATORSслотов → "committee". - Уведомить fork DB:
_fork_db.set_emergency_mode(true)(включает детерминированное разрешение хэш-ничьих). - Лог:
"EMERGENCY CONSENSUS MODE activated at block #N. No blocks for X seconds since LIB Y."
Экстренная работа
Производство блоков — мастер vs. последователь
Узлы с настроенным emergency-private-key в config.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 слотов реальных валидаторов требуется.
При выполнении условия:
dgp.emergency_consensus_active = false._fork_db.set_emergency_mode(false).- Лог:
"EMERGENCY CONSENSUS MODE deactivated at block #N. R real validators active.".
Сеть возобновляет нормальную работу на следующем цикле расписания.
Восстановление при запуске
database::open() проверяет расписание валидаторов после воспроизведения. Если какой-либо слот пустой (строка ""), экстренный режим был активен при остановке узла:
- Если
emergency_consensus_activeещё не установлен → установить и установить флаг экстренного режима fork DB. - Заполнить все слоты "committee".
- Лог:
"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, импортируются как неэкстренные.
Сводка защитных механизмов
| № | Расположение | Механизм |
|---|---|---|
| 1 | update_global_dynamic_data | Активировать только при HF12 + не активен + LIB-блок доступен |
| 2 | update_witness_schedule | Гибридное переопределение + проверка выхода при ≥75% реальных валидаторов |
| 3 | update_last_irreversible_block | Ограничить LIB значением HEAD − 1 в экстренном режиме |
| 4 | verify_signing_witness | Ослабить соответствие слот-валидатор |
| 5 | fork_db._push_block | Детерминированное разрешение хэш-ничьих |
| 6 | maybe_produce_block (мастер) | Обходить синхронизацию, устаревание, участие; пропускать minority fork |
| 7 | maybe_produce_block (последователь) | Сначала синхронизироваться; 21-блочная проверка изоляции |
| 8 | resync_from_lib | Полностью пропускать в экстренном режиме |
| 9 | stale_sync_check_task | Пропустить если голова мастера продвигается; разрешить если последователь застрял |
| 10 | handle_block | Почти догнавшие блоки обрабатываются как нормальные в DLT-экстренном режиме |
| 11 | database::open | Исправление расписания при запуске |
| 12 | validator_guard | Не подавлять восстановление ключей в экстренном режиме |
| 13 | snapshot import | Совместимая обработка полей |
| 14 | update_witness_schedule | Исключать committee из подсчёта версий харфорков |
| 15 | update_median_witness_props | Исключать committee из вычисления медианы |
Ключевые инварианты
- Детерминированная активация — использует только временны́е метки в блоках; идентична на каждом узле и при каждом воспроизведении.
- Безопасность DLT-снимка — пропускает активацию если LIB-блок отсутствует в block log.
- Неизменность экстренного форка —
resync_from_lib()отказывается выполняться в экстренном режиме. - Различие мастер/последователь — только узлы с
--emergency-private-keyявляются мастерами. - Сходимость fork DB — детерминированное разрешение хэш-ничьих гарантирует, что все узлы выбирают один блок.
- Безопасность LIB — ограничен значением HEAD − 1 для сохранения защиты отмены.
- Нейтральное голосование committee — committee голосует за текущую применённую версию харфорка, копирует медианные props.
См. также: Fair-DPOS, Разрешение форков, Узел-валидатор, Защита валидатора.