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 利用率有什么区别?

❓ 自测

检验你的理解

Q1: Merkle Proof 的大小复杂度是?

✅ 正确!Merkle Proof 只需要从叶子到根路径上每层的兄弟哈希,所以是 O(log n)。这就是为什么轻节点可以高效验证。
❌ 提示:想想树的高度和你需要提供多少个兄弟节点。

Q2: 以太坊区块头中不包含以下哪个?

✅ 正确!没有所谓的"余额树"。余额信息存在 stateRoot 对应的世界状态树里的每个账户状态中。
❌ 回顾一下:区块头包含三个 root —— state、transactions、receipts。

📝 本课小结

核心要点 1. Merkle Tree 通过层层哈希实现数据完整性验证
2. Merkle Proof 让轻节点用 O(log n) 数据验证任意状态
3. Patricia Trie 通过前缀压缩优化了深层查询
4. 以太坊有三棵树(state/transactions/receipts),每个合约还有自己的存储树
5. stateRoot(32字节)锚定了全网所有账户的完整状态
上一课
Lesson 1: 账户模型
下一课
Lesson 3: 存储槽 (Storage Slots)