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
The Versioning Killer Feature
Cosmos has built-in versioning — query any block height without extra infrastructure.
IAVL Node Structure
1// IAVL Tree — Immutable AVL + Versioned2// Used in Cosmos SDK for all module stores34// Each node in IAVL:5type Node struct {6 key []byte // storage key7 value []byte // stored value (nil for inner nodes)8 version int64 // which block height created this9 height int8 // subtree height (for AVL balance)10 size int64 // number of key-value pairs in subtree11 hash []byte // merkle hash of this node12 leftHash []byte // left child hash13 rightHash []byte // right child hash14 leftNode *Node15 rightNode *Node16}1718// DB key = hash(node)19// DB val = encoded(node)20// — exactly like Ethereum! Same concept, different tree shape.
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.
1// Cosmos Multi-Store Architecture2// Each module gets its own isolated IAVL store34CommitMultiStore {5 stores: {6 "bank": IAVLStore, // token balances7 "staking": IAVLStore, // validators, delegations8 "gov": IAVLStore, // proposals, votes9 "ibc": IAVLStore, // IBC channels, packets10 "wasm": IAVLStore, // CosmWasm contracts11 "distribution": IAVLStore, // rewards, commissions12 // ... more modules13 }14}1516// App hash (like Ethereum's stateRoot) =17// merkle root of all store roots18AppHash = merkle.Hash([19 ("bank", bankStore.root),20 ("staking", stakingStore.root),21 // ...22])
Historical Queries — Cosmos's Superpower
1// IAVL Versioning — Historical Queries Built-In!2// This is the BIG advantage over Ethereum MPT34// Each tree version is immutable — creates new root5// Old nodes are shared (copy-on-write)67v1_tree: root_v1 → [node_a, node_b, node_c]8v2_tree: root_v2 → [node_a', node_b, node_c] // only modified nodes are new9 ↑ new node (value changed)10 ↑ shared from v111 ↑ shared from v11213// Query historical state:14store.GetVersioned(height: 1000, key: "balance/alice")15// → returns Alice's balance at block 1000 without archive node!1617// Pruning: keep last N versions18// Default: keep 362880 versions (~3 weeks at 1 block/sec)1920// 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
1// Cosmos Bank module storage2// Module: bank, Key format: address bytes34// Store balance:5bankStore.Set(6 key: sdk.AccAddress(alice_bech32), // 20 bytes7 value: sdk.Coins{{"uatom", 1000000}} // protobuf encoded8)910// The key in IAVL:11// prefix "balances/" ++ address_bytes1213// Historical query (Cosmos can do this natively!):14// GET /cosmos/bank/v1beta1/balances/{address}?height=50000015ctx.WithBlockHeight(500000)16bankKeeper.GetBalance(ctx, alice, "uatom")1718// 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.
1// IBC (Inter-Blockchain Communication) — powered by IAVL2// Light clients verify state via IAVL Merkle proofs34// Proof that channel "channel-0" exists on Chain A:5// 1. Chain B has a light client of Chain A6// 2. Light client stores Chain A's latest AppHash7// 3. IBC relayer provides: IAVL Merkle proof of the IBC key8// 4. Chain B verifies: proof against stored AppHash910// IBC storage key in IAVL:11ibcStore.Set(12 key: "channelEnds/ports/transfer/channels/channel-0",13 value: proto.Marshal(channelEnd)14)1516// Proof verification:17root.Verify(18 key: ibcKey,19 value: channelEndBytes,20 proof: MerkleProof{...} // path through IAVL21) // → if true, cross-chain message is valid
How IAVL Storage → Capabilities
Immutable versioned treeHistorical state queries at any block height — no archive node required. Query Alice's balance 3 months ago directly.
Per-module isolated storesModules are isolated — bank can't accidentally touch staking state. Clean separation enables modular app development.
IAVL Merkle proofsIBC works because any state can be proven to a remote chain with a compact proof. Enables trustless cross-chain messaging.
AVL rebalancing on writesWrite-heavy workloads trigger rebalancing. More predictable than MPT but adds overhead for insert-heavy patterns.
Multiple store roots → single AppHashEach module root is separately verifiable. But the extra Merkle aggregation adds overhead per block.