168 lines
12 KiB
Markdown
168 lines
12 KiB
Markdown
---
|
|
eip: 3155
|
|
title: EVM trace specification
|
|
author: Martin Holst Swende (@holiman), Marius van der Wijden (@MariusVanDerWijden)
|
|
discussions-to: https://ethereum-magicians.org/t/eip-3155-create-evm-trace-specification/5007
|
|
status: Stagnant
|
|
type: Standards Track
|
|
category: Interface
|
|
created: 2020-12-07
|
|
---
|
|
|
|
|
|
## Simple Summary
|
|
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, 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 objectives:
|
|
- 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 finding 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-ethereums `evm` binary for code examples.
|
|
|
|
### Datatypes
|
|
|
|
| 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 |
|
|
|
|
### Output
|
|
|
|
The CUT MUST output a `json` object for EACH operation.
|
|
|
|
Required Fields:
|
|
|
|
| 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 |
|
|
| `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 |
|
|
| `memSize` | Number | Size of memory array |
|
|
|
|
Optional Fields:
|
|
|
|
| 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 |
|
|
| `returnStack` | Array of Hex-Numbers | Array of values, Stack of the called function |
|
|
|
|
*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`, `returnStack`, `memory`) MUST be initialized to empty arrays ("stack":[],) NOT to null.
|
|
If the CUT will not output 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 some summerical info, this info SHOULD have the following fields.
|
|
The summary should be a single `jsonl` object.
|
|
|
|
Required Fields:
|
|
|
|
| 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 |
|
|
|
|
OptionalFields:
|
|
|
|
| Name | Type | Explanation |
|
|
|---|---|---|
|
|
| `time` | Number | Time in nanoseconds needed to execute the transaction |
|
|
| `fork` | String | Name of the fork rules used for execution |
|
|
```
|
|
{"stateRoot":"0xd4c577737f5d20207d338c360c42d3af78de54812720e3339f7b27293ef195b7","output":"","gasUsed":"0x3","successful":"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
|
|
|
|
```bash
|
|
~/go/src/github.com/ethereum/go-ethereum/build/bin/evm --code 604080536040604055604060006040600060025afa6040f3 --json run
|
|
{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
{"pc":2,"op":128,"gas":"0x2540be3fd","gasCost":"0x3","memory":"0x","memSize":0,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"DUP1","error":""}
|
|
{"pc":3,"op":83,"gas":"0x2540be3fa","gasCost":"0xc","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"MSTORE8","error":""}
|
|
{"pc":4,"op":96,"gas":"0x2540be3ee","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
{"pc":6,"op":96,"gas":"0x2540be3eb","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
{"pc":8,"op":85,"gas":"0x2540be3e8","gasCost":"0x4e20","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SSTORE","error":""}
|
|
{"pc":9,"op":96,"gas":"0x2540b95c8","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
{"pc":11,"op":96,"gas":"0x2540b95c5","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
{"pc":13,"op":96,"gas":"0x2540b95c2","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
{"pc":15,"op":96,"gas":"0x2540b95bf","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
{"pc":17,"op":96,"gas":"0x2540b95bc","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
{"pc":19,"op":90,"gas":"0x2540b95b9","gasCost":"0x2","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0","0x2"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"GAS","error":""}
|
|
{"pc":20,"op":250,"gas":"0x2540b95b7","gasCost":"0x24abb676c","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0","0x2","0x2540b95b7"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"STATICCALL","error":""}
|
|
{"pc":21,"op":96,"gas":"0x2540b92a7","gasCost":"0x3","memory":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x1"],"returnStack":[],"returnData":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
{"pc":23,"op":243,"gas":"0x2540b92a4","gasCost":"0x0","memory":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x1","0x40"],"returnStack":[],"returnData":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b","depth":1,"refund":0,"opName":"RETURN","error":""}
|
|
{"stateRoot":"2eef130ec61805516c1f050720b520619787704a5dd826a39aeefb850f83acfd", "output":"40","gasUsed":"0x515c","time":350855}
|
|
```
|
|
|
|
|
|
## Implementation
|
|
Implementation in [go-ethereum](https://github.com/ethereum/go-ethereum/tree/master/cmd/evm)
|
|
Implementation in [OpenEthereum](https://github.com/openethereum/openethereum/tree/master/evmbin)
|
|
Implementation in [Besu](https://github.com/hyperledger/besu/tree/master/ethereum/evmtool)
|
|
Implementation in [Nethermind](https://github.com/NethermindEth/nethermind/tree/master/src/Nethermind/Nethermind.State.Test.Runner)
|
|
|
|
|
|
## 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
|
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|