erigontech / erigon

Ethereum implementation on the efficiency frontier https://erigon.gitbook.io
GNU Lesser General Public License v3.0
3.1k stars 1.08k forks source link

debug_traceCall has lower gasCost with stateOverrides #11254

Open Otto-AA opened 1 month ago

Otto-AA commented 1 month ago

System information

Erigon version: cast client -r https://eth-pokt.nodies.app/ -> erigon/2.59.3/linux-amd64/go1.21.5

I don't have access to this instance.

Expected behaviour

Running debug_traceTransaction should produce the same gasCost for each instruction, as running debug_traceCall, if we provide the prestate as stateOverrides.

Actual behaviour

debug_traceCall shows a lower gasCost for addresses, that were modified by the stateOverrides.

Note, that the prestateTracer results for debug_traceTransaction and debug_traceCall were equal, showing that they really operated on the same prestate.

Steps to reproduce the behaviour

I've created a demo script (repro.py.txt). You will need to modify the provider variable inside it, the rest should work. If you uncomment a part of the code, you can also let it output the traces as JSON files for comparison with git.

For the transaction 0x0947c75d6f6a65d121f214910ab23826226c414b63af37a54a647cf26201285c, we create vmTraces for both scenarios and compare them:

  1. Get prestates via debug_traceTransaction and the prestateTracer
  2. Convert prestates to stateOverrides format
  3. Get vmTrace via debug_traceTransaction
  4. Get vmTrace via debug_traceCall with prestate as stateOverrides and the same blockNumber
  5. Compare each step in the vmTrace until they differ

Backtrace

Traces for comparison:

Otto-AA commented 3 weeks ago

FYI, Geth has the same behaviour (see mentioned issue above).

Otto-AA commented 6 days ago

Another minimal example:

We use the state overrides to set slot 0x00 to 0x1111 and then execute the following code:

PUSH4 0x12345678
PUSH1 0x00
SSTORE

This should create gas costs 5000 (2100 + 2900) for the SSTORE:

However, it outputs the gas costs 2200 (2100 + 100) for the SSTORE:

My hunch is that the state overrides result in the slot being marked as changed, thus in the gas costs calculation it thinks it was already previously changed.

Here is the request to reproduce this example (| python3 -m json.tool is optional for legibility):

curl https://eth-pokt.nodies.app/ \
-X POST \
-H "Content-Type: application/json" \
--data '{
  "method": "debug_traceCall",
  "params": [
    {
      "from": null,
      "to": "0x1234567812345678123456781234567812345678"
    },
    "latest",
    {
      "stateOverrides": {
        "0x1234567812345678123456781234567812345678": {
          "code": "0x6312345678600055",
          "stateDiff": {
            "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000001111"
          }
        }
      }
    }
  ],
  "id":1,
  "jsonrpc":"2.0"
}' | python3 -m json.tool

And on a side note, I'm not sure if Geth has the same behaviour (see their issue, it was probably my mistake). So it may be specific to Erigon.