β‚Ώ
Storage Model
Bitcoin
UTXO Model + Binary Merkle Tree

Bitcoin has no account state. Instead, it tracks Unspent Transaction Outputs (UTXOs). The UTXO set lives in a flat LevelDB. Transactions reference UTXOs β€” no trie needed.

The UTXO Model

πŸ’°

What is a UTXO?

A UTXO = an unspent output from a previous transaction. Like a physical coin you haven't spent yet.

Your Bitcoin "balance" = sum of all UTXOs that your key can spend.

β†’

How Spending Works

  1. 1. Find UTXOs you own
  2. 2. Reference them as inputs
  3. 3. Create new output UTXOs
  4. 4. Old UTXOs are deleted from DB
  5. 5. New UTXOs are added to DB
πŸ—„οΈ

Storage Layout

blocks/ β€” raw block bytes
chainstate/ β€” UTXO set only
Both are LevelDB instances

Interactive: UTXO Set (chainstate DB)

This is what Bitcoin's LevelDB looks like. Each row is one entry. Click a UTXO to see its details.

UNSPENTKEY:0xa3b4c5...:0
1.0 BTC
UNSPENTKEY:0xf1e2d3...:1
0.5 BTC
SPENTKEY:0xc8d9e0...:0
2.0 BTC
UNSPENTKEY:0x112233...:2
0.25 BTC
↑ Crossed-out entries are spent UTXOs removed from the DB. The chainstate DB only holds live UTXOs.

How a UTXO Gets Its Storage Key

UTXO Key Formation
1Start with Transaction
Tx: Alice sends 1 BTC to Bobraw_tx_bytes

A Bitcoin transaction contains inputs (UTXOs being spent) and outputs (new UTXOs created).

2Double-SHA256 Hash
SHA256(SHA256(raw_tx_bytes))txid = 0xa3b4c5d6...
3Combine with Output Index
txid + vout_index (e.g., 0)UTXO key = txid:0
4Store in chainstate DB
'C' + txid + voutDB['C'+txid+':0'] = {amount, script}
Final DB Key:
DB['C'+txid+':0'] = {amount, script}
chainstate-db.jsjs
1// Bitcoin's UTXO Set β€” stored in LevelDB "chainstate" DB
2// Key: 'C' + txid + vout_index
3// Value: VARINT(height*2 + coinbase) + compressed_txout
4
5DB["C" + txid + vout] = {
6 height: 750000,
7 is_coinbase: false,
8 amount: 0.5 BTC, // 50,000,000 satoshis
9 scriptPubKey: "76a914...88ac" // Pay-to-PubKeyHash
10}
11
12// To check if a UTXO exists: just look it up in the DB
13// If key exists β†’ UTXO is unspent
14// If key missing β†’ already spent

Merkle Tree β€” Verifying Transactions

Each Bitcoin block header contains a Merkle root β€” a single hash that commits to all transactions in the block. This enables SPV (Simple Payment Verification): light clients can verify a tx is in a block without downloading all transactions.

Bitcoin Block Merkle Tree
LEAF0x7e232c87…LEAF0x34b244d0…LEAF0x7b977bd9…LEAF0x40625fbe…LEVEL 10x484e57cc…LEVEL 10x291367e5…MERKLE ROOT0x0cfb88df…Tx: Aliceβ†’Bob 1BTCTx: Bobβ†’Carol 0.5BTCTx: Daveβ†’Eve 2BTCTx: Fee payment
Merkle Root = hash of all child hashesLeaf = hash(transaction)Click level buttons to highlight
merkle-proof.txt
1// Merkle Inclusion Proof
2// Prove Tx3 is in the block WITHOUT downloading all transactions
3
4Block Header:
5 merkle_root = 0xabc123... // 32 bytes
6
7Proof for Tx3:
8 tx3_hash = hash(Tx3)
9 sibling_hash = hash(Tx4) // sibling at level 0
10 uncle_hash = hash(Tx1+Tx2) // uncle at level 1
11
12Verification:
13 step1 = hash(tx3_hash + sibling_hash) // = hash(Tx3+Tx4)
14 step2 = hash(uncle_hash + step1) // = merkle_root
15
16 assert step2 == block.merkle_root // βœ… Tx3 is in block!
πŸ”

Why Merkle Trees?

  • βœ… Prove a tx is in a block with just O(log n) hashes
  • βœ… Light clients verify without full blocks
  • βœ… Any change to any tx invalidates the root
  • βœ… Block header stays small (80 bytes) regardless of tx count
πŸ“

Proof Size

1,000 txs β†’ need 10 hashes (logβ‚‚ 1000)
1,000,000 txs β†’ need 20 hashes
= 20 Γ— 32 bytes = just 640 bytes!

Full Block Structure

block-structure.jsjs
1// Bitcoin Block β€” what's stored vs what's computed
2{
3 // ---- BLOCK HEADER (80 bytes) ----
4 version: 4,
5 prev_hash: "000000000000abc123...", // links to parent block
6 merkle_root:"0xdef456...", // root of all tx hashes
7 timestamp: 1704067200,
8 bits: 0x1d00ffff, // difficulty target
9 nonce: 2083236893, // proof of work
10
11 // ---- TRANSACTIONS (variable) ----
12 txs: [
13 { txid: "0xaaa...", inputs: [...], outputs: [...] },
14 { txid: "0xbbb...", inputs: [...], outputs: [...] },
15 // ...thousands more
16 ]
17}
18
19// What's in LevelDB:
20// blocks/ β†’ raw block data indexed by hash
21// chainstate/ β†’ UTXO set only (not full tx history!)

Why UTXO vs Account Model?

βœ… Bitcoin UTXO Advantages

  • πŸ”€ Parallel processing β€” UTXOs are independent
  • πŸ”’ No double-spend by design β€” each UTXO used once
  • πŸ” Simple to audit β€” total supply = sum of all UTXOs
  • πŸ›‘οΈ Privacy-friendly β€” new addresses per transaction

❌ Bitcoin UTXO Limitations

  • πŸ“œ No smart contracts β€” only Bitcoin Script
  • πŸ’Έ UTXO management β€” fragmented balance
  • 🏦 No account concept β€” complex for applications
  • πŸ“¦ Heavier transactions β€” must reference all inputs

How UTXO Storage β†’ Capabilities

βœ…
UTXO = independent coin units

Natural UTXO parallelism β€” txs spending different UTXOs can be processed independently

βœ…
No global account state trie

No complex trie updates β€” writes are fast and predictable

⚠️
Only UTXOs stored (not full tx history)

chainstate DB stays small β€” can't query old state without archive data

βœ…
Merkle tree in block header

SPV light clients possible β€” verify txs without downloading everything