State and snapshots
How Tenzro Network 1 commits chain state to roots, stores it durably, and lets new nodes start from snapshots verified against consensus-signed roots.
Every Tenzro Network 1 node keeps the chain's state: accounts and TNZO balances, contract storage for the EVM, SVM and DAML runtimes, identities, providers, models, settlements and the rest. This page covers how that state is committed, how it is stored, and how a new node gets it without replaying the chain from genesis.
State roots
State is organised in a Merkle Patricia trie. After each block executes, the node computes the state root, a single hash that commits to the entire state. Two nodes with the same root hold exactly the same state.
State roots are what consensus signs. Before a validator signs a checkpoint it checks that its own executed state root matches, and the resulting finality certificate commits to that root. See Finality certificates.
A root lets anyone prove a single fact without the whole state. A Merkle proof for an account links its value to the root through the sibling hashes along its path; if the root is certified, the value is too. Light clients and bridges rely on this.
Durability
Finalised blocks are written with a synchronous flush to disk before the node reports them as final, so a crash never loses a block the node has already confirmed. The genesis block is persisted on first start, and on every later start the node checks the data on disk against the configured genesis. If they do not match, it refuses to start.
Snapshots
A snapshot is the complete state at one height, split into chunks. Its manifest records:
| Field | Meaning |
|---|---|
height | Block height the snapshot was taken at |
state_root_hex | State root at that height |
num_chunks | Number of chunks |
chunk_hashes_hex | Hash of every chunk, in order |
created_at | When it was produced |
format | Chunk encoding version |
Producing snapshots costs disk, so it is off by default. Nodes that serve new peers, typically RPC and archive nodes, switch it on:
[snapshot]
enabled = true
interval_blocks = 100000 # blocks between snapshots
retain_count = 2 # snapshots kept on diskSnapshots are served over open RPC methods:
# List available snapshots
curl -s https://rpc.tenzro.xyz -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tenzro_listSnapshots","params":[]}'
# Fetch the manifest for one height
curl -s https://rpc.tenzro.xyz -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tenzro_getSnapshotManifest","params":{"height":<height>}}'
# Fetch one chunk
curl -s https://rpc.tenzro.xyz -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"tenzro_getSnapshotChunk","params":{"height":<height>,"chunk_index":0}}'An operator can also trigger a snapshot on its own node with tenzro_produceSnapshot, an admin method.
Starting a node from a snapshot
A snapshot comes from a peer you do not need to trust, because every part of it is checked:
- Pick a trusted root. The node takes a state root from a finality certificate it has verified against the validator set, following epoch handovers from genesis. A root can also be pinned in the genesis file or passed on the command line.
- Match the manifest. The snapshot manifest's
state_root_hexmust equal the trusted root bit for bit. If it does not, the node refuses the snapshot. - Check every chunk. Each chunk is hashed and compared with the manifest before it is applied, so a peer cannot slip in altered data.
- Commit atomically. The state is written in one step once every chunk has verified.
- Catch up. The node replays the blocks since the snapshot height, each covered by finality certificates, and joins the network at the tip.
Without a trusted root the node does not apply any chunk: snapshot sync fails closed.
To sync from a specific peer with an explicit anchor:
tenzro-node \
--data-dir ./data \
--genesis genesis.toml \
--state-sync-from https://rpc.tenzro.xyz \
--state-sync-anchor 0x<state root> \
--state-sync-height <height>--state-sync-height also makes block import reject any block at that height whose state root differs from the anchor. A genesis file can carry the same anchor, so a fresh node started with --bootstrap-dns finds a peer, fetches the newest snapshot and verifies it with no extra flags:
[weak_subjectivity]
height = <height>
state_root_hex = "0x<state root>"Reading state
tenzro_getBlockreturns a block with itsstate_root.tenzro_getFinalizedBlockreturns the latest finalised height.tenzro_getBalance,eth_getStorageAtand the other read methods return current state.