Skip to content

Управление хардфорками

VIZ Ledger координирует обновления протокола через детерминированную систему хардфорков. Хардфорки определяются во время компиляции, активируются консенсусом валидаторов и применяются автоматически в процессе обработки блоков — без перезапуска узла.


Как это работает

1. Файлы определений

Каждый хардфорк имеет выделенный файл *.hf в libraries/chain/hardfork.d/, определяющий константы времени компиляции:

cpp
// Пример: 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. При каждом обновлении расписания валидаторов узел:

  1. Собирает версию хардфорка, которую поддерживает каждый активный валидатор.
  2. Если большинство согласно на версию ≥ следующей запланированной, устанавливает 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_hardforkID последнего применённого хардфорка
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-слой обрабатывает исключение, соответствующим образом помечая отправившего пира.


Добавление нового хардфорка

  1. Создать 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))
  2. Увеличить CHAIN_NUM_HARDFORKS в 0-preamble.hf до N.

  3. Управлять новым поведением в эвалуаторах и логике времени выполнения:

    cpp
    if (db.has_hardfork(CHAIN_HARDFORK_N)) {
        // новое поведение
    } else {
        // устаревшее поведение
    }
  4. Добавить миграции состояния в apply_hardfork(N) в database.cpp:

    cpp
    case CHAIN_HARDFORK_N:
        // однократный код миграции
        break;
  5. Учесть аварийный режим: Если хардфорк изменяет планирование валидаторов или параметры цепочки, убедитесь, что аварийный валидатор исключён из затронутых вычислений.

  6. Проверить с переиндексацией: Выполнить полную переиндексацию на данных 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() на побочные эффекты

Диагностика:

json
{ "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.