question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Several bugs in `debug_traceTransaction`

See original GitHub issue

There are several bugs causing strange behavior of debug_traceTransaction.

I’ll briefly enumerate the issues below, then dig into the root cause. Here is a gist with a script that demonstrates the issues: https://gist.github.com/robmcl4/164d02de04c8708817c5c32a9c325843

Summary

  1. gas returned does not include the gas refund, so it is over the true amount of gas spent (as an aside, I would like to see structLogs to also contain the property refund with the current gas refund – this is the behavior of the current geth release, 1.10.18.)
  2. In a scenario where a contract calls itself recursively, SSTORE operations in the deeper call don’t modify the storage property of the structLogs when the outer call resumes (after the inner call completes). This improperly leads the web3 user to believe the outer call sees stale storage.
  3. In a scenario where one calls debug_traceTransaction on a transaction which occurred before the fork block, and a contract attempts to set a storage slot to 0x0 when its value prior to the transaction was nonnull (ie, you attempt to delete the slot), then the delete is not persisted and the EVM will continue to execute performing stale reads on that slot

Deeper discussion

Issue (1)

This one is simple. On blockchain.ts:1366, runTx returns a RunTxResult … the actual gas consumed is found in the property gasUsed.

Issue (2)

This is because at blockchain.ts:1287, only storageStack[eventDepth] is modified. However, stale copies of the storage may exist at higher depths, further up in the call stack. This can be fixed by iterating over storageStack and updating storage whenever the same contract is seen further up the stack.

Issue (3)

The core issue is because of the conditional statement at trie.ts:167 and trie.ts:275.

Consider that we are tracing a pre-fork transaction, and a contract attempts to delete storage (set it to 0x0). Eventually, we call trie.del(..). The conditional on line 167 evaluates to false, and the else block is taken on line 177. Critically, this means that the storage is never ‘marked’ as deleted. Subsequent reads will then notice that the slot is not available locally, query the fallback, and a stale read is observed in the EVM.

Demonstration

I run this on ubuntu 20.04, python 3.8.10, node 16.15.1, npm 8.11.0, and geth 1.10.18. I have geth running as a full archive node running on ws://127.0.0.1:8546. Before running, I install web3: python3 -m pip install web3==5.29.2

I invoke ganache using:

node \
    src/packages/ganache/dist/node/cli.js \
        --fork.url ws://localhost:8546 \
        --server.port 34451 \
        --chain.chainId 1 \
        --chain.hardfork arrowGlacier

Then, I run the linked script:

-> % python3 bug.py          
querying archive node
done querying archive node, querying ganache
done querying ganache
Gas mismatch!
653656
819869

!!!!!!!!!!!!!!!!!!!!!! BUG !!!!!!!!!!!!!!!!!!!!!!!!!
expected storage 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB slot b5f5b1f5c4654b9ad9ba55eab198a3e6fc295c570faf1562b80df25b9119f014 to be 0000000000000000000000000000000000000000000000000000000000000000 but got 0000000000000000000000000000000000000000000000000000000000000001
6017 SWAP1 slot None
6018 SHA3 slot None
6019 SWAP9 slot None
6020 MLOAD slot None
6021 DUP10 slot None
6022* SLOAD slot 0000000000000000000000000000000000000000000000000000000000000001
6023 PUSH1 slot 0000000000000000000000000000000000000000000000000000000000000001
6025 NOT slot 0000000000000000000000000000000000000000000000000000000000000001
6026 AND slot 0000000000000000000000000000000000000000000000000000000000000001
6027 SWAP1 slot 0000000000000000000000000000000000000000000000000000000000000001

The script traces transaction 0xf514259f185019704004b241cb4c6e31ed5e6e474a998b99c4b89209e304f417, both on geth and on ganache. This immediately demonstrates bugs (1), mismatched gas, and (3), stale read of 0x1 after slot ...f014 was deleted. Bug (2) appears after issue (3) is addressed. It’s also important to point out that (1) cannot be addressed without (3) – as the gas usage of SSTORE is computed incorrectly because of stale reads.

If you need a json dump of geth’s trace, I can supply that as well.

Work-Arounds

My current fixes are on my fork’s branch robmcl4/severalBugFixes.

  • (1) is fixed in 8f0838d11f8bccad33d328751d797b1e82e47b30
  • (2) is fixed in 8ffcb5199ef1e08286eebfd3d8320d3d6caf9630
  • (3) is fixed in 3eb99dcc7f37f35972d4bdf2995f4e54b64e0e87

With patches applied, the file outputs:

-> % python3 bug.py                                                                                                                               
querying archive node
done querying archive node, querying ganache
done querying ganache
no bug

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:1
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
unparalleled-jscommented, Dec 9, 2022

@unparalleled-js can you provide a reproduction?

I am using mainnet-fork and historical transaction 0xb7d7f1d5ce7743e821d3026647df486f517946ef1342a1ae93c96e4a8016eab7

When you compare the struct logs with Hardhat’s, the results are different in a few ways. There are more than twice as many struct logs in the Hardhat response versus the Ganache, and top-level log looks a lot different as well.

0reactions
davidmurdochcommented, Dec 9, 2022

@unparalleled-js can you provide a reproduction?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Support — Reporting Bugs - Xdebug
Bugs. If you think that you found a bug in Xdebug, please file a bugreport ... There is different information useful for Step...
Read more >
Optimism ( _ ) on Twitter: "We think OE can be the ...
Now let's talk about openness. For L2s, a codebase that people can understand is a codebase that people can trust. A bug in...
Read more >
Ganache 7 Ethereum Simulator - Building on Web3 is now ...
Ganache supports massive transaction tracing using the debug_traceTransaction RPC method. To use this feature, start Ganache by forking off of Mainnet (or any ......
Read more >
ethereum/remix - Gitter
and I have modified testRPC-py to use debug_traceTransaction to get that ... and I discovered an interested bug while debugging with remix: If...
Read more >
Enterprise Ethereum Alliance Client Specification v6
There are several specific functionality areas that are more prone to security issues arising from implementation bugs.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found