State & Storage

How account and storage state is represented in tries, cached in memory, persisted to disk, and garbage-collected.

Path: state-storage

Third-party documentation. This is independently authored analysis of the public Go Ethereum (Geth) codebase — not the official docs, and not reviewed or endorsed by the Go Ethereum (Geth) team.

State & Storage

Ethereum state is a key-value universe; Geth represents it with Merkle-Patricia tries so that any state snapshot commits to a single 32-byte root that peers can verify.

The two tries

Account state lives in one trie keyed by address hash: each leaf records nonce, balance, storage root, and code hash for an account. Each account's contract storage is its own trie keyed by storage slot hash, whose root is stored in the account. Block headers commit to the account-trie root — that is the entire world state compressed to 32 bytes.

Node database and caching

Trie nodes persist in a key-value store (Pebble by default, LevelDB historically), deduplicated by node hash — unchanged subtrees are shared between blocks, which makes history cheap and reorgs shallow operations. On top sits a multi-layer in-memory cache: recent tries stay hot, writes batch into memory until commit, and a write-only diff layer accelerates repeated access during block processing.

Pruning

Because every historical state revision is technically reachable, unpruned archives grow enormous. Geth's default mode prunes old trie nodes that fall outside the recent window, keeping disk bounded while sacrificing historical-state queries. Archive nodes disable pruning and pay full disk cost in exchange for answering state at any historical block.

Snapshots

A flat, per-account storage-layer snapshot accelerates both execution (fast account/slot lookups without descending tries) and snap sync (serving state as verifiable flat ranges). The snapshot builds incrementally from the trie as the chain extends.