Skip to content

Схема базы данных

VIZ Ledger использует ChainBase — персистентное хранилище с отображением в память и несколькими индексами, построенное на Boost.Interprocess. Всё состояние цепочки находится в shared_memory.bin. Каждый тип объекта ассоциирован с контейнером Boost.MultiIndex, определяющим его первичные и вторичные индексы.


Реестр типов объектов

Каждый персистентный объект имеет уникальный числовой type ID, объявленный в chain_object_types.hpp. Полный набор отслеживаемых типов объектов:

ОбъектПримечания
dynamic_global_propertyСинглтон: текущее состояние цепочки, head-блок, LIB, инфляция
accountВсе зарегистрированные аккаунты
account_authorityНаборы authority master/active/regular
witness (валидатор)Регистрации валидаторов, ключи подписи, счётчики голосов
transactionОжидающие/недавние транзакции (окно TAPOS)
block_summary65536-слотовый TAPOS-буфер ID блоков
witness_scheduleСинглтон: расписание активных валидаторов
contentПосты и комментарии (устарело)
content_typeМетаданные заголовка/тела контента
content_voteГолоса за контент
witness_voteГолоса за валидаторов от аккаунтов
hardfork_propertyСинглтон: отслеживание текущего/следующего хардфорка
withdraw_vesting_routeПравила маршрутизации вывода
master_authority_historyИстория изменений master-ключей
account_recovery_requestОжидающие запросы восстановления аккаунта
change_recovery_account_requestОжидающие изменения аккаунта восстановления
escrowЭскроу-переводы
vesting_delegationАктивные делегирования SHARES
fix_vesting_delegationЗаписи исправления делегирований
vesting_delegation_expirationДелегирования в окне возврата
account_metadataJSON-метаданные аккаунта
proposalУправленческие предложения
required_approvalТребования одобрения предложений
committee_requestЗапросы на финансирование комитета
committee_voteГолоса комитета
inviteИнвайты аккаунтов
award_shares_expireИстекающие наградные SHARES
paid_subscriptionПредложения подписок
paid_subscribeАктивные подписки
witness_penalty_expireИстечения штрафов за пропуски валидатора
block_post_validationЗаписи поствалидации блоков

Объект аккаунта

Аккаунты хранят балансы, состояние вестинга, метрики делегирования, пропускную способность, флаги аукциона/продажи и участие в управлении.

Ключевые поля: name, balance (VIZ), vesting_shares, delegated_vesting_shares, received_vesting_shares, energy, next_vesting_withdrawal, validators_voted_for, recovery_account.

Индексы:

ТегКлючТип
by_ididуникальный
by_namenameуникальный
by_account_on_saleфлаг продажинеуникальный
by_account_on_auctionфлаг аукционанеуникальный
by_account_on_sale_start_timeвремя начала продажинеуникальный
by_subaccount_on_saleфлаг продажи субаккаунтанеуникальный
by_next_vesting_withdrawal(next_vesting_withdrawal, id)составной

Составной индекс by_next_vesting_withdrawal обеспечивает пакетную обработку предстоящих выплат вывода за O(log N).


Объект контента

Объекты контента представляют посты и комментарии с метаданными голосования, выплат и вложенности. Эти объекты устарели — новые приложения должны использовать custom_operation.

Индексы на content:

ТегКлюч
by_idid
by_cashout_time(cashout_time, id)
by_permlink(author, permlink)
by_root(root_content, id)
by_parent(parent_author, parent_permlink, id)
by_last_update(parent_author, last_update, id) — нагружает API
by_author_last_update(author, last_update, id) — нагружает API

Индексы на content_vote:

ТегКлюч
by_idid
by_content_voter(content, voter) — уникальный
by_voter_content(voter, content) — уникальный
by_voter_last_update(voter, last_update, content)
by_content_weight_voter(content, weight, voter) — для лидербордов

Объекты валидатора

Индексы validator_object:

ТегКлюч
by_idid
by_nameowner — уникальный
by_vote_name(votes, owner)
by_counted_vote_name(counted_votes, owner)
by_schedule_time(virtual_scheduled_time, id) — O(log N) планирование слотов

Индексы witness_vote_object:

ТегКлюч
by_idid
by_account_witness(account, validator) — уникальный
by_witness_account(validator, account) — уникальный

Индекс by_schedule_time используется планировщиком производства блоков для выбора следующего валидатора за O(log N).


Объекты предложений и требуемых одобрений

Индексы proposal_object:

ТегКлюч
by_idid
by_account(author, title) — уникальный
by_expirationexpiration — неуникальный

Индексы required_approval_object:

ТегКлюч
by_idid
by_account(account, proposal)

Объект инвайта

ТегКлюч
by_idid
by_invite_keyпубличный ключ — неуникальный
by_statusстатус — неуникальный
by_creatorсоздатель — неуникальный
by_receiverполучатель — неуникальный

Вспомогательные объекты

withdraw_vesting_route:

ТегКлюч
by_withdraw_route(from_account, to_account) — уникальный
by_destination(to_account, id)

escrow:

ТегКлюч
by_from_id(from, escrow_id) — уникальный
by_to(to, id)
by_agent(agent, id)
by_ratification_deadline(is_approved, ratification_deadline, id)

vesting_delegation:

ТегКлюч
by_delegation(delegator, delegatee) — уникальный

vesting_delegation_expiration:

ТегКлюч
by_expirationexpiration — неуникальный
by_account_expiration(delegator, expiration)

Fork-база данных

Fork-база данных (fork_database) поддерживает дерево блоков в памяти для управления форками цепочки. Работает отдельно от персистентного хранилища chainbase.

Связанный индекс — блоки канонической цепочки, индексированные по ID и номеру блока.
Несвязанный индекс — осиротевшие или неупорядоченные блоки, родитель которых ещё не известен.

Добавление блока
  ├── Родитель известен в связанном индексе?
  │     ДА  → связать блок, вставить в связанный индекс, обновить head
  │     НЕТ → вставить в несвязанный индекс
  └── Попытаться связать ожидающие несвязанные блоки

Когда поступает новый блок, ID которого совпадает с родителем несвязанного блока, _push_next() каскадно обходит несвязанный индекс и продвигает эти блоки в связанную цепочку.

Операции с ветками:

  • fetch_branch_from(first, second) — обходит обе ветки для поиска общего предка. Возвращает (first_branch, second_branch) для переключения форков.
  • set_max_size(n) — усекает блоки старше n, ограничивает потребление памяти.
  • walk_main_branch_to_num(n) — итерирует главную цепочку до определённого номера блока.

Валидность блока: Блоки, помеченные как невалидные, никогда не продвигаются. Добавление блока за пределами максимального окна переупорядочивания вызывает assert.


Управление индексами

Основные индексы регистрируются в database::initialize_indexes(). Плагины регистрируют дополнительные индексы через add_plugin_index<T>() в plugin_startup().

cpp
// Регистрация основных индексов (database.cpp)
add_core_index<account_index>();
add_core_index<witness_index>();
// ...

// Регистрация индексов плагинов (запуск плагина)
db.add_plugin_index<my_custom_index>();

Связи объектов

account ──(author)──► content ──► content_vote ◄──(voter)── account
account ──(delegator)──► vesting_delegation ──► account (delegatee)
account ──(account)──► witness_vote ──► witness (validator)
account ──(author)──► proposal ──► required_approval ◄──(account)── account
account ──(creator/receiver)──► invite
escrow: from + to + agent → escrow_object

Руководство по оптимизации запросов

Быстрые поиски:

  • Аккаунт по имени → by_name (уникальный, O(log N))
  • Расписание валидаторов → by_schedule_time (упорядочен по виртуальному времени)
  • Контент по author+permlink → by_permlink (уникальный составной)
  • Голоса по content+weight → by_content_weight_voter (лидерборды)

Пакетная обработка:

  • Выводы вестинга → итерировать by_next_vesting_withdrawal вперёд
  • Истекающие делегирования → итерировать by_expiration вперёд
  • Истекающие предложения → итерировать by_expiration вперёд

Избегайте полного сканирования: всегда используйте тег с индексом. Составные индексы упорядочены прежде всего по крайнему левому ключу — ставьте наиболее селективное или часто фильтруемое поле первым.


Расширение схемы для плагинов

Для добавления пользовательского типа объекта:

  1. Определить класс объекта, наследующий от chainbase::object<type_id, MyObject>.
  2. Объявить chainbase::shared_multi_index_container с нужными индексами.
  3. Зарегистрировать через db.add_plugin_index<MyIndex>() в plugin_startup().
  4. Добавить макросы FC_REFLECT для сериализации.
cpp
class my_object : public chainbase::object<my_object_type, my_object> {
    id_type          id;
    account_name_type account;
    uint64_t          value;
};

using my_index = chainbase::shared_multi_index_container<
    my_object,
    indexed_by<
        ordered_unique<tag<by_id>,
            member<my_object, my_object::id_type, &my_object::id>>,
        ordered_unique<tag<by_account>,
            member<my_object, account_name_type, &my_object::account>>
    >
>;

Эволюция схемы

Новый хардфорк → новые поля или объекты. Руководящие принципы:

  • Сохранять семантику первичных ключей стабильной между хардфорками.
  • Добавлять новые поля как опциональные или с умолчаниями; никогда не менять существующий порядок полей.
  • Ограждать использование новых индексов проверками has_hardfork() при реплее.
  • Добавлять новые теги MultiIndex рядом с существующими — никогда не удалять тег, который могут запрашивать реплеирующие узлы.

См. также: Разработка плагинов, Виртуальные операции, Управление хардфорками.