EIP-3155: EVM 追踪规范
EVM 追踪的 JSON 格式
Authors | Martin Holst Swende (@holiman), Marius van der Wijden (@MariusVanDerWijden) |
---|---|
Created | 2020-12-07 |
Last Call Deadline | 2025-03-01 |
摘要
为状态测试执行期间的 EVM 追踪引入一种新的 JSON 标准。
动机
以太坊虚拟机在以太坊上执行所有智能合约代码。 为了更好地调试智能合约和状态测试,引入了一种通用格式来记录 EVM 的每个执行步骤。 Go-Ethereum、Parity-Ethereum、Nethermind 和 Besu 实现了这种格式。 由于通用格式没有明确定义,因此实现方式略有不同,这使得开发足够的工具变得困难,从而大大降低了追踪的实用性。
此 EIP 有多个目标:
- 将规范移动到更显眼的位置,以鼓励新的客户端实现它
- 严格定义之前版本中未解决的极端情况
- 允许在执行期间引入新字段时更新规范
- 提供示例输出
在所有主要客户端中实现此 EIP,使我们能够创建有意义的差异模糊器,用于模糊主网和所有即将到来的硬分叉的 EVM 实现。 它还有助于在链分裂的情况下快速找到执行中的差异。
此 EIP 将使用户能够创建更好的差异模糊基础设施,以比较所有主要以太坊客户端的 EVM 实现。 这可能有助于发现当前客户端实现中存在的错误。
规范
客户端应该能够执行简单的交易以及代码并返回追踪。在下文中,我们将此客户端称为 CUT(被测客户端),并使用 go-ethereum 的
evm
二进制文件作为代码示例。
数据类型
类型 | 说明 | 示例 |
---|---|---|
Number | 普通 json 数字 | “pc”:0 |
Hex-Number | 十六进制编码的数字 | “gas”:”0x2540be400” |
String | 普通字符串 | “opName”:”PUSH1” |
Hex-String | 十六进制编码的字符串 | |
Array of x | x 编码值的数组 | |
Key-Value | 键值结构,键和值编码为十六进制字符串 | |
Boolean | Json bool 可以是 true 或 false | “pass”: true |
输出
CUT 必须为每个操作输出一个 json
对象。
必需字段
名称 | 类型 | 说明 |
---|---|---|
pc |
Number | 程序计数器 |
op |
Number | 操作码 |
gas |
Hex-Number | 执行此操作前剩余的 gas |
gasCost |
Hex-Number | 此操作的 gas 消耗 |
memSize |
Number | 内存数组的大小 |
stack |
Array of Hex-Numbers | 堆栈上所有值的数组 |
depth |
Number | 调用堆栈的深度 |
returnData |
Hex-String | 函数调用返回的数据 |
refund |
Hex-Number | 全局 gas 退还量 |
可选字段
名称 | 类型 | 说明 |
---|---|---|
opName |
String | 操作名称 |
error |
Hex-String | 错误的描述(如果支持,应包含 revert 原因) |
memory |
Array of Hex-Strings | 所有已分配值的数组 |
storage |
Key-Value | 所有存储值的数组 |
示例:
{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"depth":1,"error":null,"opName":"PUSH1"}
stack
、memory
和memSize
是操作执行前的值。- 所有数组属性(
stack
、memory
)必须初始化为空数组("stack":[]
),而不是 null。 - 如果 CUT 不会输出
memory
或storage
的值,则省略memory
和storage
字段。 这可能是因为 CUT 不支持追踪这些字段,或者已配置为不追踪它们。 - 无论是否支持
memory
,都必须存在memSize
字段。 - 客户端应实现一种禁用记录存储的方法,因为 stateroot 包括所有存储更新。
- 客户端应按照此 EIP 中列出的相同顺序输出字段。
如果发生错误,CUT 不得为 STOP
操作输出行:
示例:
{"pc":2,"op":0,"gas":"0x2540be3fd","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0x40"],"depth":1,"error":null,"opName":"STOP"}
摘要和错误处理
在执行结束时,CUT 必须打印摘要信息;此信息应具有以下字段。
摘要应该是一个单独的 jsonl
对象。
必需字段
名称 | 类型 | 说明 |
---|---|---|
stateRoot |
Hex-String | 执行交易后状态 trie 的根 |
output |
函数的返回值 | |
gasUsed |
Hex-Number | 交易使用的所有 gas |
pass |
Boolean | Bool,指示交易是否成功执行 |
可选字段
名称 | 类型 | 说明 |
---|---|---|
time |
Number | 执行交易所需的时间(以纳秒为单位) |
fork |
String | 用于执行的分叉规则的名称 |
示例:
{"stateRoot":"0xd4c577737f5d20207d338c360c42d3af78de54812720e3339f7b27293ef195b7","output":"","gasUsed":"0x3","pass":true,"time":141485}
理由
此 EIP 主要基于先前非官方的 EVM 追踪文档。 它试图涵盖尽可能多的极端情况,以实现真正的客户端兼容性。 选择数据类型以及字段是否可选,是为了尽可能与当前实现兼容。
向后兼容性
此 EIP 与 ethereum 完全向后兼容,因为它仅引入了更好的追踪基础设施,客户端可以选择实现。
客户端
此 EIP 与 go-ethereum 完全向后兼容。OpenEthereum、Besu 和 Nethermind 客户端必须稍微更改其 openethereum-evm
evmtool
和
nethtest
的 JSON 输出,以符合新的和更严格的规范。如果新客户端想要加入差异模糊测试组,则需要实现此更改。
测试用例
${BESU_HOME}/bin/evmtool --code 0x604080536040604055604060006040600060025afa6040f3 {"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
{"pc":2,"op":128,"gas":"0x2540be3fd","gasCost":"0x3","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"DUP1"}
{"pc":3,"op":83,"gas":"0x2540be3fa","gasCost":"0xc","memSize":0,"stack":["0x40","0x40"],"depth":1,"refund":0,"opName":"MSTORE8"}
{"pc":4,"op":96,"gas":"0x2540be3ee","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
{"pc":6,"op":96,"gas":"0x2540be3eb","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
{"pc":8,"op":85,"gas":"0x2540be3e8","gasCost":"0x4e20","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x40"],"depth":1,"refund":0,"opName":"SSTORE"}
{"pc":9,"op":96,"gas":"0x2540b95c8","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
{"pc":11,"op":96,"gas":"0x2540b95c5","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
{"pc":13,"op":96,"gas":"0x2540b95c2","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0"],"depth":1,"refund":0,"opName":"PUSH1"}
{"pc":15,"op":96,"gas":"0x2540b95bf","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
{"pc":17,"op":96,"gas":"0x2540b95bc","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0"],"depth":1,"refund":0,"opName":"PUSH1"}
{"pc":19,"op":90,"gas":"0x2540b95b9","gasCost":"0x2","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0","0x2"],"depth":1,"refund":0,"opName":"GAS"}
{"pc":20,"op":250,"gas":"0x2540b95b7","gasCost":"0x24abb676c","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0","0x2","0x2540b95b7"],"depth":1,"refund":0,"opName":"STATICCALL"}
{"pc":21,"op":96,"gas":"0x2540b92a7","gasCost":"0x3","memory":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x1"],"returnData":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b","depth":1,"refund":0,"opName":"PUSH1"}
{"pc":23,"op":243,"gas":"0x2540b92a4","gasCost":"0x0","memory":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x1","0x40"],"returnData":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b","depth":1,"refund":0,"opName":"RETURN"}
{"stateRoot":"0x8fa0dcc7f1d2383c89e5737c2843632db881c0946e80b71fe7175365e6538797","output":"0x40","gasUsed":"0x515c","pass":true,"fork":"Istanbul"}
安全注意事项
追踪的代价很高。
公开用于公开创建追踪的端点可能会打开拒绝服务向量。
客户端应考虑将追踪端点置于与其他端点不同的标志后面。
版权
版权及相关权利通过 CC0 放弃。
Citation
Please cite this document as:
Martin Holst Swende (@holiman), Marius van der Wijden (@MariusVanDerWijden), "EIP-3155: EVM 追踪规范 [DRAFT]," Ethereum Improvement Proposals, no. 3155, December 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-3155.