Skip to main content
Committed state has to satisfy two demands that pull against each other. Execution wants the current value of a key, fast, millions of times. Verification wants a proof that a value was what the network says it was at a particular version. Serving both from one structure means doing both badly. Storage therefore splits the problem across separate stores, each shaped for its own access pattern.

Three stores

Committed block
Ledger storetransactions · outputs · events · write sets · accumulators · block metadata
Replay and audit
State KV storecurrent and historical values, sharded sixteen ways, hot state in its own tier
Queries and execution
State Merkle storea versioned sparse Merkle tree, plus the indexes tracking superseded nodes
Verification — proofs
A read that only needs a value does not pay for tree traversal, and a proof is not reconstructed from a store optimized for point lookups.
The ledger store holds the chain’s history: transactions, their outputs and auxiliary data, events, write sets, block metadata, and the accumulators. This is what you replay. The state KV store holds state values, addressed by key and version. This is what execution and queries read. It is sharded sixteen ways, so writes from one block spread across sixteen independent RocksDB instances rather than contending on one. Frequently-accessed state is additionally kept in its own tier, so the working set of an active market does not have to be found among everything the chain has ever stored. The state Merkle store holds the authenticated structure — the tree nodes that let a value be proven, and the indexes that track which nodes have been superseded. Splitting them means a read that only needs a value does not pay for tree traversal, and a proof does not have to be reconstructed from a store optimized for point lookups.

Authentication

Two Merkle structures do different jobs.
Versioned sparse Merkle tree
Accumulators
Your key and its value
sibling hash
sibling hash
State root, committed by consensus
Your transaction, or an event
Transaction accumulator · event accumulatoreach commits to everything included so far, in order
Ledger root, committed by consensus
The tree proves what a value was; the accumulators prove what happened and in what order. Together they make “this transaction is in the chain at this position” provable without holding the chain.
A versioned sparse Merkle tree authenticates state. Every key has a position determined by its hash, and every version of the tree shares the nodes that did not change — so writing one key in a block adds a path, not a tree. A proof for a key at a version is the path from that key’s leaf to the root the network committed. Accumulators authenticate sequence. One accumulates transactions and one accumulates events, each producing a root that commits to everything included so far in order. This is what makes “this transaction is in the chain at this position” provable without holding the chain. Between them: the state tree proves what a value was, the accumulators prove what happened and in what order.

Speculative state

A block’s results exist before they are committed. Rather than writing them into the durable tree and undoing that if the block does not commit, uncommitted state is held in an in-memory sparse Merkle overlay on top of the last committed version. Execution reads through the overlay and sees a consistent view. If the block commits, the overlay is materialized. If it does not, the overlay is dropped and nothing durable was ever touched. This is what keeps speculative execution from leaving debris in storage.

Caching

Tree nodes are cached at two levels: a version-aware cache that keeps recent versions addressable, and a least-recently-used cache underneath it. The access pattern of a trading chain — a small set of hot keys touched every block, against a long tail touched rarely — is exactly the shape these are for.

Pruning

Keeping every version forever is a choice, not a requirement. Three independent pruners run against the three stores, each with its own retention policy: one over the ledger, one over state values, one over Merkle nodes. The Merkle and state-value pruners are driven by stale indexes written at the same time as the data. When a version supersedes a node or a value, the superseded entry is recorded as stale at that version. Pruning is then a range scan over an index rather than a search for garbage — the writer already said what would become collectable and when.
Retention is an operator decision with real consequences. A node pruned aggressively serves current state efficiently and cannot answer historical queries or serve state sync to a node starting from further back. An archival node keeps everything and pays for it. See Run a node.

Backup and restore

The stores can be backed up and restored independently of the running node, which is what makes it possible to stand up a node from a snapshot rather than replaying from genesis, and to verify a restored node’s state against the committed roots rather than trusting the backup.

Where to go next

State sync

How a node catches up to the chain without replaying all of it.

State model

What is being stored, and which representation is authoritative.

Indexer

Reconstructing history from committed records.

Run a node

Node roles, and how to ask about operating one.