Tutorials → 04

Verified State

Understand how Lattice uses Merkle trees for state verification, content-addressed storage, and trustless data integrity.

The Big Idea

Every piece of data in Lattice is content-addressed: its identifier is a hash of its content. This means:

Content-Addressed Storage in Practice

Every block, transaction, and state entry in Lattice goes through the Acorn content-addressed storage layer.

import Acorn
import AcornMemoryWorker

let worker = MemoryCASWorker(capacity: 10_000)

// Store data — the CID is deterministic from content
let data = Data("account:0xabc has balance 1000".utf8)
let cid = ContentIdentifier(for: data)
await worker.store(cid: cid, data: data)

print("CID: \(cid.description)")
// CID: bafk...  (deterministic hash of the content)

// Anyone with this CID can verify the data's integrity
if let retrieved = await worker.get(cid: cid) {
    let valid = ContentIdentifier(for: retrieved) == cid
    print("Data integrity verified: \(valid)")  // true
}
CID guarantees
A ContentIdentifier is a cryptographic commitment. If someone hands you a CID and data that matches it, you know the data hasn't been tampered with — even if the sender is malicious. This is the foundation of trustless verification.

Merkle Trees: The State Layer

Lattice uses Cashew's Merkle data structures for all chain state. Each state entry is a leaf in a Merkle tree, and the tree's root hash commits to the entire state.

          Root Hash: 0xabc123...
              /            \
         Hash(L+R)       Hash(L+R)
          /    \          /    \
    balance:A  balance:B  state:x  state:y
    = 1000     = 500      = "foo"  = "bar"

The root hash is stored in the block header. This means every block commits to the entire state at that point in time.

How State Verification Works

When you want to prove a specific value exists in the state, you don't need the full tree — just a Merkle proof: the sibling hashes along the path from the leaf to the root.

What a Merkle Proof Contains

To prove balance:A = 1000 exists:

    Root: 0xabc123     ← block header contains this
         /       \
    ✓ H(L+R)    [H(L+R)]  ← sibling hash provided
      /    \
  [balance:A]  [H(B)]     ← value + sibling hash provided
   = 1000

Proof = [value, sibling_hash_1, sibling_hash_2, ...]
Size  = O(log n) hashes for n state entries

A verifier recomputes the root from the proof. If it matches the root in the block header, the value is genuine.

How Lattice Uses This Internally

Block State Commitments

Every block contains state headers — Merkle roots for different state categories:

StateDescriptionUsed For
HomesteadAccount balances and general stateBalance checks, state queries
FrontierPending operations (withdrawals, deposits)Cross-chain verification
Parent HomesteadParent chain's state referenceChild chain verifying parent

Transaction Validation via State

When a miner validates a transaction, it checks the action's oldValue against the current state tree. If the state says key "balance:A" is 1000 but the transaction claims it's 500, the transaction is rejected. This is verified via Merkle proof.

// This action says: "change greeting from nil to hello"
let action = Action(
    key: "greeting",
    oldValue: nil,            // asserts key doesn't exist yet
    newValue: "hello"
)

// If "greeting" already exists in the state tree,
// the Merkle proof will show oldValue is wrong
// → transaction rejected
Optimistic execution, verified state

Actions in Lattice are optimistic state transitions: the sender asserts what the current state is (oldValue) and what it should become (newValue). The network verifies these assertions against the Merkle state tree. Invalid assertions are rejected at the protocol level.

This is more flexible than account-model blockchains (like Ethereum) because it supports arbitrary key-value state, not just balance transfers. And it's more verifiable than UTXO models because the full state tree is committed in every block.

Cross-Chain Verification

This is where Merkle proofs really shine. When a child chain needs to verify a withdrawal on the parent chain (from Tutorial 3), it doesn't ask the parent — it reads the proof from the parent block that's anchored in the child's chain:

  1. Parent block includes a withdrawal in its state
  2. Parent block hash is referenced in the child block (anchoring)
  3. Child chain reads the parent block's state root
  4. Child chain verifies the withdrawal exists via Merkle proof against that root

No RPC calls, no trust assumptions, no bridge validators. The proof is in the math.

The Storage Stack

Merkle trees are backed by the tiered Acorn storage chain. When you verify a proof, the CAS layer fetches each node:

Verification request: "Does key X exist in state root R?"

    ① Check MemoryCASWorker (hot cache)
        ↓ miss
    ② Check DiskCASWorker (persistent store)
        ↓ miss
    ③ Fetch from NetworkCASWorker (P2P via Ivy)
        ↓ found!
    ④ Backfill: store in disk → store in memory
    ⑤ Verify hash chain from leaf to root R

This layered approach means frequently-accessed state stays in memory, cold state lives on disk, and missing data is transparently fetched from peers.

Practical Implications

ScenarioHow Merkle State Helps
Light client Verify any state entry with just the block header and a small proof — no need to download the full state
Cross-chain transfers Trustlessly verify operations on other chains using embedded state roots
State sync New nodes can verify state snapshots without replaying every block from genesis
Fraud detection Anyone can prove a block contains invalid state transitions by showing the Merkle proof contradicts the claimed oldValue
Sparse storage Nodes don't need the full state tree — they can lazily load branches on demand from the CAS layer

What's Next

You've now covered the four core concepts of Lattice:

  1. Running a chain — genesis, mining, transactions
  2. Child chains — hierarchy, merged mining, independent throughput
  3. Cross-chain transfers — withdrawal/deposit/receipt lifecycle
  4. Verified state — Merkle proofs, CAS, trustless verification

Explore the API reference for each package: