以太坊黄皮书学习笔记(三)深入理解状态转换函数与交易处理

在以太坊黄皮书学习笔记的前两篇中,我们探讨了以太坊作为“世界计算机”的宏伟愿景,并初步接触了其核心的状态树(State Tree)交易树(Transactions Tree)收据树(Receipts Tree)这三大默克尔帕特里夏树(Merkle Patricia Trie, MPT)结构,这些数据结构共同构成了以太坊区块链的“骨架”,记录了系统的历史状态和所有发生过的行为。

本篇笔记,我们将深入到这个“骨架”的“灵魂”——状态转换函数(

随机配图
State Transition Function, Δ),理解了Δ,我们才能真正明白一笔交易是如何被系统处理,并最终改变世界状态的。

什么是状态转换函数(Δ)?

以太坊的状态可以被看作是一个巨大的、分布式的数据库,这个数据库存储了所有账户的余额、代码、存储等信息,而状态转换函数Δ,就是这个数据库的“操作规则”。

Δ函数描述了一个“状态”如何根据一笔“交易”,转变为一个新的“状态”,这个过程可以抽象为一个数学表达式:

S(t) = Δ(S(t-1), T(t))

这个公式的含义是:

  • S(t):在区块 t 被执行之后,以太坊的最终状态
  • S(t-1):在区块 t 被执行之前,以太坊的初始状态
  • T(t):区块 t 中包含的所有交易集合
  • 状态转换函数,它接收旧状态和一笔交易,输出一个中间状态,它会将这个中间状态与下一笔交易再次作为输入,直到区块中的所有交易都被处理完毕。

Δ函数是以太坊确定性保证的核心,只要输入(初始状态和交易)相同,无论在哪个节点上运行,输出(最终状态)都必然是相同的,这正是所有节点能够达成共识、维护同一个账本的基础。

Δ函数的内部:一笔交易的完整生命周期

黄皮书用极其严谨和形式化的语言定义了Δ函数,我们可以将其分解为处理单笔交易 T 的几个关键步骤,这正是以太坊虚拟机执行交易的底层逻辑。

假设我们要处理一笔交易 T = (nonce, gasprice, startgas, to, value, data, v, r, s),其处理流程如下:

步骤 1:交易验证

在执行交易之前,必须先验证其有效性,这一步是确保交易发起者拥有足够的权限和资源。

  1. 检查签名:根据交易中的 v, r, s 值,使用椭圆曲线算法恢复出发送者的地址,如果恢复出的地址与交易 from 字段不匹配,则交易无效。
  2. 检查 nonce:查询发送者账户 A 的当前 nonce 值,如果交易中的 nonce 不等于账户 A 的当前 nonce,则交易无效。
  3. 检查余额与 Gas:查询发送者账户 A 的当前余额,如果账户余额不足以支付 value(转账金额) + gasprice * startgas(预估的总费用),则交易无效。
  4. 检查 Gas Limit:如果交易的 startgas 大于区块的 gaslimit,则交易无效。

如果以上任何一项检查失败,交易将被标记为无效,状态不会发生改变,但交易发起者仍需支付一部分 Gas(用于支付验证成本)。

步骤 2:初始化执行环境

交易验证通过后,EVM 将为这笔交易创建一个独立的执行环境,也称为“沙箱”。

  1. 创建接收者账户:如果交易 to 字段为空(即创建合约交易),则创建一个新的合约账户,新账户的 nonce 为 1,code 为空,storage 为空。to 不为空,则目标账户 B 必须已经存在。
  2. 转移 Ether:从发送者账户 A 的余额中扣除 value,并将其增加到接收者账户 B 的余额中。
  3. 扣除预付 Gas 费:从发送者账户 A 的余额中扣除 gasprice * startgas 这笔总费用,并将其暂时存入一个“矿工奖励”账户中。
  4. 设置执行上下文:初始化 EVM 的所有寄存器和内存,包括 gas(剩余 Gas,初始为 startgas)、value(交易传递的 Ether 值)、sender(发送者地址)、callvalue 等。

步骤 3:执行交易代码

这是整个过程中最核心的一步,即运行 EVM 字节码。

  • 对于普通转账交易to 是一个普通账户,则没有代码需要执行,此步骤直接跳过。
  • 对于合约创建/调用交易:EVM 会开始执行接收者账户的代码。
    • 合约创建:执行 data 字段中的字节码,如果代码执行成功(即没有耗尽 Gas 或导致其他错误),则执行结果(返回的字节串)将被设置为合约账户的 code,如果执行失败,则状态回滚到交易执行之前。
    • 合约调用:执行 to 地址合约的代码,EVM 会按照指令集逐条执行字节码,这个过程会修改内存、栈,并根据指令消耗 Gas。

步骤 4:处理 Gas 消耗与退款

在代码执行过程中,每一条指令都会消耗一定量的 Gas,EVM 会实时跟踪剩余的 Gas。

  • Gas 耗尽:如果在执行过程中 gas 耗尽,交易会立即失败,状态会完全回滚到执行之前,但矿工仍能获得已消耗的 Gas 作为报酬。
  • 执行成功:如果代码正常执行完毕,会进入 Gas 结算阶段。
    1. Gas 退款:某些操作(如 SSTORE 将一个值从非零变为零)会返还一部分 Gas,返还的 Gas 不会超过初始 Gas 的一半。
    2. 支付矿工:剩余的 Gas(startgas - 消耗的 Gas)会被返还给发送者,而消耗掉的 Gas(gasprice * 消耗的 Gas)则作为矿工的手续费,存入其账户。

步骤 5:更新状态

这是交易处理的最后一步,将执行结果“写入”状态。

  1. 更新 Nonce:发送者账户 Anonce 值加 1。
  2. 清除临时状态:在执行过程中,EVM 可能修改了合约的临时存储,这些修改只有在交易成功时才会被永久写入到状态树的 storage 中。
  3. 生成收据:为这笔交易生成一个收据,并将其添加到当前区块的收据树中,收据包含了交易哈希、状态(成功/失败)、Gas 使用量、日志 bloom 过滤器以及日志等信息,这对于轻客户端和事件监听至关重要。

从抽象到具体

通过学习黄皮书中对状态转换函数 Δ 的定义,我们不再是简单地“发送一笔交易”,而是能够清晰地描绘出其背后精确的、可执行的步骤:

  1. 验证:确保交易格式正确、发送者有足够权限和资金。
  2. 初始化:创建或准备账户,转移价值和预付 Gas 费用。
  3. 执行:在 EVM 中运行代码,改变内存和临时状态,并消耗 Gas。
  4. 结算:处理 Gas 的消耗、退款和矿工奖励。
  5. 提交:将最终的变更(Nonce、Storage 等)永久写入世界状态,并生成收据。

这个严谨的流程,是以太坊作为“确定性状态机”的基石,它确保了全球成千上万的节点,无论出于何种动机,只要遵循相同的规则(Δ函数),就能对世界的状态变化达成一致,下一篇文章,我们将继续深入,探讨如何通过区块头将这些状态转换高效地链接起来,形成最终的区块链结构。

本文由用户投稿上传,若侵权请提供版权资料并联系删除!