以太坊虚拟机(EVM)每秒要拒绝多少笔 malformed transaction?没人统计过,但验证层是第一道闸门。REVM 的 handler 把这件事拆成了两步:先看环境对不对,再算 gas 够不够。本文顺着代码走一遍,看看一笔交易是怎么被"验明正身"的。
环境检查:版本特性有没有配齐
validate_env 是入口。它从上下文里捞出当前 spec(即 EVM 版本),然后做两件事。
第一,MERGE 之后必须有 prevrandao。 这是 PoS 切换后的随机信标字段,没设置直接报错 PrevrandaoNotSet。
第二,CANCUN 之后必须有 blob 相关配置。 4844 引入的 blob 交易需要 excess_blob_gas_and_price,缺了同样打回。
代码逻辑很直白:if spec.is_enabled_in(SpecId::MERGE) && context.block().prevrandao().is_none(),就 return Err。没有魔法,就是版本特性对齐检查。
完事之后调用 validate_tx_env,进入交易级别的验证。
交易验证:还没执行就要算清 gas 账
validate_initial_tx_gas 是第二步。它的任务是在执行前算出两笔数:InitialGas 和 FloorGas。
InitialGas 是交易实际能用的 gas——扣掉固有成本(21000 基础 + 数据费用 + 访问列表费用等)。FloorGas 是 4844 引入的"地板价",防止 blob 交易被压价。
如果用户给的 gas_limit 连 InitialGas 都 cover 不住,直接 InvalidTransaction::CallGasCostMoreThanGasLimit。还没进 EVM 就省了算力。
代码结构:为什么拆成两步
validate_env 和 validate_initial_tx_gas 分离,是为了错误隔离。环境错是区块配置问题,gas 错是用户输入问题,报错信息能精确定位。
再看 validate_env 内部的委托链:handler.rs → validation.rs → validate_tx_env。每层只做一件事,方便测试和 mock。context 作为泛型参数 CTX: ContextTr 贯穿始终,解耦了具体存储实现。
这种设计让 REVM 可以嵌入不同场景——全节点、轻客户端、甚至游戏引擎里的嵌入式 EVM——只换 context 实现,验证逻辑不动。
一个细节:#[inline] 标记在 Rust 里提示编译器内联展开。验证是高频路径,减少函数调用开销值得。
验证层没有优化空间了吗?CANCUN 之后 blob 验证的逻辑还在膨胀,下次硬分叉会不会再加一条 if 分支?
热门跟贴