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:
- Integrity is automatic — if you have the hash and the data, you can verify they match. No trust required.
- Deduplication is free — identical data always produces the same hash, so it's stored exactly once.
- Proofs are compact — you can prove a value exists in a huge state tree by providing just a small chain of hashes.
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
}
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:
| State | Description | Used For |
|---|---|---|
| Homestead | Account balances and general state | Balance checks, state queries |
| Frontier | Pending operations (withdrawals, deposits) | Cross-chain verification |
| Parent Homestead | Parent chain's state reference | Child 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
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:
- Parent block includes a withdrawal in its state
- Parent block hash is referenced in the child block (anchoring)
- Child chain reads the parent block's state root
- 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
| Scenario | How 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:
- Running a chain — genesis, mining, transactions
- Child chains — hierarchy, merged mining, independent throughput
- Cross-chain transfers — withdrawal/deposit/receipt lifecycle
- Verified state — Merkle proofs, CAS, trustless verification
Explore the API reference for each package: