ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

EIP-7906 交易断言:用 TXTRACE / TXDIFF / EVENTDATACOPY 操作码在链上检查交易结果

EIP-7906 交易断言:用 TXTRACE / TXDIFF / EVENTDATACOPY 操作码在链上检查交易结果 EIP-7906 交易断言用 TXTRACE / TXDIFF / EVENTDATACOPY 操作码在链上检查交易结果【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs本文是 Ethereum Improvement Proposal 仓库中 EIP-7906Transaction Assertions via State Diff Opcode的技术解读。该提案为 EVM 引入TXTRACE、TXDIFF、EVENTDATACOPY三个操作码以及一个新的POST_TX帧模式让合约能够在交易执行结束后检查该交易的余额变化、存储变化、代码变化与事件日志并在断言失败时无条件回滚整个交易执行体——这是面向盲签风险、让钱包与 dApp 对交易结果施加链上约束的核心基础设施。读完本文你将掌握三个操作码的完整参数语义、POST_TX帧的执行规则、状态差异的枚举与按键查询模型、Gas 定价以及断言合约的安全编写要点。背景与动机为什么需要交易结果断言提案开篇指出一个现实问题截至目前被盗加密资产的总价值已超过一个中等规模国家的年度 GDP这种损失很大程度上源于用户无法真正理解一笔已签名交易将执行什么。普通用户或钱包应用收集、审查、分析交易将要执行的 EVM 代码的能力极其有限导致用户在与以太坊交互时实际上是在盲签blind signing把自己暴露在显著风险之下。EIP-7906 的思路不是让用户读懂代码而是换一个角度限制并检查交易的结果outcome。通过让钱包与 dApp 有能力观察并约束交易可能产生的状态变化用户就获得了一个可量化的风险削减工具——这也是 EIP-7906 的核心动机所在。实现这一目标的载体是建立在 EIP-8141Frame Transaction 之上的一个新的帧模式POST_TX作为交易的尾部后缀帧执行只读地观察整笔交易的最终结果三个新操作码TXTRACE枚举完整交易状态差异、TXDIFF按键直接查询单个条目、EVENTDATACOPY把事件数据拷贝进内存。它们共同构成一套链上断言机制钱包或 dApp 把一段断言逻辑附加到交易上如果断言失败交易的执行效果被丢弃但交易仍然有效、Gas 照常支付。全景速览三个操作码与POST_TX帧EIP-7906 的抽象描述可以浓缩为一句话它让一个合约可以检查正在执行它的这笔交易的最终状态差异——余额、存储、代码的变更以及发出的事件。构件作用TXTRACE枚举整笔交易的完整状态差异按索引访问TXDIFF按键地址 / 地址slot直接查询某一具体余额、codehash 或存储槽EVENTDATACOPY将某条事件的非索引数据拷贝到内存POST_TX帧模式上述操作码唯一合法的执行环境只读、作为交易尾部后缀执行TXTRACE、TXDIFF、EVENTDATACOPY的操作码字节以及POST_TX的帧模式值由 EIP-8141 中的帧交易家族注册表frame-transaction family registry统一分配EIP-8141 是这一族的权威登记处而TXTRACE/TXDIFF的参数空间由本 EIP 自己定义。本 EIP 定义的相关常量如下NameValueTXTRACE_GAS_COSTTBDEVENTDATACOPY_GAS_COSTTBDPOST_TX3POST_TX帧模式只读的交易尾部后缀EIP-7906 硬依赖 EIP-8141requires: 8141并在其帧交易规范上增加一个新的mode值POST_TX与 EIP-8141 已定义的DEFAULT、VERIFY、SENDER并列。EIP-8141 的帧模式表见源码原本是0 DEFAULT以ENTRY_POINT身份执行、1 VERIFY交易校验、2 SENDER以tx.sender身份执行EIP-7906 将其扩充为POST_TX 3。在本 EIP 生效的所有地方EIP-8141 帧交易需遵守以下新增规则每个帧的静态约束由assert frame.mode 3改为assert frame.mode 4从而接纳POST_TX为合法模式值。POST_TX帧必须构成tx.frames的一个连续尾部后缀一旦某个帧的模式是POST_TX其后所有帧也必须是POST_TX违反此规则的帧交易无效。与DEFAULT、VERIFY帧一样POST_TX帧的caller是ENTRY_POINTEIP-8141 中定义为address(0xaa)见 EIP-8141 常量表。POST_TX帧以STATICCALL方式执行禁止一切状态修改。与VERIFY帧不同POST_TX帧不享有EIP-8141 在 APPROVE 指令0xaa 中定义的APPROVE豁免在POST_TX帧的调用子树中调用APPROVE就是普通的STATICCALL违规会导致该调用帧异常停机并按下面的失败规则处理。如果POST_TX帧 revert 或异常停机整笔交易的执行体被无条件回滚。这会覆盖本应生效的原子批处理atomic-batch展开行为POST_TX失败总是校验/回滚整笔交易执行直到校验前缀validation prefix而不是仅仅展开某个原子批。POST_TX的 revert 或异常停机不会使交易无效这与VERIFY帧 revert 不同交易仍然有效、被打包进区块并生成一个status 0的失败回执。校验前缀中产生的所有状态变更例如通过APPROVE支付 Gas、deploy帧中的账户创建被永久提交payer 被足额收取到失败点为止所消耗的 Gas。在 EIP-8141 的 默认代码default code 机制下POST_TX帧与SENDER、DEFAULT帧的处理方式相同。TXTRACE枚举整笔交易的状态差异TXTRACE用于检索截至当前调用点的整笔交易状态差异。它接受(param, index)两个栈输入与 EIP-8141 的FRAMEPARAM操作码模式一致。可用参数如下paramin2Return value0x00必须为 0balances_changed- 净余额发生变化的地址数量0x01必须为 0slots_changed- 净存储发生变化的(address, slot)对数量0x02必须为 0contracts_deployed- 新部署了合约代码的地址数量0x03balances_changed中的索引change_address- 余额变化的账户地址0x04balances_changed中的索引balance_before- 交易开始时该地址的余额0x05balances_changed中的索引balance_after- 截至本次TXTRACE调用时该地址的余额0x06slots_changed中的索引change_address- 存储变化的账户地址0x07slots_changed中的索引slot_key- 发生变化的存储槽键0x08slots_changed中的索引slot_value_before- 交易开始时该槽的值0x09slots_changed中的索引slot_value_after- 截至本次TXTRACE调用时该槽的值0x0Acontracts_deployed中的索引deployed_address- 新部署合约的地址0x0Bcontracts_deployed中的索引codehash_after- 新部署合约的 codehash0x0C必须为 0events_count- 发出事件的总数0x0Devents_count中的索引events_address- 发出该事件的合约地址0x0Eevents_count中的索引event_topic_count- 该事件的主题数0–40x0Fevents_count中的索引event_topic0- 事件第一个主题若无主题则异常停机0x10events_count中的索引event_topic1- 事件第二个主题若无则异常停机0x11events_count中的索引event_topic2- 事件第三个主题若无则异常停机0x12events_count中的索引event_topic3- 事件第四个主题若无则异常停机0x13events_count中的索引event_data_len- 事件非索引数据的字节长度0x14必须为 0gas_pre_charge- 从 Gas payer 扣除的总金额0x15必须为 0gas_payer_address- 被收取 Gas 预扣费的地址三个操作码TXTRACE、EVENTDATACOPY、TXDIFF只在POST_TX模式帧内合法。在其他任何上下文——包括 legacy 交易、EIP-1559 交易、或其他任何 EIP-8141 帧模式——执行它们都会导致异常停机。两个特殊的全局参数值得展开gas_pre_charge0x14对带 blob 的交易它包含 blob 费用即gas_pre_charge gas_limit × gas_price blob_count × GAS_PER_BLOB × blob_base_fee。其中GAS_PER_BLOB 131,072来自 EIP-4844见 EIP-8141 引用常量。gas_payer_address0x15是调用过APPROVE(APPROVE_PAYMENT)或APPROVE(APPROVE_EXECUTION_AND_PAYMENT)的那个帧的目标地址即 EIP-8141 的payer——它可能并不是交易发送者例如由 paymaster 代付。状态差异语义before值反映的是整笔交易执行开始前的 prestate 值早于本交易任何状态写入after值反映的是本次TXTRACE调用时的当前状态。交易开始到TXTRACE调用之间的中间写入不可单独观察。一个地址出现在balances_changed中当且仅当其余额在TXTRACE调用时与交易开始时不同。这包括施加在 Gas payer 地址上的 Gas 费预扣。调用方在计算某个地址的净 ETH 转入/转出时应通过gas_payer_address0x15找到 Gas payer并从中减去gas_pre_charge0x14。一个(address, slot)对出现在slots_changed中当且仅当该槽在TXTRACE调用时的值与交易开始时不同。同一槽在交易中的多次写入会折叠为单一条目。一个地址出现在contracts_deployed中当且仅当它的 codehash 在交易期间从空 codehash 变为一个非空的、且不是EIP-7702 委托指示符delegation designator的 codehash。CREATE/CREATE2之后留下空代码的账户不会被枚举因为它没有代码变化。TXDIFF按键直接查询单个条目TXTRACE擅长整体枚举但它没有机制直接查询某一个具体账户的余额 / codehash / 某个具体存储槽。TXDIFF正是为此补充的用键直接访问与TXTRACE的枚举模型互补。TXDIFF接受(param, address, in3)三个输入参数表如下paramin2in3Return value0x00addressslot_key值slot_value_before0x01addressslot_key值slot_value_after0x02address必须为 0balance_before0x03address必须为 0balance_after0x04address必须为 0codehash_before0x05address必须为 0codehash_after0x06address必须为 0address_slots_count0x07addressaddress_slots_count中的索引对应slots_changed的TXTRACE全局索引0x08address必须为 0address_events_count0x09addressaddress_events_count中的索引对应events_count的TXTRACE全局索引0x0Aaddress必须为 0account_change_flags如果查询的键address/(address, slot)在交易期间从未被修改TXDIFF参数0x00–0x05对before和after变体都返回当前的实时值。按地址重映射Per-Address Views参数0x06、0x07、0x08、0x09暴露的是对TXTRACE枚举的存储槽与事件的按地址过滤视图计数参数0x06、0x08返回该视图的大小对没有任何条目的地址返回0。索引参数0x07、0x09把按地址的局部索引in3映射为对应表中的全局索引。返回的全局索引可直接用于TXTRACE的逐条目参数以及EVENTDATACOPY。如果TXDIFF收到一个无效的局部索引即大于等于视图计数的值则发生异常停机。账户变更标志Account Change Flags参数0x0A返回一个位掩码汇总该账户状态的所有净变化。位序遵循账户元组(nonce, balance, storage_root, code_hash)的字段顺序BinarySet when0b0001账户 nonce 与交易 prestate 值不同0b0010balance_after ! balance_before0b0100账户的任一存储槽与 prestate 值不同address_slots_count 00b1000codehash_after ! codehash_before所有更高位均为 0。被置位只反映交易 prestate 与调用时状态之间的净差异交易内被修改后又恢复原值的字段不会置位。account_change_flags 0当且仅当该账户的内部状态包括其全部存储与交易 prestate 完全一致。注意nonce 的实际值不可通过TXTRACE或TXDIFF观察。Gas 成本与 EIP-2929 接入TXDIFF中可能回退到读取实时状态的参数使用 EIP-2929 的访问列表决定成本存储槽参数0x00、0x01若(address, slot)不在已访问存储列表中则COLD_SLOAD_COST2100否则WARM_STORAGE_READ_COST100。余额与 codehash 参数0x02–0x05若地址不在已访问地址集合中则COLD_ACCOUNT_ACCESS_COST2600否则WARM_STORAGE_READ_COST100。按地址视图与标志参数0x06–0x0A统一收取TXTRACE_GAS_COST的固定费用。这些参数完全由交易本地的状态差异回答绝不读取实时状态。对于参数0x00–0x05被访问的槽或地址在调用后被加入 EIP-2929 访问列表并且在 EIP-7928 生效处像其他读取状态的 opcode 一样记录在块级访问列表中EIP-7928 引入了强制性的块级访问列表与交易后状态差异见 eip-7928.md。TXTRACE与EVENTDATACOPY只读取已记录的差异和日志数据因此不新增任何访问参数0x06–0x0A也不与 EIP-2929 访问列表交互。此外codehash_before对未部署的合约等于空 codehash即0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470与 EIP-8141 帧分派逻辑 中判空代码的常量一致。保留输入与结果排序保留输入上述参数表中标记为必须为 0的所有in2/in3操作数 MUST 为零提供非零值会导致异常停机。结果排序TXTRACE返回的余额与存储槽变更按受影响的地址作为数值uint160升序枚举。单个地址内的存储变更按存储槽键作为数值uint256升序排序。事件按交易执行期间发出的顺序枚举与其在交易内的全局日志索引一致。排序之所以必须是确定性的是因为状态差异模型把同一槽的所有中间写入折叠为单一条目——同一槽可能被交错的重入调用多次写入却只产生一条记录因此执行顺序本身无法定义确定性的差异顺序按地址与槽键排序保证了与执行流无关的规范有序枚举。事件则因每条事件都是不可折叠的独立实体、天然对应日志索引故采用发出顺序。EVENTDATACOPY把事件数据拷入内存EVENTDATACOPY把事件数据拷贝到内存。Gas 成本与CALLDATACOPY一致固定成本 3加上由内存扩展与拷贝产生的可变成本。栈布局StackValuetop - 0event_indextop - 1memOffsettop - 2dataOffsettop - 3length不产生栈输出值。行为语义与CALLDATACOPY一致从事件的非索引数据中、从字节偏移dataOffset开始拷贝length字节到memOffset起始的内存区域。若event_index events_count发生异常停机。若dataOffset length超出事件数据长度发生异常停机。EVM 事件携带 0–4 个主题每个是 32 字节字主题 0 惯例上是事件签名哈希主题 1–3 携带索引参数。需要验证转移了哪个代币、授权了哪个地址、涉及哪个标识符的断言合约必须直接检查这些索引值。访问到event_topic_count及之外的主题槽会异常停机与其他所有索引参数越界行为一致。事件非索引数据是变长的无法作为单个 32 字节栈字返回因此采用与CALLDATACOPY同语义的内存拷贝操作码这是 EVM 处理变长数据的惯用方式。设计权衡为什么这样设计参数选择模式TXTRACE沿用FRAMEPARAM的(param, index)双参数模式EIP-8141 的 FRAMEPARAM 家族保持接口一致避免为每条 trace 信息引入单独的操作码。枚举 vs 直接查询TXTRACE用基于索引的访问暴露全量可观察状态变化TXDIFF补充直接按键访问。典型交易断言的 Gas 成本相比存储修改本身可忽略不计。事件按发出顺序排列需要线性扫描而大多数断言脚本预期枚举全部允许的状态变化并不需要二分查找。TXDIFF解决的关键缺口是TXTRACE无法直接检查这笔交易前usdc.balances[...]的值这类具体值断言合约必须在排序输出上自行实现搜索而且存储的点查询需要address与slot两个键放不进TXTRACE现有的 2 参数形态。按地址视图的动机对事件而言没有高效替代方案——事件按发出顺序枚举找一个合约的事件要对整笔交易的事件做线性扫描而无关事件数量是攻击者可控制的恶意 dApp 可以用廉价日志填充交易把断言 Gas 成本抬高到超出其 stipend。按地址视图使成本只与断言实际检查的合约活动成正比。返回全局索引则让参数空间保持很小一次转换调用即可接入所有现有TXTRACE参数与EVENTDATACOPY。变更标志作为盾牌断言常见断言是盾牌式的——断言某账户未被交易影响。没有标志参数就需要三次独立查询余额、codehash、存储计数且仍观察不到 nonce。account_change_flags把整个检查压缩为一次操作码调用flags 0即保证账户状态含全部存储与交易 prestate 完全一致。事件被有意排除在位掩码之外发出事件不是对账户状态的改变合约保持沉默的检查可单独用address_events_count 0实现。回执表示与反 DoS为什么POST_TXrevert 不把交易整个排除出区块并回滚 Gas 支付因为那会引入严重的 DoS 向量攻击者可消耗接近区块 Gas 上限的 Gas然后在POST_TX帧中免费 revert。让交易保持有效并提交校验前缀是严格必要的以保证区块构建者为其执行工作获得补偿。因此POST_TXrevert 被设计为应用层执行回滚而非协议层失效生成标准的status 0回执同时保留 Gas 支付。同理校验前缀不会被POST_TXrevert 回滚。在正确构造的 EIP-8141 交易中钱包软件构造校验前缀不受信任的 dApp 动作严格放在执行阶段SENDER帧——只要钱包自身的校验前缀逻辑可信提交校验前缀就不是用户侧的安全缺陷而是保护网络免受 DoS 的必然要求。向后兼容TXTRACE、EVENTDATACOPY、TXDIFF占用的是此前未使用的操作码槽位不对任何现有操作码、交易类型或预编译做修改因此现有合约与工具不受影响。本提案对 EIP-8141 是硬依赖这三个操作码只能在 EIP-8141 的POST_TX帧内执行。legacy 交易、EIP-1559 交易、以及其他任何 EIP-8141 帧模式都无法使用这些操作码。安全考量不充分的断言比没有断言更危险主要风险是虚假安全感断言逻辑检查得过少会让用户误以为交易安全。基于TXTRACE构建的钱包与 dApp 必须确保断言逻辑覆盖受保护操作的所有相关状态变化生态应当把不完整的断言视为与完全没有断言等同。POST_TX模式能保证一旦断言触发它会干净且无条件地使整笔交易失效包括此前帧已APPROVE的 Gas 支付且无法通过原子批标志或排在断言之后的帧部分绕过——但它本身并不会让任何单个断言更严格或更正确。强制POST_TX帧包含协议层面没有要求交易必须包含POST_TX帧。想使用POST_TX帧的智能账户必须在其VERIFY帧的校验逻辑中显式配置严格要求每一笔其批准的交易的帧序列中都包含它自己的特定POST_TX帧且不得存在任何可绕过的机制。由于交易可含多个POST_TX帧且它们保证构成交易末尾的连续后缀智能账户可以反向迭代定位自己所需的断言帧。同时必须保证所有POST_TX帧的目标是不可变的、不可升级的合约——若断言合约可升级恶意交易可能在执行期间升级它以绕过安全检查。只要VERIFY帧在一切状态变更之前执行在校验时检查POST_TX帧目标就能保证交易总是对着预期的不可变策略求值。EIP-7906 给出了 Solidity 伪代码function requirePostTx() internal view { bool found false; for (uint256 i tx.frames.length; i 0; i--) { Frame memory f tx.frames[i - 1]; if (f.mode ! 3) { break; // POST_TX frames are a contiguous suffix; no more left to check } if (f.target this.postTxEnforcer) { // (Optionally check f.data matches the expected POST_TX method selector) found true; break; } } require(found, Missing required POST_TX frame for this account); }断言 Gas 耗尽枚举TXTRACE结果的断言合约可能耗尽 Gas。在当前以太坊配置下一笔交易最多可产生约42,600 条事件最坏情况下对其逐条断言需要大量 Gas。防御措施断言合约应读取总条目计数确保其在安全上限之下调用断言的框架层必须按其预期处理的条目数按比例转发 Gas stipend只关心特定合约的断言应使用按地址的TXDIFF视图而非枚举全局表使 Gas 成本与无关且可能被攻击者控制的条目解耦在完成枚举循环前耗尽 Gas 的断言没有验证完整结果任何基于TXTRACE的框架必须把断言 OOG 视为显式断言 revert。deploy帧的边界deploy帧只能安装tx.sender的代码这是 EIP-8141 的内存池传播规则Structural Rules而非协议不变量——没有任何东西阻止它产生其他副作用TXTRACE会暴露这些副作用但POST_TX无法撤销它们。钱包绝不能将deploy帧用于部署tx.sender之外的任何用途也不能依赖POST_TX去捕捉或逆转它。与框架生态的衔接在仓库中可以看到 EIP-8141 家族配套的参考实现例如 assets/eip-8141/CanonicalPaymaster.sol 中最小化的规范 paymaster它作为VERIFY帧目标校验 65 字节的 secp256k1 签名后调用APPROVE(scope0x1)批准支付——这正是TXTRACE的gas_payer_address/gas_pre_charge参数所要配合处理的payer 与 sender 分离场景。EIP-7906 的POST_TX断言帧与这类校验前缀协作的典型交易结构是VERIFY校验 批准→SENDER用户操作可多个→POST_TX断言帧可多个。多个POST_TX帧的存在允许相互独立的断言提供方在不主动协作的情况下组合每个交易断言模块在自己帧内运行断言逻辑并可在检查失败时独立地使交易失效。结语EIP-7906 通过POST_TX帧模式与TXTRACE/TXDIFF/EVENTDATACOPY三个操作码把交易结果的可检查性带进了 EVM枚举 按键查询 事件数据访问三种能力相互补全配合只读的尾部后缀帧与失败即整笔回滚的语义为钱包和 dApp 提供了一条对抗盲签风险的链上路径。其设计处处体现反 DoS 考量提交校验前缀、保留 Gas 支付、按地址视图隔离攻击者控制的日志、确定性枚举地址/槽排序与安全的断言生命周期不可变目标、VERIFY前置强制包含。作为一个 Draft 状态的 Core 类标准requires: 2929, 8141其常量如TXTRACE_GAS_COST仍标注 TBD具体操作码字节与参数分配以 EIP-8141 注册表为准读者可结合 EIP-2929、EIP-7928、EIP-7702、EIP-4844 等关联规范深入研读。【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表