Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Nodes

Nodes are long-running processes that apply an application's state and state transition function by processing events, updating metadata, and optionally producing attestations or proofs, while exposing APIs for external interaction. At a high level, a node continuously:

  • Fetches new events from a source chain or oracle (Fetcher)
  • Applies the app’s state transition to a local state (StateBuffer)
  • Updates metadata (heights, hashes, roots), and archives state snapshots (Archiver)
  • Optionally produces an attestation (signature) or a zero-knowledge proof for the new state (Attester/Prover)
  • Optionally exposes an API (ex. JSON-RPC) to read state and metadata

Under the hood, node code calls into one of the void-node loops:

  • run_app(RunConfig, ...) – execution-only
  • run_attesting_app(RunAttestingConfig, ...) – execution + attestations
  • run_proving_app(RunProvingConfig, ...) – execution + ZK proving

For startup, they either:

  • start_app(state_buffer, fetcher, archiver) – initialize from archive, then start fetching
  • start_app_with(start_state, start_metadata, state_buffer, fetcher) – initialize from an explicit state (e.g., benchmarks)

Node flavors and examples

  • Execution node (execution only)

    • Example: e2e/zk/node/
    • Uses EvmChainEventFetcher, ByteArchiver, StreamingKvStateBuffer, run_app
  • Attesting node (TEE / signature-based)

    • Example: e2e/tee/node/
    • Uses OracleEventFetcher (multiple EVM RPCs), ByteArchiver, StreamingKvStateBuffer, ECDSAAttester, run_attesting_app
  • Proving node (ZK)

    • Example: e2e/zk/proving-node/host/
    • Uses EvmChainEventFetcher, ByteArchiver, StreamingKvStateBuffer, Risc0Prover, run_proving_app
  • Benchmark node (uses test scenario)

    • Example: benchmarks/max-tps/
    • Uses TestEventFetcher, ByteArchiver, StreamingKvStateBuffer, start_app_with, run_app

Core components (from void/void-node/)

  • Fetchers (fetcher)

    • Role: connect to an event source and stream “bundles” of events, tracking heights and latest height boundaries (useful during syncing).
    • Implementations (feature-gated):
      • test-fetcher – in-memory test/scenario driver (benchmarks)
      • evm-fetcher – reads events from an EVM chain
      • oracle-fetcher – reads via an oracle stream (supports multiple EVM endpoints)
  • Archivers (archiver)

    • Role: persist and restore state and metadata; create periodic snapshots according to a min-height interval.
    • Implementations (feature-gated):
      • byte-archiver – byte-oriented archive (used by examples)
      • empty-archiver – no-op (useful for tests)
  • State Buffers (state_buffer)

    • Role: own the app State and StateMetadata with safe concurrent access; expose read/write closures and a read-only StateReader for powering APIs (ex. JSON-RPC).
    • Implementations (feature-gated):
      • stream-state-buffer – streaming KV buffer with concurrent readers (uses more memory, but allows for near limitless reads with low performance overhead)
      • lock-state-buffer – lock-based buffer (reads slow down performance, but uses less memory)
      • empty-state-buffer – no-op (useful for tests)
  • Provers and Attesters (prover/attester)

    • Role: produce proofs/attestations of an apps state (state_root) and processed events (events_hash).
    • Attester (StateAttester): produces a signature attesting to {events_hash, state_root} at intervals. Example: ECDSAAttester takes a hex private key (or reads from env if not provided explicitly).
    • Attester Implementations (feature-gated):
      • ecdsa – takes a hex private key (or reads from env if not provided explicitly) to sign over {events_hash, state_root}.
    • Prover (StateProver): produces a recursive proof for the transition from prior proof + new events + witness.
    • Prover Implementations (feature-gated):
      • r0/r0-cuda – proves using the Risc Zero zkVM.
      • sp1 – proves using the SP1 zkVM.

Runtime flow

1) Startup

  • start_app_with(...) when you already have an initial state/metadata (benchmarks)
  • start_app(...) to restore from archive and continue syncing

2) Loop (all variants)

  • Fetch next events bundle
  • Apply state_transition(state, &events)
  • Flush state (state.flush() or state.flush_with_witness() for ZK)
  • Update metadata: latest, sync_height, sync_hash, events_hash, state_root, and proof fields
  • When at configured height interval, archive snapshot via archiver.archive

3) Additional steps by variant

  • Attesting: when at configured height interval, call attester.attest, and write to metadata
  • Proving: when at configured height interval, build witness, call prover.prove, and update metadata with returned proof

JSON-RPC surface

Nodes often start a lightweight Axum-based server to expose read APIs over the StateBuffer’s StateReader, using additional tools found in void/void-node/.

  • For example, the nodes in the end-to-end tests add an rpc_router and call axum_server::start("127.0.0.1:<port>", rpc_router).
  • The json_rpc module in void/void-node/ provides helpers to build routers from a StateBuffer and method list.

Extending or writing your own node

To customize a node:

  • Pick the appropriate fetcher (or implement Fetcher)
  • Choose an archiver policy (or implement Archiver<S>)
  • Choose a state buffer (or implement StateBuffer<S>)
  • Decide whether you need attestations (StateAttester) or proofs (StateProver) (or neither), and use the corresponding run loop
  • Expose the parts of state you need via the JSON-RPC helpers (or implement custom server solution)