全网重放的机器

以太坊执行层的量子升级(5/5)

  1. 以太坊账户抽象的安全骨架
  2. 以太坊原生账户抽象的交易解剖
  3. 一笔后量子交易的一生
  4. 一笔交易身边的世界
  5. 全网重放的机器
目录

上一篇讲完了一笔交易身边的世界:谁替无码者作答,谁买单,交易怎么旅行,块怎么出,风险搬去了哪。这一篇往下潜一层,进到机器内部,在三个尺度上回答同一个问题:你提交一笔交易之后,谁在哪里为它烧 CPU,按什么价? 宏观尺度是旅程与重放,微观尺度是那台烧 CPU 的虚构机器,中观尺度是合约实践留下的活证词。

动笔之前先立三根钉子,各拔掉一个常见的错觉。第一,世界上不存在「提交区块」这回事,所有提交路径运送的都是交易。第二,proposer 不验证别人的块,它是全网唯一的造块者,验证是其余所有人的工作。第三,「上链」不是任何人执行的动作,而是一个逐渐硬化的过程。

提交的真相:三条路运送的都是交易

把提交路径重新画准。路径一,经节点 RPC:eth_sendRawTransaction 打到自己的节点或公共 RPC,运送的是签名交易的完整字节(type ‖ payload),进那个节点的候车室,再经 gossip 泛洪进公共池。路径二,私有通道:同样的 RPC 调用,打到 Flashbots Protect 或 builder 的私有端点,同样的字节,不 gossip,直达 builder 的私有订单流。至于常被并列的第三条「提交进公共池」,它不是提交方式,是路径一的第二跳。所以准确说法是两条半;「自己的节点」与「别人的 RPC」的区别是信任,自己的节点不审查你、不偷看你的交易牟利,这是运营问题,不是协议问题。

交易在这些路径上旅行、汇集,最终躺进某个 builder 或节点的候车室。到此为止,还没有任何「区块」存在。全网每 12 秒只诞生一个区块,由唯一被抽中的 proposer 造出;区块是下一节那个人的作品,不是任何用户或普通节点的提交物。

proposer 的十二秒:全网唯一的造块者

先把角色扶正:proposer 不验证任何人提交的块,它造块;验证是其余所有人的工作,后面两节再讲。它的十二秒逐段拆开。

槽位开始之前,RANDAO 的抽签结果由信标链状态决定性导出,proposer 提前约一个 epoch 就知道自己被抽中;若用 MEV-Boost,验证者早已向 relay 注册,登记收款地址与偏好。槽位开始,取货,两条路二选一:

本地路(自建区块,走 Engine API,共识客户端 CL 与执行客户端 EL 的本机接口):
① CL → EL:engine_forkchoiceUpdated(head, safe, finalized, payloadAttributes)
属性一到,EL 就用候车室里的交易持续构建候选块
② CL → EL:engine_getPayload(payloadId) → 拿回 ExecutionPayload(完整交易列表)
外包路(MEV-Boost):
① 向各 relay:getHeader(slot, parentHash, pubkey) → 收回若干 (区块头, 出价)
② 取最高价,把区块头装进"盲信标块",用验证者 BLS 私钥签名
③ submitBlindedBlock → relay 验签后释出完整区块体,并代为广播

本地路的组装还有后半段:CL 把 ExecutionPayload 装进一个更大的容器,信标块,填上 slot、proposer 编号、父块根,塞进本槽位收集的 attestations、给全网随机数熵池的 randao_reveal、存取款和同步委员会聚合等共识层负载,算好共识状态根,整体做 BLS 签名。然后,大约在槽位的第 0 到 1 秒之间,把 SignedBeaconBlock 丢上共识层 gossip。到此,proposer 的全部权力行使完毕。它的「上链动作」就是这一次广播,再无其他。 它不能宣布这个块有效,不能命令任何人接受;接下来它和所有人一样,只能看网络的脸色。剧本的后半场属于别人:第 4 秒是本槽位见证委员会的投票截止线,第 8 秒聚合者打包投票,供下一个 proposer 装进下一个块。

区块解剖:两层容器的套娃

合并之后的区块,是共识层容器套着执行层负载:

SignedBeaconBlock ← 共识层 gossip 上的对象,SSZ 序列化
├─ signature: proposer 的 BLS 签名
└─ message: BeaconBlock
├─ slot / proposer_index / parent_root
├─ state_root ← 共识状态根(验证者表、余额、检查点)
└─ body
├─ randao_reveal / attestations[] / slashings[] / deposits[] …
├─ blob_kzg_commitments[] ← blob 只留承诺,本体走 sidecar 旁路
└─ execution_payload ← 你熟悉的"以太坊区块"活在这一层
├─ parent_hash / fee_recipient / block_number / timestamp
├─ prev_randao / gas_limit / gas_used / base_fee_per_gas
├─ state_root ← 执行状态根(账户世界的 MPT 根)
├─ receipts_root / logs_bloom / block_hash / withdrawals[]
└─ transactions[] ← 一列不透明字节串,每项 = type ‖ payload

三个细节必须点破。其一,两个 state_root:信标块层的是共识状态(验证者集合、余额、最终性检查点)的根,ExecutionPayload 层的才是执行状态(账户、存储)的根;两本账,两棵树,两个根,别混。其二,transactions 就是字节串列表,每笔交易以规范完整形态原样躺在里面,区块不理解交易,只运载它们。其三,序列化是双轨制,接回第二篇的护照:共识层用 SSZ,自带长度框架,不透明字节串直接作元素,不需要任何壳;执行层内部(历史同步、RLP 区块体、交易 trie)仍是 RLP 的世界,类型化交易在那里要办 b8/b9 护照。同一条「不透明原子项进容器」的原则,两种框架下的两种实现。

验证 = 重放 + 对承诺:gas 的终极含义

谁在验?每一个收到这个块的全节点,不管有没有质押;质押者只是额外多做一件事,投票,下一节讲。验什么?两层清单。

共识层清单由 CL 执行:BLS 签名;proposer 的身份,这条可验证,因为 RANDAO 抽签是信标状态的确定性函数,任何节点都能重算「这个 slot 该谁出块」,冒名一眼识破;slot 与父块的关系;randao_reveal 的有效性;块内 attestations 的合法性;然后执行共识层自己的状态转换,比对共识状态根。

执行层清单是重头戏,CL 经 engine_newPayload 把负载递给 EL,本质一句话:把 builder 干过的活从头再干一遍,然后对答案。

① 一致性:用 payload 字段重建执行层区块头(transactionsRoot 由交易列表
现场重算),keccak 之,必须等于声称的 block_hash
② 逐笔重放:按序对每笔交易完整执行状态转换。解码分流、静态检查、验签
(那笔 Falcon 帧交易的 2.1M gas VERIFY 帧,在这里被每个节点重新跑一遍:
SIGPARAM 拷贝、NTT、APPROVE)、帧循环、扣费、生成回执
③ 对答案(承诺比对):
state_root 重放完的账户世界的根 == 声称值?一比特错,全块无效
receipts_root 全部回执(8141 的逐帧 status 与 payer 就住在这里)对得上?
gas_used 逐笔累加 == 声称值?
base_fee 按 1559 公式从父块推出的值 == 声称值?(费率不是想填就填)
blob 记账 用量与 KZG 承诺逐一匹配?

看清设计的重心:验证的主体不是「检查一堆签名」,而是重放全部执行,加比对密码学承诺;签名检查只是重放的一个子步骤。承诺,那一排 root,是让作弊必被抓的机关:builder 想少执行一笔、多印一分钱、改一个存储槽,state_root 就对不上,块作废,竞价白付。

由此得到 gas 的终极定义:你付的钱,买的是全网几千个节点各自把你的交易重演一遍的 CPU 时间。 那笔 Falcon 交易贵,是因为它让每个节点都陪跑两百万 gas 的格运算。两个推论顺手记下:8141 的逐帧回执经 receipts_root 被承诺进区块头,所以每一帧的 status 和 gas 用量都是共识级数据,不是某个节点的日志;而一个预编译如果定价错了,错的不是一次计算,是全网每个节点在每次重放中的那一次计算,后文「最坏 CPU 覆盖」的定价原则和 DoS 武器化的担忧,全部源自「验证等于全网重放」这一个事实。

验证 = 重放 + 对承诺:builder 造一次,全网各自重演一遍

「上链」是硬化过程,不是动作

proposer 广播之后发生的,是一个三阶段的过程。阶段一,投票,秒级:本槽位的见证委员会(全体验证者按 epoch 均分到 32 个槽位,每人每 epoch 投一次)各自完成上一节的全套验证,认可就在第 4 秒前广播 attestation,「我看到的链头是它」。阶段二,分叉选择,持续进行:每个节点独立运行 LMD-GHOST,数累积的投票权重,在可能并存的分支里选出当前链头;一个块「在链上」的日常含义只是「分叉选择目前把它算在获胜分支里」,这是可撤销的,投票风向、网络分区、更晚出现的竞争块都可能触发重组。阶段三,最终化,约 12.8 分钟:Casper FFG 在 epoch 粒度运行,检查点获得三分之二总质押的投票即为 justified,连续的 justified 使前一个变为 finalized;finalized 之后要撤销这个块,意味着至少三分之一的质押被罚没。「上链」到这里,才从「网络当前的多数意见」硬化为「经济上不可逆的历史」。大额交易等确认,等的就是这道线。

全流程的动词各归其主:提交的是用户,对象永远是交易;构造的是 builder 或 proposer 本地的执行客户端,对象是 ExecutionPayload;发布的是本槽位唯一的 proposer,一次 gossip 广播;验证的是其余所有全节点;投票的是本槽位的见证委员会;选链是每个节点各自跑分叉选择;最终化是三分之二质押的集体行为。

到这里,宏观的旅程讲完了。第四节说每个节点都要重放你那两百万 gas,现在下潜到重放发生的机器内部,回答一个更根本的问题:预编译和字节码都烧节点的 CPU,为什么同一道数学题,两条路的价差能到百倍?答案没有魔法:因为两条路烧掉的真实 CPU 时间,就差两到三个数量级,而 gas 是对真实 CPU 时间的诚实计价。要看清这一点,得先把机器拆开。

EVM:一台虚构的 CPU,以及缠在每条指令上的解释税

EVM 不是你电脑里那颗 CPU。它是一份纸面规格书,定义了一台不存在的计算机:256 位字长,栈式架构,约 150 个操作码;每个客户端用软件把这台假机器模拟出来,geth 用 Go 写了一个模拟器,reth 用 Rust 写了一个。合约字节码是喂给假机器的程序。费这么大劲虚构一台机器,有四个不可妥协的理由。确定性:共识要求几千个异构节点对同一笔交易算出逐比特相同的结果,真实硬件做不到(浮点舍入、未定义行为、指令集差异),纸面机器可以,每条指令的语义被规范钉死到比特级。沙箱:合约是陌生人的任意代码,假机器是一间没有门窗的屋子,字节码只摸得到栈、内存、存储这几样规范给的家具,物理上够不着宿主的文件系统与网络。可计量、可中断:gas 要求每一步可计价,燃料耗尽要能在精确的那一步停下,解释器天然做得到,原生代码无法在第 3891 条机器指令处精确掐断。代码即数据:字节码要作为状态上链、被哈希进 codeHash,必须是平台无关的紧凑表示。四条买到了安全与共识,代价是:模拟是昂贵的。

昂贵到什么程度,看解释器的主循环。每个客户端的 EVM 核心都同构于这段伪代码:

while True:
op = code[pc] # ① 取指:数组读一个字节
info = JUMP_TABLE[op] # ② 译码:查表取处理函数、标价、栈需求
if stack.depth < info.min_stack: # ③ 栈安全检查(每步)
异常停机
if gas_left < info.price: # ④ gas 记账(每步!)
out_of_gas
gas_left -= info.price
info.execute(stack, mem, ...) # ⑤ 真正干活
pc += 1 # ⑥ 步进

数一数一条标价 3 gas 的 ADD 让真实 CPU 干了多少事:一次取指,一次查表加函数调用,两次比较一次减法,然后是「正事」,而正事本身也注了水,EVM 的字长是 256 位而真实 CPU 只有 64 位寄存器,两个操作数是内存里的 4 乘 64 位大数结构,所谓一次加法等于从栈(它是堆上的数据结构,不是寄存器)弹出两个大数、做四次带进位的 64 位加法、再压回去,最后自增步进。合计:一条 3 gas 的 ADD,背后是几十到上百条真实机器指令;原生代码做一次 64 位加法,是一条指令,约 0.3 纳秒。 这就是解释税:取指、译码、检查、记账、字宽模拟、栈搬运,六项开销缠在每一条操作码上,只有第⑤步的核心一下是「你想要的计算」。税率约两个数量级。

密码学还要在税上加税。现代密码学库的武器是手写汇编的域运算内核、Montgomery 乘法的寄存器级调度、一条指令并行算四到八路的向量指令、预计算表;这些在 EVM 字节码里根本无法表达,EVM 没有寄存器概念,没有 SIMD,没有你能控制的乘法器,你只有一条 MULMOD(8 gas),解释器内部是一个软件大数例程,百纳秒量级。Falcon 再挨一刀:参考实现依赖浮点 FFT,而 EVM 没有浮点,链上实现被迫整数化改写。拿同一次 secp256k1 验签对账,数学核心约几千次有限域乘法:字节码路,每次域乘是一条 MULMOD 加前后伺候的一串栈操作,滚起来再加上模逆、哈希、解压缩,数十万 gas 起步,朴素实现过百万;预编译路,libsecp256k1 级的汇编实现,真实耗时数十微秒,标价 3000 gas。

同一次加法的两张账单:解释器的六道工序与原生的一条指令

gas 是诚实的计量器

先做汇率对账。gas 定价的锚是「最慢合规客户端的真实耗时」,历史校准下来,主网的换算率约在每 gas 几十纳秒的量级。拿这把尺量两边:预编译路,3000 gas 乘几十纳秒,约几十上百微秒,正是原生实现的真实耗时,账是平的;字节码路,几十万 gas 乘几十纳秒,几十毫秒,也不是宰你,解释器真的要烧这么多 CPU 才能把同一道题在假机器里演完。两条路用同一个汇率;价差如实映照 CPU 时间差。gas 是诚实的计量器,预编译赢在真实成本,不是赢在收费标准。

但「诚实计量」只是谜底的一半。另一半是计价单位的跃迁:协议对字节码只能按操作码逐条计价,它看不见你的意图,你在跑 Falcon 还是在算斐波那契,协议眼里只有指令流,于是只能给每条指令一个按最坏情况定的保守单价,总价逐条累加,解释税因此被逐条固化进价格。预编译干的事,本质是向协议注册一个语义单位:「地址 0x01 等于验一次 secp256k1 签名」。协议从此知道你在干什么,计价单位从「解释一小步」跃迁为「原生完成一整件事」,一口价,按实测的原生最坏耗时定。减 gas 等于真实减负加计价单位升维,缺一不可。

客户端各显神通,为什么不撕裂共识?因为共识对预编译只要求两样东西逐比特一致:输入到输出的映射,和扣掉的 gas 数。geth 花 40 微秒、reth 花 25 微秒毫无关系,耗时不是共识对象,结果与价格才是;这个约定成立,是因为预编译全是无状态纯函数,共识只看黑箱两端。对照之下,字节码那一侧苛刻得多:解释器必须每一步一致,包括燃料在哪条指令上耗尽,out-of-gas 的精确位置决定回滚点,是共识的一部分。由此解释两件事:预编译可以放心往死里优化,黑箱内自由;而价目表按最慢的主流合规实现定,某家客户端把解释器提速十倍,不会也不能让 MULMOD 降价,重定价永远是治理事件。

把机制装进前几篇的成本模型,三条车道排齐:字节码车道,在假机器里逐条解释,β 被解释税放大两三个数量级;新操作码车道(EVMMAX,EIP-6690,停滞中),给假机器添置专用硬件,模乘指令加预置的 Montgomery 上下文,不离开虚拟机压低 β,是中间车道;预编译车道,把整个函数搬出假机器,语义注册加原生执行加一口价,β 直接换成原生系数。这张表还藏着一个精细的角落:KECCAK256 本身就是一条被补贴的操作码,30 gas 起步,协议早已内置原生实现;但它暴露的只是固定 256 位输出的哈希接口,不是 SHAKE 那种海绵挤出式的可变长输出。按标准原文实现 SHAKE,等于在字节码里手搓 keccak 置换,解释税全额照付;于是出现了一个耐人寻味的现象,链上的 Falcon 实现把 SHAKE 换成 keccak256 链式伪随机数生成器,本质是弃标准原文、骑上补贴操作码。成本模型里的 α 项,在 EVM 里只有背离标准才便宜,机制根源就在这里。这也是第三篇说「编码与实现的规范性是每代方案都要重付一次的税」在哈希侧的镜像。

补贴名录:预编译

前面几节把「贵」讲透了,现在讲「便宜」从哪来。预编译合约的意思是:这个函数不走 EVM 解释器,它被预先编译进了客户端二进制本身。链上没有它的字节码,它的代码活在每个节点的可执行文件里,名字描述的是执行位置:不在虚拟机里,在虚拟机的宿主里。

它的出身值得讲,因为历史正在重演。创世时期,合约需要重型密码学,典型场景就是合约内验 ECDSA,当时有三个选项。用 EVM 字节码实现:椭圆曲线运算在 256 位解释器里跑,百万 gas,经济死刑;注意既视感,这正是今天链上 Falcon、链上 ML-DSA 实现的处境,ARBITRARY 车道的全部痛苦一句话就是「活在没有自己预编译的世界里」。加新操作码:指令集是通用原语的地盘,256 个槽位经不起领域函数的污染。最后胜出的是杂交物:对调用者呈现为合约,普通 CALL 调它,参数摆内存,返回值读内存;对执行者呈现为原生函数,EVM 发现目标地址在预编译表里,跳过字节码加载,直接调客户端内置实现,按规范写死的价格收 gas。合约的外衣,原生代码的内脏。精确定义:预编译等于协议保留的低位地址,加一份规范(输入格式、输出、gas 公式),加每个客户端各自的原生实现;无字节码(对 0x01 做 EXTCODESIZE 返回 0,天真的「是不是合约」检查在这里翻车),无私钥,无部署,分叉激活即存在。到这里,本系列见过的三类协议钦定地址可以合成一句:0xaa 是身份常数,无码无行为,只出现在 caller 位;0x8141 是预部署,有真代码,可被调用;预编译无字节码,行为在客户端原生代码里,走 CALL 接口。

看一眼这张名录,它是一部「以太坊决定补贴哪些密码学」的沉积岩。0x01 是 ecrecover,3000 gas,创世就在,解锁了合约内验签,多签钱包、元交易、整个链下授权范式都长在它上面。0x05 是大数模幂,Byzantium 加入,经历过两次重定价。0x06 到 0x08 是 BN254 曲线的加、乘、配对,同样是 Byzantium,zkSNARK 的链上验证全靠它们,没有这三个地址就没有 zk-rollup 产业,而且最初定价太高,EIP-1108 砍价之后 ZK 应用潮才真正点燃。0x0a 是 KZG 点评估,伴随 blob 交易而来。0x0b 到 0x11 是 BLS12-381 全套,Pectra 加入。0x100 是 P256VERIFY,先在 L2 试水(RIP-7212),再进 L1(EIP-7951),把 passkey 和手机安全芯片接上了链。读出三层意思:每个条目都曾是「EVM 里贵到没法用」的东西;每次收编都解锁一个应用类,配对预编译造出 ZK 产业是「一个预编译改变生态」的铁证;这张表是治理的沉积岩,哪种密码学被协议补贴、何时、什么价,全是硬分叉级的决策。后量子验证预编译的草案家族(EIP-8051、8052 一系),申请的就是这张表的下一行:请协议为格密码,做一次 2015 年为 secp256k1、2017 年为配对曲线、2025 年为 P256 做过的事。

机械细节一并给全。假设 ML-DSA 预编译已存在于某个地址,VERIFY 帧里调用它长这样:

① SIGPARAM copy:把 2420 字节签名从 ARBITRARY 条目拷进内存
② 摆盘:按预编译规范拼输入 [公钥(SLOAD) ‖ 消息哈希(TXPARAM 0x08) ‖ 签名]
③ STATICCALL(gas, 预编译地址, in, inSize, out, 32)
④ EVM 查预编译表:命中 → 不加载任何字节码,调客户端原生函数
⑤ 原生验证跑完(向量指令,微秒级),按规范标价扣 gas,是标价,不是逐条计量
⑥ 返回 1 → require → APPROVE(0x3)

三个要点。调用用的是普通 STATICCALL,没有任何专用指令,这就是「合约外衣」的含义。预编译全是无状态纯函数,STATICCALL 合法,VERIFY 帧里畅行,8141 的前缀 trace 规则明文放行,于是「验证太贵」这个问题从政治问题(要不要为它分叉)变成了定价问题(标价多少)。它是共识关键代码,五六个客户端各以不同语言实现同一份规范,任何一家差一个比特,链就在那笔交易上分叉,这就是「加一个预编译等于硬分叉加高强度审查」的原因,治理成本的来源。

最后是定价。原则一条:gas 必须覆盖最坏情况下、所有客户端里最慢实现的 CPU 时间;定低了,攻击者用它塞满区块拖垮验证节点,预编译自己就成了 DoS 武器,而上一节说过,定价错误会被「每个节点乘每次重放」放大。历史反复证明会定错:modexp 两次重定价,BN254 配对定高了砍价后才有 ZK 潮,定价错误真实、昂贵,而且只能靠更好的测量数据修复。于是那条不等式的每一项都落了地:预编译标价 X 是治理待定的未知数;它的下限是「覆盖最坏 CPU」的成本地板,原生 ML-DSA 验签在现代 CPU 上是百微秒以内的活,按几十纳秒每 gas 的锚定汇率折算,地板可能低至数千 gas 的量级;它的上限是公共池预算的余量,十万减去签名结构费和前缀其他帧的份额,约九万。夹缝比直觉宽得多,但地板的精确位置,最坏情况、无向量指令的回退实现、输入解析开销、DoS 安全边际,不是拍脑袋的事,它需要一份能把「这个预编译该标多少钱」从直觉变成可审计算式的测量报告。 这也回答了第三篇留下的问题:从 ARBITRARY 车道到方案表车道之间,还有一条中间车道,预编译车道,两者的密码学都跑原生速度,差别在调用位置与治理深度。预编译由你的 EVM 代码发起调用,签名仍是 ARBITRARY 条目,可自省,所以永无聚合,成本计入 VERIFY 帧、受十万预算约束,治理上只需要一个预编译 EIP,最小的解耦件;方案表由协议在任何帧之前发起,字节不透明,持聚合门票,治理上要动方案表、定义签名者解析规则,深整合。一个旁证照亮价差:P256 在 8141 方案表里标 6700,略低于它的预编译价,表内验证连那次 CALL 的开销都省了。

实践即证词:ecrecover、permit 与 P256 三部曲

理论讲完,用链上最普及的实践作证。「合约里验 ECDSA 签名用得挺广泛」这个观察背后没有玄机,或者说,玄机就是前两节。

Solidity 里的 ecrecover(hash, v, r, s) 看着像内置函数,编译器实际生成的是:把 128 字节输入摆进内存,STATICCALL 打给预编译 0x01,从输出读 32 字节,左补零的 20 字节地址。椭圆曲线那几千次域运算,一次都没进解释器,全部发生在客户端内置的原生代码里,固定 3000 gas 加百来 gas 的调用开销。合约做编排,预编译做数学。顺手解释一个反直觉现象:链上验签生态几乎全长在 secp256k1 上,不是这条曲线多优越,是只有它在创世名录上。

那字节码部分在干什么?卫生,不是数学。库函数(OpenZeppelin 的 ECDSA 一类)包的是:签名长度检查;强制 low-s,第二篇讲过的 EIP-2 延展性历史,库层再兜一遍底;恢复位归一;零地址防呆,ecrecover 失败返回的是零地址而不是 revert,如果拿返回值去比对一个可能未初始化的存储槽(默认值恰好是零),伪造的签名就能「通过」,库替你挡掉这一枪。合计几百 gas,卫生费,不是数学费。

把一次 permit(EIP-2612)的账单摊开,更有意思。owner 链下签一份结构化授权,任何人把 permit(...) 连签名提交上链,合约验签后直接写 allowance,owner 全程零 gas,这正是上一篇讲的中继者时代那条血脉活到今天的主流后裔。账单里:calldata 一两千 gas;EIP-712 摘要构造几百,用的又是被补贴的 KECCAK256;卫生检查几百;ecrecover 约 3100;然后 nonce 自增一次存储写约五千,allowance 写入一次冷存储写两万多。看清重心:整笔五到八万 gas 里,验签只占三千出头,大头是两次存储写。「合约内验签很贵」的直觉,在有预编译的世界里是错的:贵的是状态,不是被补贴的密码学。 反事实推一步:若 0x01 不存在,那几千次域乘全额落进解释器,单次验签数十万到上百万 gas,permit、元交易、治理签名、会话密钥,整个「签名即授权」的合约生态根本不会诞生。

反事实其实无需想象,链上刚刚演完一遍。passkey 用的 P-256 曲线不在创世名录上,0x01 只认 secp256k1。于是想接 passkey 的人,真的用纯 Solidity 和 Yul 写了 P-256 验签(FCL、Daimo 验签器一系),优化到极限也要二三十万 gas 的量级,这就是「无预编译税」的实价。然后 RIP-7212 在 L2 先行,标价 3450;EIP-7951 把它收进 L1,约 6900。同一次验证,价格塌缩两个数量级,passkey 钱包的应用潮随之而来。P256 的三部曲,字节码硬扛、L2 试水、L1 收编,就是后量子预编译剧本的上一次完整放映;今天链上 Falcon 的百万 gas 和当年 Daimo 验签器的三十万 gas,是同一部电影里不同曲线的同一幕。

P256 三部曲:字节码硬扛、L2 试水、L1 收编,下一场的主角是格密码

还有一层更深的差异藏在 0x01 的名字里。它叫 ec「recover」,做的不是验证,是公钥恢复:输入摘要和签名,吐出签名者,依赖的是 ECDSA 特有的曲线结构。这个性质被低估的后果是:以太坊账户从创世起就不需要在任何地方公布公钥,65 字节的签名隐式携带了它,地址比对即验证完成。而 ML-DSA 和 Falcon 没有恢复模式,格签名的数学不支持从签名反推公钥,验证必须显式喂入公钥。所以后量子世界里,公钥必须旅行(装进 calldata,ML-DSA-44 的公钥 1312 字节)或者定居(住进存储,或未来的公钥别名机制)。第三篇账户把 897 字节 Falcon 公钥放进存储槽,根源在这里:不只是后量子公钥大,更是后量子签名丢失了恢复性质,公钥第一次不得不现身。

最后把 permit 接回帧交易的语言,赌注随之看清。owner 那份链下签名,在 8141 术语里就是一枚典型的显式 msg 凭证,签的是另一个摘要,向链上导入「owner 授权过这份 allowance」的事实,与第二篇的会话密钥双条目同族。而一个「后量子 permit」,Falcon 签的授权凭证要在任意合约内被验证,今天等于目标合约里的全额字节码验证,经济死刑;有了后量子预编译才能活。这把预编译提案的赌注从「账户层验签」扩大到了整个「EVM 内签名即授权」的设计空间:permit、ERC-1271、治理签名、会话密钥,全押在名录的下一行上。

总结

压回一段话。一笔交易提交之后的世界是这样运转的:三条路运送的都是交易,全网每 12 秒只诞生一个区块,由 RANDAO 抽中的唯一 proposer 造出,它的全部权力就是一次广播;区块是共识层容器套执行层负载的套娃,交易以不透明字节躺在最里层;验证等于重放加对承诺,每个全节点把 builder 干过的活从头再干一遍,再比对那一排 root,于是 gas 的终极含义是全网几千个节点各自重演你交易一遍的 CPU 时间;「上链」随投票、选链、最终化逐渐硬化,finalized 之后撤销要烧掉三分之一质押。往机器里看,EVM 是一台为确定性、沙箱、可计量和代码即数据而虚构的 CPU,代价是两个数量级的解释税,密码学还要在税上加税;gas 是诚实的计量器,两条路用同一把汇率尺,预编译赢在真实成本加计价单位的跃迁,而不是收费标准。预编译名录是协议的密码学补贴史,每个条目都曾贵到没法用,每次收编都解锁一个应用类,定价靠「最坏 CPU 覆盖」原则,错了就被全网重放放大,修复靠更好的测量。实践作证:合约验签广泛,恰因字节码不在验签,卫生归解释器,数学归原生,大头归存储;P256 用三部曲替后量子把整条路预演了一遍;而失去恢复性质的后量子公钥必须旅行或定居,预编译的赌注覆盖着整个签名即授权的设计空间。系列走到这里,前面铺过的每一条线,两条车道、字节地板、聚合门票、定价论证,都汇进了同一个交汇点:下一行名录写不写、标价多少,是一道要用测量报告去回答的治理题。

这个系列还会回来:方案表与预编译的定价论证怎么打,聚合证明的后续规范长什么样,以及组合式路线(EIP-8202)会把签名敏捷性带向哪里。