HomeEIPsNewsletter
EIPsEIP-3155
EIP-3155

EVM trace specification

A JSON format for EVM traces
ReviewStandards Track: Interface
Created: 2020-12-07
Martin Holst Swende (@holiman), Marius van der Wijden (@MariusVanDerWijden)
DiscussionsOriginal linkEdit
1 min read

The EIP-3155 proposal aims to introduce a new JSON standard for EVM traces during execution of state tests in order to improve the debugging of smart contracts and state tests. It seeks to strictly define corner cases, allow for updates to the specification, and provide sample output. Implementing this proposal in all major clients would enable users to create better differential fuzzing infrastructure to compare the EVM implementations of all major Ethereum clients against each other, potentially helping to find bugs that are currently present in the client implementations.

Video
Anyone may contribute to propose contents.
Go propose
Original

Abstract

Introduce a new JSON standard for EVM traces during execution of state tests.

Motivation

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:

  • Move the specification to a more visible place to encourage new clients to implement it
  • Strictly define corner cases that were not addressed in the previous version
  • Allow for updates to the specification in case new fields are introduced during execution
  • Provide sample output

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.

Specification

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.

Datatypes

TypeExplanationExample
NumberPlain json number"pc":0
Hex-NumberHex-encoded number"gas":"0x2540be400"
StringPlain string"opName":"PUSH1"
Hex-StringHex-encoded string
Array of xArray of x encoded values
Key-ValueKey-Value structure with key and values encoded as hex strings
BooleanJson bool can either be true or false"pass": true

Output

The CUT MUST output a json object for EACH operation.

Required Fields

NameTypeExplanation
pcNumberProgram Counter
opNumberOpCode
gasHex-NumberGas left before executing this operation
gasCostHex-NumberGas cost of this operation
memSizeNumberSize of memory array
stackArray of Hex-NumbersArray of all values on the stack
depthNumberDepth of the call stack
returnDataHex-StringData returned by function call
refundHex-NumberAmount of global gas refunded

Optional Fields

NameTypeExplanation
opNameStringName of the operation
errorHex-StringDescription of an error (should contain revert reason if supported)
memoryArray of Hex-StringsArray of all allocated values
storageKey-ValueArray of all stored values

Example:

{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"depth":1,"error":null,"opName":"PUSH1"}
  • The stack, memory and memSize are the values before execution of the op.
  • All array attributes (stack, memory) MUST be initialized to empty arrays ("stack":[]) NOT to null.
  • If the CUT will not be outputting values for 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.
  • The memSize field MUST be present regardless of memory support.
  • Clients SHOULD implement a way to disable recording the storage as the stateroot includes all storage updates.
  • Clients SHOULD output the fields in the same order as listed in this EIP.

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"}

Summary and Error Handling

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.

Required Fields

NameTypeExplanation
stateRootHex-StringRoot of the state trie after executing the transaction
outputReturn values of the function
gasUsedHex-NumberAll gas used by the transaction
passBooleanBool whether transaction was executed successfully

Optional Fields

NameTypeExplanation
timeNumberTime in nanoseconds needed to execute the transaction
forkStringName of the fork rules used for execution

Example:

{"stateRoot":"0xd4c577737f5d20207d338c360c42d3af78de54812720e3339f7b27293ef195b7","output":"","gasUsed":"0x3","pass":"true","time":141485}

Rationale

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.

Backwards Compatibility

This EIP is fully backward compatible with ethereum as it only introduces a better tracing infrastructure that is optional for clients to implement.

Clients

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.

Test Cases

${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"}

Security Considerations

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.

Further reading
Anyone may contribute to propose contents.
Go propose
Adopted by projects
Anyone may contribute to propose contents.
Go propose

Not miss a beat of EIPs' update?

Subscribe EIPs Fun to receive the latest updates of EIPs Good for Buidlers to follow up.

View all
Serve Ethereum Builders, Scale the Community.
Resources
GitHub
Supported by