Foundry Anvil 新增 `debug_executionWitness` 端点:基于父状态构建的执行见证(Execution Witness)详解
Foundry Anvil 新增debug_executionWitness端点基于父状态构建的执行见证Execution Witness详解【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundryAnvilFoundry 内置的本地以太坊开发节点在anvil: minor变更中新增了对debug_executionWitnessRPC 端点的支持该端点返回请求区块的完整父状态所构建的 best-effort 执行见证格式与 reth 的debug_executionWitness一致且在分叉fork模式下不可用。阅读本文后你将理解执行见证execution witness的概念与用途、Anvil 实现该端点的方式与全部限制条件并掌握如何通过cast或 HTTP RPC 调用该端点、解读返回结构以及为无状态stateless重放或区块验证工具链提供输入数据。1. 变更背景一条 changelog fragment 背后的内容本主题源自仓库 .changelog/anvil-debug-execution-witness.md其完整内容如下--- anvil: minor --- Added support for the debug_executionWitness RPC endpoint, returning a best-effort witness built from the full parent state of the requested block. Not supported while forking.在 Foundry 的 changelog 体系中详见 .changelog/README.md.changelog/目录下的每个文件都是一个变更片段fragmentfrontmatter 将本次变更映射到某个工作区包此处为anvil及版本级别minor即次要版本变更正文则是进入发版说明的条目。该片段声明的核心能力有三点新增debug_executionWitnessRPC 端点返回的是best-effort尽力而为的见证构建自被请求区块的完整父状态full parent state分叉模式下不支持。2. 什么是执行见证Execution Witness执行见证是面向无状态以太坊stateless Ethereum与区块验证场景的概念。在无状态客户端模型下验证节点不再维护完整的世界状态而是随每个区块附带一份见证用于证明该区块执行过程中读取/写入了哪些状态使验证方无需本地全量状态即可重放执行并校验结果。见证通常包含以下成分状态 Trie 节点账户状态与存储槽在 Merkle Patricia Trie 中的节点 RLP 编码合约代码执行中涉及的所有合约字节码Preimage原像地址与存储槽对应的 key 原像用于将 hash 映射回明文 key祖先区块头BLOCKHASH指令可访问的历史区块头。reth 提供了同名的debug_executionWitness端点Anvil 的新实现明确以与 reth 格式一致为目标便于使用同一套下游工具消费两种节点的输出。3. Anvil 的实现原理全量父状态见证3.1 为什么是全量父状态而非最小见证Anvil 在日常运行中不记录某个区块执行究竟触达了哪些状态这一点在源码注释中被明确点出。因此它无法像 reth 那样给出仅包含本次执行访问状态的最小见证而是退而求其次把整个父状态编码进见证。具体来说见证中包含见 crates/anvil/src/eth/backend/mem/mod.rs 中debug_execution_witness的实现父状态 Trie 的全部节点包含所有账户及其所有存储 Trie的 RLP 编码全部合约代码所有账户地址与存储槽的 preimage。由于全量父状态是任何最小见证的严格超集因此针对该区块的无状态重放仍然可以成功——验证方需要的所有状态都在其中。代价是见证体积随总状态量增长而不是随区块实际访问的状态量增长。在测试链状态有限上这完全可接受但在大状态链上会明显膨胀。3.2 核心调用链从 RPC 入口到底层实现调用链为RPC 层crates/anvil/core/src/eth/mod.rs 中定义了DebugExecutionWitness(BlockNumber)并以#[serde(rename debug_executionWitness, with sequence)]绑定到 JSON-RPC 方法名参数按序列sequence方式解析支持0x1、latest等区块标识并配有test_serde_debug_execution_witness序列化测试crates/anvil/core/src/eth/mod.rs。API 层crates/anvil/src/eth/api.rs 中的debug_execution_witness(block: BlockNumber)处理器通过node_info!(debug_executionWitness)记录调用日志后转交 backend。Backend 层crates/anvil/src/eth/backend/mem/mod.rs 中的Backend::debug_execution_witness执行实际的见证构建其中调用state_trie_witness定义于 crates/anvil/src/eth/backend/mem/state.rs计算状态根与 Trie 节点集合。3.3 实现细节在 backend 实现crates/anvil/src/eth/backend/mem/mod.rs中构建流程为将区块号解析为数字超出当前最优区块高度时返回BlockOutOfRange错误通过number.checked_sub(1)求父区块号——创世块number 0没有父状态直接返回 genesis block has no parent state to build a witness from 错误在 256 个区块的BLOCKHASH窗口内收集本地已知的祖先区块头BLOCKHASH_HISTORY因此返回的headers可能不足 256 个在父区块对应的数据库快照上通过state.maybe_full_db()判断是否处于 fork 模式分叉时本地只有远端访问过的账户且本地计算的状态根与远端链不一致因此直接返回 debug_executionWitness is not supported while forking 错误见 crates/anvil/src/eth/backend/mem/mod.rs非分叉时用state_trie_witness生成状态根与全部 Trie 节点遍历所有账户收集账户地址与每个存储槽32 字节大端作为 preimage key对每个账户去重收集合约代码跳过KECCAK_EMPTY空代码对 keys 排序去重后组装返回ExecutionWitness { state, codes, keys, headers }。3.4 已知限制务必注意综合 changelog 与源码注释该端点在以下场景不可用或输出不完整限制说明分叉模式forking不支持直接报错。因为本地仅知远端访问过的账户且本地计算的状态根与远端链状态根不一致创世块无父状态无法构建见证返回错误--prune-history若父区块状态已从状态历史中被裁剪无法构建见证headers完整性仅包含本地已知的BLOCKHASH窗口256 个内的祖先区块头可能少于 256 个4. 如何在 Anvil 上调用debug_executionWitness4.1 启动节点在本地启动 Anvil非分叉模式使用默认链即可获得完整状态历史anvil需要钱包/合约状态时可先用anvil启动后通过cast send或脚本部署合约、发送交易使状态增长再请求见证。4.2 使用cast rpc调用Foundry 自带的cast rpc可直接发起原始 JSON-RPC 调用例如请求latest区块的见证cast rpc debug_executionWitness latest指定具体区块高度cast rpc debug_executionWitness 0x1对创世块的请求会得到报错genesis block has no parent state to build a witness from。4.3 使用 curl 调用curl -X POST -H Content-Type: application/json \ --data {jsonrpc:2.0,method:debug_executionWitness,params:[latest],id:1} \ http://localhost:85454.4 返回结构解读响应为ExecutionWitness对象包含四个字段state父状态 Trie 全部节点含所有存储 Trie的 RLP 编码数组其中必然包含父区块的状态根节点codes所有非空合约代码的字节码数组keys所有账户地址与存储槽 preimagekey数组已排序去重headersBLOCKHASH窗口内本地已知的祖先区块头 RLP 编码数组。5. 测试用例行为如何被验证集成测试 crates/anvil/tests/it/api.rs 中的can_get_execution_witness完整覆盖了上述行为启动测试节点部署SimpleStorage合约并调用setValue使状态包含合约代码与存储断言创世块请求返回错误无父状态通过 HTTP 客户端请求debug_executionWitness参数为部署/写入后的区块号断言返回的state节点集合中包含父区块状态根对节点做 keccak256 后与parent.header.state_root比对证明见证覆盖完整父状态断言keys中包含发送方、接收方地址的 preimage以及 32 字节的存储槽 key证明地址与槽位原像都被收集。该测试直接验证了全量父状态 preimage 收集的实现语义可作为理解本端点的最小可复现样例。6. 典型应用场景无状态重放stateless re-execution借助见证在未持有全量状态的节点上重放目标区块校验执行结果与状态根区块验证工具链以 reth 兼容格式消费 Anvil 节点输出的见证复用既有验证器/见证解析代码测试与开发在本地测试环境中快速导出某一区块父状态的完整快照语义Trie 节点 代码 preimage用于调试状态证明相关逻辑。7. 小结debug_executionWitness是 Anvil 在anvil: minor变更中新增的调试类 RPC 端点以全量父状态策略构建 best-effort 执行见证与 reth 输出格式兼容保证无状态重放可用。它适用于非分叉、未裁剪状态历史的本地测试链在分叉模式、创世块以及--prune-history场景下不可用或输出不完整。结合本文给出的调用方式cast rpc/curl、返回结构说明与源码实现细节你可以快速将该端点接入自己的状态见证或无状态验证实验。【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考