上一篇把 ERC-4337 的安全骨架拆完,结论停在一句话上:bundler 是「不硬分叉就要账户抽象」的代价。协议只认 EOA 发起、ECDSA 签名、发送者自付 gas 的交易,所以需要一个翻译、垫资人兼风险承担者,把 UserOp 转写成协议认识的形状。EIP-8141,也就是原生账户抽象,选择付另一边的价:硬分叉一次,教会协议直接认识「验证逻辑写在账户代码里」的交易。中间商消失,它扛着的问题(免费空窗、模拟风险)则原班人马搬进协议。这一篇拆开 8141 的交易本体:信封怎么封,帧怎么排,两个奇怪的地址是什么,授权怎么说出口,签名哈希怎么算,内存池机器怎么把关。一个诚实的提醒:8141 目前仍是草案,下文的类型号与参数(比如 0x06、100000)都以最终进分叉的定案为准。
全文的主线还是接着第一篇走。那里的轴心是「谁付 gas,由谁保证他真的会付」;这一篇把镜头对准另一半的问题:验证这件事,由谁来问、以什么身份问、账户以什么方式答。 三代系统给出了三种答案,而 8141 的答案是:协议亲自登门。
三代「第一推动力」
第一篇讲过合约的铁律:没有私钥,永远被动,任何字节码的任何一次执行都必须被调用唤起,所有调用链上溯源头必是一笔交易。合约没有心跳,没有定时器,不被调用的代码在状态树里就是死数据。那么问题来了:账户里那段验证代码,由谁发起第一次调用?
legacy/1559: 交易本身就是调用。协议在 EVM 之外原生验完 ECDSA, 以 sender 的身份发起唯一的顶层调用。 验证环节里,账户代码零参与。
4337: 第一推动力是 bundler 的一笔普通 L1 交易,打到 EntryPoint 合约, EntryPoint(链上字节码)再 CALL 你账户的 validateUserOp。 调用你的,是链上的另一个合约。
8141: 第一推动力是客户端的帧循环,原生代码,不属于任何链上实体。 它读帧列表、构造调用上下文、把账户的字节码喂给 EVM 解释器。 调用你的,是协议本身。一句话对齐三代:验证这件事,legacy 里协议自己做、不问账户;4337 里合约转包给账户;8141 里协议亲自登门问账户。它把 legacy 的「协议亲自处理」与 4337 的「账户代码作答」焊在一起,删掉了中间那层合约转包商。
被动性不是实现细节,是安全模型的地基。VERIFY 帧(马上会讲)敢用 STATICCALL 那么严的语义,是因为答题人不需要改卷子的权力;内存池敢模拟验证代码,是因为被动纯函数的作答可以重问,同样的问题问两遍(模拟一遍、上链一遍)应该得到同样的答案。第一篇里 7562 沙箱的那些禁令,本质是把「可重问」从事实升格为纪律;8141 把同样的纪律直接写进了协议。
信封:一个字节的分流
先看最外层。EIP-2718 定义了类型化交易信封:一笔类型化交易就是 TransactionType ‖ TransactionPayload 的裸字节拼接,类型号取值 0x00 到 0x7f,payload 是不透明的字节数组,内部怎么编码由各类型自己定。主网现役的谱系:无前缀的 legacy 交易(裸 RLP 列表);0x01 是 EIP-2930 的访问列表交易;0x02 是 EIP-1559 的双费率交易,当前的主流;0x03 是 EIP-4844 的 blob 交易;0x04 是 EIP-7702,第一篇的主角之一。8141 拟用 0x06,但草案空间正在撞车:EIP-8202 认领了 0x05,EIP-8105 同时认领 0x05 和 0x06,EIP-7727 也在排队。草案层的号码不作数,谁先进分叉谁定案。
为什么读一个字节就能无歧义分流?这要下到 RLP 的地基。RLP 序列化任何数据,首字节同时编码「什么结构」和「多长」,全部规则只有五条:
0x00–0x7f 单字节自编码:值 ≤ 127 的字节,编码就是它自己0x80–0xb7 短字符串:0x80 + 长度(内容 0–55 字节)0xb8–0xbf 长字符串:0xb7 + n,n 是"长度字段本身占几个字节"(两层结构)0xc0–0xf7 短列表:0xc0 + 载荷总长(0–55 字节)0xf8–0xff 长列表:0xf7 + n,同上(两层结构)legacy 交易的定义是 rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s]),顶层是列表,首字节只能落在 0xc0 以上;而光 r 和 s 就占 64 字节,载荷永远塞不进短列表的 55 字节,所以裸的 legacy 交易总是以 0xf8 或 0xf9 开头(300 字节的载荷写成 f9 01 2c:首字节说「后面 2 个字节是长度字段」,长度字段里装着 0x012c = 300)。EIP-2718 把类型号取在 0x00 到 0x7f,与列表前缀区完全不相交,于是读一个字节即可分流,零解析成本;0x80 到 0xbf 一段被刻意留白,当缓冲和余量。
这里藏着一个容易混淆的层次,值得专门钉死:RLP 是一门语法,交易解析是一个协议;类型字节写在协议里,不写在语法里。 RLP 解码器只认识那五条前缀规则,不认识「交易」和「类型」;2718 的分发规则活在客户端代码里。同一个字节 0x02,交易解析器读作「这是 1559 型交易」,RLP 解码器读作「这是整数 2」,含义由读它的层赋予。把完整的 02 f8 6a … 直接喂给 RLP 解码器会报错:它读完 0x02 就认为一个项结束了,尾部剩下一大串字节(geth 会给 trailing bytes 一类的错误)。type ‖ payload 整体不是合法 RLP,2718 明文把它定义为不透明字节串。
那这坨「非 RLP 之物」怎么装进区块体?区块体的交易列表是一个真正的 RLP 列表,列表的载荷是各元素编码首尾相接,解码器靠每个元素自定界逐个落脚。把 type ‖ payload 裸拼进去是灾难:0x02 被撕出来解析成整数 2,剩下的字节被静默错解成一笔字段数不对的无类型交易,比崩溃更危险。解决办法是给它办一本护照:用长字符串前缀把整笔交易包成一个原子项。b9 01 2c 对列表解码器说的是「接下来 300 字节是一个整体,别解释内容,跳过去」。两种交易并排躺在区块体里的真实形状:
f9 xx xx ← 交易列表:长列表前缀 b9 01 2c ← 元素 1 的壳:300 字节的原子字符串 02 f9 01 28 … ← 类型化交易原封不动(1 + 3 + 296 = 300 字节) f8 6c ← 元素 2:legacy 交易,自身就是列表,裸着进 80 85 … ← 108 字节的九字段载荷一句收束:剥类型字节是读取时的分流动作,字符串壳是存储时的收纳格式;交易的规范形态永远是完整的 type ‖ payload,谁要把它装进 RLP 列表,谁就得给它办护照。这些字节层的细节不只是趣味:8141 的交易哈希、签名哈希、内存池解码全都建立在这套编码之上,后面每一节都会踩到它。
帧:协议替你发起的一次调用
拆开 8141 的 payload,核心是一张帧列表,至多 64 帧。一帧等于协议替这笔交易发起的一次顶层调用,六个字段各管一事,其中最重要的两个是 mode 和 flags,它们回答两个完全正交的问题:mode 只回答这次调用的 msg.sender 是谁、按什么规则执行;flags 只回答这一帧被静态授权做什么。
mode 有三种,与 4337 的角色一一对应。SENDER 帧以你的账户为 caller,以你的身份行动,是唯一可以携带 value 的模式,对应 UserOp 里 callData 干活的那一段。VERIFY 帧以 0xaa 为 caller(下一节解释这个地址),STATICCALL 语义,唯一被允许的「写」是 APPROVE,一旦 revert 整笔交易作废,对应 validateUserOp。DEFAULT 帧同样以 0xaa 为 caller,但它是普通的非静态调用,负责协议侧的杂务,比如部署新账户、执行 paymaster 的 postOp,对应 4337 里 EntryPoint 亲自去 call factory 和 postOp 的那些动作。
flags 是一个小小的位字段。低两位声明这一帧允许的 APPROVE 范围:不许批、只批付费、只批执行、都许,四档。静态声明的意义在内存池:节点零执行就能给每一帧分类,识别验证前缀的形状变成纯结构操作。第一篇里 bundler 靠动态启发式自保;这里换成了协议级的静态可判定性。第三位是原子标志,语义是「把我和下一帧绑进同一个原子组」:连续置位的帧加上其后第一个未置位的帧构成一组,组内任何一帧失败,整组回滚,组里剩下的帧标记为跳过,预算退还。VERIFY 帧不可置位也不可被绑进组,因为它失败等于整笔交易作废,谈不上「跳过」;末帧自然也不可置位,它没有下一帧。
静态约束每一条都配着理由。value 只许出现在 SENDER 帧,因为 0xaa 没有处置你资金的权限;批执行的 VERIFY 帧必须 target 自己,因为只有你自己的代码有资格说「我同意执行」;批付费的 VERIFY 帧可以 target 任何人,因为替别人付钱是 paymaster 的正当业务;target 留空等于指向自己,RLP 的空串占 1 字节而地址占 21 字节,最高频的形状省下 20 字节,这是 8141 的最小交易能压到 139 字节基线的来源之一。
两个标准形状看一遍,帧的语感就有了:
自付单操作(两帧): F0 VERIFY target 空(→自己) flags 0x3 验签通过后一步批完付费与执行 F1 SENDER → USDC.transfer
代付批量(五帧): F0 VERIFY → 自己 flags 0x2 批执行 ┐ 顺序锁死:F1 的守卫 F1 VERIFY → paymaster flags 0x1 批付费 ┘ 要求"执行已获批"先落位 F2 SENDER → USDC 原子位置位 ┐ F3 SENDER → DEX 原子位不置 ┘ 原子组 {F2, F3} F4 DEFAULT → paymaster postOppaymaster 敢在 F1 批付费,是因为它的代码可以用跨帧自省指令(FRAMEPARAM、FRAMEDATALOAD)当场检查 F2、F3 里确实含有「给我转够手续费」的调用。第一篇里 paymaster 要靠质押加白名单豁免才敢营业;在这里,它的自保手段变成了直接读帧列表。
两个奇怪的地址:0xaa 与 0x8141
规范里有两个反复出现的地址,它们是两类完全不同的东西,放在一起对照最清楚。
0xaa,规范里叫 ENTRY_POINT,但它与 4337 的 EntryPoint 只有名字相似:它没有代码,没有部署,不会被调用,不存在「触发」这回事。4337 的 EntryPoint 是真实的链上合约,bundler 用普通交易去 call 它的 handleOps;8141 把整套编排逻辑搬进了客户端原生代码,geth、reth 的状态转换函数里多了一段帧循环。但 EVM 的调用模型要求每次调用必须有一个 caller,VERIFY 和 DEFAULT 帧是「协议替交易发起的调用」,规范于是钦定:这类调用的 caller 一律填 address(0xaa)。它是协议在 EVM 世界里的身份占位,永远只出现在主语位,从不出现在 target 位。账户代码里写 require(msg.sender == address(0xaa)),与 4337 里 require(msg.sender == entryPoint) 同构,而且更硬:0xaa 没有对应私钥、没有代码,普通调用的 msg.sender 永远是某个真实账户,你能观察到 caller 是 0xaa 的唯一途径,就是协议正在以 VERIFY 或 DEFAULT 帧执行你。彩蛋一枚:0xaa 字面上就是「AA」,而 APPROVE 指令的编号恰好也是 0xaa,地址与指令同号,是规范作者刻意的双关。
0x8141,规范里叫 EXPIRY_VERIFIER,是另一类东西:它有代码,是一个预部署。协议在分叉的那一刻直接赋予这个地址规范定义的行为,没有任何人执行过部署交易,先例是 EIP-4788 的 beacon root 合约和 EIP-2935 的历史哈希合约。用法:想给交易加过期时间,就在帧列表最前面放一个 VERIFY 帧,target 指向 0x8141,data 放 8 字节大端时间戳;这段规范代码检查 block.timestamp ≤ expiry,不满足就 revert,整笔作废。地址字面值等于 EIP 编号,规范作者的纪念章,与 0xaa 同族的趣味。真正值得记的是设计理由:为什么做成「指向预部署的帧」而不是一个交易字段?其一,格式正交,一切皆帧,不为过期单开特例;其二,内存池静态可读,看到 target 是 0x8141 且 data 恰好 8 字节,零执行就能读出这笔交易的存活期限,到点直接驱逐;其三,第一篇讲过验证段为什么要禁 TIMESTAMP,而「过期」是一个正当需求,把这条禁令的唯一豁免收拢进一处规范代码,审计面收敛到一个点。协议给正当需求开了一扇窗,而且是有边框的窗。
APPROVE:亲口说出的授权
现在到 8141 与 4337 分水岭最深的地方。4337 里,账户在 validateUserOp 里返回一个值,由 EntryPoint 合约解读这个返回值并完成记账:授权是一个被中间人解释的返回值。8141 里,账户代码执行一条指令,协议直接认:授权是亲口说出的动作。
APPROVE 是一条真实的 EVM 指令,编号 0xaa,嵌在你账户的字节码里,与 ADD、MSTORE 同类。VERIFY 帧的执行上下文是 caller 等于 0xaa、ADDRESS 等于你的账户,而这条指令的守卫恰恰是 ADDRESS 必须等于帧解析出的 target:是你的代码,在你的身份下,说出批准。0xaa 什么都不执行,它没有代码,是敲门的人,不是签字的人。带满参数的 APPROVE(0x3) 一次完成四件事:置起「发送者已批准」的标记,把 payer 设为你,nonce 递增,再按这笔交易的最大成本预扣 gas 钱。注意记账的落点:这些是交易级的上下文变量,活在客户端内存里,不是任何合约的存储槽,协议直接解释这条指令的语义。第一篇里 4337 的 prefund 是一笔真实的转账,锁进 EntryPoint 合约的托管;8141 的预扣是协议自己记的账。那条付款分界线原封不动,线前失败无人买单、线后失败只烧签名者,只是它从合约逻辑降到了协议原语。
顺带一条审计口径:DELEGATECALL 保留 ADDRESS,所以被委托的库同样能有效地执行 APPROVE。把验证逻辑委托出去,委托的不是一次函数调用,是「以你的身份说出批准」的资格,被委托的库必须按完全可信来对待。
规范签名哈希:一条老规矩的两代演化
8141 的签名不再是信封上的 v、r、s,而是一张签名列表,每个条目是 [scheme, signer, msg, signature] 四元组。要理解它的哈希规则,得先回 legacy 补一块地基,那里藏着一条从创世块用到今天的老规矩。
先看时间顺序上的悖论。legacy 交易九个字段,v、r、s 是它自己的最后三个字段,不是什么外挂附件。但签名是「用私钥对某个摘要做运算」的输出,签的那一刻,v、r、s 还不存在;「对九字段整体哈希再签名」要求签名对包含它自己的数据做承诺,做不到。唯一的出路:签名哈希只对前六个字段计算。两条流水线各走各的:
签名时(钱包侧): sighash = keccak(rlp([六字段, chainId, 0, 0])) ← 不含 v/r/s ECDSA(私钥, sighash) → r, s, 恢复位 → 组装 v 完整交易 = rlp([六字段, v, r, s]) txHash = keccak(完整交易字节) ← 含 v/r/s
验证时(节点侧): 拆出 v/r/s,用其余字段独立重算 sighash ecrecover(sighash, v, r, s) → 恢复出发送者地址两个哈希由此分工:sighash 是密码学锚点,私钥承诺的对象,授权语义的全部所在;txHash 是标识符,浏览器、RPC、日志里那个名字。还有一个推论值得停一秒:legacy 交易没有 from 字段,发送者不是被声明的,是从签名里恢复出来的。这套恢复式身份只对 ECDSA 这类支持公钥恢复的方案成立;8141 要支持任意签名方案,只能把 sender 升为显式的一等字段,恢复式变成声明式,签名列表负责证明这个声明。
再插两段会转世的历史。其一,EIP-155 为了防跨链重放,要把 chainId 掺进被签的内容,它的手法是把 v、r、s 的三个座位填上 chainId 和两个零占位再哈希:座位保留,内容摘除。其二,ECDSA 有一个 s 值延展性:对同一个 sighash,(r, s) 和 (r, n−s) 在数学上都有效,第三方可以把在途交易的 s 翻个面,得到授权仍然有效但 txHash 变了的「同一笔」交易,依赖 txHash 追踪状态的系统会被搞晕,比特币生态的 Mt. Gox 事件让这类混乱出了名。以太坊的修复是 EIP-2:强制 low-s,超过曲线阶一半的 s 直接判整笔无效,用规范编码要求消灭延展性。给「延展性」下一个正式定义:第三方手里没有你的私钥,却能改动在途交易的某些字节,得到另一笔仍然有效的交易;会被这种改动波及的值,就叫可延展。s 翻面改变的是 txHash 这个名字,sighash 这个授权锚一动不动。记住这两段,它们都会在 8141 乃至后量子的世界里换个形状回来。
现在看 8141 的规范签名哈希,定义只有两行:
compute_sig_hash(tx): 对每个 msg 为空的签名条目,把它的 raw signature 字节置空 return keccak(0x06 ‖ rlp(tx)) # 类型号做前缀:跨类型域分离为什么必须摘除?亲手搭一遍循环就明白了。反设不摘,天真地对全字节哈希:填好一切,签名位放个占位符,算出 H₁,签 H₁ 得到 s₁,把 s₁ 写进交易,字节变了,重算得到 H₂ ≠ H₁,节点将检验「s 是否签了 H₂」,而 s₁ 签的是 H₁,失败;对 H₂ 重签得到 s₂,写入,字节又变,H₃ ≠ H₂,又失败。你在追一个每次落笔就移动的靶子。终止这场追逐需要的不是「再签一次」,而是一个自洽解:
s* = Sign(sk, H(tx 含 s*))满足 f(x) = x 的解叫不动点。这里「无解」的严谨说法是:没有任何求解方法。安全哈希的雪崩性质让 H 表现为一个黑箱随机函数,这个方程没有任何代数结构可以利用,唯一的「算法」是盲试,每次命中概率约 2⁻²⁵⁶。密码学语境下,这就等价于无解。摘除从定义上把签名逐出原像:H 只依赖非签名内容,先算 H,再签,再填,写入不再改变 H,靶子钉死,一签即中。与 legacy 逐位对齐看:legacy 的 sighash 对六字段计算,v、r、s 的座位按不存在处理;8141 的规范哈希对全交易计算,msg 为空的条目把 signature 座位按空处理,msg 显式的条目签名字节原样保留。同一条「被签之物不能包含签名自己」的老规矩,在多签名列表上的推广。这一代还更优雅了一层:交易哈希与签名哈希共用同一条公式,唯一差异是空 msg 条目的 signature 字节在场(得到交易哈希)还是置空(得到规范签名哈希);chainId 也已是一等字段,155 那出占位符的戏法光荣退休。顺带钉死一个容易滑过去的点:这不是「签名前、签名后」两个版本。签名填回去之后,交易永远以全字节形态存在并广播;验证时,节点把同一份字节按「空 msg 条目的 signature 置空」的读法重算一遍 H。两个哈希是同一份最终字节的两种读法,同时存在,一个当名字,一个当锚点。
那个反复出现的 msg 字段是什么?它是每个签名条目自己的字段,只回答一个问题:这条签名签的是哪个摘要。 空,意思是「我签的就是本交易的规范签名哈希」,这是主签名,legacy 时代 v、r、s 的直系继承人,整笔交易里权力最大、最承重的一条;EOA 用默认逻辑发帧交易时,携带的唯一条目就是一条空 msg 的 SECP256K1 签名,这一条就是全部授权。显式的 32 字节,意思是「我签的是这个别的摘要」,比如一张 permit、一份会话密钥的授权书,它是辅助的证据件。为什么「签本交易」用空来表示,而不把哈希 H 直接写进 msg?因为写不进去:msg 本身是 rlp(tx) 的一部分,是 H 原像的一部分,把 H 写进去就改变了 H,又在追靶子了。一个对象无法在自己体内写下自己的哈希,只能放一个自指的符号去指它;空,就是那个符号。 签署对象只有两种可能,本交易(写不出,只能指)或者别的摘要(协议猜不出,必须写),恰好两个取值,是逻辑二分的必然形状,不是功能清单碰巧列了两项。
显式 msg 有自己的纪律。它的字节必须留在原像里,被规范哈希承诺住,否则中继者可以在途中偷换字节而不惊动任何空 msg 的签名者;但要看清,这个承诺是单向的。显式 msg 条目被交易抱紧了,它自己对这笔交易却零承诺:它只向交易导入一个客户端担保过的事实,「签名者 S 确实对摘要 M 签过名」,仅此而已,不认证也不授权本交易。于是有一个经典陷阱:VERIFY 代码如果只检查显式 msg 的签名就 APPROVE,那这条签名可以被剪下来,贴到另一笔帧列表完全不同的交易上,再次放行,因为授权链条没有任何一环锚定在本交易上。纪律只有一条:授权必须在某处闭合到那个不可延展的锚点,要么核对规范哈希(TXPARAM 指令能读到它),要么显式约束每一个后续帧。正确的姿势是两种取值配合,会话密钥的标准范式:
条目 0:[SECP256K1, 会话密钥 K, 空, s₀] ← K 签规范哈希:锚定"这一笔"条目 1:[SECP256K1, owner, 摘要 M, s₁] ← owner 早前签的委托书 M: "K 可在 DEX X 交易,限额 Y,有效期至 T"VERIFY 帧代码: 读条目 1:确认 owner 签过 M(客户端已验,读事实即可) 按 M 的语义检查各 SENDER 帧是否都在授权范围内 读条目 0:确认签了规范哈希的正是 K → APPROVE条目 1 回答「K 凭什么有权」,条目 0 回答「这一笔是不是 K 发的」。缺了 1,K 是一把无主之钥;缺了 0,授权飘在空中,可以被任意重放。一句话:空 msg 给交易上锁,显式 msg 给交易递证据;安全的账户逻辑永远让证据服务于锁,而不是代替锁。
摘除规则还有两份红利。第一份是并行签署:多条空 msg 条目在 H 的定义里都按空算,于是 H 对每一位签名者都相同,而且不依赖对方的字节,多位签名者可以并行、以任意顺序签署,没有「你先签我才能算哈希」的排队;一把 ECDSA 加一把 P256 的 2-of-2,或者过渡期一把 ECDSA 加一把 Falcon 的混合双签,在这个格式里是原生表达。第二份红利更深远:空 msg 条目的签名字节从定义上就不在任何人的原像里,将来把它们从区块里删掉、换成一份聚合证明,不会惊动任何签名。一个被数学逼出来的动作,被规范作者设计成了一份资产。这张「聚合门票」发给了谁、没发给谁、凭什么,是下一篇的核心。
内存池机器:免费的失败必须早抓
第一篇的教训在这里迎来终局测试:可编程验证打开 DoS 之门,免费输入撬动真实 CPU;4337 把这个风险外包给 bundler,8141 把它接回协议自己的 mempool,靠一台两层的验证机器重新收拢口子。
层 A 是内存池准入。节点收到一笔帧交易,依次做:首字节分流,进入帧交易解码器;RLP 解码九个字段,跑完全部纯静态检查,帧数不超过 64、flags 保留位、mode 约束、签名条目编码合规;计算规范签名哈希,先行验证所有协议方案的签名;检查 nonce 精确衔接,同一个 sender 在池里同时只挂一笔(第一篇里 2938 那条 mempool 规则的转世);然后是帧交易独有的一步,payer 无法静态读出,必须模拟验证前缀,前缀形状要匹配规定的几种,模拟全程套 trace 规则,且前缀各帧的 gas 上限总和加上签名费不得超过 100000,模拟到 payer 落定即停;最后查 payer 的余额与预留。全部通过,才入池、才转发。层 B 是区块执行,完整的状态转换:nonce、验签、帧循环、原子组、终局 payer 检查、扣费退款、逐帧回执。
三条结论性判断,值得抄下来。
第一,传统交易的准入是 O(1):一次 ecrecover 加两次状态读,零 EVM 参与。帧交易把「谁付钱」变成了图灵完备的问题,而内存池规则的全部使命,就是把这个口子收拢回「至多十万 gas 的模拟可判」。第一篇里 2938 的三道镣铐,在这里有了具体的数字。
第二,invalid 不等于 revert。验签失败等于 invalid:不进块,没有任何人为它付费;帧的 revert 则照常计费。invalid 是免费的,所以验签必须前置到准入阶段拦截,否则它就是一个免费的 DoS 面,这正是「协议方案的签名要先于任何帧验证」的原因。付款分界线的语言在这里依然适用:invalid 活在线前,revert 活在线后。
第三,共识有效的集合,严格大于公共池可传播的集合。一笔交易可以在共识层完全合法,却因为验证前缀超出十万 gas 的预算而被公共内存池谢客,只能通过私有通道直接递给区块构建者。这个夹层不是理论上的角落。下一篇会看到:今天,每一笔后量子交易都住在里面。
总结
把这一篇压回一段话。EIP-8141 用一次硬分叉把验证的提问权收回协议手里:信封沿用 2718 的一字节分流(地基是 RLP 五条前缀规则,以及「类型字节属于协议、字符串壳属于存储」的层次感);交易本体变成一张至多 64 帧的列表,mode 定身份、flags 定权限,原子位把相邻的帧绑成全成全败的组;协议以 0xaa 的名义登门发问,预部署 0x8141 替它看表,账户用一条 APPROVE 指令亲口作答,授权从「被中间人解释的返回值」变成「协议直接认的动作」,prefund 的托管转账变成客户端内存里的预扣记账。签名从信封上的 v、r、s 长成一张多方案的签名列表,规范签名哈希用逐条目摘除延续了「被签之物不能含签名自己」的老规矩,空 msg 上锁,显式 msg 递证据,顺手买到并行签署和一张聚合门票。内存池机器用纯静态检查加十万 gas 的前缀模拟把「免费的失败」重新关进笼子,代价是在「共识有效」与「公共池可传播」之间划出了一个夹层。4337 的每个角色都能在这里找到对应物:EntryPoint 溶解成帧循环,validateUserOp 变成 VERIFY 帧,prefund 变成 APPROVE 的预扣,7562 沙箱变成协议内置的 trace 规则,bundler 则消失在区块构建者里。第一篇那条付款分界线原封不动,只是从用户态搬进了内核。
下一篇把一条真正的后量子签名放上这套轨道:看它在哪一步被谁拦下,同一条签名为什么在两条车道上差出百倍的价格,以及当「地址等于私钥」的等式被拆掉之后,账户的所有权、恢复和信任分别从哪里来。