Управление хардфорками
VIZ Ledger координирует обновления протокола через детерминированную систему хардфорков. Хардфорки определяются во время компиляции, активируются консенсусом валидаторов и применяются автоматически в процессе обработки блоков — без перезапуска узла.
Как это работает
1. Файлы определений
Каждый хардфорк имеет выделенный файл *.hf в libraries/chain/hardfork.d/, определяющий константы времени компиляции:
// Пример: 9.hf
#define CHAIN_HARDFORK_9 9
#define CHAIN_HARDFORK_9_VERSION version(1, 0, 9)
#define CHAIN_HARDFORK_9_TIME (fc::time_point_sec(1650000000))Файл преамбулы (0-preamble.hf) объявляет общее количество и схему объекта hardfork_property. Текущее значение: CHAIN_NUM_HARDFORKS = 12.
2. Консенсус валидаторов
Валидаторы публикуют предпочтительную версию следующего хардфорка через versioned_chain_properties_update_operation. При каждом обновлении расписания валидаторов узел:
- Собирает версию хардфорка, которую поддерживает каждый активный валидатор.
- Если большинство согласно на версию ≥ следующей запланированной, устанавливает
next_hardforkиnext_hardfork_time.
3. Активация во время обработки блоков
Когда время head-блока превышает next_hardfork_time и достаточное количество валидаторов поддерживает версию, узел вызывает apply_hardfork(N). Все последующие изменения поведения управляются проверками has_hardfork(N) в эвалуаторах, логике инфляции и свойствах цепочки.
История хардфорков
| HF | Ключевые изменения |
|---|---|
| 1 | Исправление вычисления медианы |
| 2 | Исправление порога одобрения комитета |
| 3 | Незначительные исправления протокола |
| 4 | Операции наград, последовательности custom-операций |
| 5 | Исправления пропускной способности и authority |
| 6 | Штрафы за пропуски валидатора, подсчёт голосов |
| 7 | Корректировки социального/контентного слоя |
| 8 | Очистка протокола |
| 9 | Система инвайтов, платные подписки, комиссии валидатора, withdraw_intervals |
| 10 | Модель инфляции |
| 11 | Изменения модели эмиссии |
| 12 | Аварийное восстановление консенсуса (см. ниже) |
HF12: Аварийное восстановление консенсуса
HF12 вводит автоматическое восстановление сети при остановке производства блоков.
Активация
Если метка времени последнего необратимого блока (LIB) отстаёт от системных часов более чем на CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC (1 час), аварийный режим активируется автоматически. Создаётся аварийный аккаунт валидатора (CHAIN_EMERGENCY_VALIDATOR_ACCOUNT = "committee") с известным публичным ключом (CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY) и вставляется в расписание производства блоков.
Трёхуровневое обеспечение безопасности
| Состояние сети | Условие | Поведение |
|---|---|---|
| Здоровое | Участие ≥ 33% | Принудительно применяет безопасные умолчания; переопределяет ручную конфигурацию |
| Критическое | Участие < 33% | Уважает переопределения ручной конфигурации для восстановления оператором |
| Аварийное | Аварийный режим активен | Автоматически обходит проверки устаревания и участия |
Расширенное планирование валидаторов
- Гибридное расписание: Аварийный валидатор заполняет недоступные слоты, сохраняя позиции реальных валидаторов.
- Переключение форков с взвешиванием голосов: В качестве основного критерия сравнения форков используется сумма сырых голосов от уникальных не-committee валидаторов.
- Исключение из медианы: Голоса аварийного валидатора за свойства исключаются из вычисления медианных параметров цепочки.
Объект hardfork_property
Персистентный hardfork_property_object (синглтон в chainbase) отслеживает:
| Поле | Описание |
|---|---|
processed_hardforks | Вектор времён применённых хардфорков |
last_hardfork | ID последнего применённого хардфорка |
current_hardfork_version | Текущая применяемая версия протокола |
next_hardfork | Версия следующего запланированного хардфорка |
next_hardfork_time | Когда активируется next_hardfork |
Для HF12+ дополнительные поля отслеживают состояние аварийного консенсуса.
Жизненный цикл базы данных и хардфорки
При открытии
database::open()
→ init_schema(), initialize_indexes(), initialize_evaluators()
→ загрузить hardfork_property из chainbase
→ init_hardforks() ← заполнить массивы _hardfork_times[] и _hardfork_versions[]
→ assert: ревизия chainbase == head_block_numПри переиндексации
Реплей всех блоков из block_log с флагами пропуска (без проверки подписей, без merkle) для ускорения. apply_hardfork() срабатывает на каждой границе хардфорка во время реплея, обеспечивая детерминированную реконструкцию состояния.
При применении блока
process_hardforks()
→ проверить, превышено ли next_hardfork_time
→ проверить, поддерживает ли консенсус валидаторов версию
→ если да: apply_hardfork(N)
→ выполнить специфичные для версии миграции состояния
→ обновить current_hardfork_versionОткат и переключение форков
База данных использует undo-сессии для атомарного применения блоков — частичные сбои откатываются чисто.
При переключении форков fetch_branch_from() возвращает обе ветки к общему предку, откатывает текущую ветку и заново применяет новую. HF12 добавляет взвешенное по голосам сравнение цепочек в этом процессе.
При сбое применения блока запись в fork-базе данных удаляется и выбрасывается исключение. P2P-слой обрабатывает исключение, соответствующим образом помечая отправившего пира.
Добавление нового хардфорка
Создать
N.hfвlibraries/chain/hardfork.d/:cpp#define CHAIN_HARDFORK_N N #define CHAIN_HARDFORK_N_VERSION version(1, 0, N) #define CHAIN_HARDFORK_N_TIME (fc::time_point_sec(UNIX_TIMESTAMP))Увеличить
CHAIN_NUM_HARDFORKSв0-preamble.hfдо N.Управлять новым поведением в эвалуаторах и логике времени выполнения:
cppif (db.has_hardfork(CHAIN_HARDFORK_N)) { // новое поведение } else { // устаревшее поведение }Добавить миграции состояния в
apply_hardfork(N)вdatabase.cpp:cppcase CHAIN_HARDFORK_N: // однократный код миграции break;Учесть аварийный режим: Если хардфорк изменяет планирование валидаторов или параметры цепочки, убедитесь, что аварийный валидатор исключён из затронутых вычислений.
Проверить с переиндексацией: Выполнить полную переиндексацию на данных mainnet-блоков для подтверждения детерминированного реплея, воспроизводящего идентичное состояние.
Устранение неполадок
| Симптом | Вероятная причина | Решение |
|---|---|---|
| Хардфорк не срабатывает | Консенсус валидаторов не достигнут | Убедиться, что валидаторы публикуют целевую версию; проверить API get_next_scheduled_hardfork() |
| Несоответствие ревизии при открытии | Ревизия chainbase ≠ head block num | Переиндексировать из block log или восстановить из снапшота |
| Исчерпание памяти при переиндексации | Разделяемая память слишком мала | Увеличить shared-file-size; включить автоизменение размера |
| Аварийный режим не активируется | HF12 ещё не применён | Убедиться, что current_hardfork_version ≥ 1.0.12 |
| Несоответствие состояния после переиндексации | Недетерминированная ветка has_hardfork() | Проверить apply_hardfork() на побочные эффекты |
Диагностика:
{ "method": "database_api.get_hardfork_version", "params": [] }
{ "method": "database_api.get_next_scheduled_hardfork", "params": [] }Контрольный список обновления
- [ ] Определить
N.hfс реалистичной меткой времени (согласовать с валидаторами) - [ ] Увеличить
CHAIN_NUM_HARDFORKSв0-preamble.hf - [ ] Реализовать миграции
apply_hardfork(N) - [ ] Управлять изменениями поведения проверками
has_hardfork() - [ ] Сделать резервную копию базы данных и block log перед развёртыванием
- [ ] Запустить узел в режиме только-чтение для проверки совместимости
- [ ] Мониторить логи на события активации хардфорка
- [ ] Согласовать с валидаторами публикацию новой версии
- [ ] Подтвердить, что
get_next_scheduled_hardfork()показывает ожидаемую версию/время
См. также: Свойства цепочки, Валидаторы, Схема базы данных, Database API.