测试用例全解)
BSC 客户端源码解析用 evm t8n 计算 Ethash 难度——GrayGlacierEIP-5133测试用例全解【免费下载链接】bscA BNB Smart Chain client based on the go-ethereum fork项目地址: https://gitcode.com/GitHub_Trending/bs/bsc本篇技术指南围绕 bsc 仓库中 cmd/evm/testdata/19/readme.md 这一官方测试用例展开讲解如何利用evm t8ntransition子命令在调用方不提供难度值时自动按 Ethash 规则计算区块难度并重点演示GrayGlacierEIP-5133分叉下的计算行为。读完本文你将掌握 t8n 输入/输出 JSON 的完整字段语义、难度计算底层的调用链以及如何通过切换分叉名复现 London / ArrowGlacier / GrayGlacier 三种难度结果的差异。一、测试用例概览目录结构与文件清单用例位于 cmd/evm/testdata/19/共包含 7 个文件职责如下文件作用readme.md用例说明文档给出完整运行命令与输出样例env.json环境区块上下文输入描述待构建区块与父区块信息alloc.json初始账户状态输入txs.json交易集合输入本用例为空数组exp_london.jsonLondon 分叉下的期望结果exp_arrowglacier.jsonArrowGlacierEIP-4345分叉下的期望结果exp_grayglacier.jsonGrayGlacierEIP-5133分叉下的期望结果该用例的核心验证点是在--input.env中不提供currentDifficulty时evm t8n会基于父区块的parentDifficulty与父区块时间戳按指定分叉的 Ethash 难度算法自动推导当前区块难度。这正是 readme.md 开头所述 This test shows how theevm t8ncan be used to calculate the (ethash) difficulty, if none is provided by the caller 的含义。二、输入文件逐字段解读2.1 env.json区块环境上下文env.json 的完整内容如下{ currentCoinbase: 0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b, currentGasLimit: 0x750a163df65e8a, currentBaseFee: 0x500, currentNumber: 13000000, currentTimestamp: 100015, parentTimestamp: 99999, parentDifficulty: 0x2000000000000 }各字段语义对应 t8n 工具内部stEnv结构见 cmd/evm/internal/t8ntool/execution.go字段值说明currentCoinbase0xc94f...f0b当前区块的矿工/出块地址区块奖励接收方currentGasLimit0x750a163df65e8a当前区块 Gas 上限currentBaseFee0x500当前区块基础费1280 weiLondon 之后必填currentNumber13000000当前区块高度十进制表示currentTimestamp100015当前区块时间戳parentTimestamp99999父区块时间戳用于难度的时间衰减因子计算parentDifficulty0x2000000000000父区块难度这是难度推导的关键输入注意两点没有currentDifficulty字段——这正是本用例的主题让工具自行计算没有currentRandomprevRandao——因为本用例仍处于 PoWEthash阶段尚未到 Merge。2.2 alloc.json初始账户状态alloc.json 预置了两个账户{ a94f5374fce5edbc8e2a8697c15331677e6ebf0b: { balance: 0x5ffd4878be161d74, code: 0x, nonce: 0xac, storage: {} }, 0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192: { balance: 0xfeedbead, nonce: 0x00 } }第一个账户带有空code与空storage第二个账户仅声明balance与nonce。由于本用例不执行任何交易这两个账户最终会以原样状态参与状态根stateRoot的计算——这也解释了为什么三个分叉期望文件中的stateRoot完全相同见第四节。2.3 txs.json空交易集合txs.json 内容仅为[]表示区块内没有交易。因此输出中gasUsed 0x0、receipts []txRoot与receiptsRoot都是空树的 RLP 根0x56e81f...。三、核心命令如何用 evm t8n 计算难度在 cmd/evm/testdata/19/readme.md 中给出的完整命令在cmd/evm目录下运行已构建的evm二进制./evm t8n --input.alloc./testdata/19/alloc.json --input.txs./testdata/19/txs.json --input.env./testdata/19/env.json --output.resultstdout --state.forkGrayGlacier命令分解如下参数含义t8ntransition子命令的别名在 cmd/evm/main.go 中注册用途为 Executes a full state transition执行完整状态转换--input.alloc初始状态 JSON 路径--input.txs交易列表 JSON 路径--input.env区块环境 JSON 路径--output.resultstdout结果输出到标准输出而非写入文件--state.forkGrayGlacier指定状态转换所依据的分叉规则即--state.fork标志对应的ForknameFlag官方文档给出的完整运行日志与输出为[userwork evm]$ ./evm t8n --input.alloc./testdata/19/alloc.json --input.txs./testdata/19/txs.json --input.env./testdata/19/env.json --output.resultstdout --state.forkGrayGlacier INFO [03-09|10:45:26.777] Trie dumping started root6f0588..7f4bdc INFO [03-09|10:45:26.777] Trie dumping complete accounts2 elapsed176.471µs INFO [03-09|10:45:26.777] Wrote file filealloc.json { result: { stateRoot: 0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc, txRoot: 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421, receiptsRoot: 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421, logsHash: 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347, logsBloom: 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, receipts: [], currentDifficulty: 0x2000000004000, gasUsed: 0x0, currentBaseFee: 0x500 } }日志中的三行分别对应Trie 状态树转储开始/完成accounts2与 alloc.json 中的两个账户一致以及将处理后的状态写出为alloc.json快照文件默认输出目录为当前目录可通过--output.basedir调整。四、运行结果解读currentDifficulty 从何而来输出result对象中的关键字段对应ExecutionResult结构见 cmd/evm/internal/t8ntool/execution.go 与 gen_execresult.go字段值解读stateRoot0x6f0588...7f4bdc最终状态根与 alloc.json 两个账户的初始状态一致无交易改动状态txRoot0x56e81f...空交易树根receiptsRoot0x56e81f...空收据树根无交易则无收据logsHash/logsBloom空值形态无日志receipts[]无收据currentDifficulty0x2000000004000本用例的核心结果工具自行计算的当前区块难度gasUsed0x0无交易Gas 用量为零currentBaseFee0x500与输入的currentBaseFee一致1280 wei对比父区块难度parentDifficulty 0x2000000000000与计算结果currentDifficulty 0x2000000004000二者仅相差0x4000即 16384。这个微小的增量正是 Ethash 难度炸弹difficulty bomb在 GrayGlacier 规则下的残留贡献——由于 EIP-5133 将难度炸弹再次推迟约 100 天在高度 13,000,000 处炸弹指数已被压低对难度的抬升几乎可以忽略。五、跨分叉对比London / ArrowGlacier / GrayGlacier本用例目录下预置了三个分叉的期望结果文件输入完全一致同一 env.json、alloc.json、空的 txs.json仅--state.fork不同期望文件分叉currentDifficulty相对父难度的增量exp_london.jsonLondon0x2000000200000较大exp_arrowglacier.jsonArrowGlacier0x2000080000000最大exp_grayglacier.jsonGrayGlacier0x2000000004000最小仅0x4000三个文件中stateRoot、txRoot、receiptsRoot、logsHash、logsBloom、receipts、gasUsed、currentBaseFee完全一致唯一差异是currentDifficulty。这说明状态转换与交易执行结果与分叉选择无关毕竟没有交易而难度计算高度依赖分叉规则。另外期望文件中的result比 readme 日志输出多了一个requests: null字段这是更新版本 t8n 工具的扩展字段。难度差异的根源在于各分叉对难度炸弹的**延迟量bomb delay**不同。Ethash 难度公式在父难度基础上叠加时间调整项与炸弹项2^((fakeNumber / 100000) - 2)其中fakeNumber number - bombDelaybombDelay 越大同一高度下炸弹贡献越小。对应关系为LondonEIP-3554在高度 12,965,000 激活延迟约 9,700,000 个区块ArrowGlacierEIP-4345在高度 13,773,000 激活额外延迟约 10,700,000 个区块GrayGlacierEIP-5133即 readme 中标注的 Eip 5133在高度 15,050,000 激活在 ArrowGlacier 基础上再延迟 700,000 个区块合计约 11,400,000推迟约 100 天。主网上的激活高度可直接在 params/config.go 中查到LondonBlock: big.NewInt(12_965_000), ArrowGlacierBlock: big.NewInt(13_773_000), GrayGlacierBlock: big.NewInt(15_050_000),由于本用例的currentNumber 13000000恰好介于 London 与 ArrowGlacier 激活高度之间处于难度炸弹重新抬头的时间窗口因此三个分叉在同一高度下的难度差异被清晰放大——这正是该用例被设计出来的目的直观展示不同炸弹延迟常数对难度数值的影响。六、底层原理calcDifficulty 的实现细节t8n 的难度计算实现在 cmd/evm/internal/t8ntool/execution.go 的calcDifficulty函数中其代码注释明确写着Note: this method only works for ethash engine.该方法仅适用于 Ethash 引擎。核心逻辑如下func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime uint64, parentDifficulty *big.Int, parentUncleHash common.Hash) *big.Int { uncleHash : parentUncleHash if uncleHash (common.Hash{}) { uncleHash types.EmptyUncleHash } parent : types.Header{ ParentHash: common.Hash{}, UncleHash: uncleHash, Difficulty: parentDifficulty, Number: new(big.Int).SetUint64(number - 1), Time: parentTime, } // ...调用 ethash.CalcDifficulty(config, currentTime, parent) 完成实际计算 }几个值得注意的实现要点合成父区块头工具并不需要真实父区块而是根据输入的parentDifficulty、parentTimestamp以及currentNumber - 1合成一个最小化的父区块头再交给 Ethash 引擎计算。未提供 unclehash 的兜底本用例的 env.json 中没有提供父区块的parentUncleHash此时函数将其替换为types.EmptyUncleHash。readme 中特别强调 no provided unclehash for the parent block指的就是这一分支。叔叔哈希会影响难度公式中的调整项有叔叔时难度调整因子不同因此这里显式回退到无叔叔的默认值保证结果可复现。分叉规则选择最终调用 Ethash 引擎的CalcDifficulty时会依据--state.fork构建出的ChainConfig判断当前区块父高度 1处于哪个分叉从而选择对应的难度函数London / ArrowGlacier / GrayGlacier并套用各自不同的炸弹延迟常数。这也解释了为什么调用方不提供currentDifficulty也能算出难度Ethash 难度本质上是parentDifficulty、时间戳差、区块高度与分叉常数的纯函数t8n 只需补齐这些输入即可。七、复现与验证把输出和期望文件对齐要验证本用例可按以下步骤操作在仓库根目录构建evm工具go build -o evm ./cmd/evm进入 cmd/evm 目录执行 readme 中的命令./evm t8n --input.alloc./testdata/19/alloc.json --input.txs./testdata/19/txs.json --input.env./testdata/19/env.json --output.resultstdout --state.forkGrayGlacier将输出的result.currentDifficulty与 exp_grayglacier.json 中的0x2000000004000对比并核对stateRoot、txRoot、receiptsRoot等字段完全一致。做交叉验证时只需替换--state.fork的值# London 分叉 ./evm t8n --input.alloc./testdata/19/alloc.json --input.txs./testdata/19/txs.json --input.env./testdata/19/env.json --output.resultstdout --state.forkLondon # ArrowGlacier 分叉 ./evm t8n --input.alloc./testdata/19/alloc.json --input.txs./testdata/19/txs.json --input.env./testdata/19/env.json --output.resultstdout --state.forkArrowGlacier分别得到0x2000000200000与0x2000080000000即与 exp_london.json、exp_arrowglacier.json 一致。三个分叉、三种难度、同一组状态根一测便知。八、实战扩展t8n 的其他可用参数除了本用例用到的参数外t8ntransition子命令在 cmd/evm/main.go 中注册了更完整的标志集可用于更复杂的测试场景标志说明--output.basedir输出文件的基础目录默认当前目录--output.alloc将处理后的状态alloc写到指定文件默认写alloc.json--output.body输出区块体RLP 十六进制--state.chainid指定链 ID影响交易签名校验与 EIP-155 行为--state.reward指定区块奖励金额可设为0关闭 PoW 奖励--trace/--trace.tracer/--trace.tracerconfig启用交易级 trace并选择内置 tracer如callTracer、4byteTracer或自定义 JS tracer--trace.nostack/--trace.nomemory/--trace.noreturndata控制 trace 输出内容粒度借助这些参数可以把 testdata/19 的骨架改造成自己的难度/状态转换验证脚本例如在 env.json 中调整parentDifficulty、parentTimestamp与currentTimestamp的差值观察时间调整因子对难度的作用或在 txs.json 中填入真实交易验证包含执行负载时的完整状态转换流程。九、小结通过 cmd/evm/testdata/19/readme.md 这个精炼的测试用例我们可以完整掌握evm t8n的输入输出 JSON 契约env / alloc / txs → result当调用方省略currentDifficulty时工具如何基于parentDifficulty与父时间戳自动推导 Ethash 难度London、ArrowGlacier、GrayGlacier 三个分叉在相同高度下难度数值的差异及其背后的难度炸弹延迟常数EIP-3554 / EIP-4345 / EIP-5133底层 calcDifficulty 的实现细节合成父区块头、EmptyUncleHash兜底、仅适用于 Ethash 引擎的边界。对于从事 BSC 主网同步、PoW 阶段区块回溯验证或自定义状态转换测试的开发者而言这个用例既是 t8n 工具链的入门样板也是一份可复现、可扩展的难度计算参考基准。【免费下载链接】bscA BNB Smart Chain client based on the go-ethereum fork项目地址: https://gitcode.com/GitHub_Trending/bs/bsc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考