以太坊让区块链从「账本」变成「世界计算机」。账户模型、MPT 状态树、EVM、Gas 与 EIP-1559——完整理解可编程区块链的架构骨架。
本章目录:
7.1 以太坊的愿景:从电子现金到可编程价值 7.2 账户模型与状态转换 7.3 以太坊状态机与状态树(MPT) 7.4 EVM 执行模型与指令集 7.5 Gas 计费机制与 EIP-1559 7.6 预编译合约与扩展 7.7 交易生命周期与确认
7.1 以太坊的愿景:从电子现金到可编程价值
如果说比特币回答了"在没有中央银行的情况下,如何创造和转移不可篡改的货币",那么以太坊则问了一个更根本的问题:"在没有中央协调者的情况下,如何执行任意程序?" 这一愿景将区块链从'支付结算网络'转变为'通用状态机'——所谓的世界计算机。
7.1.1 从比特币脚本的有限性到图灵完备 EVM
比特币脚本(Script)是一种堆栈式非图灵完备 的指令集。它没有循环、没有可行的复杂分支、没有状态存储。每个脚本只能表达"谁可以花费这些 UTXO"的有限条件(时间锁、多签、哈希原像等)。
Script : { OP } → Stack Manip → Valid/Invalid \text{Script} : \{\text{OP}\} \rightarrow \text{Stack Manip} \rightarrow \text{Valid/Invalid} Script : { OP } → Stack Manip → Valid/Invalid
这种设计是比特币安全极简 哲学的体现——脚本越简单,攻击面越小。
以太坊的选择是反面 :EVM(Ethereum Virtual Machine)是图灵完备的栈式虚拟机 ,支持循环、条件分支、子程序调用(通过合约间调用)和状态持久化。
graph LR
subgraph 比特币
A[UTXO A] --> B["锁定脚本<br>Script"]
B --> C["满足条件?"]
C -->|是| D["解锁并消费"]
style B fill:#e1f5e1
end
subgraph 以太坊
E["账户+合约"] --> F["EVM 执行<br>Turing-Complete"]
F --> G["状态更新"]
G --> H["存储持久化"]
F --> I["内部调用<br>DELEGATECALL/STATICCALL"]
style E fill:#e6f3ff
style F fill:#ccebff
end
这种扩展的代价与回报 :
------ ----------- -----------
计算能力 有限(Acyclic) 图灵完备(需 Gas 停机)
代码大小 520 字节上限 24KB(EIP-3860)
7.1.2 理念差异:资产 vs 状态
比特币的核心抽象是资产流 :UTXO 是未花费的"硬币",交易是"硬币"的所有权转移。
UTXO set ∈ { 0 , 1 } N × Amount \text{UTXO}_{\text{set}} \in \{0, 1\}^{N} \times \text{Amount} UTXO set ∈ { 0 , 1 } N × Amount
以太坊的核心抽象是状态机 :一个巨大的全局状态 σ \sigma σ ,每次交易 T T T 通过状态转换函数 Υ \Upsilon Υ 将其推向下一个状态。
σ ′ = Υ ( σ , T ) \sigma' = \Upsilon(\sigma, T) σ ′ = Υ ( σ , T )
其中 σ \sigma σ 是一个键值映射:地址 → \rightarrow → 账户(包含余额、nonce、codeHash、storageRoot)。
graph TD
A["世界状态 σ"] -->|交易 T1| B["状态 σ'__1"]
B -->|交易 T2| C["状态 σ'__2"]
C -->|交易 T3| D["状态 σ'__3"]
style A fill:#e6f3ff
style D fill:#e6f3ff
关键认知 :比特币交易摧毁旧 UTXO 并创建新 UTXO(硬币被熔化并重铸);以太坊交易保持地址不变,只更新账户的内部字段(余额、nonce、storage 根)。
7.1.3 账户模型的两面性
优势 :
合约可编程性 :部署一次,永久可调用,含有内部状态;状态可查询性 :任意账户的当前状态可由轻客户端通过 Merkle 验证;用户体验 :用户拥有固定地址,余额即地址属性(无"找零"混乱)。
劣势 :
状态膨胀 :所有历史状态变更都累积在全局状态中,无法像 UTXO 那样"剪枝已花费";隐私性损失 :地址余额是全局可见的,UTXO 的碎片混币天然提供一定的隐私性;复杂度 :MPT 的维护成本、状态访问的 Gas 定价、状态快照同步的 engineering 挑战。
关键认知一 :比特币与以太坊的根本分歧不是技术路线的优劣,而是抽象层次的选择 。比特币选择在协议层保持极简,让应用层自行补全(如闪电网络、Ordinals);以太坊选择在协议层嵌入可编程状态机,让创新直接在链上发生。
7.2 账户模型与状态转换
以太坊没有 UTXO,只有账户。每个账户不是一张"支票",而更像是一个"银行账户"——有自己的余额、交易计数器、合约代码和持久化存储。这一节剖析账户的四种字段、 nonce 的防重放机制,以及以太坊黄皮书中定义的核心状态转换函数 Υ ( σ , T ) \Upsilon(\sigma, T) Υ ( σ , T ) 。
7.2.1 两种账户类型:EOA 与合约账户(CA)
以太坊网络中的每个账户由 20 字节地址唯一标识。账户数据结构如下(RLP 编码):
Account = [nonce, balance, storageRoot, codeHash]
------ ------ ----- --------------
nonce uint64 已发送交易数 已创建合约数(通过 CREATE/CREATE2)
balance uint256 持有 ETH(以 Wei 计,1 ETH = 10¹⁸ Wei) 持有 ETH
storageRoot bytes32 空 Keccak 根(默认值) 合约存储的 MPT 根
codeHash bytes32 空 Keccak 根(默认值) 部署时的字节码哈希
外部拥有账户(EOA) :由私钥控制,是人类用户与以太坊交互的入口。EOA 的 storageRoot 和 codeHash 为空。
合约账户(CA) :由创建交易部署,由自身代码逻辑控制。CA 的 balance 可以非零(合约可持有 ETH)、storeageRoot 和 codeHash 非空。
graph TD
A["地址 0xabc...
20字节"] --> B{codeHash 为空?}
B -->|是| C["EOA<br>用户控制
nonce=发送tx数
storage为空"]
B -->|否| D["CA<br>代码控制
nonce=创建数
storage=MPT根
code=字节码哈希"]
style C fill:#e1f5e1
style D fill:#ccebff
7.2.2 Nonce 的防重放机制
在没有账户模型的系统中,双重花费通过 UTXO 引用的唯一性防止。以太坊使用 nonce (递增序号)实现完全相同的目的:
tx valid ⟺ tx.nonce = σ [ sender ] . nonce \text{tx}_{\text{valid}} \iff \text{tx.nonce} = \sigma[\text{sender}].\text{nonce} tx valid ⟺ tx.nonce = σ [ sender ] . nonce
一旦交易被执行,发送者账户的 nonce 自动递增:
σ ′ [ s e n d e r ] . nonce = σ [ s e n d e r ] . nonce + 1 \sigma'[sender].\text{nonce} = \sigma[sender].\text{nonce} + 1 σ ′ [ se n d er ] . nonce = σ [ se n d er ] . nonce + 1
连续 nonce 要求 prevents 攻击 :
若 tx.nonce 过低(已被使用)→ 拒绝(已存在交易); 若 tx.nonce 过高(跳过当前序号)→ 放入队列等待中间 nonce 到来; 若 tx.nonce 正确 → 执行,nonce 递增。
这意味着交易必须按严格顺序执行 。用户无法故意以 nonce=5 和 nonce=6 发起两笔不同的用相同 ETH 的交易来制造双花——交易池会拒绝冲突。
// account-nonce-sim.ts
// 纯内置:模拟 EOA 账户状态与 nonce 防重放
interface EOA { address: string; nonce: number; balance: bigint; }
class WorldState {
private accounts: Map<string, EOA> = new Map();
private nonceQueue: Map<string, Array<{ tx: { to: string; value: bigint; nonce: number } }>> = new Map();
ensureAccount(addr: string): EOA {
if (!this.accounts.has(addr)) {
this.accounts.set(addr, { address: addr, nonce: 0, balance: 0n });
}
return this.accounts.get(addr)!;
}
sendTx(from: string, tx: { to: string; value: bigint; nonce: number; gasLimit: bigint; gasPrice: bigint }): string {
const acc = this.ensureAccount(from);
const cost = tx.value + (tx.gasLimit * tx.gasPrice);
if (tx.nonce !== acc.nonce) {
if (tx.nonce > acc.nonce) {
// 未来 nonce:挂起
const q = this.nonceQueue.get(from) || [];
q.push({ tx });
this.nonceQueue.set(from, q);
return `queued_for_nonce_${tx.nonce}`;
}
return 'rejected_nonce_too_low';
}
if (cost > acc.balance) return 'rejected_insufficient_balance';
// 执行
acc.balance -= cost;
const to = this.ensureAccount(tx.to);
to.balance += tx.value;
acc.nonce++;
// 处理挂起的中间 nonce
let next = this.nonceQueue.get(from)?.shift();
// 简化:实际应递归处理直到 gap
return `executed_at_nonce_${tx.nonce}`;
}
}
const state = new WorldState();
const alice = '0xAlice';
state.ensureAccount(alice);
state.sendTx(alice, { to: '0xBob', value: 1000n, nonce: 0, gasLimit: 21000n, gasPrice: 20n }); // 转账给 Bob
console.log(`Alice nonce: ${(state['accounts'] as any).get(alice).nonce}`);
// 输出 nonce 从 0 递增到 1
// 若再次以 nonce=0 发送 → rejected_nonce_too_low
7.2.3 状态转换函数 Υ ( σ , T ) \Upsilon(\sigma, T) Υ ( σ , T )
以太坊黄皮书用数学形式定义了状态转换的通用框架。
对于任意交易 T T T ,及其发送者地址 S ( T ) S(T) S ( T ) :
σ ′ = Υ ( σ , T ) \sigma' = \Upsilon(\sigma, T) σ ′ = Υ ( σ , T )
状态转换的初始条件检查:
σ [ S ( T ) ] . nonce = T n and σ [ S ( T ) ] . balance ≥ T n ⋅ p + v \sigma[S(T)].\text{nonce} = T_n \quad \text{and} \quad \sigma[S(T)].\text{balance} \geq T_n \cdot p + v σ [ S ( T )] . nonce = T n and σ [ S ( T )] . balance ≥ T n ⋅ p + v
其中 T n T_n T n 为交易 nonce,p p p 为 gas price,v v v 为转账金额。若任一条件不满足,交易回滚(但发送者仍需支付已消耗的 Gas 作为 DoS 防护)。
_gas_开销的数学表达 :
g = min ( g 0 + ∑ i ∈ T i g i , g l i m i t ) g = \min\left(g_0 + \sum_{i \in T_i} g_i, \ g_{limit}\right) g = min ( g 0 + i ∈ T i ∑ g i , g l imi t )
其中 g 0 g_0 g 0 为交易基本开销,g i g_i g i 为每字节/每个操作的 Gas,g l i m i t g_{limit} g l imi t 为交易设置的上限。
关键认知 :以太坊的账户模型将"货币所有权"和"状态执行"统一到同一抽象中。Nonce 不只是计数器,而是整个账户历史执行序列的严格顺序保证 。理解这一点,才能理解为何以太坊的交易池(mempool)远比比特币更复杂——它维护的不是一组 UTXO,而是一个按地址维度排序的 nonce 队列森林。
7.3 以太坊状态机与状态树(MPT)
以太坊的"世界状态"是一个巨大的键值数据库,存储数亿个账户的 balance、nonce、code 和 storage。这个数据结构必须满足三个苛刻条件:(1) 每次区块后能快速切换到新状态;(2) 轻客户端能验证任意账户的存在性;(3) 历史状态能被快速回滚。Patricia Merkle Trie(MPT)以优雅的方式同时解决了这三个问题。
7.3.1 Patricia Trie + Merkle Tree 的融合
MPT 是两个经典数据结构的叠加:
Patricia Trie(前缀树 / 压缩 Trie) :用共享前缀压缩键空间,确保树高仅与键长度成正比;Merkle Tree :每个节点的哈希是其子节点的哈希函数,使根哈希成为整棵树的密码学摘要。
MPT Node ∈ { Null , Leaf ( path , value ) , Extension ( path , next ) , Branch [ 16 + 1 ] } \text{MPT Node} \in \{\text{Null}, \text{Leaf}(\text{path}, \text{value}), \text{Extension}(\text{path}, \text{next}), \text{Branch}[16+1]\} MPT Node ∈ { Null , Leaf ( path , value ) , Extension ( path , next ) , Branch [ 16 + 1 ]}
---------- --------- ------
Leaf0 键的终点,RLP 编码 = [encodedPath, value]
Extension1 压缩一段公共前缀,指向 1 个子节点
Branch16 + 1 16 分支对应 hex 字符 [0-f] + 1 个可选 value 字段
graph TD
A[Root<br>H(H(B)=r1)] --> B[Branch<br>index 'a']
B --> C[Leaf<br>encodedPath<b|d2c| → value<br>(Alice's account)]
B --> D[Extension<br>path: <b|d...>]
D --> E[Branch<br>index '2']
E --> F[Leaf<br>path <b|d2...|3| → value<br>(Bob's account)]
style A fill:#ccebff
style C fill:#e6f3ff
style F fill:#e6f3ff
7.3.2 三棵树结构及其职责
一个以太坊区块的头部包含三棵树的根哈希:
状态树(State Trie) :stateRoot —— 当前所有账户状态的快照;交易树(Tx Trie) :txRoot —— 当前区块内所有交易的 trie;收据树(Receipt Trie) :receiptRoot —— 当前区块内所有交易收据的 trie。
Block Header = ( … , stateRoot , txRoot , receiptRoot , … ) \text{Block Header} = (\dots, \text{stateRoot}, \text{txRoot}, \text{receiptRoot}, \dots) Block Header = ( … , stateRoot , txRoot , receiptRoot , … )
状态树的特殊性 :状态树是全局跨块累积 的——每个新区块基于上一区块的状态树做增量修改,而不是从 0 开始重建。未触及的账户路径直接复用前一状态的子树。
graph LR
A[Block N<br>stateRoot = H(σ_N)] -->|增量更新| B[Block N+1<br>stateRoot = H(σ_N+1)]
A --> C[txRoot = txs in N]
B --> D[txRoot = txs in N+1]
A -.-> E["未更改子树<br>直接复用"]
E -.-> B
style E fill:#ffffcc
键编码方式 :状态树的键不是原始地址,而是地址的 Keccak256 哈希的十六进制字符(64 个 hex 字符) ,然后按 4 比特半字节(nibble)逐层导航。这保证了树的均匀分布。
7.3.3 轻客户端验证:MPT 证明
轻客户端只保存区块头(约 500 字节/块),要验证"地址 0xabc... 的余额是多少",只需向全节点请求该地址的 Merkle 证明路径 。
路径证明大小 :O ( log 16 N ) O(\log_{16} N) O ( log 16 N ) 个节点,每个节点约 32-530 字节。对于 2 亿账户,树高约 log 16 ( 2 × 10 8 ) ≈ 7 \log_{16}(2\times10^8) \approx 7 log 16 ( 2 × 1 0 8 ) ≈ 7 层,证明大小约 1-3KB。
// mpt-verify-sim.ts
// 纯内置:模拟简化的 MPT 路径验证
type MPTNode =
| { type: 'null' }
| { type: 'branch'; children: (string | null)[]; value: string | null; hash: string }
| { type: 'leaf'; path: string; value: string; hash: string }
| { type: 'ext'; path: string; next: string; hash: string } // 指向下一个节点的哈希
// 简化的 Keccak 占位符
function keccak256(data: string): string {
// 模拟:累加字符编码取 BigInt 后取模
let h = 0n;
for (const c of data) h = (h * 31n + BigInt(c.charCodeAt(0))) % (1n << 256n);
return '0x' + h.toString(16).padStart(64, '0');
}
// RLP 编码占位符
function fakeRlpEncode(parts: string[]): string {
return parts.map(p => p.length.toString(16).padStart(2, '0') + p).join('');
}
// 计算节点哈希
function nodeHash(node: MPTNode): string {
if (node.type === 'null') return '0x' + '0'.repeat(64);
if (node.type === 'branch') return keccak256(fakeRlpEncode([...node.children.filter(Boolean) as string[], ...(node.value ? [node.value] : [])]));
if (node.type === 'leaf') return keccak256(fakeRlpEncode([node.path, node.value]));
return keccak256(fakeRlpEncode([node.path, node.next]));
}
// 验证路径:给出一系列节点,从叶子向上哈希,最终比对 root
function verifyMPT(root: string, keyNibbles: number[], proof: MPTNode[], expectedValue: string): boolean {
let currentHash = '';
let currentNode: MPTNode | null = null;
// 从 proof 尾部(叶子)开始,反向重建上一层哈希
let idx = proof.length - 1;
for (let depth = 0; depth < proof.length; depth++) {
const node = proof[proof.length - 1 - depth];
if (node.type === 'leaf') {
if (node.value !== expectedValue) return false;
currentHash = nodeHash(node);
} else if (node.type === 'branch') {
const childIdx = keyNibbles[depth];
const child = node.children[childIdx];
if (child === null) return false;
// 验证当前哈希是否与分支节点的引用匹配
if (depth > 0 && child !== currentHash) return false;
currentHash = nodeHash(node);
} else if (node.type === 'ext') {
if (depth > 0 && node.next !== currentHash) return false;
currentHash = nodeHash(node);
}
}
return currentHash === root;
}
// 示例:构建简单的 2 层分支证明
const leaf: MPTNode = { type: 'leaf', path: 'b0', value: '1000', hash: '' };
const leafHash = nodeHash(leaf);
const branch: MPTNode = { type: 'branch', children: Array(16).fill(null) as any, value: null, hash: '' };
branch.children[11] = leafHash; // 'b' = 11
branch.hash = nodeHash(branch);
const root = branch.hash;
const proof: MPTNode[] = [branch, leaf];
const key = [11, 0]; // 'b0'
console.log('验证 Merkle 证明:', verifyMPT(root, key, proof, '1000'));
// 输出 true,验证了从 leaf → branch → root 的哈希链
7.3.4 Gas 与状态存储的定价经济学
存储不是免费的。每个非零存储槽的创建(SSTORE)需要消耗 20,000 Gas ,修改消耗 5,000 Gas ,清零可返还 4,800 Gas (EIP-3529 后上限为 Gas 消耗的 1/5)。
G_{sstore} = \begin{cases}
20000 & \text{if } val \neq 0 \text{ and } orig = 0 \quad \text{(从零到非零的写入)} \\
5000 & \text{if } val \neq 0 \text{ and } orig \neq 0 \quad \text{(覆盖修改)} \\
-4800 & \text{if } val = 0 \text{ and } orig \neq 0 \quad \text{(清零返还)}
end{cases}
这种清零返还机制 激励合约开发者在不再需要时释放存储,但 EIP-3529 增加了返还上限,防止 GasToken 套利攻击。
关键认知二 :MPT 不是"为了密码学而密码学"的炫技。它是状态可验证性、轻客户端可行性和历史状态可回滚 三个工程需求的最小交集解。状态树的根哈希(stateRoot)让区块头成为整个"世界状态"(数亿个账户)的 32 字节指纹——这是账户模型区块链最核心的密码学成就。
7.4 EVM 执行模型与指令集
EVM 不是一台物理计算机,而是为去中心化共识设计的一套确定性指令集。它没有随机数生成器,没有文件系统,没有网络调用,也没有系统时钟——它的状态转换输出对同一输入(交易+上下文+状态)是 100% 确定性的。本节走进 EVM 的五个核心组件:程序计数器、内存、堆栈、存储和调用环境。
7.4.1 EVM 的五大运行时组件
每个 EVM 实例在执行交易时维护五个独立区域:
------ ------ ---------- ------
Stack 数组 (LIFO) 最多 1024 个 256 位字 操作数与中间结果
Memory 字节数组 线性扩展,字节寻址 临时数据(函数调用、数组拷贝)
Storage 键值映射 (uint256→uint256) 永久持久化,跨交易保留 合约永久状态
Environment 上下文结构 每交易固定 msg.sender, msg.value, block.number 等
graph TD
subgraph EVM 运行时
A["PC<br>程序计数器"] --> B["执行引擎"]
C["Stack<br>LIFO 1024槽"] --> D[PUSH/POP/SWAP/DUP]
E["Memory<br>线性字节数组"] --> F[MLOAD/MSTORE]
G["Storage<br>键值映射
永久持久化"] --> H[SLOAD/ SSTORE]
I["环境上下文"] --> J["区块/交易/ 调用元数据"]
end
style C fill:#ffcccc
style G fill:#ccffcc
style E fill:#ffffcc
7.4.2 指令集:256 位栈式 RISC
EVM 定义了约 140+ 个操作码(opcode),每个都是单字节(0x00-0xFF)。核心分类:
------ ---------- ---------- ------
算术 ADD, MUL, SUB, DIV, MOD3-5 溢出回绕(uint256 无符号)
比较/位 LT, GT, EQ, AND, OR, NOT3 布尔/按位运算
栈操作 PUSH1-PUSH32, DUP1-DUP16, SWAP1-SWAP16, POP3 栈操作
内存 MLOAD, MSTORE, MSTORE83+ 按字扩展计费
存储 SLOAD, SSTORE100 / 20000 最昂贵操作之一
环境 ADDRESS, BALANCE, CALLER, TIMESTAMP2-100 读取环境变量
控制流 JUMP, JUMPI, PC, JUMPDEST8 跳转目标必须先标记
调用 CALL, STATICCALL, DELEGATECALL100+ 跨合约调用
系统 CREATE, CREATE2, SELFDESTRUCT32000+ 合约创建与销毁
日志 LOG0-LOG4375+ 事件输出(不可合约内读取)
关键约束 :
溢出回绕 :ADD/MUL 在超过 2²⁵⁶ 时自动截断(不像 CPU 抛异常),开发者需手动用防溢出库(如 Solidity 0.8 后自动溢出检查);栈深度限制 :1024,超深则 CALL 回滚;无跳转表 :JUMP 的目标必须是字面量或栈顶值,且目标地址必须是 JUMPDEST 指令——防止跳转到数据段。
// evm-simple-vm.ts
// 纯内置:模拟极简 EVM 栈执行(ADD, PUSH, MUL)
class SimpleEVM {
private stack: bigint[] = [];
private pc = 0;
private halted = false;
private gas: bigint;
constructor(gasLimit: bigint) { this.gas = gasLimit; }
push(v: bigint) {
if (this.stack.length >= 1024) throw new Error('Stack overflow');
this.gas -= 3n;
this.stack.push(v & ((1n << 256n) - 1n));
}
pop(): bigint {
if (this.stack.length === 0) throw new Error('Stack underflow');
this.gas -= 3n;
return this.stack.pop()!;
}
add() {
const a = this.pop();
const b = this.pop();
this.gas -= 3n;
this.push((a + b) % (1n << 256n)); // 溢出回绕
}
mul() {
const a = this.pop();
const b = this.pop();
this.gas -= 5n;
this.push((a * b) % (1n << 256n));
}
run(bytecode: { op: 'PUSH' | 'ADD' | 'MUL'; val?: bigint }[]) {
while (this.pc < bytecode.length && !this.halted && this.gas > 0n) {
const inst = bytecode[this.pc++];
if (inst.op === 'PUSH') this.push(inst.val!);
else if (inst.op === 'ADD') this.add();
else if (inst.op === 'MUL') this.mul();
}
return { stack: [...this.stack], gas: this.gas, pc: this.pc };
}
}
// 执行: (3 + 4) * 5 = 35
const bytecode = [
{ op: 'PUSH' as const, val: 3n },
{ op: 'PUSH' as const, val: 4n },
{ op: 'ADD' as const },
{ op: 'PUSH' as const, val: 5n },
{ op: 'MUL' as const },
];
const evm = new SimpleEVM(100000n);
const result = evm.run(bytecode);
console.log(`栈顶: ${result.stack[0]} (期望 35)`);
console.log(`剩余 gas: ${result.gas}`);
7.4.3 跨合约调用:CALL, DELEGATECALL, STATICCALL
操作码 上下文继承 msg.sendermsg.value可修改存储?
-------- ----------- ------------- ------------- ------------
CALL新上下文 调用者(原调用者或当前合约) 可发送 是
DELEGATECALL继承当前地址 原始调用者 继承当前 msg.value 是(改的是当前合约的存储)
STATICCALL只读上下文 调用者 不可发送 否(回滚)
sequenceDiagram
participant U as User
participant A as 合约 A
participant B as 合约 B
U->>A: 调用 f()
A->>B: CALL(发送 ETH, 调用 g())
Note over A,B: B 的 msg.sender = A, msg.value = 发送量
A->>B: STATICCALL(调用 h())
Note over A,B: B 无法修改任何存储,否则回滚
A->>B: DELEGATECALL(调用 g())
Note over A,B: B 看到的是 U 作为 msg.sender<br>且修改的是 A 的存储!
DELEGATECALL 是代理合约模式 (如 OpenZeppelin 的 TransparentProxy、ERC-1967)的基础:一个轻量代理合约持有所有 state,通过 DELEGATECALL 将执行转发到可升级的实现合约,但态始终保存在代理上。
7.4.4 执行环境:区块上下文与交易上下文
EVM 运行时可访问的环境变量:
-------- ------ -----------
BLOCKHASH(n)最近 256 个区块的哈希 否(历史固定)
TIMESTAMP当前区块时间戳(秒) 矿工可在小范围内操纵
DIFFICULTY/PREVRANDAOPoW 难度 / PoS 随机信标 不可操纵
CALLER/ORIGIN直接调用者 / 交易发起者 否
CALLVALUE本次调用发送的 Wei 否(由交易定)
RETURNDATASIZE上一个调用的返回数据长度 否
关键安全提示 :TIMESTAMP 和 BLOCKHASH 可被矿工轻微操纵,因此不应 用于要求真随机性的场景。合约开发者通常使用 Chainlink VRF 或 commit-reveal 协议获取可信随机数。
关键认知三 :EVM 的图灵完备不是赠与,而是约束 。1024 栈深度、Gas 上限、不可访问外部世界——这些约束将停机问题转化为"资源耗尽即停"。理解 EVM 的确定性本质(同一输入必出同一输出),才能理解为何以太坊的状态转换可以在全网数万节点上并行执行后得到完全一致的结果 ——这是共识的前提。
7.5 Gas 计费机制与 EIP-1559
Gas 在以太坊中不是燃料的隐喻,而是去中心化计算的通用定价机制 。它解决了两个根本问题:(1) 防止无限循环和 DoS 攻击——每个操作都有可量化成本;(2) 在资源稀缺时提供公平分配的市场机制。EIP-1559 将这套市场从"一价拍卖"改造为"双因素定价",是区块链经济设计史上最重要的实验之一。
7.5.1 从指令成本到交易定价
EVM 中每个操作码消耗特定量的 Gas。一笔交易的总成本:
Total Cost ETH = GasUsed × GasPrice \text{Total Cost}_{\text{ETH}} = \text{GasUsed} \times \text{GasPrice} Total Cost ETH = GasUsed × GasPrice
其中 GasUsed 是该交易实际执行过程中所有操作码的累计成本。GasPrice 由发送者在交易签名时自行设定。
常见 Gas 基准值(伦敦升级后) :
交易基础开销 TXBASE 21,000 每笔交易最低成本
SSTORE(首次写入非零)20,000 最昂贵操作,因为写磁盘
SLOAD100(热)/ 2,100(冷) 状态访问成本,分冷热状态
CREATE32,000 + bytecost(200/gas) 部署新合约
内存扩展 mem = 3 × words + words² / 512非线性二次增长
7.5.2 Pre-EIP-1559:首价拍卖的缺陷
在 EIP-1559 之前,以太坊采用首价拍卖(First-Price Auction) :
Fee = GasUsed × GasPrice bid \text{Fee} = \text{GasUsed} \times \text{GasPrice}_{\text{bid}} Fee = GasUsed × GasPrice bid
用户需要猜测当前网络的"合理价格"。这种机制的问题:
价格波动极大 :高峰时 1 Gwei,拥堵时飙升至 200+ Gwei;过高出价浪费 :用户为保险出高价,实际只需出最低价即可纳入;矿工可操纵 :矿工可将自有交易(MEV)优先,甚至留空区块迫使价格下降;用户体验差 :钱包无法准确估计"当前合理价格"。
7.5.3 EIP-1559:双因素定价模型
EIP-1559(伦敦升级,2021 年 8 月 5 日)引入了一套全新的交易格式和定价机制。
三个新参数
BaseFee 协议自动计算的基础费用,被销毁 协议/算法
MaxPriorityFee 用户给矿工/验证者的"小费" 用户
MaxFeePerGas 用户愿意支付的最高单价上限 用户
实际支付单价 :
GasPrice e f f e c t i v e = min ( BaseFee + MaxPriorityFee , MaxFeePerGas ) \text{GasPrice}_{effective} = \min\left(\text{BaseFee} + \text{MaxPriorityFee}, \ \text{MaxFeePerGas}\right) GasPrice e f f ec t i v e = min ( BaseFee + MaxPriorityFee , MaxFeePerGas )
总费用拆分 :
Total = GasUsed × BaseFee ⏟ 被销毁(通缩压力) + GasUsed × PriorityFee ⏟ 给验证者(激励) \text{Total} = \underbrace{\text{GasUsed} \times \text{BaseFee}}_{\text{被销毁(通缩压力)}} + \underbrace{\text{GasUsed} \times \text{PriorityFee}}_{\text{给验证者(激励)}} Total = 被销毁(通缩压力) GasUsed × BaseFee + 给验证者(激励) GasUsed × PriorityFee
BaseFee 自动调节算法
协议设定每区块 15M Gas 的目标使用量 ,最大上限 30M。
BaseFee n + 1 = BaseFee n × ( 1 + GasUsed n − GasTarget GasTarget × 1 8 ) \text{BaseFee}_{n+1} = \text{BaseFee}_n \times \left(1 + \frac{\text{GasUsed}_n - \text{GasTarget}}{\text{GasTarget}} \times \frac{1}{8}\right) BaseFee n + 1 = BaseFee n × ( 1 + GasTarget GasUsed n − GasTarget × 8 1 )
若区块使用 15M(目标值)→ BaseFee 不变; 若区块使用 30M(满)→ BaseFee 上升 12.5%; 若区块使用 0M(空)→ BaseFee 下降 12.5%。
graph TD
A[GasUsed=10M<br><15M] -->|BaseFee × 0.875| B["BaseFee 下降 12.5%"]
C["GasUsed=15M<br>=目标"] -->|BaseFee 不变| D["稳定"]
E["GasUsed=30M<br>满"] -->|BaseFee × 1.125| F["BaseFee 上升 12.5%"]
D --> G["用户 MaxPriorityFee 小费竞争纳入"]
style B fill:#ccffcc
style F fill:#ffcccc
7.5.4 "超声货币"叙事:销毁的通缩引擎
EIP-1559 的 BaseFee 销毁使得以太坊每天的销毁量可预测。在繁忙时期(如 NFT 发行、DeFi 热潮),销毁速度可超过区块奖励的发行速度:
Net Issuance = Block Rewards − BaseFee Burn \text{Net Issuance} = \text{Block Rewards} - \text{BaseFee Burn} Net Issuance = Block Rewards − BaseFee Burn
当 BaseFee Burn > Block Rewards \text{BaseFee Burn} > \text{Block Rewards} BaseFee Burn > Block Rewards 时,ETH 总供应量负增长 ,形成通缩。
// eip1559-basefee-sim.ts
// 纯内置:模拟 EIP-1559 BaseFee 调节与销毁逻辑
function simulateBaseFee(
initialBaseFee: bigint,
blockGasUsed: bigint[],
gasTarget: bigint
): { baseFees: bigint[]; totalBurn: bigint } {
let baseFee = initialBaseFee;
const baseFees: bigint[] = [baseFee];
let totalBurn = 0n;
for (const gas of blockGasUsed) {
const delta = gas - gasTarget;
// 每 1/8 的调整系数
const adjustment = 1n + (delta * 1n) / (gasTarget * 8n);
// 整数运算模拟
baseFee = (baseFee * adjustment) / 1n;
if (baseFee < 1n) baseFee = 1n;
baseFees.push(baseFee);
totalBurn += gas * baseFee;
}
return { baseFees, totalBurn };
}
// 模拟 5 个区块:15M → 20M → 30M → 15M → 5M
const gasUsed = [15_000_000n, 20_000_000n, 30_000_000n, 15_000_000n, 5_000_000n];
const result = simulateBaseFee(1_000_000_000n, gasUsed, 15_000_000n);
console.log('BaseFee 序列 (Gwei):');
result.baseFees.forEach((f, i) => console.log(` Block i : {i}: i : {Number(f) / 1e9} Gwei`));
console.log(`总计销毁: ${result.totalBurn / 1e18n} ETH-equivalent units`);
// 输出:拥堵时上升,空闲时稳定/下降,验证 BaseFee 的动态调节
7.5.5 交易池选择与排序
在 EIP-1559 下,交易池按 有效 Gas 价格 排序:
sortKey = min ( maxFeePerGas , baseFee + maxPriorityFeePerGas ) \text{sortKey} = \min\left(\text{maxFeePerGas}, \ \text{baseFee} + \text{maxPriorityFeePerGas}\right) sortKey = min ( maxFeePerGas , baseFee + maxPriorityFeePerGas )
graph LR
A["用户提交交易"] --> B["验证 MaxFeePerGas >= BaseFee"]
B --> C["加入 mempool"]
C --> D["按有效价格降序排序"]
D --> E["区块构建器选择前 N 笔<br>至 gas limit"]
E --> F["执行 + 纳入区块"]
style E fill:#ccffcc
这与 PoW/PoS 的区块构建者(Builder)角色紧密相关。在 PBS(Proposer-Builder Separation)下,Builder 专门优化交易排序以捕获 MEV(最大可提取价值),验证者仅选择最高价值区块头。
关键认知四 :EIP-1559 不是简单的"降价改革",而是将交易定价权 从矿工/验证者手中部分收归协议。BaseFee 的可预测性降低了用户猜测成本,销毁机制将使用价值回流到全体 ETH 持有者,而 PriorityFee 仍保留激励验证者的经济杠杆。
7.6 预编译合约与扩展
预编译合约是以太坊协议中"不可升级的底层函数库"——它们以固定地址存在,以固定 Gas 执行原生优化的密码学原语(如椭圆曲线运算、模幂运算)。如果没有预编译合约,ZK 验证和 BLS12-381 签名聚合在 EVM 中将因 Gas 成本而不可行。
7.6.1 什么是预编译合约
预编译合约(Precompiled Contracts)是在 EVM 字节码层直接原生实现 的特殊合约,部署于固定地址 0x01 至 0x0A(以及未来的扩展地址)。
与 Solidity 合约不同,它们:
没有字节码 :直接由客户端的 Go/C++/Rust 代码执行;Gas 成本固定且低廉 :不经过 EVM 解释循环,直接调用底层优化库;不可被覆盖或部署新代码到其地址 :协议层硬编码。
graph TD
A["EVM 执行"] --> B{目标地址 < 0x20?}
B -->|是| C["路由到原生预编译实现"]
B -->|否| D["加载合约字节码并解释执行"]
C --> E["直接调用 libsecp256k1 / blst 等"]
E --> F["返回结果到 EVM 栈"]
style C fill:#ccffcc
style D fill:#ffffcc
7.6.2 核心预编译合约详解
地址 名称 功能 典型 Gas 在 ZK/Rollup 中的角色
------ ------ ------ ---------- --------------------
0x01ecrecover从签名中恢复公钥/地址 3,000 验证交易签名
0x02sha256SHA-256 哈希 60 + 12/字 通用哈希
0x03ripemd160RIPEMD-160 哈希 600 + 120/字 比特币兼容地址生成
0x04identity直接返回输入(数据拷贝) 15 + 3/字 代理合约中的数据转发
0x05modexp大整数模幂 a b m o d m a^b \mod m a b mod m 根据复杂度 RSA、VDF 验证
0x06ecAdd椭圆曲线 BLS12-381 点加 150 ZK-SNARK 证明验证
0x07ecMul椭圆曲线 BLS12-381 标量乘 6,000 ZK-SNARK 证明验证
0x08ecPairing双线性配对 e ( P , Q ) e(P,Q) e ( P , Q ) 34,000 + 45,000/对 ZK-SNARK 核心验证
0x09blake2fBlake2b 压缩函数 按轮次计费 跨链哈希兼容
7.6.3 为什么需要预编译:以 ecPairing 为例
双线性配对(Bilinear Pairing)是 ZK-SNARK(如 Groth16)验证的核心数学操作。其定义:
e : G 1 × G 2 → G T e: \mathbb{G}_1 \times \mathbb{G}_2 \rightarrow \mathbb{G}_T e : G 1 × G 2 → G T
满足双线性性质:e ( a P , b Q ) = e ( P , Q ) a b e(aP, bQ) = e(P, Q)^{ab} e ( a P , b Q ) = e ( P , Q ) ab
如果用 Solidity 纯实现配对运算,仅一个 pairing 的 EVM Gas 成本将远超区块 Gas 上限(30M)。但通过预编译 0x08,可在约 113,000 Gas 内完成 2 对点的配对验证——这使得以太坊主网能直接验证 ZK 证明 。
// precompile-cost-sim.ts
// 纯内置:模拟预编译与纯 EVM 实现的 Gas 成本差距
interface PrecompileCall {
address: number; // 0x01 - 0x09
baseGas: number;
perWordGas: number;
name: string;
}
function estimateGas(call: PrecompileCall, inputWords: number): number {
return call.baseGas + call.perWordGas * inputWords;
}
// ECRECOVER: 3,000 gas 恢复签名
const ecrecover: PrecompileCall = { address: 0x01, baseGas: 3000, perWordGas: 0, name: 'ecrecover' };
console.log(`ECRecover 恢复 1 个签名: ${estimateGas(ecrecover, 0)} gas`);
// ecPairing: ~113,000 gas 验证 Groth16
const ecPairing: PrecompileCall = { address: 0x08, baseGas: 34000, perWordGas: 45000, name: 'ecPairing' };
console.log(`ecPairing 2 对验证: ${estimateGas(ecPairing, 2)} gas`);
// 纯 Solidity 实现估算(假设 1000 EVM 操作 per pairing step)
const pureEvmCost = 1000 * 20000; // 约 20,000,000 gas
console.log(`若纯 Solidity 实现配对: ~${pureEvmCost} gas (超出区块上限!)`);
// 输出对比:预编译 113k vs 纯实现 ~20M → 节约 ~180 倍
7.6.4 预编译在零知识 rollup 中的核心地位
ZK-Rollup(如 zkSync、StarkNet、Polygon zkEVM)的核心流程:
在 L2 上执行大量交易; 生成一个简洁的零知识证明 (证明"这些交易正确执行后导致了这一新的状态根"); 在 L1 上调用 ecPairing 预编译验证该证明 ; 若验证通过,则 L2 状态根被正式确认。
graph LR
A["L2 执行 10,000 笔交易"] --> B["生成 ZK 证明
大小: ~1KB"]
B --> C["L1 合约调用 0x08 ecPairing"]
C --> D["验证通过?"]
D -->|是| E["L2 状态根写入 L1"]
D -->|否| F["证明无效,拒绝"]
C --> G["全节点无需重放 L2 交易"]
style C fill:#ccffcc
style E fill:#ccffcc
没有 ecAdd, ecMul, ecPairing 这三个预编译,ZK-Rollup 的 L1 验证成本将高到不可行,扩容路线约半数将失去根基。
7.6.5 未来预编译:Blob 交易与 KZG 承诺
EIP-4844(Dencun 升级,2024 年 3 月)引入 Blob 交易 ——一种携带 125KB 临时"blob 数据"的交易类型。该数据不在 EVM 中直接访问,而是通过 KZG 多项式承诺验证:
C = K Z G -commit ( D ) ∈ G 1 , 对数据 D = { d 0 , d 1 , . . . , d n − 1 } C = KZG\text{-commit}(D) \in \mathbb{G}_1, \quad \text{对数据 } D = \{d_0, d_1, ..., d_{n-1}\} C = K Z G -commit ( D ) ∈ G 1 , 对数据 D = { d 0 , d 1 , ... , d n − 1 }
EIP-4844 新增预编译 pointevaluation_precompile,用于在 DAS(数据可用性采样)中验证 KZG 评估证明。这是以太坊扩容路线图的核心——数据分片层的前置 。
关键认知五 :预编译合约不是优化,而是协议与密码学前沿之间的桥梁 。当密码学研究人员发现双线性配对的效率足以支撑 ZK-SNARK 时,协议工程师通过预编译将其引入 EVM。这一设计让以太坊可以在不更改执行模型的情况下,持续吸收密码学进步——ecPairing 之于 ZK,blake2f 之于跨链,pointevaluation 之于 L2 扩容。
7.7 交易生命周期与确认
一笔以太坊交易从用户点击"发送"到被全网最终确认,要经历构造、签名、广播、入池、排序、执行、出块、收据生成、执行状态确认等十余个环节。理解这一完整生命周期,是理解以太坊用户体验、交易延迟以及矿工/提取者可提取价值(MEV)的前提。
7.7.1 交易构造与签名
发送者使用私钥对交易的RLP 编码哈希 进行签名。以太坊使用 ECDSA(secp256k1) ,与比特币相同曲线,但签名字段包含 v, r, s,其中 v 编码链 ID 和奇偶性。
交易字段(EIP-1559 类型 2 交易) :
maxPriorityFeePerGas uint256 给验证者的小费
maxFeePerGas uint256 最高可接受单价
gasLimit uint64 交易消耗的 Gas 上限
to address / null 接收者地址或 null(表示合约创建)
data bytes 调用输入数据(calldata)
accessList 列表 EIP-2930 热状态预声明(可选)
graph LR
A["用户私钥"] -->|签名| B["RLP 编码交易 + v,r,s"]
B --> C["广播到 P2P 网络"]
C --> D["节点接收"]
D --> E{签名验证通过?}
E -->|是| F["加入 mempool"]
E -->|否| G["丢弃"]
style A fill:#ffcccc
style F fill:#ccffcc
7.7.2 Mempool 与交易选择
交易进入各全节点的 内存池(mempool/txpool) 。按前述 EIP-1559 规则,交易池按有效价格排序:
sortKey j = min ( maxFeePerGas j , baseFee + maxPriorityFeePerGas j ) \text{sortKey}_j = \min(\text{maxFeePerGas}_j, \ \text{baseFee} + \text{maxPriorityFeePerGas}_j) sortKey j = min ( maxFeePerGas j , baseFee + maxPriorityFeePerGas j )
交易池的复杂性 :
地址内顺序 :同一地址的 tx 必须按 nonce 严格顺序执行,nonce 不连续时后续交易挂起;链上状态依赖 :若某交易的 to 字段是合约,其执行结果(如 SSTORE)影响后续从同一地址发出的交易 Gas 估算;PBS(Proposer-Builder Separation) :在 PoS 中,Builder 专门构建最优价值区块(含 MEV 提取),而 Proposer(验证者)仅选择最高价值区块头,自己无法查看交易内容(防止时间窃贼攻击)。
7.7.3 区块纳入与状态执行
Builder/验证者从交易池中选择交易,按顺序逐个执行:
对每笔交易 :
检查 nonce 匹配; 检查签名有效性; 检查余额 >= GasLimit × MaxFeePerGas + value; 按当前 stateRoot 执行 EVM(更新内存、堆栈、存储、返回数据); 生成交易回执(Receipt)。
Gas 累积 :所有交易 GasUsed 之和不超过区块 GasLimit(30M)。状态提交 :执行完所有交易后,计算新 stateRoot。区块打包 :将交易列表、交易根、收据根、新 stateRoot、时间戳、parentHash 封装为新区块。
graph TD
A["mempool 排序"] --> B["选择前 N 笔"]
B --> C["逐笔执行 EVM"]
C --> D{执行成功?}
D -->|是| E["更新状态树"]
D -->|否| F["回滚该交易状态\n但仍收取已耗 Gas"]
E --> G["生成回执"]
F --> G
G --> H["计算新 stateRoot"]
H --> I["封装区块"]
style E fill:#ccffcc
style F fill:#ffcccc
7.7.4 交易回执与状态确认
每笔交易都有一个回执(Receipt) ,包含:
logsBloom 256 字节过滤器,加速日志查询
logs 交易执行中触发的所有事件(LOG0-LOG4 输出)
cumulativeGasUsed 区块中截止该交易的累计 Gas
用户如何确认交易成功?
钱包向 RPC 节点查询 eth_getTransactionReceipt; 检查 status === 1; 可选:监听 logs 中合约触发的事件(如 Transfer 事件)确认具体业务逻辑。
// tx-receipt-verify-sim.ts
// 纯内置:模拟交易生命周期状态检查
interface TxReceipt {
status: 0 | 1;
gasUsed: number;
logs: Array<{ topic0: string; data: string }>;
blockNumber: number;
blockHash: string;
}
function confirmTransaction(
receipt: TxReceipt | null,
currentBlock: number,
requiredConfirmations: number
): { confirmed: boolean; safe: boolean; reason: string } {
if (!receipt) return { confirmed: false, safe: false, reason: '交易尚未入块' };
if (receipt.status === 0) return { confirmed: true, safe: false, reason: '交易执行失败(回滚)' };
const confs = currentBlock - receipt.blockNumber + 1;
const safe = confs >= requiredConfirmations;
return {
confirmed: true,
safe,
reason: safe
? `已确认 c o n f s 个区块,达到安全阈值 {confs} 个区块,达到安全阈值 co n f s 个区块,达到安全阈值 {requiredConfirmations}`
: `已入块但仅有 c o n f s 个确认,建议等待 {confs} 个确认,建议等待 co n f s 个确认,建议等待 {requiredConfirmations} 个`,
};
}
const receipt: TxReceipt = {
status: 1, gasUsed: 21000, logs: [], blockNumber: 100, blockHash: '0xabc...',
};
console.log(confirmTransaction(receipt, 101, 12));
// 输出: 1 确认,未达 12 安全阈值
console.log(confirmTransaction(receipt, 112, 12));
// 输出: 12 确认,安全
7.7.5 最终性确认:LMD-GHOST + Casper FFG
在 PoS 以太坊中,"多少确认才算安全"的标准与 PoW 不同。
区块 head :区块已被提出并传播;Justified :2/3 验证者对该区块的 epoch 投票确认;Finalized :该区块及其所有祖先区块被 2/3 验证者"最终化",理论上不可被回滚(除非 1/3+ 质押被罚没)。
graph LR
A["已提出"] --> B["已证明<br>Attestation 2/3"]
B --> C["已确定<br>Justified"]
C --> D["已最终化<br>Finalized"]
D --> E["不可逆转<br>除非 1/3+ 罚没"]
style D fill:#ccffcc
style E fill:#ccffcc
交易所通常等待 12-20 个 slot (约 2.5-4 分钟)视为"入账确认"; 开发者的用户体验建议:finalized 块前不要视为最终(但大多数日常应用 1-2 slot 即可)。
关键认知六 :交易不是"发送即完成"。从用户签名到链上可见(~12 秒),从可见到不易回滚(~2 分钟),从不易回滚到最终化(~13 分钟)——每个阶段有不同的安全假设。理解这个阶梯,才能设计不会在高并发场景下出错的 DApp。
第7章 以太坊架构与 EVM —— 章节总结
三个关键认知
以太坊是通用状态机,不只是支付网络 。从比特币的 UTXO/Script 到以太坊的账户/EVM,核心范式从"资产转移"跃迁为"状态转换"。账户模型将货币、身份、代码和存储统一到单个键值状态空间 σ \sigma σ 中,使得任意程序逻辑可在链上部署与执行。Merkle Patricia Trie 是世界状态的密码学快照 。状态树的根哈希(stateRoot)将数亿个账户、数万亿个存储槽压缩为 32 字节。轻客户端只需保存区块头,即可通过 O ( log N ) O(\log N) O ( log N ) 的 Merkle 路径验证任何账户状态——这是账户模型可行性的密码学基石。EIP-1559 是区块链经济设计史上的重要实验 。它将交易定价从首价拍卖改造为 protocol-enforced 双因素模型(BaseFee + PriorityFee),并引入销毁机制将手续费的使用价值回流至 ETH 持有者。BaseFee 的动态调节算法(12.5% / block 调整)在无需人工干预的情况下实现了供需平衡。
核心公式与代码索引
状态转换 Υ ( σ , T ) \Upsilon(\sigma, T) Υ ( σ , T ) 07.02 黄皮书核心函数
Nonce 条件: T n = σ [ S ( T ) ] . n o n c e T_n = \sigma[S(T)].nonce T n = σ [ S ( T )] . n o n ce 07.02 防重放机制
余额条件: σ [ S ( T ) ] . b a l a n c e ≥ T n ⋅ p + v \sigma[S(T)].balance \geq T_n \cdot p + v σ [ S ( T )] . ba l an ce ≥ T n ⋅ p + v 07.02 交易有效性
EVM 栈式求值与溢出回绕 07.04 极简 VM 模拟
CALL/DELEGATECALL/STATICCALL 07.04 跨合约调用语义
有效 Gas 价格公式 07.05 EIP-1559 核心
BaseFee 自动调节: × ( 1 ± 12.5 % ) \times(1 \pm 12.5\%) × ( 1 ± 12.5% ) 07.05 拥堵/空闲响应
预编译地址映射 0x01-0x0A 07.06 原生密码学加速
ecPairing 与 ZK-Rollup 关系 07.06 L1 验证 L2 证明
KZG 多项式承诺 07.06 EIP-4844 数据分片前瞻
确认状态阶梯 07.07 proposed→justified→finalized
核心 Mermaid 图索引
比特币 UTXO vs 以太坊账户模型(7.1) 世界状态链式转换图(7.1) EOA 与合约账户对比图(7.2) Kademlia/MPT 路径导航图(7.3) 状态树增量更新复用图(7.3) EVM 五组件架构图(7.4) 跨合约调用序列图(7.4) EIP-1559 动态调节流程图(7.5) 预编译路由 vs 字节码执行图(7.6) ZK-Rollup L1 验证流程图(7.6) 交易执行/回滚流程图(7.7) PoS 最终性阶梯图(7.7)
第7章架构与协议对比
---------- -------- -------- ------
账户模型 UTXO 账户+Nonce 状态机 vs 资产流
代码执行 Script(非图灵完备) EVM(图灵完备,Gas 停机) 通用性 vs 极简性
状态存储 无(短期脚本执行) MPT 持久化全局状态 可编程性 vs 膨胀性
费用市场 首价拍卖 EIP-1559 双因素 可预测性 vs 简洁性
密码学加速 无 预编译合约 0x01-0x0A ZK 与跨链基础
最终性确认 概率性(6 区块惯例) 确定性(epochs) PoW 算力 vs 质押经济
第7章算法与数据结构
----------- ------ -------- ------
Patricia Merkle Trie 全局状态存储与验证 O ( k ) O(k) O ( k ) 操作, O ( log N ) O(\log N) O ( log N ) 证明07.03
RLP 编码 交易/区块/节点序列化 O ( n ) O(n) O ( n ) 07.07
ECDSA (secp256k1) 交易签名与地址推导 O ( 1 ) O(1) O ( 1 ) (预编译加速)07.07
EVM 栈机 合约字节码解释执行 O ( ∣ c o d e ∣ × G a s − 1 ) O(|code| \times Gas^{-1}) O ( ∣ co d e ∣ × G a s − 1 ) 07.04
EIP-1559 调节算法 基础费用自动平衡 O ( 1 ) O(1) O ( 1 ) / 区块07.05
LMD-GHOST + Casper FFG 选择最重重链 + 最终化 投票开销 O ( N ) O(N) O ( N ) , 确定性 O ( 1 ) O(1) O ( 1 ) 07.07
下一章衔接桥
第7章让以太坊的"世界计算机"形象从抽象愿景落地为具体可计算模型——EVM + MPT + Gas + 预编译。第8章将深入以太坊扩容方案 :从侧链到状态通道,从 ZK-Rollup 到 Optimistic Rollup,以及以太坊 2.0 分片路线图的工程权衡。如果说第7章是"能计算",第8章就是"算得快、算得起"。
本章总结完毕。前往 → 第8章 扩容方案:从 L2 到分片
评论
0评论加载中…