diff --git a/docs/_dapp/tracing-filtered.md b/docs/_dapp/tracing-filtered.md index 97cbf3a85..a9e75f1ad 100644 --- a/docs/_dapp/tracing-filtered.md +++ b/docs/_dapp/tracing-filtered.md @@ -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("") ``` - + 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(""), 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)