EVM:驱动世界计算机的虚拟机
上一篇留了一个问题:全局状态表只是一张表,谁来执行改写它的逻辑?答案是 EVM——一台所有节点都在运行的确定性沙盒虚拟机。本文讲清 EVM 的执行模型(源码 → 字节码 → 操作码)、为什么「同样输入必得同样输出」是共识的前提、「一次编写多链运行」的 EVM 兼容生态,以及它如何构成以太坊的网络性护城河。
引言:状态有了,谁来执行?
上一篇(账户模型 vs UTXO)结束时留了一个问题:以太坊的全局状态只是一张全网共享的大表,数据自己不会变——一笔交易说「把 Alice 的余额改成 Y、把合约存储的第 0 号槽位写成 Z」,凭什么是这个结果?谁来执行这段逻辑?又如何保证全世界成千上万个节点执行出一模一样的答案?
答案是 EVM ——以太坊虚拟机。它是以太坊从「记账机器」升级为「世界计算机」的那颗 CPU:每个节点都在自己机器上的一个沙盒里运行同样的程序,得出同样的状态改动,然后对「下一张状态表长什么样」达成共识。
这一篇拆四个问题:EVM 到底是什么(一台确定性沙盒);它怎么执行程序(源码 → 字节码 → 操作码);为什么那么多链都「兼容 EVM」;以及这一切为什么构成以太坊最深的护城河。
EVM 是什么:一台确定性沙盒
先把定义拆成两半。
沙盒(Sandbox):EVM 是一个完全隔离的运行环境。合约代码在 EVM 里跑,能碰到的只有以太坊给它准备的东西——栈、内存、临时存储、合约自己的持久存储、以及最近几个区块的少量信息。它读不到你节点的硬盘,调不了操作系统,也发不了网络请求。一份恶意的合约再怎么折腾,都被关在这个盒子里。
确定性(Deterministic):同样的初始状态 + 同样的输入,必然得出同样的输出。没有随机数、没有系统时间、没有「看运气」的成分——任何不确定的来源都被排除在设计之外。
确定性不是工程优化,是共识的前提。回想阶段 1 的 工作量证明:矿工生产区块,全网全节点验证。到了以太坊,验证意味着什么?意味着每个全节点都要亲手把区块里的每笔交易重新执行一遍,看状态改动是否与区块声称的一致:
矿工/验证者打包区块:
交易 A → EVM 执行 → 状态改动 SA
交易 B → EVM 执行 → 状态改动 SB
...
全节点验证区块:
交易 A → 自己的 EVM 再执行一遍 → 得到 SA'
交易 B → 自己的 EVM 再执行一遍 → 得到 SB'
...
SA == SA' 且 SB == SB' 且 ... → 接受区块
任何一处对不上 → 拒绝区块
如果 EVM 不是确定性的——同一笔交易在你机器上算出 5、在我机器上算出 6——验证就无从谈起,共识直接崩塌。所以「世界计算机」这个定位的正确读法是:它是一千台独立计算机在同时算同一道题、并因为答案必然一致而不需要互相信任。
执行模型:从源码到状态改写
第一步:源码编译成字节码
开发者写的是 Solidity 这类高级语言,但 EVM 不读源码。部署合约时,编译器把源码翻译成 字节码 ——一串十六进制表示的指令序列。真正上链的就是这串字节码,它被存进合约账户的「代码」字段(还记得账户模型那篇里的全局状态表吗?合约账户那一行的「代码 / 存储」列)。
这也意味着链上没有人能「读」你的合约——大家看到的是字节码,不是源码。区块浏览器里能显示源码,是因为开发者主动公开了源码并让浏览器验证「源码编译结果 == 链上字节码」。
第二步:操作码逐条执行
字节码的最小组成单元是 操作码 (opcode)。每个操作码占一个字节,告诉 EVM 做一件极小的事:从栈上取两个数相加、读某个存储槽位、跳转到另一条指令……EVM 是一台栈式计算机——不通过寄存器,而是把操作数压进一个栈,每条指令从栈顶取数、算完把结果压回去。这个栈有 1024 层的深度上限(正常程序远碰不到,只在病态递归时才会触及,这里一句带过)。
用一笔真实调用串起来:
合约调用 → EVM 执行 → 状态改写
你发起交易:调用代币合约的 transfer(Bob, 10)
│
▼
┌─────────────────────────────────────────┐
│ EVM 沙盒(每个验证节点各跑一份) │
│ │
│ 字节码: 6080604052... PUSH20 MLOAD ... │
│ │ 解码 │
│ ▼ │
│ 操作码流(栈式逐条执行): │
│ CALLDATALOAD ← 读入参数 (Bob, 10) │
│ SLOAD ← 读 Alice 余额槽位 │
│ DUP2 LT ← 余额够不够? │
│ SSTORE ← 写 Alice 余额 −10 │
│ SSTORE ← 写 Bob 余额 +10 │
│ ... │
└─────────────────────────────────────────┘
│
▼
全局状态表:两个余额槽位被改写
(区块内按 nonce 排序的下一笔交易接着执行)
注意最后一步:EVM 不返回「结果」给谁——它直接改写全局状态表的槽位。执行的产出就是状态变更本身,这也是「交易 = 状态改写」在执行层的实现。
执行的同义词:烧钱
每执行一条操作码都有固定定价(ADD 一个价、SSTORE 另一个价)。执行越多、碰存储越多,消耗越多。这里先记住结论——计算不免费——具体怎么计价是下一篇 Gas 的主题。
一次编写,多链运行:EVM 兼容生态
EVM 的故事到上一节只是「以太坊自己的虚拟机」。真正让它超车的,是它成了行业的事实标准。
Arbitrum、Base、Optimism、zkSync……今天绝大多数 Layer2 ,以及不少独立公链,都选择直接实现 EVM 作为自己的执行层。对开发者这意味着:同一份 Solidity 代码、同一套开发工具、同一套测试流程,换个 RPC 地址就能部署到另一条链——「一次编写,多链运行」。
为什么 Rollup 们天然要兼容 EVM?因为 Optimistic Rollups 和 ZK Rollups 的思路都是「把执行搬下链、把数据/证明留在主网」——搬下去的执行引擎如果沿用 EVM,主网验证者与生态工具可以原样复用,迁移成本最低。
这条生态覆盖面有多宽,可以看本站 Web3 研究报告里的口径:EVM 兼容链已包括 Arbitrum、Base、Optimism、zkSync 等主流 Rollup 。费用差距也是这个架构的直接红利:以太坊主网 Gas 通常在个位数到几十 gwei 的量级波动(Dencun 升级后长期低于 10 gwei),而 L2(Arbitrum/Base)往往不足 0.01 gwei——同样的计算,单价差出约三个数量级。
网络性护城河
那么问题来了:虚拟机谁都能写,换一套指令集(非 EVM 链确实这么做过)不行吗?
技术上当然行。难的是搬走 EVM 背后的网络效应:
- 开发者:以太坊生态的开发者规模是行业最大的存量,会 Solidity / EVM 工具链的人最多。兼容 EVM = 立刻接入这个人才池。
- 工具与审计:编译器、测试框架、区块浏览器、调试器、审计公司的经验积累——十几年沉淀全围绕 EVM 字节码。新 VM 要从零重建这一切。
- 资产与合约流动性:无数已部署、已审计、已沉淀 TVL 的合约都是 EVM 字节码。「换 VM」意味着这些资产要么冻结、要么走跨链桥——两者都是摩擦。
- 安全的复利:EVM 的坑(重入、整数溢出等)被研究得最透,最佳实践与审计知识沉淀最厚。一个新 VM 的「未知坑」本身就是风险。
所以每一条新链做技术选型时都面对同一个算术题:自创 VM 的差异化收益,抵不抵得过放弃 EVM 网络的迁移成本?多数时候答案是否定的——于是它们兼容 EVM,而这又进一步壮大了 EVM 网络本身。护城河不是虚拟机设计得多精妙,而是「所有人都在用」这件事本身。
这与 Bitcoin 的网络效应同构:不是密码学多先进,而是算力、节点、共识与品牌共同构成的锁定。区别在于比特币的护城河围绕「价值存储」这一个问题,EVM 的护城河围绕「可编程性」这一整类问题。
总结:执行有了,如何计价?
这一篇把「世界计算机」的 CPU 拆开了:
- EVM 是一台确定性沙盒:同样输入必得同样输出,这是上千个节点能各自独立验证、进而达成共识的前提
- 执行模型是源码 → 字节码 → 操作码逐条执行:栈式机改写全局状态槽位,执行产出就是状态变更
- 「一次编写多链运行」的 EVM 兼容生态(Arbitrum/Base/Optimism 等)让它成为行业事实标准
- 围绕开发者、工具、资产与安全知识沉淀的网络效应,构成以太坊最深的护城河
但还有一个我们反复按下不表的问题:执行每条操作码都有定价——凭什么 ADD 一个价、SSTORE 另一个价?谁定的?怎么防止一笔交易把全网节点拖死? 一句话:去中心化的世界里,没有免费的 CPU 时间。执行必须计价,而且必须由发起人付费。
这个定价系统就是 Gas ——下一篇文章的主角。
相关文章:
- 账户模型 vs UTXO:以太坊换了一种记账方式 — 前篇:EVM 改写的那张全局状态表从哪来
- UTXO 模型:比特币的会计学 — 镜像对照:比特币为什么不需要虚拟机
- 工作量证明 — 共识的 Bitcoin 侧:EVM 的确定性为哪一类共识服务
- 比特币白皮书精读 — 一切的原始蓝图
- 比特币网络实际运行 — 全节点独立验证的 Bitcoin 版本,对照 EVM 的重放验证