Introduce a new JSON standard for EVM traces during execution of state tests.
The Ethereum Virtual Machine executes all smart contract code on ethereum. In order to debug smart contracts and state tests better, a common format was introduced to log every execution step of the EVM. This format was implemented by Go-Ethereum, Parity-Ethereum, Nethermind and Besu. Since the common format was not well-defined, the implementations differed slightly, making it hard to develop adequate tooling which reduces the usefulness of tracing significantly.
This EIP has multiple goals:
Implementing this EIP in all major clients allows us to create meaningful differential fuzzers that fuzz EVM implementations for the mainnet and all upcoming hardforks. It also helps to find differences in execution quickly in the case of a chain split.
This EIP will enable users to create better differential fuzzing infrastructure to compare the EVM implementations of all major Ethereum clients against each other. This could help to find bugs that are currently present in the client implementations.
Clients should be able to execute simple transactions as well as code and return traces. In the following, we will call this client CUT (client under test) and use go-ethereum's
evm
binary for code examples.
Type | Explanation | Example |
---|---|---|
Number | Plain json number | "pc":0 |
Hex-Number | Hex-encoded number | "gas":"0x2540be400" |
String | Plain string | "opName":"PUSH1" |
Hex-String | Hex-encoded string | |
Array of x | Array of x encoded values | |
Key-Value | Key-Value structure with key and values encoded as hex strings | |
Boolean | Json bool can either be true or false | "pass": true |
The CUT MUST output a json
object for EACH operation.
Name | Type | Explanation |
---|---|---|
pc |
Number | Program Counter |
op |
Number | OpCode |
gas |
Hex-Number | Gas left before executing this operation |
gasCost |
Hex-Number | Gas cost of this operation |
memSize |
Number | Size of memory array |
stack |
Array of Hex-Numbers | Array of all values on the stack |
depth |
Number | Depth of the call stack |
returnData |
Hex-String | Data returned by function call |
refund |
Hex-Number | Amount of global gas refunded |
Name | Type | Explanation |
---|---|---|
opName |
String | Name of the operation |
error |
Hex-String | Description of an error (should contain revert reason if supported) |
memory |
Array of Hex-Strings | Array of all allocated values |
storage |
Key-Value | Array of all stored values |
Example:
{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"depth":1,"error":null,"opName":"PUSH1"}
stack
, memory
and memSize
are the values before execution of the op.stack
, memory
) MUST be initialized to empty arrays ("stack":[]
) NOT to null.memory
or storage
then the memory
and storage
fields are omitted.
This can happen either because the CUT does not support tracing these fields or it has been configured not to trace it.memSize
field MUST be present regardless of memory
support.The CUT MUST NOT output a line for the STOP
operation if an error occurred:
Example:
{"pc":2,"op":0,"gas":"0x2540be3fd","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0x40"],"depth":1,"error":null,"opName":"STOP"}
At the end of execution, the CUT MUST print summary info; this info SHOULD have the following fields.
The summary should be a single jsonl
object.
Name | Type | Explanation |
---|---|---|
stateRoot |
Hex-String | Root of the state trie after executing the transaction |
output |
Return values of the function | |
gasUsed |
Hex-Number | All gas used by the transaction |
pass |
Boolean | Bool whether transaction was executed successfully |
Name | Type | Explanation |
---|---|---|
time |
Number | Time in nanoseconds needed to execute the transaction |
fork |
String | Name of the fork rules used for execution |
Example:
{"stateRoot":"0xd4c577737f5d20207d338c360c42d3af78de54812720e3339f7b27293ef195b7","output":"","gasUsed":"0x3","pass":"true","time":141485}
This EIP is largely based on the previous non-official documentation for EVM tracing. It tries to cover as many corner cases as possible to enable true client compatibility. The datatypes and if a field is optional is chosen to be as compatible with current implementations as possible.
This EIP is fully backward compatible with ethereum as it only introduces a better tracing infrastructure that is optional for clients to implement.
This EIP is fully backward compatible with go-ethereum. OpenEthereum, Besu and Nethermind clients would have to change their JSON output of
openethereum-evm
evmtool
and
nethtest
slightly do adhere to the new and stricter specs. New clients would need to implement this change if they want to be part of the differential fuzzing group.
${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"}
Tracing is expensive.
Exposing an endpoint for creating traces publicly could open up a denial of service vector.
Clients should consider putting trace endpoints behind a separate flag from other endpoints.
Copyright and related rights waived via CC0.