EIP-3607 深度解析:拒绝带合约代码地址发起的交易(Reject Transactions from Senders with Deployed Code)
2026/9/15 21:14:16 网站建设 项目流程

EIP-3607 深度解析:拒绝带合约代码地址发起的交易(Reject Transactions from Senders with Deployed Code)

【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs

导读

EIP-3607 是 Ethereum 核心协议(Core 类别)的一项共识规则澄清:任何tx.sender地址已部署合约代码的交易都必须被拒绝为无效。它解决的是 160 位地址空间下"合约账户与外部账户(EOA)发生地址碰撞"这一现实可行的攻击面,防止攻击者先部署一个貌似安全的合约(如代币包装合约、AMM 合约)吸引用户资金,再使用同一地址对应的 EOA 私钥将这些资金转走。阅读本文后,你将掌握 EIP-3607 的规范细节、背后的碰撞攻击模型、go-ethereum 中的参考实现(含 diff),以及它与 EIP-7702、EIP-3074、EIP-3541 等提案的衔接关系。

背景:为什么需要这条规则

地址碰撞攻击的可行性

Ethereum 地址目前只有 160 位(20 字节)。攻击者可以生成约2**80个 EOA 密钥,并模拟从这些 EOA 各部署一个合约,理论上即可期望找到一对"EOA 地址 == 合约地址"的碰撞。

这种最朴素的攻击形式需要存储2**80个地址,约2.4 * 10**25字节(24 Yottabyte)内存,构成了实际障碍。但 EIP-3607 指出,存在不需要大量存储的**循环查找算法(cycle finding algorithms)**来完成碰撞搜索。据作者估算,投入约 100 亿美元硬件与电费,可以在约一年内找到一次合约与 EOA 之间的地址碰撞——这意味着碰撞攻击在当下是"经济上可行"的,而非科幻场景。

协议行为的未定义地带

Yellow Paper(黄皮书)并未明确说明:当一个交易来自已部署了合约代码的账户时,客户端应如何处理——推测是因为当年认为这种情况不可行。默认假设是大多数客户端在当前状态下会放行这类交易。

EIP-3607 的动机正是把这种未定义行为明确为"始终禁止",从而堵住地址碰撞中最现实、最严重的攻击路径。文中也提到社区正在讨论迁移到 256 位地址(碰撞抵抗复杂度提升到2**128,当前被认为在可预见的未来不可行),但在 160 位地址下,碰撞问题"现在已经可以被实际解决",协议层面必须先做出防护。

规范(Specification)

EIP-3607 的规范非常简洁,但语义精确:

任何tx.senderCODEHASH != EMPTYCODEHASH的交易必须被拒绝为无效,其中EMPTYCODEHASH = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470

同时明确了三条强制要求:

  1. 无效交易必须被客户端拒绝,不得被包含进区块
  2. 包含此类交易的区块必须被视为无效区块
  3. EMPTYCODEHASH是空代码的 Keccak-256 哈希(即keccak256("")的固定常量值),判断"是否部署了代码"的唯一依据就是账户的 codeHash 是否等于该常量。

判断时机

检查发生在状态转换(state transition)阶段:在确认发送者 nonce 正确之后,必须添加"发送者是 EOA"的检查。这里的"发送者"指从交易签名中恢复出的地址(tx.sender/msg.From())。

原理分析(Rationale)

EIP-3607 将其自身定位为对既有协议行为的澄清(clarification)而非共识规则升级,理由如下:

  • 协议从一开始就隐含假设:合约账户的行为受限于合约代码——账户资金不应突然能被某个私钥支配;
  • 过去只是隐式假设 160 位地址长度足以提供碰撞抵抗,因此该情形"永远不会发生";
  • 现在这个假设被打破,需要显式规定此前未定义情形下的行为。

未覆盖的其它攻击向量

作者明确承认,EIP-3607 并不能排除所有碰撞攻击,只堵住了最严重的一类。文档列出的其余向量包括:

  1. 部署前诱导转账:攻击者说服用户在合约部署前向某地址转账。某些应用(如状态通道)本身要求这种行为,难以杜绝;
  2. 链重组(reorg):合约部署后发生链重组,若重组移除了合约部署交易,资金仍可凭私钥取走;
  3. 自毁(selfdestruct)后取回代币:合约自毁的意图通常是让其中的 ERC20 等代币被永久销毁,但攻击者可凭该地址的密钥重新访问这些代币。

这些场景对攻击者而言利用难度大得多、收益预期低得多,因此不太可能具备经济可行性——这正是本 EIP 只处理"最严重向量"的取舍逻辑。

向后兼容性(Backwards Compatibility)

文档对兼容性给出了三点明确说明:

  • 主网风险极低:这种攻击若已在主网发生过,社区极大概率已经知晓。而把合约"同时当作 EOA 使用"的正当理由也不存在——若真需要这种能力,直接在合约里加几个方法即可,不必斥资数十亿美元制造哈希碰撞;
  • 私有网络需自查:私有网络可能已在创世(genesis)时部署了"同时充当 EOA"的合约,升级后必须检查此规则是否影响其既有工作流;
  • RPC 例外可选项:客户端可以选择对eth_calleth_estimateGas这类 RPC 调用禁用此规则,因为部分多签(Multi-Sig)合约正是利用这些调用"以多签合约自身地址作为发起方"来构造交易的。

测试用例(Test Cases)

EIP-3607 给出了一个可直接落地的测试向量:给定如下创世(genesis)账户分配——

Address: 0x71562b71999873DB5b286dF957af199Ec94617F7 Balance: 1000000000000000000 // 1 ether Nonce: 0 Code: 0xB0B0FACE

与该地址对应私钥(b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291)签发的每一笔交易都必须被拒绝,且不得被打包进区块。注意该测试地址故意带有非空代码0xB0B0FACE,其 codeHash 必然不等于EMPTYCODEHASH,因此完美命中新规则。

参考实现(Reference Implementation)

文档给出了伪代码级的状态转换检查,并附带了 go-ethereum 的实际 diff(仓库中位于 assets/eip-3607/geth.diff)。

伪代码

// Make sure the sender is an EOA Set ch to the CodeHash of the sender account if ch is not equal to EmptyCodeHash then return ErrSenderNoEOA end if

go-ethereum 中的真实落地

从源码结构看,该检查被插入到core/state_transition.gopreCheck()函数中,紧随"发送者 nonce 正确"校验之后:

// Make sure the sender is an EOA if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash { return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, st.msg.From().Hex(), codeHash) } // Make sure that transaction feeCap is greater than the baseFee (post london) if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { ... }

这段 diff 有三个值得注意的实现细节:

  • 检查位置:位于preCheck()(交易预检阶段),在 nonce 校验之后、London baseFee 检查之前,保证在进入 EVM 执行前就拦截非法交易;
  • 错误类型:复用ErrSenderNoEOA错误并携带地址与 codeHash 上下文,便于客户端向用户呈现可读的错误信息;
  • 查询方式:通过st.state.GetCodeHash(...)读取发送者账户的 codeHash 与emptyCodeHash常量比较,性能开销极小,且不依赖任何外部状态变更。

安全考量(Security Considerations)

EIP-3607 是一次严格的安全升级:它仅仅把"此前合法"的交易变为"非法"。

  • 这类交易不存在任何正当用途,因此不应带来安全副作用;
  • 由于新有效性规则是旧规则的严格超集(旧规则下合法的新规则下也合法,反之不然),本 EIP可以作为软分叉(soft fork)实施,无需硬分叉级别的共识协调成本。

后续演进:与 EIP-7702、EIP-3074、EIP-3541 的衔接

EIP-3607 并非孤立存在,仓库中的后续提案延续了它的设计原则:

EIP-7702:委托代码下的定向放宽

EIP-7702(Set EOA account code for one transaction)在其requires列表中显式引用了 3607(见 eip-7702.md 第 11 行),并修改了本 EIP 的限制:

修改 EIP-3607 施加的限制,允许"代码是合法委托指示符(delegation indicator)即0xef0100 || address"的 EOA 发起交易;其它任何代码值的账户不得发起交易。

也就是说,EIP-7702 在保留 EIP-3607 拒绝逻辑的前提下,为0xef0100 || address格式的委托代码开了一个精确豁免通道,使 EOA 能够"伪装"为合约参与 ERC-4337 等流程(详见 eip-7702.md 的 Transaction origination 小节)。这一豁免依赖 EIP-3541 先期拒绝0xEF起始字节代码的部署,从而保证0xef0100前缀不会被历史合约占用——三者构成了环环相扣的防御链。

EIP-3074:原则的延续

EIP-3074(AUTH and AUTHCALL opcodes)同样在文档中引用 EIP-3607,并阐明其立场:虽然 3607 聚焦于交易发起(transaction origination),但其作者认为意图是清晰的——既拥有代码又拥有已知私钥的账户,不应被允许代表该账户发起任意调用,因此 EIP-3074 也保持了这一性质(详见 eip-3074.md 第 319 行附近的说明)。

总结

EIP-3607 用一条极简规则——tx.sender的 codeHash 必须等于EMPTYCODEHASH——封死了 160 位地址空间下最具经济可行性的碰撞攻击路径。它以软分叉形式落地,兼容性风险集中在私有网络的创世部署与多签合约的 RPC 场景;而其"禁止带码账户发起交易"的原则,后续被 EIP-7702 以0xef0100委托指示符形式精确豁免、被 EIP-3074 作为设计准则延续,成为现代 EOA 能力演进中不可绕过的共识基石。文章版权声明遵循 CC0(见 LICENSE.md),可自由引用与传播。

【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询