Skip to content

feat: Add Gas dimension tracers #459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open

Conversation

relyt29
Copy link
Collaborator

@relyt29 relyt29 commented May 13, 2025

This PR introduces two native and two live tracers, txGasDimensionLogger and txGasDimensionByOpcode, that on a per-opcode basis track the consumption of gas across multiple dimensions:

  1. Computation - opcodes that operate on the stack, memory, simple math
  2. State access - reading and writing to existing state, storage slots, accounts tree etc
  3. State growth - expanding the amount of state in existence, either by setting a value in a smart contract from 0->Non-Zero or by creating a new account to have to track in the accounts tree
  4. History growth - logging log events which are stored in the history (as opposed to the state)

txGasDimensionLogger

The logger is based off the default geth opcode struct logger. It will return each opcode, and its associated gas dimensional consumption, e.g.

{                                                                                               
  "jsonrpc": "2.0",                                                                             
  "id": 1,                                                                                      
  "result": {                                                                                   
    "gasUsed": 994729,                                                                          
    "gasUsedForL1": 968000,                                                                     
    "gasUsedForL2": 26729,                                                                      
    "intrinsicGas": 21064,                                                                      
    "adjustedRefund": 0,                                                                        
    "failed": false,                                                                            
    "txHash": "0x23eb17459bc5cbdea76949afa741a4cdcec06183d26de389c6d3b168be3800be",             
    "blockTimestamp": 1746807439,                                                               
    "blockNumber": 17,                                                                          
    "dim": [                                                                                    
      {                                                                                         
        "pc": 0,                                                                                
        "op": "PUSH1",                                                                          
        "depth": 1,                                                                             
        "cost": 3,                                                                              
        "cpu": 3                                                                                
      },    
      {                                                                                         
        "pc": 903,                                                                              
        "op": "SSTORE",                                                                         
        "depth": 1,                                                                             
        "cost": 100,                                                                            
        "rw": 100,                                                                              
        "refund": 4800                                                                          
      },  

txGasDimensionByOpcode

The transaction gas dimensions by opcode tracer returns the total sum of gas consumed by each different opcode, split out by dimension, e.g:

                                                                                                                                                                                             
  "jsonrpc": "2.0",                                                                                                                                                                           
  "id": 1,                                                                                                                                                                                    
  "result": {                                                                                                                                                                                 
    "gasUsed": 1003063,                                                                                                                                                                       
    "gasUsedForL1": 968000,                                                                                                                                                                   
    "gasUsedForL2": 35063,                                                                                                                                                                    
    "intrinsicGas": 21064,                                                                                                                                                                    
    "adjustedRefund": 8765,                                                                                                                                                                   
    "failed": false,                                                                                                                                                                          
    "txHash": "0xe6d0c5c6e330b7d993855afd610d01fc1d8f8257a70b83684a2d24a9418afc5a",                                                                                                           
    "blockTimestamp": 1746646719,                                                                                                                                                             
    "blockNumber": 18,                                                                                                                                                                        
    "dimensions": {                                                                                                                                                                           
      "ADD": {                                                                                                                                                                                
        "gas1d": 6,                                                                                                                                                                              
        "cpu": 6             
      },
      "SLOAD": {                                                                                                                                                                                 
        "gas1d": 2200,                                                                                                                                                                              
        "cpu": 200,                                                                                                                                                                              
        "rw": 2000                                                                                                                                                                               
      },                                                                                                                                                                                         
      "SSTORE": {                                                                                                                                                                                
        "gas1d": 20100,                                                                                                                                                                             
        "cpu": 0,                                                                                                                                                                                
        "rw": 100,                                                                                                                                                                               
        "growth": 20000,                                                                                                                                                                             
        "refund": 19900                                                                                                                                                                              
      },   

Tracer Invocation

The tracers need to have a parameter of a transaction hash to operate upon. If you have that and your node either is an archive node with the state or the transaction hash you're tracing is in the last 128 blocks then you can run the tracer like so:

export TX_HASH_TO_LOOK_AT="0x348f7124e88620273caee233f25472009c5ba92a2d6743ae161045da8c533f6c";
 
curl "http://127.0.0.1:8547" \
-X POST \
-H "Content-Type: application/json" \
--data '{  
    "method":"debug_traceTransaction",
    "params":["'"$TX_HASH_TO_LOOK_AT"'", {"tracer": "txGasDimensionByOpcode"}],
    "id":1,
    "jsonrpc":"2.0"
}' | jq > tx.byOpcode.json;   
                                     
curl "http://127.0.0.1:8547" \
-X POST \
-H "Content-Type: application/json" \
--data '{
    "method":"debug_traceTransaction",
    "params":["'"$TX_HASH_TO_LOOK_AT"'", {"tracer":"txGasDimensionLogger"}],
    "id":1,
    "jsonrpc":"2.0"
}' | jq > tx.Logger.json;

Live Tracers

The live tracers are identical use the native tracers under the hood. The difference with the live tracers is that they dump the output of the tracer to disk in protobuf format to then in in order to save on storage space relative to json. Python or whatever can then generate serialization code from the .proto file to ingest the protobuf output.

Live Tracer Invocation

The live tracers can be run like so:

./target/bin/nitro --dev \
    --execution.vmtrace.tracer-name "txGasDimensionByOpcode" \
    --execution.vmtrace.json-config '{"path":"gas-dimension-logs","chainConfig":{"chainId":42161,"homesteadBlock":0,"daoForkBlock":null,"daoForkSupport":true,"eip150Block":0,"eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"muirGlacierBlock":0,"berlinBlock":0,"londonBlock":0,"clique":{"period":0,"epoch":0},"arbitrum":{"EnableArbOS":true,"AllowDebugPrecompiles":false,"DataAvailabilityCommittee":false,"InitialArbOSVersion":6,"InitialChainOwner":"0xd345e41ae2cb00311956aa7109fc801ae8c81a52","GenesisBlockNum":0}}}'

For example, this will create a directory called gas-dimension-logs in the root of the repo that will have each block as a subfolder, and a protobuf dumped file for the gas dimensions by transaction. The tracer expects a chain configuration because I was unable to get the chain configuration in the tracer creation context without having a bigger diff. So it expects it to be passed in by json - you can use the nitro repository's chain info as a reference for a chain configuration if you don't have one.

this PR is identical to and replaces #457.

relyt29 added 30 commits May 9, 2025 13:42
Support live tracing and by RPC, support all pure-CPU opcodes.
Support the simple state access opcodes.

Rest still TODO
1. EXTCODECOPY with no memory expansion, and warm storage access
2. EXTCODECOPY with memory expansion and warm storage access
3. EXTCODECOPY with memory expansion and cold storage access
4. EXTCODECOPY with no memory expansion and cold storage access
Cold access list SLOAD
Warm access list SLOAD
all of the LOG opcodes with both indexed and unindexed data
Selfdestruct to:
* cold, empty account target, sender has balance
* warm, empty account target, sender has balance
* warm, code/value at target, sender has balance
* warm, code/value at target, no money to send
* cold, code/value at target, sender has balance
Tested:
* calling to a contract that is cold but has code
* calling to a contract that is cold and has no code
Tested:
* calling to a contract that is warm and has code
* calling to a contract that is cold and has code
* calling to a contract that is warm and has no code
* calling to a contract that is cold and has no code
Tested by hand:
* Delegatecall to warm address, to a contract with code
* Delegatecall to cold address, to a contract with code
* Delegatecall to cold address, to a contract with no code
* Delegatecall to warm address, to a contract with no code
Tested by Hand:
* warm access list+ target has code + zero value
* warm access list+ target has code + positive value
* warm access list+ target has no code + zero value
* warm access list+ target has no code + positive value
* cold access list+ target has code + zero value
* cold access list+ target has code + positive value
* cold access list+ target has no code + zero value
* cold access list+ target has no code + positive value
Tested By hand:
* CREATE
* CREATE2
* CREATE sending value
* CREATE2 sending value

sending value does not affect gas dimensions but I tested it anyways
Tested by hand in variations:
* writing to a new slot
* writing to the same slot twice with the same value
* writing to the same slot twice with a different value
* writing to a slot that was uninitialized and setting it back to zero
* writing to a slot that was cold but set to non-zero and setting it to
  nonzero
CALL, DELEGATECALL, EXTCODECOPY, etc. Tested by hand.
relyt29 added 5 commits May 9, 2025 13:42
This PR exposes the GasUsed, L1GasUsed, and L2GasUsed from the
transaction reciept directly in the tracer response. It also changes
the tracers for CALL, CALLCODE, STATICCALL, DELEGATECALL, CREATE and
CREATE2 to explicitly return the gas consumed by the child execution
of the call when the call stack depth is increased by those opcodes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
s CLA signed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant