@@ -37,6 +37,7 @@ import (
3737 "github.com/ethereum/go-ethereum/core"
3838 "github.com/ethereum/go-ethereum/core/rawdb"
3939 "github.com/ethereum/go-ethereum/core/state"
40+ "github.com/ethereum/go-ethereum/core/tracing"
4041 "github.com/ethereum/go-ethereum/core/types"
4142 "github.com/ethereum/go-ethereum/core/vm"
4243 "github.com/ethereum/go-ethereum/crypto"
@@ -185,6 +186,94 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
185186 return nil , vm.BlockContext {}, nil , nil , fmt .Errorf ("transaction index %d out of range for block %#x" , txIndex , block .Hash ())
186187}
187188
189+ type stateTracer struct {
190+ Balance map [common.Address ]* hexutil.Big
191+ Nonce map [common.Address ]hexutil.Uint64
192+ Storage map [common.Address ]map [common.Hash ]common.Hash
193+ }
194+
195+ func newStateTracer (ctx * Context , cfg json.RawMessage , chainCfg * params.ChainConfig ) (* Tracer , error ) {
196+ t := & stateTracer {
197+ Balance : make (map [common.Address ]* hexutil.Big ),
198+ Nonce : make (map [common.Address ]hexutil.Uint64 ),
199+ Storage : make (map [common.Address ]map [common.Hash ]common.Hash ),
200+ }
201+ return & Tracer {
202+ GetResult : func () (json.RawMessage , error ) {
203+ return json .Marshal (t )
204+ },
205+ Hooks : & tracing.Hooks {
206+ OnBalanceChange : func (addr common.Address , prev , new * big.Int , reason tracing.BalanceChangeReason ) {
207+ t .Balance [addr ] = (* hexutil .Big )(new )
208+ },
209+ OnNonceChange : func (addr common.Address , prev , new uint64 ) {
210+ t .Nonce [addr ] = hexutil .Uint64 (new )
211+ },
212+ OnStorageChange : func (addr common.Address , slot common.Hash , prev , new common.Hash ) {
213+ if t .Storage [addr ] == nil {
214+ t .Storage [addr ] = make (map [common.Hash ]common.Hash )
215+ }
216+ t.Storage [addr ][slot ] = new
217+ },
218+ },
219+ }, nil
220+ }
221+
222+ func TestStateHooks (t * testing.T ) {
223+ t .Parallel ()
224+
225+ // Initialize test accounts
226+ var (
227+ key , _ = crypto .HexToECDSA ("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" )
228+ from = crypto .PubkeyToAddress (key .PublicKey )
229+ to = common .HexToAddress ("0x00000000000000000000000000000000deadbeef" )
230+ genesis = & core.Genesis {
231+ Config : params .TestChainConfig ,
232+ Alloc : types.GenesisAlloc {
233+ from : {Balance : big .NewInt (params .Ether )},
234+ to : {
235+ Code : []byte {
236+ byte (vm .PUSH1 ), 0x2a , // stack: [42]
237+ byte (vm .PUSH1 ), 0x0 , // stack: [0, 42]
238+ byte (vm .SSTORE ), // stack: []
239+ byte (vm .STOP ),
240+ },
241+ },
242+ },
243+ }
244+ genBlocks = 2
245+ signer = types.HomesteadSigner {}
246+ nonce = uint64 (0 )
247+ backend = newTestBackend (t , genBlocks , genesis , func (i int , b * core.BlockGen ) {
248+ // Transfer from account[0] to account[1]
249+ // value: 1000 wei
250+ // fee: 0 wei
251+ tx , _ := types .SignTx (types .NewTx (& types.LegacyTx {
252+ Nonce : nonce ,
253+ To : & to ,
254+ Value : big .NewInt (1000 ),
255+ Gas : params .TxGas ,
256+ GasPrice : b .BaseFee (),
257+ Data : nil }),
258+ signer , key )
259+ b .AddTx (tx )
260+ nonce ++
261+ })
262+ )
263+ defer backend .teardown ()
264+ DefaultDirectory .Register ("stateTracer" , newStateTracer , false )
265+ api := NewAPI (backend )
266+ tracer := "stateTracer"
267+ 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 }})
268+ if err != nil {
269+ t .Fatalf ("failed to trace call: %v" , err )
270+ }
271+ expected := `{"Balance":{"0x00000000000000000000000000000000deadbeef":"0x3e8","0x71562b71999873db5b286df957af199ec94617f7":"0xde0975924ed6f90"},"Nonce":{"0x71562b71999873db5b286df957af199ec94617f7":"0x3"},"Storage":{"0x00000000000000000000000000000000deadbeef":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x000000000000000000000000000000000000000000000000000000000000002a"}}}`
272+ if expected != fmt .Sprintf ("%s" , res ) {
273+ t .Fatalf ("unexpected trace result: have %s want %s" , res , expected )
274+ }
275+ }
276+
188277func TestTraceCall (t * testing.T ) {
189278 t .Parallel ()
190279
0 commit comments