Storage Model
Cosmos / Tendermint
IAVL Tree (Immutable AVL + Versioned)

Cosmos uses an IAVL tree — an immutable, versioned AVL tree. The killer feature: historical state queries built-in, and each module has its own isolated store that powers IBC interoperability.

IAVL vs Ethereum MPT

🌳

What is IAVL?

IAVL = Immutable AVL Tree with versioning. It's a balanced binary tree (AVL) where old versions are never modified.

Like Git — each commit creates a new root, but shares unchanged subtrees with previous versions.

⚖️

AVL vs Patricia Trie

MPT (Ethereum): 16-way trie, nibble keys, O(64) depth
IAVL (Cosmos): Binary tree, balanced, O(log n) depth
AVL auto-balances via rotations. MPT doesn't balance — it has fixed 64-nibble depth.
📚

The Versioning Killer Feature

Ethereum needs archive nodes (multi-TB) for historical queries.

Cosmos has built-in versioning — query any block height without extra infrastructure.

IAVL Node Structure

iavl-node.gogo
1// IAVL Tree — Immutable AVL + Versioned
2// Used in Cosmos SDK for all module stores
3
4// Each node in IAVL:
5type Node struct {
6 key []byte // storage key
7 value []byte // stored value (nil for inner nodes)
8 version int64 // which block height created this
9 height int8 // subtree height (for AVL balance)
10 size int64 // number of key-value pairs in subtree
11 hash []byte // merkle hash of this node
12 leftHash []byte // left child hash
13 rightHash []byte // right child hash
14 leftNode *Node
15 rightNode *Node
16}
17
18// DB key = hash(node)
19// DB val = encoded(node)
20// — exactly like Ethereum! Same concept, different tree shape.
IAVL tree visual (binary, balanced)
root (height=3, v=latest)
left (h=2)right (h=2)
ll(h=1)lr(h=1)rl(h=1)rr(h=1)
Each node stores: key, value, version, height, hash
DB key = hash(node) → same concept as Ethereum

Multi-Store Architecture

Unlike Ethereum's single state trie, Cosmos gives each module its own IAVL store. The app hash is a Merkle root of all store roots.

multistore.gogo
1// Cosmos Multi-Store Architecture
2// Each module gets its own isolated IAVL store
3
4CommitMultiStore {
5 stores: {
6 "bank": IAVLStore, // token balances
7 "staking": IAVLStore, // validators, delegations
8 "gov": IAVLStore, // proposals, votes
9 "ibc": IAVLStore, // IBC channels, packets
10 "wasm": IAVLStore, // CosmWasm contracts
11 "distribution": IAVLStore, // rewards, commissions
12 // ... more modules
13 }
14}
15
16// App hash (like Ethereum's stateRoot) =
17// merkle root of all store roots
18AppHash = merkle.Hash([
19 ("bank", bankStore.root),
20 ("staking", stakingStore.root),
21 // ...
22])

Historical Queries — Cosmos's Superpower

versioning.gogo
1// IAVL Versioning — Historical Queries Built-In!
2// This is the BIG advantage over Ethereum MPT
3
4// Each tree version is immutable — creates new root
5// Old nodes are shared (copy-on-write)
6
7v1_tree: root_v1 → [node_a, node_b, node_c]
8v2_tree: root_v2 → [node_a', node_b, node_c] // only modified nodes are new
9 ↑ new node (value changed)
10 ↑ shared from v1
11 ↑ shared from v1
12
13// Query historical state:
14store.GetVersioned(height: 1000, key: "balance/alice")
15// → returns Alice's balance at block 1000 without archive node!
16
17// Pruning: keep last N versions
18// Default: keep 362880 versions (~3 weeks at 1 block/sec)
19
20// Ethereum comparison:
21// ETH: need archive node for historical queries (100s of GB)
22// Cosmos: built-in versioning, configurable retention

✅ Cosmos: Built-in Versioning

  • Query balance at block 1,000,000 → works
  • No special archive node needed
  • Configurable pruning (keep last N blocks)
  • Copy-on-write: only modified nodes duplicated
  • Enables time-locked governance

❌ Ethereum: No Built-in History

  • Historical queries need an archive node
  • Archive node = 14+ TB of data
  • MPT only stores current state
  • Past state discarded unless archived
  • Most RPCs don't support old blocks

Bank Module Storage Example

bank-module.gogo
1// Cosmos Bank module storage
2// Module: bank, Key format: address bytes
3
4// Store balance:
5bankStore.Set(
6 key: sdk.AccAddress(alice_bech32), // 20 bytes
7 value: sdk.Coins{{"uatom", 1000000}} // protobuf encoded
8)
9
10// The key in IAVL:
11// prefix "balances/" ++ address_bytes
12
13// Historical query (Cosmos can do this natively!):
14// GET /cosmos/bank/v1beta1/balances/{address}?height=500000
15ctx.WithBlockHeight(500000)
16bankKeeper.GetBalance(ctx, alice, "uatom")
17
18// This works WITHOUT archive node because IAVL stores versions

IBC — Powered by IAVL Merkle Proofs

Inter-Blockchain Communication works because IAVL produces Merkle proofs that light clients on other chains can verify.

ibc-proof.gogo
1// IBC (Inter-Blockchain Communication) — powered by IAVL
2// Light clients verify state via IAVL Merkle proofs
3
4// Proof that channel "channel-0" exists on Chain A:
5// 1. Chain B has a light client of Chain A
6// 2. Light client stores Chain A's latest AppHash
7// 3. IBC relayer provides: IAVL Merkle proof of the IBC key
8// 4. Chain B verifies: proof against stored AppHash
9
10// IBC storage key in IAVL:
11ibcStore.Set(
12 key: "channelEnds/ports/transfer/channels/channel-0",
13 value: proto.Marshal(channelEnd)
14)
15
16// Proof verification:
17root.Verify(
18 key: ibcKey,
19 value: channelEndBytes,
20 proof: MerkleProof{...} // path through IAVL
21) // → if true, cross-chain message is valid

How IAVL Storage → Capabilities

Immutable versioned tree

Historical state queries at any block height — no archive node required. Query Alice's balance 3 months ago directly.

Per-module isolated stores

Modules are isolated — bank can't accidentally touch staking state. Clean separation enables modular app development.

IAVL Merkle proofs

IBC works because any state can be proven to a remote chain with a compact proof. Enables trustless cross-chain messaging.

⚠️
AVL rebalancing on writes

Write-heavy workloads trigger rebalancing. More predictable than MPT but adds overhead for insert-heavy patterns.

⚠️
Multiple store roots → single AppHash

Each module root is separately verifiable. But the extra Merkle aggregation adds overhead per block.