Skip to content

Обзор архитектуры

VIZ Ledger реализован как модульный C++-демон (vizd), состоящий из слоёв библиотек и системы плагинов. На этой странице описаны структурные слои, паттерны проектирования и взаимодействие компонентов.


Слоистая структура

┌─────────────────────────────────────────────────────────────────┐
│  Программы                                                      │
│  vizd (демон узла)           cli_wallet (CLI-кошелёк)           │
├─────────────────────────────────────────────────────────────────┤
│  Плагины                                                        │
│  chain │ validator │ p2p │ webserver │ json_rpc │ database_api  │
│  social_network │ snapshot │ committee_api │ invite_api │ ...   │
├─────────────────────────────────────────────────────────────────┤
│  Основные библиотеки                                            │
│  libraries/chain     — машина состояний блокчейна, fork db      │
│  libraries/protocol  — типы операций, транзакции                │
│  libraries/network   — P2P-обмен сообщениями                    │
│  libraries/api       — общие типы свойств API                   │
│  libraries/wallet    — вспомогательные инструменты сборки транзакций │
│  libraries/time      — утилиты времени с синхронизацией NTP     │
└─────────────────────────────────────────────────────────────────┘

Библиотеки

БиблиотекаКлючевой файлНазначение
libraries/chaindatabase.hppСостояние блокчейна: счета, блоки, объекты, fork DB, разделяемая память
libraries/protocoloperations.hppОбъединение static_variant всех 64+ типов операций
libraries/networknode.hppP2P-движок: соединения с пирами, синхронизация, распространение сообщений
libraries/apichain_api_properties.hppОбщие типы, возвращаемые плагинами API
libraries/walletwallet.hppВызовы API удалённого узла, построение транзакций
libraries/timetime.hppСинхронизация NTP для тайминга блоковых слотов

Плагины

Плагины регистрируются во фреймворке AppBase при запуске и реализуют хуки жизненного цикла (plugin_initialize, plugin_startup, plugin_shutdown).

ПлагинРоль
chainОткрывает базу данных, валидирует и применяет блоки/транзакции
validatorПроизводит блоки по расписанию (Fair-DPOS), управляет NTP и watchdog
p2pУправляет соединениями с пирами, синхронизирует блоки, распространяет транзакции
webserverHTTP и WebSocket сервер для доступа к API
json_rpcМаршрутизирует JSON-RPC-запросы к зарегистрированным плагинам API
database_apiЗапросы на чтение: счета, блоки, транзакции, глобальные данные
social_networkИндексирует и запрашивает контент, голоса, ответы
snapshotСоздаёт и восстанавливает снапшоты состояния
committee_apiЗапросы по заявкам committee worker
invite_apiЗапросы объектов инвайтов
paid_subscription_apiЗапросы платных подписок
account_historyИндекс истории операций по счету
account_by_keyПоиск счетов по публичному ключу
followИндекс отношений подписок/игнорирования
tagsИндексирование контента по тегам
validator_apiЗапросы расписания валидаторов и ключей подписи
debug_nodeТестовые утилиты: внедрение блоков, установка времени

Паттерны проектирования

Наблюдатель на основе событий (Сигналы)

Database цепочки генерирует события fc::signal в ключевых точках. Плагины подписываются на эти сигналы для реализации индексирования, истории и уведомлений без привязки к ядру цепочки.

push_block() / push_transaction()

    ├── pre_apply_operation  ──► плагины-подписчики (пре-хуки)
    ├── [evaluator применяет изменение состояния]
    ├── post_apply_operation ──► плагины-подписчики (пост-хуки)
    └── applied_block        ──► плагины-подписчики (блок финализирован)

Фабрика + Стратегия (Реестр Evaluator'ов)

Каждый тип операции имеет выделенный класс evaluator. EvaluatorRegistry сопоставляет идентификаторы типов операций с экземплярами evaluator'ов. При применении транзакции:

  1. Database извлекает тег типа операции из static_variant.
  2. Реестр возвращает зарегистрированный evaluator.
  3. Метод evaluator'а do_apply(op) изменяет состояние базы данных.

Добавление новой операции требует только регистрации нового evaluator'а — без изменений в цикле диспетчеризации.

Архитектура на основе плагинов (AppBase)

vizd/main.cpp регистрирует все плагины в AppBase перед вызовом app().exec(). Каждый плагин объявляет свои параметры и зависимости; AppBase управляет порядком и жизненным циклом.

main() ──► register_plugin<chain>()
       ──► register_plugin<validator>()
       ──► register_plugin<p2p>()
       ──► ...
       ──► app().initialize(argc, argv)
       ──► app().startup()
       ──► app().exec()   ← цикл событий до SIGINT/SIGTERM

Разделение MVC

СлойКомпонентОтветственность
Данныеlibraries/chain/databaseПостоянство состояния, валидация, сигналы
УправлениеПлагины (chain, validator, p2p)Жизненный цикл, приём блоков/транзакций, координация
ПредставлениеПлагины API (database_api, social_network, …)Конечные точки запросов только на чтение

Поток данных: входящий блок

Пир (P2P) ──► p2p_plugin::handle_block()
              ──► chain_plugin::accept_block()
                  ──► database::push_block()
                      ├── валидация заголовка и подписи блока
                      ├── для каждой транзакции:
                      │     ├── валидация авторитетов
                      │     └── evaluator->do_apply(operation)
                      ├── обработка виртуальных операций (вознаграждения, кэшауты)
                      ├── сигнал applied_block
                      └── обновление fork DB / LIB

Поток данных: API-запрос

Клиент (HTTP/WS) ──► webserver_plugin
                  ──► json_rpc_plugin::call()
                      ──► registry.find_api_method(api, method)
                          ──► database_api / social_network / ...
                              ──► database::get_*(...)
                                  ──► возврат JSON-результата

Модель параллелизма

ЗадачаПодход
Операции записиЕдинственный поток записи (опциональный параметр single-write-thread)
Операции чтенияНесколько параллельных читателей через разделяемую память chainbase
P2P-ввод/выводВыделенный пул потоков boost::asio::io_service
Таймер производства блоковИзолированные io_service + поток в плагине валидатора для предотвращения задержек P2P
Обслуживание RPCНастраиваемый пул потоков (rpc-endpoint-thread-pool-size)

Важнейший инвариант: только один поток может удерживать блокировку записи на базе данных в любой момент времени. Весь код evaluator'ов и обработки блоков выполняется под этой блокировкой.


База данных в разделяемой памяти

Состояние хранится в memory-mapped файле (shared_memory.bin), управляемом chainbase. Ключевые свойства:

  • Все объектные индексы (счета, блоки, контент, валидаторы, …) находятся в этом файле.
  • Файл увеличивается инкрементально, когда свободное место падает ниже порога.
  • При чистом завершении файл согласован; сбой может потребовать воспроизведения с лога блоков.
  • Узлы могут экспортировать снапшот состояния разделяемой памяти на границе блока — см. Снапшоты.

Карта исходников

ФайлРоль в архитектуре
programs/vizd/main.cppРегистрация плагинов и запуск
libraries/chain/include/graphene/chain/database.hppИнтерфейс основной базы данных и сигналы
libraries/chain/include/graphene/chain/evaluator_registry.hppФабрика evaluator'ов операций
libraries/network/include/graphene/network/node.hppИнтерфейс делегата P2P-узла
libraries/protocol/include/graphene/protocol/operations.hppОбъединение типов операций
plugins/chain/plugin.cppПлагин цепочки: приём блоков/транзакций
plugins/json_rpc/plugin.cppДиспетчеризация JSON-RPC