共识不等执行:Monad 如何把状态根推迟三个块 图 1
共识不等执行:Monad 如何把状态根推迟三个块 · 图 1

多数 EVM 链的共识在投票之前先要知道执行结果:提议者必须先把块里的交易全部执行完、算出状态根,才能把区块发出去;验证者也要先重跑一遍交易,才决定投不投票。Monad 把这条链条拆开了。它的架构文档把这叫异步执行(Asynchronous Execution):共识只对交易顺序表态,执行挪到一条稍落后的泳道里,用满整段出块时间。本文按官方文档拆这套机制,以及它用什么办法保证迟一拍不会变成错拍。

交错执行为什么吃亏

文档先把旧模式的问题摆出来。在以太坊等链上,节点达成共识时同时同意两件事:块里的交易列表,和执行完这些交易后的状态默克尔根。于是执行被塞进共识的关键路径里:出块者要在提案前执行一遍,验证者要在投票前再执行一遍,留给执行的时间既要覆盖两遍执行、还要给跨洲网络通信留出余量。文档给了一个量化参照(属于文档自述的口径):以太坊每块 3000 万 gas 的最坏情形大约对应 100 毫秒的执行预算,而区块时间是 12 秒——执行只占出块时间约百分之一。这迫使每块 gas 上限必须按最坏情形保守设定。

顺序定了,状态就已经定了

示意:202609023100 机制示意 异步执行的关键推理是:执行是确定性的,所以顺序一旦定下来,状态其实已经被唯一确定,执行只是把这个真相展开。文档强调,即便块里有交易「失败」——比如余额不够的转账——它依然有效力且结果确定,失败也是共识的一部分。于是 Monad 的共识节点对块的排序投票,而不去等状态根出来。投票不依赖执行结果,执行预算就从共识缝隙里被解放出来,两条泳道并行占满同一段出块时间。

延迟默克尔根:往回看三个块

不等当前状态根不等于放弃校验。文档给出的保险是给每个块带一个 D 个块之前的默克尔根:提案里包含的是往回第 D 个块执行完时的状态根,D 是系统级参数,文档记录测试网与主网当前取 3。这个延迟根的校验属于区块有效性的一部分——出块者带了个错的延迟根,区块会被直接拒。由此得到两条推论:其一,网络对块 N 达成共识(文档说明通常在收到含块 N 的 QC-on-QC 的块 N+2 时完成)意味着大家确认了块 N-D 的官方后果,轻客户端可以查询到块 N-D 时刻的状态证明;其二,任何在块 N-D 上执行出错的节点,会从块 N 开始掉出共识,回滚到块 N-D-1 的结束状态,再顺次重放后续区块。

投机执行与预留余额

在提案还没最终确定(要到之后两个槽位才定)的窗口里,节点可以先在本地把块 N 投机执行掉:若它如预期被最终确定,直接把默克尔根指针指过去即可;eth_call 与 eth_estimateGas 也可以打在更接近最新状态的投机状态上。另一处配套是预留余额(Reserve Balance):共识只能看到往回 k 块(文档在同一参数上另一页写作 k=3)的延迟状态视图,为确保排进块的交易确实付得起 gas,协议对共识时刻哪些交易可被排入、执行时刻哪些交易不回退设了轻约束。文档的说法是多数用户和开发者不必关心它,遇到边界情形再以那页文档核对。

该注意什么

这套设计的代价被明说了:状态可见性比顺序落后 D 个块,轻客户端拿状态证明要多等一拍以上;共识层的 gas 校验依赖延迟视图,规则比即时视图复杂;节点执行出错时的恢复方式是回滚重放而非即时隔离。参数取值(如 D)会随版本调整,应以当时的架构文档为准。本文只按官方文档描述机制,不构成对任何公链性能或安全性的判断,也不构成投资建议。