MODULE 1 · EVM 内部原理
Lesson 2: 状态树
📖 阅读 ~12 分钟
🧪 2 个实验
❓ 2 道测验
以太坊的"世界状态"
上一课我们知道每个账户有 4 个状态字段。但一个核心问题是:几亿个账户的状态如何高效组织、快速查询、并且能被任何人验证?
答案是:Merkle Patricia Trie(默克尔帕特里夏树)—— 一种结合了哈希树和前缀树的数据结构。
💡 为什么不用普通数据库?
普通数据库(MySQL/Postgres)可以存数据,但你无法证明数据没被篡改。以太坊需要的是:任何人拿到一个 state root(32字节哈希),就能验证任意账户的状态是否正确 —— 不需要信任任何人。
先理解 Merkle Tree
Merkle Tree(默克尔树)是一种哈希树:每个叶子节点是数据的哈希,每个内部节点是其子节点哈希的哈希。最终汇聚成一个根哈希 (Root Hash)。
// 简化的 Merkle Tree
Root Hash
/ \
Hash(AB) Hash(CD)
/ \ / \
Hash(A) Hash(B) Hash(C) Hash(D)
| | | |
Data A Data B Data C Data D
Merkle Tree 的超能力:Merkle Proof
要证明 Data C 存在于这棵树中,你不需要提供所有数据。只需要:
// 验证 Data C 只需 3 个值(蓝色标记)
Root Hash ← 已知(在区块头里)
/ \
Hash(AB) ← 提供这个 Hash(CD) ← 自己算
/ \ / \
Hash(C) Hash(D) ← 提供这个
|
Data C ← 要验证的数据
验证过程: Hash(C) = Keccak256(Data C) ✓
Hash(CD) = Keccak256(Hash(C) + Hash(D)) ✓
Root = Keccak256(Hash(AB) + Hash(CD)) = 已知 Root ✓
🔍 这就是轻节点的原理
轻节点不需要下载全部状态(几百GB),只需要区块头中的 state root(32字节),就能通过 Merkle Proof 验证任意一个账户的余额。证明大小只有 O(log n)。
Patricia Trie:前缀树优化
纯 Merkle Tree 的问题:地址是 20 字节 = 40 个十六进制字符,如果每一层只看一个字符,树会有 40 层深,查询很慢。
Patricia Trie(前缀压缩树)的解决方案:如果某条路径上只有一个值,就把中间路径压缩成一个节点。
// 普通 Trie(很深)
Root → a → b → c → d → e → f → Value
// Patricia Trie(压缩了)
Root → [abcdef] → Value
// 以太坊用的 Modified MPT 有 3 种节点:
Branch Node — 16 个子节点(对应 0-f)+ 1 个 value
Extension Node — 共享前缀 + 指向下一个节点
Leaf Node — 剩余路径 + 账户状态值
以太坊的四棵树
以太坊每个区块头里存了三个 root hash,对应三棵不同的树:
stateRoot
世界状态树 — 所有账户 (address → state) 的全量映射
key = Keccak256(address)
transactionsRoot
交易树 — 本区块中所有交易
key = RLP(tx_index)
receiptsRoot
收据树 — 每笔交易的执行结果(日志、Gas 消耗等)
key = RLP(tx_index)
storageRoot
存储树 — 每个合约账户自己的私有存储(嵌套在状态树内)
key = Keccak256(slot_number)
💡 关键理解
状态树是持久的、累积的:每个新区块只修改受影响的节点,大部分节点从上一个区块复用。交易树和收据树是临时的:每个区块各自独立的一棵。
State Root 的意义
区块头中的 stateRoot 是整棵状态树的 Keccak-256 哈希,仅 32 字节。但它锚定了全网所有账户的完整状态。
📦 区块头包含
- 🔗 parentHash — 父区块哈希
- 🌳 stateRoot — 状态树根
- 📋 transactionsRoot — 交易树根
- 🧾 receiptsRoot — 收据树根
- ⛽ gasUsed / gasLimit
- 🕐 timestamp
🔒 这意味着
- ✅ 任何状态改变 → stateRoot 变化
- ✅ 验证者只比对 32 字节
- ✅ 轻节点可验证任意状态
- ✅ 篡改任何数据 → root 不匹配
- ✅ 历史状态可追溯(归档节点)
- ✅ 跨链桥用它验证源链状态
🧪 动手实验
🔬 实验 1:读取区块头中的 stateRoot
获取最新区块,观察其中的 stateRoot 字段 —— 这 32 字节哈希锚定了全网状态。
chainlab lab evm block latest --chain ethereum
思考:两个连续区块的 stateRoot 为什么不同?即使没有交易,stateRoot 会变吗?
🔬 实验 2:对比不同链的最新区块
对比以太坊和 Arbitrum 的区块结构差异。L2 的 stateRoot 有什么特殊含义?
chainlab lab evm block latest --chain arbitrum
思考:为什么 L2 的区块号比 L1 大得多?Gas 利用率有什么区别?
❓ 自测
📝 本课小结
核心要点
1. Merkle Tree 通过层层哈希实现数据完整性验证
2. Merkle Proof 让轻节点用 O(log n) 数据验证任意状态
3. Patricia Trie 通过前缀压缩优化了深层查询
4. 以太坊有三棵树(state/transactions/receipts),每个合约还有自己的存储树
5. stateRoot(32字节)锚定了全网所有账户的完整状态
←
下一课
Lesson 3: 存储槽 (Storage Slots)
→