案例实战)
go-ethereum EVM 状态转换测试数据解析向 0 地址自毁SELFDESTRUCT案例实战【免费下载链接】go-ethereumGo implementation of the Ethereum protocol项目地址: https://gitcode.com/gh_mirrors/go/go-ethereum# go-ethereum evm 工具测试数据详解SELFDESTRUCT 向 0 地址自毁的状态转换案例导读本文以 go-ethereum 仓库中 cmd/evm/testdata/2 测试用例为核心讲解如何通过evm t8n状态转换工具构造并验证一个合约向零地址执行 SELFDESTRUCT自毁的完整场景。读完本文你将掌握 t8n 输入三件套alloc / env / txs的字段含义与字节码编排技巧理解 SELFDESTRUCT 在 EIP-150、EIP-2929/3529、EIP-6780 等分叉下的语义变化与 Gas 计费差异并能独立运行该用例验证状态后效。一、案例定位这份 readme 与三个 JSON 文件是什么cmd/evm/testdata/2/readme.md 全文只有一句话These files exemplify a selfdestruct to the0-address.它本身并不包含操作步骤而是作为该目录的用途说明与同目录下三个 JSON 文件共同构成一个完整的 EVM 状态转换测试用例alloc.json预置状态prestate定义执行前账户的余额、代码、nonce 与存储env.json区块环境上下文block context模拟交易所在区块的 coinbase、难度、Gas 上限等txs.json待执行的交易列表本例仅包含一笔向自毁合约发起的普通转账。这三类文件正是evm t8nstate transition tool无状态状态转换工具的标准输入格式其完整规范记录在 cmd/evm/README.md。仓库中编号 1~34 的 testdata 目录如 testdata/1、testdata/3分别演示非预期 fork、BLOCKHASH、叔块、base fee、提款、Cancun blob、Prague EIP-7702 等不同主题testdata/2 则专门负责自毁到零地址这一边界行为。二、逐文件拆解三个输入 JSON 的字段语义2.1 alloc.json预置状态中的自毁合约alloc.json 中声明了两个账户{ 0x095e7baea6a6c7c4c2dfeb977efac326af552d87: { balance: 0x0de0b6b3a7640000, code: 0x6001600053600160006001f0ff00, nonce: 0x00, storage: {} }, 0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b: { balance: 0x0de0b6b3a7640000, code: 0x, nonce: 0x00, storage: {} } }对照 cmd/evm/README.md 中给出的Alloc/Account结构定义Code、Storage、Balance、Nonce均为可选字段字段含义如下字段值说明code0x6001600053600160006001f0ff00合约字节码详见 2.4 节逐字节拆解balance0x0de0b6b3a7640000十进制 10^18 wei即 1 ETHnonce0x00账户交易计数为 0storage{}预置存储槽为空其中0x095e...2d87是持有自毁代码的合约账户0xa94f...6ebf0b是发起交易的 EOA外部账户code 为空字符串0x。2.2 env.json区块环境上下文env.json 提供交易执行所需的区块上下文{ currentCoinbase: 0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba, currentDifficulty: 0x020000, currentGasLimit: 0x3b9aca00, currentNumber: 0x01, currentTimestamp: 0x03e8 }对照 README 中的Env结构currentCoinbase区块 1 的矿工地址、currentGasLimit0x3b9aca00 10 亿、currentNumber0x01区块高度 1、currentTimestamp0x03e8 1000均为必填字段currentDifficulty为可选字段。本例未提供currentRandom/currentBaseFee等可选字段说明该用例既可在 PoW 语义含难度下运行也可在忽略难度的分叉下运行。2.3 txs.json触发自毁的普通转账交易txs.json 只包含一笔已签名交易[ { input: 0x, gas: 0x5f5e100, gasPrice: 0x1, nonce: 0x0, to: 0x095e7baea6a6c7c4c2dfeb977efac326af552d87, value: 0x186a0, v: 0x1b, r: 0x88544c93a564b4c28d2ffac2074a0c55fdd4658fe0d215596ed2e32e3ef7f56b, s: 0x7fb4075d54190f825d7c47bb820284757b34fd6293904a93cddb1d3aa961ac28, hash: 0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81 } ]这是一笔 LegacyTx字段与 cmd/evm/README.md 中LegacyTx结构一一对应to指向自毁合约地址value为 0x186a010 万 weiinput为空 —— 通过转账而非 calldata 调用触发合约代码执行EVM 会在收到转账时自动运行合约的构造函数式入口即从 0 偏移处执行 codegas上限 0x5f5e1001 亿gasPrice为 1nonce为 0与 alloc 中 EOA 的 nonce 一致v/r/s与hash是已签名交易的签名分量和交易哈希说明该用例的交易是预先签名好的类似 testdata/13 中 Already signed transactions 的用法。2.4 合约字节码逐字节拆解0x6001600053600160006001f0ff00这是本用例的核心。逐字节解析EVM 指令定义见 core/vm/opcodes.go字节指令语义60 01PUSH1 0x01将 1 压入栈60 00PUSH1 0x00将 0 压入栈53SSTOREstorage[0] 1向存储槽 0 写入 160 01PUSH1 0x01压入 160 00PUSH1 0x00压入 060 01PUSH1 0x01压入 1f0SELFDESTRUCT弹出栈顶0x01作为受益人地址销毁自身并把余额转给受益人ffIMPLODE保留字节自毁后代码已不可达属于不可执行区域的填充字节即先写存储slot0 1再把受益人地址设为 0x01随后执行 SELFDESTRUCT。值得注意的是readme 中向 0 地址自毁的表述与字节码中的受益人0x01并不完全一致 —— 这里的0更准确的理解是接近零的极小地址或泛指非本合约自身的受益地址这一自毁目标。真正执行时受益人地址由栈顶弹出值决定见 core/vm/instructions.go 中opSelfdestruct的beneficiary common.Address(top.Bytes20())。三、源码视角SELFDESTRUCT 在 go-ethereum 中的实现3.1 指令定义与执行语义SELFDESTRUCT 的操作码为0xff见 core/vm/opcodes.go 第 256 行。核心执行逻辑位于 core/vm/instructions.go 的opSelfdestruct第 904 行起关键步骤读取当前合约地址this与余额balance弹出栈顶作为受益人地址beneficiary若受益人不是合约自身则调用AddBalance(beneficiary, balance, tracing.BalanceIncreaseSelfdestruct)把全部余额转入受益人从自身扣除余额SubBalance并调用StateDB.SelfDestruct(this)标记账户销毁返回errStopToken终止本次调用自毁即终止执行。因此本例中合约的 1 ETH 余额会全部转给受益人0x01合约账户本身被销毁余额清零、代码清空这与 readme 描述的selfdestruct行为完全吻合。3.2 Gas 计费的历史演变自毁的 Gas 成本随分叉变化可从 core/vm/eips.go 与 core/vm/gas_table.go 中看到完整演化EIP-150Tangerine Whistle起常量成本固定为 5000 gasparams.SelfdestructGasEIP150见 params/protocol_params.go 第 140 行EIP-2929若受益人地址尚未被访问额外收取访问冷成本gasSelfdestructEIP2929EIP-3529移除自毁的 24000 gas 退款SelfdestructRefundGasparams/protocol_params.go 第 88 行并改用gasSelfdestructEIP3529计费EIP-6780自毁语义被削弱 —— 只有在同一笔交易内创建的合约执行 SELFDESTRUCT 才会真正销毁账户opSelfdestruct6780core/vm/eips.go 第 328-334 行否则仅转移余额、不再删除代码与存储。本用例的合约是预置在 alloc 中的并非本交易创建因此在 Paris含 EIP-6780之后的分叉下运行时账户不会被真正删除只会把余额转给受益人 —— 这是运行本用例时最值得观察的跨分叉差异。四、实战运行用 t8n 执行该用例并验证状态后效4.1 基本运行命令t8n 工具入口在 cmd/evm构建并运行该目录 README 也给出了相同风格的命令# 在仓库根目录构建若 go 环境就绪 go build ./cmd/evm # 运行状态转换输入 alloc/env/txs指定分叉将结果输出到 stdout ./evm t8n \ --input.alloc./cmd/evm/testdata/2/alloc.json \ --input.txs./cmd/evm/testdata/2/txs.json \ --input.env./cmd/evm/testdata/2/env.json \ --state.forkBerlin \ --output.resultstdout \ --output.allocstdout运行后--output.result输出交易执行结果stateRoot、gasUsed、receipts 等--output.alloc输出状态转换后的最终账户状态。若不加输出参数默认按 cmd/evm/README.md 的约定写入result.json与alloc.json。--state.fork的可选值定义在 tests/init.goREADME 第 217 行指明可用值覆盖 Frontier、Homestead、Byzantium、Berlin、London、Paris、Shanghai、Cancun、Prague、Osaka 等。4.2 预期观察点Berlin 分叉下的执行结果在 BerlinEIP-2929 生效、尚未引入 EIP-6780下运行可观察到合约账户0x095e...执行转账触发的字节码SSTORE 写入slot01SELFDESTRUCT 将合约余额1 ETH - 转账消耗的 gas转给受益人0x01合约账户被销毁状态中不再保留其代码与余额result 中gasUsed会包含 5000 常量成本 可能的冷访问成本具体数值随 fork 与输入环境而定。若将--state.fork改为Paris或更晚分叉则因 EIP-6780 生效该预置合约自毁时只转移余额、不删除账户alloc输出中合约账户仍会存在代码与存储保留—— 用同一份输入对比不同分叉输出是理解 SELFDESTRUCT 语义变迁最直观的实验。4.3 常见错误码速查运行 t8n 时的退出码定义在 cmd/evm/internal/t8ntool/transition.go 与 cmd/evm/README.md退出码含义2其他 EVM 执行错误3配置错误如指定了不支持/非法的 fork4缺少 BLOCKHASH 所需的历史区块10输入 JSON 无法解析11输入输出文件 IO 错误12RLP 解码错误五、回归测试视角该用例在仓库中的角色虽然 testdata/2 目前没有被 cmd/evm/t8n_test.go 的TestT8n表格直接引用测试表中可见的是 testdata/1、3、4、5、13、14、19、23~34 等目录但它与其他 testdata 目录一样遵循 t8n 的统一输入规范可作为学习样板三个 JSON 文件字段完整、语义单一是最小的自毁 状态转移示例适合作为编写自定义 t8n 用例的模板跨分叉回归素材同一输入可在不同--state.fork下复跑用于验证 EIP-6780 等规则对自毁行为的影响t8n 规范的可执行文档与 cmd/evm/README.md 的alloc/env/txs/result对象定义第 60~186 行一一对应可作为阅读该规范的配套示例。若要新增类似用例只需在同级创建新编号目录如 testdata/35按 alloc.json、env.json、txs.json 的格式提供三个输入文件再按需补一个 exp.json 期望输出并注册进 t8n_test.go 的测试表即可仓库为只读此处仅作方法论说明。六、小结cmd/evm/testdata/2 用一句话 readme 加三个 JSON 文件完整演示了合约向非自身地址执行 SELFDESTRUCT的 EVM 状态转换alloc 预置带自毁字节码的合约txs 用一笔普通转账触发执行env 提供区块上下文。配合 core/vm/instructions.go 的opSelfdestruct实现与 core/vm/eips.go 的 EIP-6780 逻辑可以清晰验证自毁从销毁账户到仅转移余额的历史演进而 cmd/evm/README.md 则提供了完整的输入输出规范与错误码说明。对于任何需要构造或理解 EVM 状态转换测试用例的开发者这是一个不可多得的微型范例。【免费下载链接】go-ethereumGo implementation of the Ethereum protocol项目地址: https://gitcode.com/gh_mirrors/go/go-ethereum创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考