Skip to content

Architecture Overview

VIZ Ledger is implemented as a modular C++ daemon (vizd) composed of layered libraries and a plugin system. This page describes the structural layers, design patterns, and component interactions.


Layered Structure

┌─────────────────────────────────────────────────────────────────┐
│  Programs                                                       │
│  vizd (node daemon)          cli_wallet (CLI wallet)            │
├─────────────────────────────────────────────────────────────────┤
│  Plugins                                                        │
│  chain │ validator │ p2p │ webserver │ json_rpc │ database_api  │
│  account_history │ snapshot │ committee_api │ invite_api │ ...   │
├─────────────────────────────────────────────────────────────────┤
│  Core Libraries                                                 │
│  libraries/chain     — blockchain state machine, fork db        │
│  libraries/protocol  — operation types, transactions            │
│  libraries/network   — peer-to-peer messaging                   │
│  libraries/api       — shared API property types                │
│  libraries/wallet    — transaction building helpers             │
│  libraries/time      — NTP-aware time utilities                 │
└─────────────────────────────────────────────────────────────────┘

Libraries

LibraryKey filePurpose
libraries/chaindatabase.hppBlockchain state: accounts, blocks, objects, fork DB, shared memory
libraries/protocoloperations.hppstatic_variant union of all 64+ operation types
libraries/networknode.hppP2P engine: peer connections, sync, message propagation
libraries/apichain_api_properties.hppShared types returned by API plugins
libraries/walletwallet.hppRemote node API calls, transaction construction
libraries/timetime.hppNTP synchronisation for block slot timing

Plugins

Plugins are registered with the AppBase framework at startup and implement lifecycle hooks (plugin_initialize, plugin_startup, plugin_shutdown).

PluginRole
chainOpens the database, validates and applies blocks/transactions
validatorProduces blocks on schedule (Fair-DPOS), manages NTP and watchdog
p2pManages peer connections, syncs blocks, propagates transactions
webserverHTTP and WebSocket server for API access
json_rpcRoutes JSON-RPC requests to registered API plugins
database_apiRead queries: accounts, blocks, transactions, globals
account_by_keyLookup accounts by public key
validator_apiValidator schedule and signing key queries

Design Patterns

Event-Driven Observer (Signals)

The chain Database emits fc::signal events at key points. Plugins subscribe to these signals to implement indexing, history, and notifications without coupling to the core chain.

push_block() / push_transaction()

    ├── pre_apply_operation  ──► subscriber plugins (pre-hooks)
    ├── [evaluator applies state change]
    ├── post_apply_operation ──► subscriber plugins (post-hooks)
    └── applied_block        ──► subscriber plugins (block finalized)

Factory + Strategy (Evaluator Registry)

Every operation type has a dedicated evaluator class. The EvaluatorRegistry maps operation type IDs to evaluator instances. When a transaction is applied:

  1. Database extracts the operation type tag from the static_variant.
  2. Registry returns the registered evaluator.
  3. Evaluator's do_apply(op) mutates the database state.

Adding a new operation requires only registering a new evaluator — no changes to the dispatch loop.

Plugin-Based Architecture (AppBase)

vizd/main.cpp registers all plugins with AppBase before calling app().exec(). Each plugin declares its options and dependencies; AppBase handles ordering and lifecycle.

main() ──► register_plugin<chain>()
       ──► register_plugin<validator>()
       ──► register_plugin<p2p>()
       ──► ...
       ──► app().initialize(argc, argv)
       ──► app().startup()
       ──► app().exec()   ← event loop runs until SIGINT/SIGTERM

MVC Separation

LayerComponentResponsibility
Datalibraries/chain/databaseState persistence, validation, signals
ControlPlugins (chain, validator, p2p)Lifecycle, block/tx acceptance, coordination
ViewAPI plugins (database_api, account_history, …)Read-only query endpoints

Data Flow: Incoming Block

Peer (P2P) ──► p2p_plugin::handle_block()
              ──► chain_plugin::accept_block()
                  ──► database::push_block()
                      ├── validate block header, signature
                      ├── for each transaction:
                      │     ├── validate authorities
                      │     └── evaluator->do_apply(operation)
                      ├── process virtual operations (rewards, cashouts)
                      ├── emit applied_block signal
                      └── update fork DB / LIB

Data Flow: API Request

Client (HTTP/WS) ──► webserver_plugin
                  ──► json_rpc_plugin::call()
                      ──► registry.find_api_method(api, method)
                          ──► database_api / account_history / ...
                              ──► database::get_*(...)
                                  ──► return JSON result

Concurrency Model

ConcernApproach
Write operationsSingle write thread (optional single-write-thread config)
Read operationsMultiple concurrent readers via chainbase shared memory
P2P I/ODedicated boost::asio::io_service thread pool
Block production timerIsolated io_service + thread in validator plugin to prevent P2P delays
RPC servingConfigurable thread pool (rpc-endpoint-thread-pool-size)

The most important invariant: only one thread may hold a write lock on the database at a time. All evaluators and block-processing code runs under this lock.


Shared Memory Database

State is stored in a memory-mapped file (shared_memory.bin) managed by chainbase. Key properties:

  • All object indexes (accounts, blocks, content, validators, …) live in this file.
  • The file is resized incrementally when free space drops below a threshold.
  • On a clean shutdown the file is consistent; a crash may require replay from the block log.
  • Nodes can export a snapshot of the shared memory state at a block boundary — see Snapshots.

Source Map

FileRole in architecture
programs/vizd/main.cppPlugin registration and startup
libraries/chain/include/graphene/chain/database.hppCore database interface and signals
libraries/chain/include/graphene/chain/evaluator_registry.hppFactory for operation evaluators
libraries/network/include/graphene/network/node.hppP2P node delegate interface
libraries/protocol/include/graphene/protocol/operations.hppOperation type union
plugins/chain/plugin.cppChain plugin: block/tx acceptance
plugins/json_rpc/plugin.cppJSON-RPC dispatch