Харфорки
Харфорк — это обновление сети, изменяющее правила консенсуса. Все узлы должны обновиться до запланированной временной метки активации; узлы, работающие на старом программном обеспечении, разойдутся с сетью после активации.
Механизм активации
Каждый харфорк имеет:
- Уникальный номер (N).
- Unix-временную метку — самое раннее время по системным часам, когда харфорк может активироваться.
- Суперквалифицированное большинство голосов валидаторов — >80% текущего набора валидаторов должны подать сигнал новой версии харфорка через
validator_update_operation.
Оба условия должны выполняться одновременно. Валидаторы могут заблокировать нежелательный харфорк, удерживая свой голос за версию даже после наступления запланированной временной метки.
История харфорков
| # | Версия | Ключевые изменения |
|---|---|---|
| 1–10 | 1.x – 2.x | Фундамент, социальный граф, система энергии, комитет, подписки |
| 11 | 3.0.0 | — |
| 12 | 3.1.0 | Метрики коллизий форков, сравнение форков с весом голосов, режим экстренного консенсуса, улучшения NTP |
| 13 | 3.2.0 | Совместное использование наград валидаторов с распределением пропорционально голосам |
Краткое описание HF12
HF12 (версия 3.1.0) ввёл:
- Счётчик коллизий форков —
fork_collision_countиlast_fork_collision_block_numдобавлены вdynamic_global_property_object. Доступно черезget_dynamic_global_properties. - Сравнение форков с весом голосов (
compare_fork_branches()) — выбор форка использует общий делегированный SHARES по ветви валидатора + 10% бонус за более длинную цепочку. - Режим экстренного консенсуса — активируется автоматически через 1 час без блоков; аккаунт "committee" занимает все 21 слот. См. Экстренный консенсус.
- Авто-ресинхронизация minority fork — плагин validator обнаруживает изоляцию узла (21 последовательный собственный блок) и откатывается до LIB.
- Улучшения NTP — выделенный NTP-клиент с настраиваемыми серверами, интервалом и порогом времени туда-обратно.
Краткое описание HF13
HF13 (версия 3.2.0) ввёл:
Совместное использование наград валидаторов: часть награды валидатора каждого блока перераспределяется пропорционально аккаунтам, проголосовавшим за этого валидатора (по их весу голосов в SHARES).
- Новое поле в
validator_object:reward_percent— доля награды за блок, делимая с голосующими (0–10000 базисных пунктов). - Новая виртуальная операция:
validator_reward_virtual_operation— срабатывает один раз при каждом распределении наград. - Устанавливается через
validator_update_operation.
Реализация нового харфорка
Шаг 1: Создать файл определения харфорка
libraries/chain/hardfork.d/N.hf:
#ifndef CHAIN_HARDFORK_N
#define CHAIN_HARDFORK_N N
#define CHAIN_HARDFORK_N_TIME 1234567890 // Unix-временная метка — должна быть в будущем
#define CHAIN_HARDFORK_N_VERSION hardfork_version(3, N, 0)
#endifШаг 2: Увеличить константы
libraries/chain/hardfork.d/0-preamble.hf:
#define CHAIN_NUM_HARDFORKS Nlibraries/protocol/include/graphene/protocol/config.hpp (если видимо протоколу):
#define CHAIN_VERSION (version(3, N, 0))Шаг 3: Версия схемы
Если изменяется какая-либо разметка объекта chainbase (новые поля, удалённые поля, изменённые типы), увеличьте CHAIN_SCHEMA_VERSION в config.hpp:
#define CHAIN_SCHEMA_VERSION uint32_t(N)Плагин chain проверяет это при запуске. Несоответствие очищает shared_memory.bin перед открытием, предотвращая некорректное чтение из старых разметок.
Новые поля всегда должны иметь нулевые значения по умолчанию, чтобы избежать кода миграции:
uint16_t my_new_field = 0;Шаг 4: Подключить в database.cpp
init_hardforks():
FC_ASSERT(CHAIN_HARDFORK_N == N);
_hardfork_times[N] = fc::time_point_sec(CHAIN_HARDFORK_N_TIME);
_hardfork_versions[N] = hardfork_version(CHAIN_HARDFORK_N_VERSION);Случай в apply_hardfork():
case CHAIN_HARDFORK_N: {
// Миграция при необходимости. Оставить пустым с комментарием, если нулевые значения по умолчанию покрывают это.
break;
}Шаг 5: Операция и evaluator (при наличии новой операции)
- Добавить структуру в
chain_operations.hppсvalidate()и геттерами авторизации. - Добавить в
static_variantвoperations.hpp. - Объявить
DEFINE_EVALUATOR(my_new_op)вchain_evaluator.hpp. - Реализовать
do_apply()в.cppфайле evaluator — всегда сначала проверятьASSERT_REQ_HF(CHAIN_HARDFORK_N, ...). - Зарегистрировать в
initialize_evaluators()вdatabase.cpp.
Шаг 6: Обновление плагинов
| Плагин | Что обновить |
|---|---|
account_history | Добавить экстрактор воздействия для любой новой виртуальной операции |
validator_api | Добавить новые поля из validator_object в validator_api_object |
snapshot | Добавить новые объекты chainbase в serialize_state / load_snapshot |
Жизненный цикл версии схемы
Новый узел (без существующих данных):
stored = 0, compiled = N → несоответствие
очистить shared_memory (нет операций при отсутствии)
записать schema_version = N
genesis → нормальный запуск
Обновление (старый бинарный файл имел версию M < N):
stored = M, compiled = N → несоответствие
очистить shared_memory.bin
записать schema_version = N
db.open() → исключение несоответствия ревизии
→ авто-восстановление: импорт снимка + воспроизведение dlt_block_log
Нормальный перезапуск:
stored = N, compiled = N → совпадение
db.open() продолжается нормальноКлючевые файлы:
config.hpp—CHAIN_SCHEMA_VERSIONplugins/chain/plugin.cpp— логика проверки схемы и очистки<data_dir>/schema_version— текстовый файл с текущей версией
Чеклист развёртывания
- [ ]
CHAIN_NUM_HARDFORKSувеличен - [ ]
CHAIN_VERSIONобновлён (если видимо протоколу) - [ ]
CHAIN_SCHEMA_VERSIONувеличен (если изменилась разметка объекта chainbase) - [ ] Файл харфорка
.hfсоздан с будущей временной меткой активации - [ ] Все новые поля имеют нулевые значения по умолчанию; комментарий в
apply_hardforkобъясняет, почему миграция не нужна - [ ] Новый evaluator зарегистрирован в
initialize_evaluators() - [ ] Новая виртуальная операция зарегистрирована в плагине
account_history - [ ]
validator_api_objectобновлён если изменилсяvalidator_object - [ ] Плагин snapshot обновлён если добавлены новые объекты chainbase
См. также: Fair-DPOS, Экстренный консенсус, Снимки.