几乎每一条新公链的首页都会写「完全兼容 EVM」。这句话听起来像技术承诺,更像需要被追问的广告词:合约能否不改字节码直接部署?钱包与浏览器能否只换 RPC 端点就接入?还是仅仅「可以用 Solidity 写」?这三件事在工程上相差极大,对外传播时却经常被压成同一句话。
对一条既要保留 EVM 兼容、又要把执行模型改成并行的链来说,这不是修辞问题,而是每天都要守住的工程约束。改了调度顺序、加了冲突检测与多引擎之后,如何保证对合约作者可见的行为仍与以太坊主网一致,才是「兼容」二字真正值钱的地方。前文 《Bitroot 定位》 已经说明 Bitroot 为什么坚持完整 EVM 兼容;本文把「完整」拆成可核查的四层。
字节码层:兼容的最小单位是操作码,不是语法
EVM 从来不执行 Solidity 源码,它执行的是编译后的操作码序列:围绕栈、内存与存储的指令集,外加账户模型——每个地址有 nonce、余额、代码哈希与独立存储树。严格兼容意味着操作码语义、Gas 计价、栈深度限制、账户状态读写规则与以太坊目标硬分叉对齐。在此前提下,一份未经重新编译的字节码,理论上应在另一条链上得到相同的状态转换结果。
这个标准决定了迁移成本的下限。字节码级兼容意味着:已审计合约不必为「换链」而整份重审语法层差异;依赖 CREATE2 地址推导、或依赖特定操作码 Gas 成本做防重入设计的合约,不会因为底层计价悄悄变化而行为跑偏。反过来,若一条链只是「支持用 Solidity 写」,开发者拿到的只是语法熟悉感——一旦触及冷门操作码或 Gas 假设,行为随时可能漂移。
还需要核对硬分叉对齐范围:同一份字节码在 Shanghai、Cancun 或更新分叉下的合法操作码集合并不完全相同。项目若声称兼容,应写明对齐到哪一次以太坊升级,而不是用一个模糊的「EVM」概括所有历史状态。迁移团队可以用主网已部署合约的 runtime 字节码做对照部署,检查代码哈希与关键调用路径的返回值。
并行执行会额外加压这一层。乐观并行可能先并发预执行再验证回滚,但对合约作者而言,最终提交的状态转换仍必须等价于某个确定的串行次序(通常是区块内交易顺序)。若并行调度改变了可见的最终状态,那就不叫兼容,叫换了一台虚拟机。关于正确性边界与串行化等价,可参见 《乐观并发控制(OCC)入门》。把并行执行放回更宽的扩容坐标系,也可对照 《区块链扩容地图》 与 《EVM 单线程瓶颈》:兼容性要守住的,是合约作者依赖的确定性语义,而不是单线程解释器本身。
预编译层:同一地址,不同实现就是不兼容
以太坊把一部分昂贵运算做成预编译合约,固定在 0x01–0x0a 等地址上,例如椭圆曲线配对、模幂、哈希。应用与库大量硬编码这些地址与 Gas 成本。一条链若改了预编译集合、地址映射或返回值语义,表面上还能跑 Solidity,实际上已经踩穿了兼容边界——许多审计过的库会在链上直接失败或算出错误结果。
「假兼容」常见形态包括:只实现常用预编译、擅自加自定义预编译却复用以太坊保留地址、或改 Gas 却不改文档。对迁移团队来说,正确的验收方式不是看白皮书写没写「EVM 兼容」,而是对照目标硬分叉的预编译表做差分测试:同一输入、同一调用数据,主网与目标链的返回值与 Gas 消耗是否一致。配对与模幂这类路径尤其值得覆盖,因为它们既贵又常被桥、证明验证与密码学库依赖。
并行 EVM 本身不要求改预编译语义;真正的风险来自「为了加速而特化」的诱惑。任何为吞吐优化的预编译改动,都应视为显式的协议差异,而不是悄悄塞进「兼容」叙事。团队若引入自定义预编译加速特定计算,也应使用明确未占用的地址空间,并在差异文档里写清,避免与以太坊保留地址冲突。
JSON-RPC 层:工具链活不活,取决于接口而不是口号
合约能部署,不等于生态能接入。钱包、区块浏览器、索引器、监控系统依赖的是 JSON-RPC:eth_call、eth_getLogs、eth_estimateGas、eth_getTransactionReceipt 等。字段含义、错误码、日志索引规则、pending 与 latest 的语义若与以太坊客户端习惯不一致,前端与基础设施就要写适配层——迁移成本会从「换 RPC」膨胀成「重做一整条运维链路」。
值得单独强调的是 eth_estimateGas 与 trace 类接口。并行执行下,预估 Gas 若没有按最终串行语义模拟,可能系统性偏低或偏高;调试工具若拿不到与主网同构的 trace,Foundry 的 fork 测试与事故复盘都会变困难。因此,JSON-RPC 兼容不是「能连上 MetaMask」这么低的门槛,而是开发与运维日常路径是否可无感复用。过滤器订阅、eth_feeHistory、EIP-1559 相关字段是否齐全,也往往决定现成 SDK 能否少改配置直接上线。
索引与浏览器还依赖稳定的日志顺序与收据字段。若并行执行改变了「何时可见」的中间状态,但最终收据与主网同构,应用层通常仍可接受;若最终收据字段缺失或错误码体系私有化,生态工具就要分叉维护。兼容性验收应把「只读调用 + 发交易 + 拉日志 + 估 Gas」当成一条闭环,而不是只测部署成功。
工具链层:Foundry 与 Hardhat 是兼容性的终审法庭
对多数团队而言,兼容性的终审不在白皮书,而在本地:forge test、Hardhat 脚本、OpenZeppelin 合约、常用验证插件能否对着目标链 RPC 原样跑通。Foundry(Forge / Cast / Anvil)已成为许多新项目的默认测试框架;Hardhat 仍覆盖大量存量仓库。两者都假设:编译器输出的字节码、链上预编译、以及 RPC 行为与以太坊足够接近。
实用验收清单可以很短,但必须可重复:
- 用同一 Solidity 版本与优化器设置编译,部署字节码哈希是否与主网部署一致(或差异是否仅来自可解释的不可变参数)。
- 对关键预编译做差分测试。
- 用 Anvil / Hardhat Network fork 目标链状态,跑现有集成测试。
- 用钱包只改 chainId 与 RPC,完成签名、发送、收据确认全流程。
- 对项目核心合约跑一次不变式测试:余额守恒、权限检查、重入防护在目标链上是否仍成立。
任一步失败,都说明「兼容」仍有缺口。Bitroot 选择把复杂度放在运行时并行调度,而不是要求开发者改写 Solidity 或换工具链,正是为了让上述路径尽量保持成立;相关工程叙述亦可对照 《Bitroot 并行化 EVM 技术解析》 与 《多引擎并行执行设计》。与 《并行执行的三条路线》 对照可以看到:坚持字节码兼容,意味着放弃一部分可通过新账户模型换取的并行度上限,换取的是开发者生态的迁移半径。
如何读「EVM 兼容」声明
把营销句拆成四个是非题:字节码语义是否对齐目标硬分叉?预编译表是否一致?JSON-RPC 是否覆盖主网常用方法且语义同构?Foundry / Hardhat 现有项目能否少改或不改地跑通?四个「是」才接近严格兼容;任一「否」都应被写成明确的差异文档,而不是藏在兼容口号后面。
并行执行可以改变吞吐与冲突下的延迟分布,但不应该改写合约作者依赖的确定性语义。热点负载下用户感知到的重试与延迟变化,属于性能与工作负载问题,应与「语义是否兼容」分开讨论——前者见后续 《冲突热点与工作负载》 与 《性能指标词典》。若一条链用并行换来了更快的基准数字,却让工具链与字节码行为变得不可预测,那是用短期性能海报透支长期生态——而生态网络效应,才是 EVM 路线真正想买到的东西。
也有必要分清「协议兼容」与「运营兼容」。前者关心字节码与预编译;后者关心公共 RPC 稳定性、浏览器支持、验证插件与多签钱包适配。很多团队在测试网证明了协议兼容,却在主网启动周被运营兼容拖住。把四层验收写成检查表并指定负责人,比在群里重复「我们兼容 EVM」更接近可交付的工程状态。
下一篇将按读者角色给出只指向已发布文章的阅读路径,避免把不同背景的问题混成一篇谁也读不透的长文。
把检查表放进 CI 或发布清单,比把「EVM 兼容」写进口号更能防止回归:每次客户端升级后重跑预编译差分与关键 forge 测试,兼容性才会从声明变成可维护的属性。
