Skip to content

Commit

Permalink
eth/tracers: fix state hooks in API (ethereum#30830)
Browse files Browse the repository at this point in the history
When a tx/block was being traced through the API the state hooks weren't
being called as they should. This is due to ethereum#30745 moving the hooked
statedb one level up in the state processor. This PR fixes that.

---------

Co-authored-by: Martin HS <[email protected]>
Co-authored-by: Gary Rong <[email protected]>
  • Loading branch information
3 people authored Nov 29, 2024
1 parent a793bc7 commit ce8cec0
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 2 deletions.
4 changes: 2 additions & 2 deletions eth/tracers/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1007,8 +1007,8 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
return nil, err
}
}
// The actual TxContext will be created as part of ApplyTransactionWithEVM.
evm := vm.NewEVM(vmctx, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})
tracingStateDB := state.NewHookedState(statedb, tracer.Hooks)
evm := vm.NewEVM(vmctx, tracingStateDB, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})

// Define a meaningful timeout of a single transaction trace
if config.Timeout != nil {
Expand Down
89 changes: 89 additions & 0 deletions eth/tracers/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -185,6 +186,94 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
}

type stateTracer struct {
Balance map[common.Address]*hexutil.Big
Nonce map[common.Address]hexutil.Uint64
Storage map[common.Address]map[common.Hash]common.Hash
}

func newStateTracer(ctx *Context, cfg json.RawMessage, chainCfg *params.ChainConfig) (*Tracer, error) {
t := &stateTracer{
Balance: make(map[common.Address]*hexutil.Big),
Nonce: make(map[common.Address]hexutil.Uint64),
Storage: make(map[common.Address]map[common.Hash]common.Hash),
}
return &Tracer{
GetResult: func() (json.RawMessage, error) {
return json.Marshal(t)
},
Hooks: &tracing.Hooks{
OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
t.Balance[addr] = (*hexutil.Big)(new)
},
OnNonceChange: func(addr common.Address, prev, new uint64) {
t.Nonce[addr] = hexutil.Uint64(new)
},
OnStorageChange: func(addr common.Address, slot common.Hash, prev, new common.Hash) {
if t.Storage[addr] == nil {
t.Storage[addr] = make(map[common.Hash]common.Hash)
}
t.Storage[addr][slot] = new
},
},
}, nil
}

func TestStateHooks(t *testing.T) {
t.Parallel()

// Initialize test accounts
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
from = crypto.PubkeyToAddress(key.PublicKey)
to = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
genesis = &core.Genesis{
Config: params.TestChainConfig,
Alloc: types.GenesisAlloc{
from: {Balance: big.NewInt(params.Ether)},
to: {
Code: []byte{
byte(vm.PUSH1), 0x2a, // stack: [42]
byte(vm.PUSH1), 0x0, // stack: [0, 42]
byte(vm.SSTORE), // stack: []
byte(vm.STOP),
},
},
},
}
genBlocks = 2
signer = types.HomesteadSigner{}
nonce = uint64(0)
backend = newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
Nonce: nonce,
To: &to,
Value: big.NewInt(1000),
Gas: params.TxGas,
GasPrice: b.BaseFee(),
Data: nil}),
signer, key)
b.AddTx(tx)
nonce++
})
)
defer backend.teardown()
DefaultDirectory.Register("stateTracer", newStateTracer, false)
api := NewAPI(backend)
tracer := "stateTracer"
res, err := api.TraceCall(context.Background(), ethapi.TransactionArgs{From: &from, To: &to, Value: (*hexutil.Big)(big.NewInt(1000))}, rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber), &TraceCallConfig{TraceConfig: TraceConfig{Tracer: &tracer}})
if err != nil {
t.Fatalf("failed to trace call: %v", err)
}
expected := `{"Balance":{"0x00000000000000000000000000000000deadbeef":"0x3e8","0x71562b71999873db5b286df957af199ec94617f7":"0xde0975924ed6f90"},"Nonce":{"0x71562b71999873db5b286df957af199ec94617f7":"0x3"},"Storage":{"0x00000000000000000000000000000000deadbeef":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x000000000000000000000000000000000000000000000000000000000000002a"}}}`
if expected != fmt.Sprintf("%s", res) {
t.Fatalf("unexpected trace result: have %s want %s", res, expected)
}
}

func TestTraceCall(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit ce8cec0

Please sign in to comment.