update index and built-in tracing pages

This commit is contained in:
Joe 2022-11-04 11:51:46 +00:00
parent dcacefbd46
commit 86ced035c3
3 changed files with 361 additions and 47 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 KiB

@ -3,7 +3,7 @@ title: Built-in tracers
description: Explanation of the tracers that come bundled in Geth as part of the tracing API.
---
Geth comes bundled with a choice of tracers that can be invoked via the [tracing API](/docs/rpc/ns-debug). Some of these built-in tracers are implemented natively in Go, and others in Javascript. The default tracer is the opcode logger (otherwise known as struct logger) which is the default tracer for all the methods. Other tracers have to be specified by name when sending a request.
Geth comes bundled with a choice of tracers that can be invoked via the [tracing API](/docs/rpc/ns-debug). Some of these built-in tracers are implemented natively in Go, and others in Javascript. The default tracer is the opcode logger (otherwise known as struct logger) which is the default tracer for all the methods. Other tracers have to be specified by passing their name to the `tracer` parameter in the API call.
## Struct/opcode logger
@ -26,18 +26,89 @@ The struct logger (aka opcode logger) is a native Go tracer which executes a tra
Note that the fields `memory`, `stack`, `returnData`, and `storage` have dynamic size and depending on the exact transaction they could grow large in size. This is specially true for `memory` which could blow up the trace size. It is recommended to keep them disabled unless they are explicitly required for a given use-case.
It is also possible to configure the trace by passing Boolean (true/false) values for four parameters that tweak the verbosity of the trace. By default, the _EVM memory_ and _Return data_ are not reported but the _EVM stack_ and _EVM storage_ are. To report the maximum amount of data:
```shell
enableMemory: true
disableStack: false
disableStorage: false
enableReturnData: true
```
An example call:
```js
debug.traceTransaction('0xfc9359e49278b7ba99f59edac0e3de49956e46e530a53c15aa71226b7aa92c6f', {
enableMemory: true,
disableStack: false,
disableStorage: false,
enableReturnData: true
});
```
Return:
```terminal
{
"gas":25523,
"failed":false,
"returnValue":"",
"structLogs":[
{
"pc":0,
"op":"PUSH1",
"gas":64580,
"gasCost":3,
"depth":1,
"error":null,
"stack":[
],
"memory":null,
"storage":{
}
},
{
"pc":2,
"op":"PUSH1",
"gas":64577,
"gasCost":3,
"depth":1,
"error":null,
"stack":[
"0000000000000000000000000000000000000000000000000000000000000060"
],
"memory":null,
"storage":{
}
},
...
```
## Native tracers
The following tracers are implement in Go and as such have offer good performance. They are selected by their name when invoking a tracing API method, e.g. `debug.traceTransaction(<txhash>, { tracer: 'callTracer' })`.
The following tracers are implement in Go. This means they are much more performant than other tracers that are written in Javascript. The tracers are selected by passing their name to the `tracer` parameter when invoking a tracing API method, e.g. `debug.traceTransaction(<txhash>, { tracer: 'callTracer' })`.
### 4byteTracer
Solidity contract functions are [addressed](https://docs.soliditylang.org/en/develop/abi-spec.html#function-selector) by the first four four byte of the Keccak-256 hash of their signature. Therefore when calling the function of a contract, the caller must send this function selector as well as the ABI-encoded arguments as call data.
Solidity contract functions are [addressed](https://docs.soliditylang.org/en/develop/abi-spec.html#function-selector) using the first four four byte of the Keccak-256 hash of their signature. Therefore when calling the function of a contract, the caller must send this function selector as well as the ABI-encoded arguments as call data.
The `4byteTracer` collects the function selectors of every function executed in the lifetime of a transaction, along with the size of the supplied call data. The result is a `map[string]int` where the keys are `SELECTOR-CALLDATASIZE` and the values are number of occurances of this key. E.g.:
The `4byteTracer` collects the function selectors of every function executed in the lifetime of a transaction, along with the size of the supplied call data. The result is a `map[string]int` where the keys are `SELECTOR-CALLDATASIZE` and the values are number of occurances of this key. For example:
Example call:
```sh
debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
```
Return:
```terminal
> debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
{
"0x27dc297e-128": 1,
"0x38cc4831-0": 2,
@ -64,18 +135,53 @@ The `callTracer` tracks all the call frames executed during a transaction, inclu
| error | string | error, if any |
| calls | []callframe | list of sub-calls |
Example Call:
```sh
> debug.traceTransaction("0x44bed3dc0f584b2a2ab32f5e2948abaaca13917eeae7ae3b959de3371a6e9a95", {tracer: 'callTracer'})
```
Return:
```terminal
{
calls: [{
from: "0xc8ba32cab1757528daf49033e3673fae77dcf05d",
gas: "0x18461",
gasUsed: "0x60",
input: "0x000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a6",
output: "0x557904b74478f8810cc02198544a030d1829bb491e14fe1dd0354e933c5e87bd",
to: "0x0000000000000000000000000000000000000002",
type: "STATICCALL"
}, {
from: "0xc8ba32cab1757528daf49033e3673fae77dcf05d",
gas: "0x181db",
gasUsed: "0x48",
input: "0x557904b74478f8810cc02198544a030d1829bb491e14fe1dd0354e933c5e87bd",
output: "0x5fb393023b12544491a5b8fb057943b4ebf5b1401e88e44a7800000000000000",
to: "0x0000000000000000000000000000000000000002",
type: "STATICCALL"
}],
from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0",
gas: "0x1a310",
gasUsed: "0xfcb6",
input: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000",
to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d",
type: "CALL",
value: "0x0"
}
```
Things to note about the call tracer:
- Calls to precompiles are also included in the result
- In case a frame reverts, the field `output` will contain the raw return data, unlike [revertReasonTracer](#revertreasontracer) which parses the data and returns the revert message
### noopTracer
This tracer is noop. It returns an empty object and is only meant for testing the setup.
### prestateTracer
Executing a transaction requires the prior state, including account of sender and recipient, contracts that are called during execution, etc. The `prestateTracer` replays the tx and tracks every part of state that is touched. This is similar to the concept of a [stateless witness](https://ethresear.ch/t/the-stateless-client-concept/172), the difference being this tracer doesn't return any cryptographic proof, rather only the trie leaves. The result is an object. The keys are addresses of accounts. The value is an object with the following fields:
The prestate tracer has two modes: `prestate` and `diff`. The `prestate` mode returns the accounts necessary to execute a given transaction. `diff` mode returns the differences between the transaction's pre and post-state (i.e. what changed because the transaction happened). The `prestateTracer` defaults to `prestate` mode. It reexecutes the given transaction and tracks every part of state that is touched. This is similar to the concept of a [stateless witness](https://ethresear.ch/t/the-stateless-client-concept/172), the difference being this tracer doesn't return any cryptographic proof, rather only the trie leaves. The result is an object. The keys are addresses of accounts. The value is an object with the following fields:
| field | type | description |
| ------- | ----------------- | ----------------------------- |
@ -84,69 +190,274 @@ Executing a transaction requires the prior state, including account of sender an
| code | string | hex-encoded bytecode |
| storage | map[string]string | storage slots of the contract |
### revertReasonTracer
The `revertReasonTracer` is useful for analyzing failed transactions. The return value is:
- In case the transaction reverted: reason of the revert as returned by the Solidity contract
- Error message for any other failure
To run this tracer in `diff` mode, pass `tracerConfig: {diffMode: true}` in the APi call.
Example:
```js
debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'prestateTracer'})
```
Return:
```terminal
{
0x0000000000000000000000000000000000000002: {
balance: "0x0"
},
0x008b3b2f992c0e14edaa6e2c662bec549caa8df1: {
balance: "0x2638035a26d133809"
},
0x35a9f94af726f07b5162df7e828cc9dc8439e7d0: {
balance: "0x7a48734599f7284",
nonce: 1133
},
0xc8ba32cab1757528daf49033e3673fae77dcf05d: {
balance: "0x0",
code: "0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063a9c2d...
nonce: 1,
storage: {
0x0000000000000000000000000000000000000000000000000000000000000000: "0x000000000000000000000000000000000000000000000000000000000024aea6",
0x59fb7853eb21f604d010b94c123acbeae621f09ce15ee5d7616485b1e78a72e9: "0x00000000000000c42b56a52aedf18667c8ae258a0280a8912641c80c48cd9548",
0x8d8ebb65ec00cb973d4fe086a607728fd1b9de14aa48208381eed9592f0dee9a: "0x00000000000000784ae4881e40b1f5ebb4437905fbb8a5914454123b0293b35f",
0xff896b09014882056009dedb136458f017fcef9a4729467d0d00b4fd413fb1f1: "0x000000000000000e78ac39cb1c20e9edc753623b153705d0ccc487e31f9d6749"
}
}
}
```
Return (same call with `{diffMode: True}`):
```terminal
{
post: {
0x35a9f94af726f07b5162df7e828cc9dc8439e7d0: {
nonce: 1135
}
},
pre: {
0x35a9f94af726f07b5162df7e828cc9dc8439e7d0: {
balance: "0x7a48429e177130a",
nonce: 1134
}
}
}
```
### revertReasonTracer
The `revertReasonTracer` is useful for analyzing failed transactions. If the transaction reverted, the reason for the revert (according to the Solidity contract) is returned. For any other failure, the error message is returned.
Example:
```js
> debug.traceTransaction('0x97695ffb034be7e1faeb372a564bb951ba4ebf4fee4caff2f9d1702497bb2b8b', { tracer: 'revertReasonTracer' })
```
Returns:
```terminal
"execution reverted: tokensMintedPerAddress exceed MAX_TOKENS_MINTED_PER_ADDRESS"
```
## JS tracers
### noopTracer
The following are a list of tracers written in JS that come as part of Geth:
- `bigramTracer`: Counts the opcode bigrams, i.e. how many times 2 opcodes were executed one after the other
- `evmdisTracer`: Returns sufficient information from a trace to perform [evmdis](https://github.com/Arachnid/evmdis)-style disassembly
- `opcountTracer` Counts the total number of opcodes executed
- `trigramTracer`: Counts the opcode trigrams
- `unigramTracer`: Counts the occurances of each opcode
This tracer is noop. It returns an empty object and is only meant for testing the setup.
## Javascript tracers
There are also a set of tracers written in Javascript. These are less performant than the Go native tracers because of overheads associated with compiling the Javascript in Geth's Go environment.
### bigram
`bigramTracer` counts the opcode bigrams, i.e. how many times 2 opcodes were executed one after the other.
#############################
To follow along with this tutorial, transaction hashes can be found from a local Geth node (e.g. by attaching a [Javascript console](/docs/interface/javascript-console) and running `eth.getBlock('latest')` then passing a transaction hash from the returned block to `debug.traceTransaction()`) or from a block explorer (for [Mainnet](https://etherscan.io/) or a [testnet](https://goerli.etherscan.io/)).
It is also possible to configure the trace by passing Boolean (true/false) values for four parameters that tweak the verbosity of the trace. By default, the _EVM memory_ and _Return data_ are not reported but the _EVM stack_ and _EVM storage_ are. To report the maximum amount of data:
```shell
enableMemory: true
disableStack: false
disableStorage: false
enableReturnData: true
```
An example call, made in the Geth Javascript console, configured to report the maximum amount of data looks as follows:
Example:
```js
debug.traceTransaction('0xfc9359e49278b7ba99f59edac0e3de49956e46e530a53c15aa71226b7aa92c6f', {
enableMemory: true,
disableStack: false,
disableStorage: false,
enableReturnData: true
});
debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'bigramTracer'})
```
Running the above operation on the Rinkeby network (with a node retaining enough history) will result in this [trace dump](https://gist.github.com/karalabe/c91f95ac57f5e57f8b950ec65ecc697f).
Returns:
Alternatively, disabling _EVM Stack_, _EVM Memory_, _Storage_ and _Return data_ (as demonstrated in the Curl request below) results in the following, much shorter, [trace dump](https://gist.github.com/karalabe/d74a7cb33a70f2af75e7824fc772c5b4).
```terminal
{
ADD-ADD: 1,
ADD-AND: 2,
ADD-CALLDATALOAD: 1,
ADD-DUP1: 2,
ADD-DUP2: 2,
ADD-GT: 1,
ADD-MLOAD: 1,
ADD-MSTORE: 4,
ADD-PUSH1: 1,
ADD-PUSH2: 4,
ADD-SLT: 1,
ADD-SWAP1: 10,
ADD-SWAP2: 1,
ADD-SWAP3: 1,
ADD-SWAP4: 3,
ADD-SWAP5: 1,
AND-DUP3: 2,
AND-ISZERO: 4,
...
}
```
$ curl -H "Content-Type: application/json" -d '{"id": 1, "method": "debug_traceTransaction", "params": ["0xfc9359e49278b7ba99f59edac0e3de49956e46e530a53c15aa71226b7aa92c6f", {"disableStack": true, "disableStorage": true}]}' localhost:8545
### evmdis
`evmdisTracer` returns sufficient information from a trace to perform [evmdis](https://github.com/Arachnid/evmdis)-style disassembly
Example:
```js
> debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'evmdisTracer'})
```
Returns:
```terminal
[{
depth: 1,
len: 2,
op: 96,
result: ["80"]
}, {
depth: 1,
len: 2,
op: 96,
result: ["40"]
}, {
depth: 1,
op: 82,
result: []
}, {
depth: 1,
op: 52,
result: ["0"]
}, {
depth: 1,
op: 128,
result: ["0", "0"]
}, {
depth: 1,
op: 21,
result: ["1"]
}, {
depth: 1,
len: 3,
op: 97,
result: ["10"]
}, {
depth: 1,
op: 87,
result: []
}, {
depth: 1,
op: 91,
pc: 16,
result: []
},
...
```
### opcount
`opcountTracer` counts the total number of opcodes executed and simply returns the number.
Example:
```js
debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'opcountTracer'})
```
Returns:
```terminal
1384
```
### trigram
`trigramTracer` counts the opcode trigrams. Trigrams are the possible combinations of three opcodes this tracer reports how many times each combination is seen during execution.
Example:
```js
debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'trigramTracer'})
```
Returns:
```terminal
{
--PUSH1: 1,
-PUSH1-MSTORE: 1,
ADD-ADD-GT: 1,
ADD-AND-DUP3: 2,
ADD-CALLDATALOAD-PUSH8: 1,
ADD-DUP1-PUSH1: 2,
ADD-DUP2-ADD: 1,
ADD-DUP2-MSTORE: 1,
ADD-GT-ISZERO: 1,
ADD-MLOAD-DUP6: 1,
ADD-MSTORE-ADD: 1,
ADD-MSTORE-PUSH1: 2,
ADD-MSTORE-PUSH32: 1,
ADD-PUSH1-KECCAK256: 1,
ADD-PUSH2-JUMP: 2,
ADD-PUSH2-JUMPI: 1,
ADD-PUSH2-SWAP2: 1,
ADD-SLT-PUSH2: 1,
...
}
```
### unigram
######################################################
`unigramTracer` counts the frequency of occurrance of each opcode.
Example:
```js
> debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'unigramTracer'})
```
Returns:
```terminal
{
ADD: 36,
AND: 23,
BYTE: 4,
CALLDATACOPY: 1,
CALLDATALOAD: 6,
CALLDATASIZE: 2,
CALLVALUE: 1,
DIV: 9,
DUP1: 29,
DUP10: 2,
DUP11: 1,
DUP12: 3,
DUP13: 2,
...
}
```
## State overrides
It is possible to give temporary state modifications to Geth in order to simulate the effects of `eth_call`. For example, some new byetcode could be deployed to some address *temporarily just for the duration of the execution* and then a transaction interacting with that address canm be traced. This can be used for scenario testing or determining the outcome of some hypothetical transaction before executing for real.
To do this, the tracer is written as normal, but the parameter `stateOverrides` is passed an address and some bytecode.
```js
var code = //contract bytecode
var tracer = //tracer name
debug.traceCall({from: , to: , input: }, 'latest', {stateOverrides: {'0x...': {code: code}}, tracer: tracer})
```
## Summary
This page showed how to use the tracers that come bundled with Geth. There are a set written in Go and a set written in Javascript. They are invoked by passing their names when calling an API method.

@ -17,6 +17,9 @@ In its simplest form, tracing a transaction entails requesting the Ethereum node
This means there are limits on the transactions that can be traced imposed by the synchronization and pruning configuration of a node:
![state pruning options](/public/images/docs/state-pruning.png)
- An **archive** node retains **all historical data** back to genesis. It can therefore trace arbitrary transactions at any point in the history of the chain. Tracing a single transaction requires reexecuting all preceding transactions in the same block.
- A **node synced from genesis** node only retains the most recent 128 block states in memory. Older states are represented by a sequence of occasional checkpoints that intermediate states can be regenerated from. This means that states within the msot recent 128 blocks are immediately available, older states have to be regenerated from snapshots "on-the-fly". If the distance between the requested transaction and the most recent checkpoint is large, rebuilding the state can take a long time. Tracing a single transaction requires reexecuting all preceding transactions in the same block **and** all preceding blocks until the previous stored snapshot.