Консенсус Fair-DPOS
VIZ Ledger использует Fair Delegated Proof of Stake (Fair-DPOS) — расширение классического алгоритма DPoS, добавляющее контроль участия и штрафы за пропуски блоков для предотвращения получения вознаграждений без реального производства.
Как работает классический DPoS
В стандартном DPoS:
- Держатели токенов голосуют за аккаунты-валидаторы (с весом, пропорциональным SHARES).
- Валидаторы с наибольшим числом голосов планируются для производства блоков в порядке round-robin.
- Каждые 3 секунды срабатывает один слот; запланированный валидатор либо производит блок, либо пропускает слот.
VIZ работает с 21 активным валидатором в каждом раунде расписания.
Расширение «Fair»
В классическом DPoS валидатор может бесконечно пропускать блоки и при этом продолжать получать голоса (и иногда вознаграждения). Fair-DPOS добавляет:
- Отслеживание участия — 128-битная битовая маска отслеживает последние 128 слотов. Каждый слот помечается 1 (произведён) или 0 (пропущен).
- Порог участия — узел не производит блоки, если менее
required-participation% последних слотов было заполнено любым валидатором (по умолчанию 33%). Это защита от сценария minority fork. - Штрафы за пропуски — валидаторы, пропускающие блоки, накапливают счётчик пропусков. При каждой оценке хардфорка наихудшие исполнители могут быть исключены из активного набора.
- Распределение вознаграждений (HF13) — блочные вознаграждения валидаторов частично перераспределяются их голосующим, согласовывая стимулы делегаторов с производительностью валидаторов.
Расписание валидаторов
Построение расписания
Каждые CHAIN_WITNESS_SCHEDULE_BLOCK_NUM блоков (21) цепочка пересчитывает активный набор валидаторов:
- Выбираются 21 валидатор с наибольшим суммарным весом голосов (SHARES, делегированные им).
- Добавляются валидаторы с долей времени — ротируемый слот для нижестоящих валидаторов, чтобы они могли периодически участвовать и предотвращать полную концентрацию.
- Результирующие 21 слот перемешиваются с использованием идентификатора головного блока в качестве источника энтропии (детерминированное перемешивание = одинаковый результат на всех узлах).
Полученный упорядоченный список становится текущим расписанием. Каждая позиция соответствует 3-секундному слоту.
Назначение слота
При текущем времени T:
slot_num = (T - genesis_time) / CHAIN_BLOCK_INTERVAL
scheduled = schedule[slot_num % num_scheduled_validators]Временны́е метки блоков всегда являются детерминированным временем слота, а не реальными часами:
block_time = genesis_time + slot_num × 3sПропущенные слоты
Когда валидатор пропускает свой слот, update_global_dynamic_data() увеличивает current_aslot и помечает слот как пропущенный в битовой маске участия. Другие валидаторы не заполняют пропущенный слот — 3-секундный ритм продолжается со следующего слота независимо от этого.
Коэффициент участия
Коэффициент участия вычисляется как:
participation = popcount(recent_slots_filled) / 128где recent_slots_filled — 128-битное скользящее окно результатов слотов.
Производство валидаторов блокируется при падении участия ниже required-participation (по умолчанию 33%). Это предотвращает продолжение производства блоков узлом на minority fork, когда большая часть сети недоступна.
Параметр конфигурации:
required-participation = 33 # минимум %, 0–99Последний необратимый блок (LIB)
Блок становится необратимым, когда более 2/3 активных валидаторов построили на его основе. Цепочка отслеживает это в last_irreversible_block_num.
irreversibility_threshold = ceil(num_scheduled_validators * 2 / 3)При 21 валидаторе: ceil(21 × 2/3) = 14 подтверждений. Как только 14 валидаторов произвели блоки, являющиеся потомками блока N, блок N становится LIB.
Продвижение LIB приостанавливается в режиме экстренного консенсуса (см. Экстренный консенсус).
Быстрое продвижение LIB через пост-валидацию блоков
Классический путь LIB требует, чтобы 14 валидаторов произвели блоки поверх целевого — при 3 с на слот это занимает ~63 секунды. Пост-валидация блоков обеспечивает быстрый путь, заменяя неявное подтверждение производством блоков на явные подписные сообщения, сокращая время финальности до ~4 секунд.
Поток данных:
- После
apply_block(N)цепочка сохраняетvalidator_confirmation_objectдля блока N. - Каждый запланированный валидатор с загруженным подписывающим ключом подписывает
chain_id + block_idи транслируетblock_post_validation_message(P2P-сообщение типа 6009). - Принимающие пиры проверяют подпись по ончейн
validator.signing_key, затем помечают валидатора как подтвердившего этот блок. check_block_post_validation_chain()проходит от LIB+1 вперёд — если ≥14 из 21 запланированных валидаторов подтвердили блок, LIB продвигается к этому блоку, и процесс повторяется.
Классический путь DPOS (~63 с) остаётся активным как резервный на случай потери подтверждающих сообщений.
Ограничения:
- Только валидаторы в текущем перемешанном расписании учитываются в пороге 2/3.
- Пост-валидация LIB отключена во время экстренного консенсуса во избежание блокировки восстановления.
- Список подтверждений ограничен 20 записями (
CHAIN_MAX_BLOCK_POST_VALIDATION_COUNT).
Полные технические детали, включая формат сообщения и разбивку по времени — в разделе Обработка блоков → Пост-валидация блоков.
Голосование по хардфоркам
Валидаторы участвуют в активации хардфорков, устанавливая свой hardfork_version_vote через validator_update_operation. Хардфорк N активируется, когда:
- Супербольшинство (>80%) текущего набора валидаторов подало сигнал поддержки.
- Запланированная метка времени активации хардфорка наступила.
Оба условия должны выполняться одновременно. Это позволяет операторам сети блокировать нежелательные хардфорки, воздерживаясь от голосования даже после наступления запланированного времени.
Защита от minority fork
Если последние 21 последовательный блок произведены только валидаторами, входящими в настроенный набор данного узла, плагин валидатора заключает, что узел изолирован, и автоматически откатывается к LIB. Это и есть защита от minority fork.
Проверка пропускается при:
enable-stale-production = true(разработка/тестовая сеть)- Активном режиме экстренного консенсуса
Режим экстренного консенсуса
Если в течение CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC (по умолчанию 1 час) не было произведено ни одного блока, цепочка переходит в режим чрезвычайной ситуации:
- Все 21 слот валидаторов назначаются
CHAIN_EMERGENCY_VALIDATOR_ACCOUNT(«committee»). - Экстренный валидатор подписывает блоки с использованием
CHAIN_EMERGENCY_VALIDATOR_PUBLIC_KEY. - Все штрафы валидаторов сбрасываются; отключённые валидаторы повторно включаются.
- Продвижение LIB приостанавливается в период чрезвычайной ситуации.
- Режим чрезвычайной ситуации завершается после того, как
CHAIN_EMERGENCY_EXIT_NORMAL_BLOCKS(21) последовательных обычных блоков продвигают LIB выше начального блока чрезвычайной ситуации.
Полная информация — в разделе Экстренный консенсус.
HF12: сравнение форков по весу голосов
Начиная с хардфорка 12, при наличии двух конкурирующих форков на одной высоте блока цепочка использует сравнение форков по весу голосов вместо простой длиннейшей цепочки:
- Для каждой ветви форка суммируются SHARES, делегированные каждому уникальному валидатору, произведшему блок в этой ветви.
- Применяется бонус +10% к более длинной цепочке для разрешения ничьих в пользу непрерывности производства.
- Побеждает форк с большим суммарным весом голосов.
- При равенстве текущий форк сохраняется до срабатывания таймаута разрешателя коллизий (21 отсрочка).
Полный алгоритм коллизии форков — в разделе Разрешение форков.
Сводка по конфигурации
| Параметр | По умолчанию | Описание |
|---|---|---|
required-participation | 33 (33%) | Минимальное участие для производства блоков |
enable-stale-production | false | Обход проверки участия (только для тестовой сети) |
emergency-private-key | — | Опциональный ключ подписи для экстренного консенсуса |
| Активные валидаторы | 21 | Задано в CHAIN_MAX_VALIDATORS |
| Интервал блока | 3 с | CHAIN_BLOCK_INTERVAL |
| Порог LIB | ⌈21 × 2/3⌉ = 14 | Блоки, подтверждающие необратимость |
| Таймаут чрезвычайной ситуации | 3600 с | CHAIN_EMERGENCY_CONSENSUS_TIMEOUT_SEC |
Расположение ключевых источников
| Компонент | Файл |
|---|---|
| Построение расписания валидаторов | libraries/chain/database.cpp — update_witness_schedule() |
| Обновление битовой маски участия | libraries/chain/database.cpp — update_global_dynamic_data() |
| Продвижение LIB (классическое) | libraries/chain/database.cpp — update_last_irreversible_block() |
| Продвижение LIB (быстрое) | libraries/chain/database.cpp — check_block_post_validation_chain(), apply_block_post_validation() |
| Создание пост-валидации | libraries/chain/database.cpp — create_block_post_validation() |
| Трансляция пост-валидации | plugins/validator/validator.cpp — тик таймера пост-валидации |
| Голосование по хардфоркам | libraries/chain/database.cpp — process_hardforks() |
| Цикл производства | plugins/validator/validator.cpp — maybe_produce_block() |
| Активация режима чрезвычайной ситуации | libraries/chain/database.cpp — check_emergency_consensus() |
| Сравнение форков HF12 | libraries/chain/database.cpp — compare_fork_branches() |
См. также: Обработка блоков, Разрешение форков, Экстренный консенсус, Узел-валидатор.