@ -3,10 +3,10 @@ title: Filtered Tracing
sort_key: B
---
In the previous section you learned how to create a complete trace. However, those traces can include the complete status of the EVM at every point
In the previous section you learned how to create a complete trace. However, those traces can include the complete status of the EVM at every point
in the execution, which is huge. Usually you are only interested in a small subset of this information. To get it, you can specify a JavaScript filter.
**Note:** The JavaScript package used by Geth is [Goja ](https://github.com/dop251/goja ), which is only up to the
**Note:** The JavaScript interpreter used by Geth is [duktape ](https://duktape.org ), which is only up to the
[ECMAScript 5.1 standard ](https://262.ecma-international.org/5.1/ ). This means we cannot use [arrow functions ](https://www.w3schools.com/js/js_arrow_function.asp )
and [template literals ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals ).
@ -16,14 +16,14 @@ and [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/
1. Create a file, `filterTrace_1.js` , with this content:
```javascript
tracer = function(tx) {
return debug.traceTransaction(tx, {tracer:
return debug.traceTransaction(tx, {tracer:
'{' +
'retVal: [],' +
'step: function(log,db) {this.retVal.push(log.getPC() + ":" + log.op.toString())},' +
'fault: function(log,db) {this.retVal.push("FAULT: " + JSON.stringify(log))},' +
'result: function(ctx,db) {return this.retVal}' +
'result: function(ctx,db) {return this.retVal}' +
'}'
}) // return debug.traceTransaction ...
} // tracer = function ...
@ -32,8 +32,8 @@ and [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/
We could specify this function directly in the JavaScript console, but it would be unwieldy and difficult
to edit.
2. Run the [JavaScript console ](https://geth.ethereum.org/docs/interface/javascript-console ).
2. Run the [JavaScript console ](https://geth.ethereum.org/docs/interface/javascript-console ).
3. Get the hash of a recent transaction. For example, if you use the Goerli network, you can get such a value
[here ](https://goerli.etherscan.io/ ).
4. Run this command to run the script:
@ -47,24 +47,24 @@ and [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/
```javascript
tracer("< hash of transaction > ")
```
The bottom of the output looks similar to:
```json
"3366:POP", "3367:JUMP", "1355:JUMPDEST", "1356:PUSH1", "1358:MLOAD", "1359:DUP1", "1360:DUP3", "1361:ISZERO", "1362:ISZERO",
"1363:ISZERO", "1364:ISZERO", "1365:DUP2", "1366:MSTORE", "1367:PUSH1", "1369:ADD", "1370:SWAP2", "1371:POP", "1372:POP", "1373:PUSH1",
"3366:POP", "3367:JUMP", "1355:JUMPDEST", "1356:PUSH1", "1358:MLOAD", "1359:DUP1", "1360:DUP3", "1361:ISZERO", "1362:ISZERO",
"1363:ISZERO", "1364:ISZERO", "1365:DUP2", "1366:MSTORE", "1367:PUSH1", "1369:ADD", "1370:SWAP2", "1371:POP", "1372:POP", "1373:PUSH1",
"1375:MLOAD", "1376:DUP1", "1377:SWAP2", "1378:SUB", "1379:SWAP1", "1380:RETURN"]
```
6. This output isn't very readable. Run this line to get a more readable output with each string in its own line.
```javascript
console.log(JSON.stringify(tracer("< hash of transaction > "), null, 2))
```
You can read about the `JSON.stringify` function
You can read about the `JSON.stringify` function
[here ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify ). If we just
return the output we get `\n` for newlines, which is why we need to use `console.log` .
### How Does It Work?
We call the same `debug.traceTransaction` function we use for [basic traces ](https://geth.ethereum.org/docs/dapp/tracing ), but
@ -137,10 +137,10 @@ The output looks similar to this:
## Stack Information
The trace above tells us the program counter (PC) and whether the program read from storage or wrote to it. That
The trace above tells us the program counter (PC) and whether the program read from storage or wrote to it. That
isn't very useful. To know more, you can use the `log.stack.peek` function to peek into the stack. `log.stack.peek(0)`
is the stack top, `log.stack.peek(1)` the entry below it, etc. The values returned by `log.stack.peek` are
Go `big.Int` objects. By default they are converted to JavaScript floating point numbers, so you need
Go `big.Int` objects. By default they are converted to JavaScript floating point numbers, so you need
`toString(16)` to get them as hexadecimals, which is how we normally represent 256-bit values such as
storage cells and their content.
@ -184,13 +184,13 @@ you a more complete picture of the program's interaction with storage. The outpu
## Operation Results
One piece of information missing from the function above is the result on an `SLOAD` operation. The
One piece of information missing from the function above is the result on an `SLOAD` operation. The
state we get inside `log` is the state prior to the execution of the opcode, so that value is not
known yet. For more operations we can figure it out for ourselves, but we don't have access to the
storage, so here we can't.
The solution is to have a flag, `afterSload` , which is only true in the opcode right after an
`SLOAD` , when we can see the result at the top of the stack.
The solution is to have a flag, `afterSload` , which is only true in the opcode right after an
`SLOAD` , when we can see the result at the top of the stack.
```javascript
tracer = function(tx) {
@ -243,10 +243,10 @@ line itself, but that would have been a bit more work.
## Dealing With Calls Between Contracts
So far we have treated the storage as if there are only 2^256 cells. However, that is not true. Contracts
can call other contracts, and then the storage involved is the storage of the other contract. We can see
the address of the current contract in `log.contract.getAddress()` . This value is the execution context,
the contract whose storage we are using, even when we use code from another contract (by using
So far we have treated the storage as if there are only 2^256 cells. However, that is not true. Contracts
can call other contracts, and then the storage involved is the storage of the other contract. We can see
the address of the current contract in `log.contract.getAddress()` . This value is the execution context,
the contract whose storage we are using, even when we use code from another contract (by using
`CALLCODE` or `DELEGATECODE` ).
However, `log.contract.getAddress()` returns an array of bytes. We use `this.byteHex()` and `array2Hex()`
@ -276,7 +276,7 @@ tracer = function(tx) {
'getAddr: function(log) {' +
' return this.array2Hex(log.contract.getAddress());' +
'}, ' +
'step: function(log,db) {' +
' var opcode = log.op.toNumber();' +
@ -294,7 +294,7 @@ tracer = function(tx) {
' log.stack.peek(0).toString(16)); ' +
' this.afterSload = false; ' +
' } ' +
// SSTORE
' if (opcode == 0x55) ' +
' this.retVal.push(log.getPC() + ": SSTORE " +' +
@ -310,7 +310,7 @@ tracer = function(tx) {
'result: function(ctx,db) {return this.retVal}' +
'}'
}) // return debug.traceTransaction ...
} // tracer = function ...
} // tracer = function ...
```
The output is similar to:
@ -331,7 +331,7 @@ The output is similar to:
"13529: SLOAD f2d68898557ccb2cf4c10c3ef2b034b2a69dad00:b38558064d8dd9c883d2a8c80c604667ddb90a324bc70b1bac4e70d90b148ed4",
" Result: b38558064d8dd9c883d2a8c80c604667ddb90a324bc70b1bac4e70d90b148ed4",
"11041: SSTORE 22ff293e14f1ec3a09b137e9e06084afd63addf9:6 < - 0 "
]
]
```
## Conclusion
@ -341,5 +341,3 @@ or how to use the `db` parameter to know the state of the chain at the time of e
covered [in the reference ](https://geth.ethereum.org/docs/rpc/ns-debug#javascript-based-tracing ).
Hopefully with this tool you will find it easier to trace the EVM's behavior and debug thorny contract issues.
Original version by [Ori Pomerantz ](qbzzt1@gmail.com )