Обзор архитектуры
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/chain | database.hpp | Состояние блокчейна: счета, блоки, объекты, fork DB, разделяемая память |
libraries/protocol | operations.hpp | Объединение static_variant всех 64+ типов операций |
libraries/network | node.hpp | P2P-движок: соединения с пирами, синхронизация, распространение сообщений |
libraries/api | chain_api_properties.hpp | Общие типы, возвращаемые плагинами API |
libraries/wallet | wallet.hpp | Вызовы API удалённого узла, построение транзакций |
libraries/time | time.hpp | Синхронизация NTP для тайминга блоковых слотов |
Плагины
Плагины регистрируются во фреймворке AppBase при запуске и реализуют хуки жизненного цикла (plugin_initialize, plugin_startup, plugin_shutdown).
| Плагин | Роль |
|---|---|
chain | Открывает базу данных, валидирует и применяет блоки/транзакции |
validator | Производит блоки по расписанию (Fair-DPOS), управляет NTP и watchdog |
p2p | Управляет соединениями с пирами, синхронизирует блоки, распространяет транзакции |
webserver | HTTP и 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'ов. При применении транзакции:
Databaseизвлекает тег типа операции изstatic_variant.- Реестр возвращает зарегистрированный evaluator.
- Метод 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 |