Bitcoin 的输入消耗产生新输出;EVM 的世界状态主要描述账户的 nonce、余额、代码与存储承诺。合约调用携带发送者、目标、value、calldata 与可用 gas,在给定初始状态和规则版本下按确定性执行产生新状态、日志、gas 消耗及成功或失败结果。区块链(17):执行、共识客户端与验证者怎样拼成一个节点的 CL 不替 EL 重演每一步执行,但最终能否认可载荷依赖执行验证。

flowchart LR
  I[前状态根 + 签名交易 + fork 规则] --> E[EVM: 栈、内存、storage 与调用帧]
  E --> A[账户余额/nonce/代码/存储变化]
  A --> R[后状态根]
  E --> L[日志与回执: 单独承诺对象]
  E --> X[revert: 调用帧效果撤销,费用另算]

栈用于操作数,内存是调用期间的临时字节区域,storage 是合约持久化的键值空间;一次调用的中间值不自动变成永久链上状态。外部调用可以转移执行控制,必须区分调用失败返回与 revert 传播、内部状态撤销与发送者实际消耗的费用。状态根只承诺按规定编码后的世界状态,不等于共识客户端自身的验证者状态根;两者的更新时序和验证规则不同。

同一个事件为何不等于当前余额

合成托管在旧状态 S 的余额为 70,调用 confirm(order) 时读合约存储,判断角色、订单金额和是否已确认;成功才更新订单状态并发出事件,随后回执记录执行结果。区块的状态根承诺执行完这一批交易后的世界状态;日志属于回执记录,不是一个能像合约 storage 一样在下一笔交易中被调用者读取并修改的永久字段。若后来 withdraw 消耗了可领取债权,过去 Confirmed 事件仍会保留在规范历史,但当前可领余额变成 0。索引器若只看事件而跳过后续状态,不应告诉卖家“还能领 70”。

若 confirm 在内部检查后调用外部合约,外部代码也可能改变后续控制流程并触发回滚。交易内某个调用帧的 revert、顶层交易的失败回执、区块是否还在规范链,是三个层次;不能从“找不到事件”反推交易从未提交,更不能从 eth_call 的模拟值推断执行后状态根。要复算同一结果必须固定完整前状态、交易顺序、合约代码和 fork 规则。代码持久化与 EVM 内存是不同存储域;CL 的验证者状态也不能拿 EL 世界状态根代替。

练习一:在某块读取回执有事件,但后续区块状态中查不到声称的订单,哪个事实优先决定当前业务状态?答案:先核对回执是否来自现行规范链,事件是历史日志,当前状态应查询相同链语境下的合约存储并对账。练习二:把交易输入和代码保持不变,却改变前状态中的一个存储槽,能保证相同输出吗?答案:不能;确定性以相同规则与完整前状态为前提。

EL 客户端、EVM target 与链上状态根均未冻结,实际 eth_call、交易执行和根复算 NOT_RUN。可迁移原则:任何结果声明都要绑定前状态、操作字节和规则版本。参考:Ethereum 执行规范、Yellow Paper(早期语义入口)(均待读,不能证明当代网络激活);导航:区块链(17):执行、共识客户端与验证者怎样拼成一个节点 · 18 · 区块链(19):revert 以后为什么仍要核算 Gas 与 nonce。