03.04 · EVM × Smart contracts

代码如何成为全网共同执行的规则?

智能合约写下“做什么”,EVM 规定“怎么算”。当一笔调用进入网络, 独立节点会从同一旧状态出发,复算出同一个新状态——这才是“世界计算机”的技术地基。

  • 03 · 04 第三章 · 第四课
  • 约 35 分钟 阅读 + 交互
  • 前置 区块、节点、哈希
TRANSACTION setScore(42)
EVMσ → σ′
执行节点 A
执行节点 B
执行节点 C
STATE ROOT 0x71…c4
same input · same result

Core thesis

智能合约是规则,EVM 是规则的执行语义。 执行层验证每一步计算;共识层决定哪一组经过验证的状态转换成为共同历史。

01 · Separate the layers

先拆开四个经常混在一起的东西

网络、客户端、虚拟机、合约不是四种说法,而是四个层次。 先分清“谁保存规则、谁实现规则、谁被执行、谁让历史收敛”。

如果把以太坊说成一台计算机,最容易产生的误会是:世界上真的有一台叫“EVM”的服务器。 实际上,EVM 是一套抽象机器的协议语义;许多独立执行客户端分别实现它, 在各自机器上重放同一批交易。[1]

智能合约则是存放在以太坊状态中的程序:它有可执行代码,也可以拥有持久状态,并位于某个地址。 用户提交交易请求调用它;合约也可以在一次交易内部向其他合约发出消息调用。 它不是“会思考的合同”,也不会脱离交易自己醒来。[2]

Interactive · Architecture lens

点选组件,看它负责什么、不负责什么

FIG 01

当前焦点

智能合约:被执行的规则

链上真正保存并执行的是编译后的 EVM 字节码。它可以读取输入、检查条件、调用其他合约, 并在权限与 Gas 允许时修改自己的存储或转移资产。

不负责:主动发起顶层交易、决定自己何时运行、选择哪一个区块成为规范历史。

从左到右不是四台机器,而是一条职责链。现实中的验证者节点通常同时运行共识客户端与执行客户端。

02 · State transition

以太坊不只记账,它按规则改变一整个世界状态

余额只是状态的一部分。合约代码、合约存储、账户 nonce 等共同构成执行层状态; 每笔有效交易都是一次受约束的状态转换。

可以把执行抽象成一个函数: 新状态 σ′ = Υ(旧状态 σ, 交易 T)。 这个记号来自以太坊形式化规范对交易状态转换的表达。[9]

这里的“状态”不是一张余额表,而是所有相关账户当时的余额、nonce、代码承诺与存储承诺。 一笔调用还会产生收据、Gas 使用量和日志;这些不是随意的旁白,而是节点可重复验证的协议结果。

区块内交易必须按确定顺序逐笔折叠: σ₁ = Υ(σ₀,T₀)σ₂ = Υ(σ₁,T₁),依此类推。 相同交易集合换一个顺序,前置余额、nonce、合约价格或库存就可能不同,最终状态也可能随之改变。

Interactive · State lab

同一个 setScore(42),成功、回滚与本地模拟有什么不同?

FIG 02
旧状态 σ root 0x19…a2
Score.storage[0]7
Alice.nonce12
Alice.balance2.000 ETH
结果状态 σ′ root 0x71…c4
Score.storage[0]42
Alice.nonce13
Alice.balance1.999… ETH
成功:合约存储从 7 变为 42;发送者 nonce 增加,实际消耗的 Gas 以 ETH 结算,区块得到新的状态根。
为了聚焦因果关系,余额用省略号表示实际费用。状态根是整个状态结构的密码学承诺,不是把三行文本直接拼起来。

“执行失败就什么都没发生”并不准确。若顶层交易已经上链但 EVM 执行回滚, 合约状态写入会撤销,然而发送者的 nonce 已被使用,已经消耗的计算也仍需付费。 如果只是通过 RPC 做 eth_call,节点在本地模拟,不进入区块,也不会留下共享状态变化。 还要区别“进入前即无效”:签名、nonce 或余额等基本有效性不满足的交易,不能作为有效交易被区块执行,也不会产生链上 receipt。

03 · From source to bytecode

Solidity 源码不会直接在链上运行

人写高级语言,编译器产出字节码和 ABI;部署交易安装程序,调用交易只携带地址、数值与一串编码后的字节。

EVM 不理解变量名、注释或“这段代码的商业意图”。它理解的是字节码中的操作码。 Solidity、Vyper 等语言必须先编译,才能部署给 EVM 执行。[3]

ABI(应用二进制接口)则像一份“如何把人类函数调用编码成字节”的说明书。 对典型 Solidity 外部函数调用,calldata 前 4 字节是函数选择器, 后面是按类型编码的参数。选择器取函数规范签名 Keccak-256 哈希的前 4 字节。[5]

Interactive · Lifecycle

部署是在“安装程序”,调用是在“给程序输入”

FIG 03
01 · AUTHOR

编写源码

开发者用高级语言表达状态和规则。

contract Score { uint256 score; function setScore(uint256 next) external { score = next; } }
02 · COMPILE

编译

编译器产出创建字节码、运行时字节码与 ABI。

creation bytecode runtime bytecode ABI source ≠ bytecode
03 · TRANSACT

发送部署交易

to 为空;data 携带创建代码和构造参数。

to: null data: 0x6080… value: 0 gasLimit: …
04 · INSTALL

创建合约账户

初始化代码运行,其返回值成为地址上的运行时字节码。

address: 0xScore… code: 0x6080… storage[0]: 0
部署也是交易:先执行初始化代码,成功后才把它返回的运行时字节码保存到新地址;部署失败则没有合约代码被安装。
区块浏览器显示的 Solidity 源码通常来自额外的“源码验证”:把公开源码按指定编译设置重编译,再与链上字节码比较。协议真正执行的是字节码。

04 · Inside the EVM

EVM 执行时,数据住在哪里?

“变量”在源码里看起来相似,到了 EVM 却可能属于完全不同的空间。 生命周期、可写性和 Gas 成本差异巨大。

EVM 是 256 位字长的栈式虚拟机,栈最大深度为 1024 项。 每次消息调用还有临时 memory;每个合约账户拥有持久 storage; 交易输入以 calldata 进入。现代 EVM 还提供仅在一笔交易内存活的 transient storage。[1]

初学者不需要马上背操作码,但必须先建立“数据寿命”心智模型: 一次操作、一次调用、一笔交易、跨交易持久化,是四个不同时间边界。

Interactive · EVM workspace

切换数据空间,观察寿命与边界

FIG 04

OPERATION WORKSPACE

Stack:操作码手边的计算栈

EVM 的大多数指令从栈顶取值、计算,再把结果压回栈顶。栈元素是 256 位字, 最大深度 1024;它适合当前执行帧里的即时运算,不负责跨调用保存业务状态。

生命周期当前调用帧
写入方式操作码压入 / 弹出
主要边界不持久化
图示强调概念层次,不代表实际内存布局。不同操作的 Gas 成本由当前协议版本定义,不能只用“快慢”判断。

score = 42 为例:42 可能先从 calldata 解码到栈或 memory, 最终由存储写入指令改变合约 storage。交易成功后,持久存储变化进入新世界状态; 如果调用回滚,当前失败作用域的这些写入不会提交。[6]

05 · One call, end to end

一笔 setScore(42) 如何穿过以太坊?

把钱包、交易池、区块提议、执行、验证、状态根和最终性接起来。 这条链一旦通了,前面所有名词才真正成为架构。

顶层交易由可授权交易的账户签名并广播。交易包含目标地址、数值、输入数据、nonce 与费用上限等字段; 当目标地址有可执行代码时,执行客户端会以 calldata 为输入启动相应代码。[4]

Interactive · Dual-lane journey

逐步查看:谁在算,谁在决定顺序

FIG 05

STEP 01 · WALLET / ABI

钱包把“setScore(42)”翻译成机器输入

尚未改变链上状态

执行轨what result?
ABI 编码函数选择器与参数 42,构造目标地址、nonce、Gas 与费用字段
共识轨which history?
此时还没有候选区块;共识层尚未对这笔请求作出任何历史选择

钱包根据 ABI 生成 calldata。用户看到“调用函数”,网络看到的是一组带类型语义的字节。 仅仅点击按钮不会自动改链;下一步还需要账户授权并广播交易。

边界:钱包是界面与签名工具,不是共识权威;它可以把意图显示错,所以签名前必须核对实际交易字段。
为简化阅读,图中把网络传播与客户端内部细节压缩成七步。合并后的验证者节点通过执行客户端与共识客户端协作完成完整验证。

06 · Execution ≠ consensus

执行层回答“结果对不对”,共识层回答“哪段历史算数”

两层必须协作,但不能用一句“验证者执行并确认”把职责糊在一起。 分清它们,是理解合并后以太坊架构的关键。

给定同一个前状态与同一组有序交易,执行层应当得出唯一后状态。 但网络可能在短时间看到不同候选区块;共识层需要用提议、证明、分叉选择与最终性规则让节点收敛到规范历史。 两类客户端通过 Engine API 交换执行载荷与有效性结论。[12][13]

因而,“执行正确”是一个必要条件,却不等于“这个候选区块已经成为最终历史”。 反过来,共识权重也不能投票把错误计算变成正确计算:无效执行载荷应被合规节点拒绝。

Comparison · Two layers

同一节点软件栈里的两条判断链

FIG 06
问题
执行层 / EVM
共识层
主要输入
前状态、按顺序排列的交易、区块执行环境
候选共识区块、验证者证明、检查点与协议时序
核心判断
签名/nonce/余额等是否有效;字节码每一步如何执行
跟随哪个链头;哪些检查点已 justified / finalized
主要输出
后状态、状态根、收据、日志、Gas 使用与成功状态
规范链视图与逐步增强的经济最终性
不能单独做
不能靠 EVM 自己决定哪一个竞争区块成为历史
不能靠多数投票接受违反执行规则的状态根
“执行载荷”把两层连接起来:共识区块承载执行层交易与结果承诺;客户端之间通过定义好的接口协作。

07 · Determinism & offchain boundary

为什么合约不能直接请求天气网站?

所有诚实节点必须对同一输入复现同一输出。 任何可能因地点、时间或服务器响应而不同的外部读取,都会破坏这条前提。

EVM 的关键不是“算得快”,而是可重复、可验证。 节点使用协议允许的前状态、交易与区块上下文计算;不同实现若产生不同结果,就会造成共识级问题。

因此合约不能像普通后端那样在执行中直接发 HTTP 请求。节点 A 可能得到晴天,节点 B 可能超时, 节点 C 可能被不同 CDN 返回值命中。以太坊无法从这些答案中确定唯一状态。 若需要链外事实,通常由预言机或其他参与者把数据作为可验证的链上输入提交;合约再按规则消费它。[2]

Interactive · Re-execution

相同输入、错误实现与链外分歧

FIG 07
一致:三个独立实现都得到同一后状态承诺。网络不需要信任某台主服务器发布“正确答案”。

链外世界

天气、价格、比赛结果

事实可能由不同来源、不同时间与不同权限观察;HTTP 响应本身不属于 EVM 的确定性协议输入。

签名数据
交易 / 消息

链上世界

可复算的状态转换

数据进入链上后,合约验证发送者、格式、时效或聚合规则,再以同一输入执行确定性逻辑。

预言机解决的是“如何把链外陈述带入链上”的机制与信任问题,并不会把链外事实自动变成绝对真相。

08 · Gas & atomicity

Gas 给计算设边界,回滚给组合调用设安全语义

EVM 允许通用程序,却不能允许无限工作。 每条操作按协议计量;调用成功就提交,失败作用域的状态变化则撤销。

Gas 是计算与资源使用的计量单位,Gas fee 才是用户最终支付的费用。 可以先记住:执行费 ≈ gas used × effective gas price。 Gas limit 则给一笔交易最多可消耗的工作量设置上限。

如果执行耗尽 Gas,当前执行失败并回滚相应状态变化;已经消耗的 Gas 不会因为“没有业务结果”而免费。 REVERT 指令允许合约主动停止、回滚当前失败作用域并返回错误数据, 而不会像耗尽 Gas 那样自动吃掉该调用剩余的全部 Gas。[7][14]

Interactive · Call tree

成功、未捕获回滚、捕获子调用失败

FIG 08
合约 A · 写入 pending = true
合约 B · 检查条件并更新
COMMIT · 两层写入一起生效

A 调用 B,B 成功返回,A 继续执行;顶层交易成功时,整个调用树中未回滚的状态写入一起提交。

成功:组合调用提供“要么整组按路径成功,要么按异常语义撤销”的原子基础。
进阶细节:低级调用或 try/catch 可以让调用方观察子调用失败并继续;因此精确说法是“失败调用帧的状态回滚”,是否让顶层一起失败取决于调用方如何处理。

09 · Read versus write

“调用合约”可能只是本地模拟,也可能真正改链

前端里的一个按钮,背后可能走两条完全不同的路径。 是否签名、是否进区块、是否付费、是否留下状态,是判断关键。

读取 getScore() 常由节点通过 eth_call 在指定区块状态上本地执行; 它不创建链上交易,用户不为这次 RPC 模拟支付费用。写入 setScore(42) 则需要授权交易、等待被打包并支付执行费用。

判断项 本地读取 / eth_call 状态写入 / transaction
需要账户签名? 通常不需要;可以带模拟的 from 需要有效授权,顶层交易有发送者
进入交易池与区块? 不进入,只由被请求节点本地执行 广播后等待提议者纳入候选区块
用户付 Gas fee? 不向链支付;服务商仍承担计算资源 按链上实际 Gas 使用与有效价格结算
改变共享状态? 不会;返回值是对某个状态的模拟结果 成功且进入规范历史后产生持久状态变化
结果会不会过期? 会;下一块或并发交易可能改变读取前提 一旦执行,结果属于该区块上下文;最终性再增强历史确定性

Solidity 的 view / pure 描述函数对状态的约束,不等于“在任何场景都没有 Gas”。 从前端以 eth_call 模拟时用户不付链上费用;若另一个合约在一笔真实交易中调用这些函数, 相关 EVM 指令仍消耗该交易的 Gas。[4]

普通 Solidity 函数的返回值不会自动永久写进交易 receipt。返回数据在执行期间交给调用者; 链下应用可通过 eth_call 取得模拟返回值,或在真实交易后查看 receipt 中的 status 与 logs。 需要让链下索引器长期观察的信息,通常由合约明确发出事件日志。

10 · Boundaries & misconceptions

六个误区,决定你是否真的理解了“世界计算机”

口号往往省略边界。把下面六句话改准确, 你就已经拥有分析钱包、DApp、合约与节点问题的基础框架。

误区 01 · “EVM 是一台全球服务器”

EVM 是共同执行语义,不是单点机器

多个执行客户端实现同一协议,在独立节点上复算。任何一台机器都不是全网结果的唯一发布者。

误区 02 · “合约会自动运行”

合约必须处在某笔交易的调用树里

EOA 或协议允许的账户授权发起顶层交易;合约只能在被调用后继续调用其他合约。

误区 03 · “全世界每个人都执行每笔交易”

执行节点复算,普通用户设备未必复算

全执行节点验证状态转换;轻客户端、托管钱包和普通浏览器可能依赖证明或 RPC,而非本地完整执行。

误区 04 · “链上跑的是 Solidity”

EVM 执行的是部署后的字节码

源码与 ABI 帮助人和工具理解;只有重编译匹配,才能把公开源码与实际字节码可靠对应。

误区 05 · “交易回滚就完全没成本”

状态可回滚,资源消耗与顶层 nonce 不倒退

失败交易仍占区块空间、消耗计算;回滚保护状态一致性,不是免费撤销按钮。

误区 06 · “代码就是法律,没人能改”

要检查代理、管理员、预言机和治理边界

某个地址的运行时代码通常不可原地改写,但系统可通过代理等模式把逻辑指向新实现;权限结构才决定实际控制面。

Summary · Mental model

从意图到共同历史,只保留五个动作

FIG 09
ENCODE

编码意图

ABI 把函数与参数变成 calldata。

AUTHORIZE

授权请求

账户签名交易,声明 nonce 与资源上限。

EXECUTE

确定性执行

EVM 解释字节码,读取并尝试改变状态。

VERIFY

独立复算

执行节点验证同一后状态、收据与 Gas。

FINALIZE

历史收敛

共识选择规范链,最终性增强不可逆确定性。

记住这五步,之后学习交易、Gas、EVM、合约和 PoS 时,只是在分别放大其中一段。

11 · Understanding check

不用背术语,检查你能否解释因果

每题只选一个最准确答案。答完后,尝试不看页面说出: “为什么相同字节码能在没有中央服务器时形成共同状态?”

1. EVM 最准确的定位是什么?

EVM 是协议定义的执行模型;客户端可以用不同语言实现,但对有效输入必须产生兼容结果。

2. 调用 setScore(42) 的 calldata 前 4 字节通常表达什么?

典型 Solidity ABI 调用以 4 字节函数选择器开头,后面跟按 ABI 编码的参数。

3. 一笔已上链交易执行 REVERT 后,哪项说法正确?

回滚保护状态一致性;它不会否认节点已实际完成的计算,也不会让顶层交易 nonce 重用。

4. 为什么合约不能在执行中直接请求任意天气 API?

链外数据必须通过某种可进入共识的输入机制提交;预言机解决输入与信任,而非取消确定性要求。

5. 执行层与共识层的分工,哪项最准确?

共识不能把无效执行投票变有效;执行也不能独自解决竞争区块的规范顺序。

12 · Glossary & primary sources

把术语钉在正确层级上

下面不是背诵表,而是一组最小定义。遇到新概念时, 先问它属于输入、执行、状态,还是共识。

EVM
Ethereum Virtual Machine。协议定义的确定性执行环境;执行客户端实现它,用于解释 EVM 字节码。
智能合约
部署在以太坊地址上的程序与相关状态。它由交易或消息调用触发,而不是独立进程。
bytecode
EVM 原生执行的字节序列。高级语言源码需要编译为创建字节码与运行时字节码。
calldata
消息调用的只读输入字节;典型 ABI 调用包含 4 字节函数选择器和编码参数。
storage
账户关联的持久键值存储;成功交易中的写入会影响后续状态,修改通常比临时数据操作昂贵。
状态转换
把前状态与有效交易按协议规则映射为后状态的过程,可抽象为 σ′ = Υ(σ, T)。
Gas
EVM 工作量与资源使用的计量单位。Gas fee 是 Gas 使用量与每单位有效价格共同形成的 ETH 费用。
REVERT
停止当前执行并回滚失败作用域状态变化、返回错误数据的 EVM 指令;已消耗 Gas 不会倒退。
执行层
处理交易有效性、EVM 执行、状态、收据和执行载荷的协议与客户端职责集合。
共识层
通过 PoS 提议、证明、分叉选择与最终性规则,让节点对规范区块历史收敛。

一手资料

  1. ethereum.org · Ethereum Virtual Machine (EVM) — EVM 的状态机定位、栈式执行、memory、storage 与 transient storage。
  2. ethereum.org · Introduction to smart contracts — 合约的代码与状态、调用方式、可组合性和链外数据边界。
  3. ethereum.org · Compiling smart contracts — 高级语言源码如何变成 EVM 可执行字节码与操作码。
  4. ethereum.org · Transactions — 交易字段、calldata、合约部署/调用、Gas 与交易生命周期。
  5. Solidity documentation · Contract ABI Specification — 4 字节函数选择器与参数编码的规范。
  6. Solidity documentation · Introduction to Smart Contracts — storage、transient storage、memory、stack、交易与消息调用。
  7. EIP-140 · REVERT instruction — 回滚状态变化、返回错误数据以及剩余 Gas 语义。
  8. EIP-7702 · Set Code for EOAs — 账户代码委托指示器、Type 4 交易与传统 EOA/合约边界的变化。
  9. Ethereum Yellow Paper — 交易状态转换函数 Υ 的形式化表达;用于理解模型,当前升级细节应以活跃执行规范为准。
  10. Ethereum Execution Layer Specifications — 跟随网络升级维护的执行层规范与测试基准。
  11. ethereum.org · Smart contract composability — 合约作为公开可组合组件的模型与边界。
  12. Ethereum Execution APIs · Engine API — 合并后共识客户端与执行客户端交换载荷、构建请求和有效性结论的接口规范。
  13. Ethereum Consensus Specifications — 共识状态转换、分叉选择、验证者职责与最终性的活跃规范。
  14. ethereum.org · Gas and fees — Gas 计量、费用、Gas limit 与执行资源边界。
  15. EIP-6780 · SELFDESTRUCT only in same transaction — Cancun 以后 SELFDESTRUCT 对既有合约代码与存储的现行语义。

核对日期:2026-07-25。协议会继续升级;涉及具体操作码、Gas 定价、交易类型与客户端行为时,应回到当前执行规范和对应 EIP。

Lesson complete · 03.04

现在,你可以把“世界计算机”从口号还原成机制

它不是一台被所有人远程使用的超级服务器,而是一套让独立节点能够 编码同一请求、执行同一规则、核对同一状态变化,并对历史顺序收敛的协议系统。

下一课 · 完整链路 一笔调用,如何让全网得到同一个新状态? 把签名、交易池、区块、EVM 执行、状态根与最终性连成一条可追踪的因果链。