github.com/ethereum/go-ethereum@v1.16.1/eth/tracers/api_test.go (about) 1 // Copyright 2021 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package tracers 18 19 import ( 20 "context" 21 "crypto/ecdsa" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "math/big" 26 "os" 27 "reflect" 28 "slices" 29 "sync/atomic" 30 "testing" 31 "time" 32 33 "github.com/ethereum/go-ethereum/common" 34 "github.com/ethereum/go-ethereum/common/hexutil" 35 "github.com/ethereum/go-ethereum/consensus" 36 "github.com/ethereum/go-ethereum/consensus/beacon" 37 "github.com/ethereum/go-ethereum/consensus/ethash" 38 "github.com/ethereum/go-ethereum/core" 39 "github.com/ethereum/go-ethereum/core/rawdb" 40 "github.com/ethereum/go-ethereum/core/state" 41 "github.com/ethereum/go-ethereum/core/tracing" 42 "github.com/ethereum/go-ethereum/core/types" 43 "github.com/ethereum/go-ethereum/core/vm" 44 "github.com/ethereum/go-ethereum/crypto" 45 "github.com/ethereum/go-ethereum/eth/tracers/logger" 46 "github.com/ethereum/go-ethereum/ethdb" 47 "github.com/ethereum/go-ethereum/internal/ethapi" 48 "github.com/ethereum/go-ethereum/internal/ethapi/override" 49 "github.com/ethereum/go-ethereum/params" 50 "github.com/ethereum/go-ethereum/rpc" 51 ) 52 53 var ( 54 errStateNotFound = errors.New("state not found") 55 errBlockNotFound = errors.New("block not found") 56 ) 57 58 type testBackend struct { 59 chainConfig *params.ChainConfig 60 engine consensus.Engine 61 chaindb ethdb.Database 62 chain *core.BlockChain 63 64 refHook func() // Hook is invoked when the requested state is referenced 65 relHook func() // Hook is invoked when the requested state is released 66 } 67 68 // newTestBackend creates a new test backend. OBS: After test is done, teardown must be 69 // invoked in order to release associated resources. 70 func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend { 71 backend := &testBackend{ 72 chainConfig: gspec.Config, 73 engine: ethash.NewFaker(), 74 chaindb: rawdb.NewMemoryDatabase(), 75 } 76 // Generate blocks for testing 77 _, blocks, _ := core.GenerateChainWithGenesis(gspec, backend.engine, n, generator) 78 79 // Import the canonical chain 80 options := &core.BlockChainConfig{ 81 TrieCleanLimit: 256, 82 TrieDirtyLimit: 256, 83 TrieTimeLimit: 5 * time.Minute, 84 SnapshotLimit: 0, 85 ArchiveMode: true, // Archive mode 86 } 87 chain, err := core.NewBlockChain(backend.chaindb, gspec, backend.engine, options) 88 if err != nil { 89 t.Fatalf("failed to create tester chain: %v", err) 90 } 91 if n, err := chain.InsertChain(blocks); err != nil { 92 t.Fatalf("block %d: failed to insert into chain: %v", n, err) 93 } 94 backend.chain = chain 95 return backend 96 } 97 98 func (b *testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { 99 return b.chain.GetHeaderByHash(hash), nil 100 } 101 102 func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { 103 if number == rpc.PendingBlockNumber || number == rpc.LatestBlockNumber { 104 return b.chain.CurrentHeader(), nil 105 } 106 return b.chain.GetHeaderByNumber(uint64(number)), nil 107 } 108 109 func (b *testBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { 110 return b.chain.GetBlockByHash(hash), nil 111 } 112 113 func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { 114 if number == rpc.PendingBlockNumber || number == rpc.LatestBlockNumber { 115 return b.chain.GetBlockByNumber(b.chain.CurrentBlock().Number.Uint64()), nil 116 } 117 return b.chain.GetBlockByNumber(uint64(number)), nil 118 } 119 120 func (b *testBackend) GetCanonicalTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) { 121 tx, hash, blockNumber, index := rawdb.ReadCanonicalTransaction(b.chaindb, txHash) 122 return tx != nil, tx, hash, blockNumber, index 123 } 124 125 func (b *testBackend) TxIndexDone() bool { 126 return true 127 } 128 129 func (b *testBackend) RPCGasCap() uint64 { 130 return 25000000 131 } 132 133 func (b *testBackend) ChainConfig() *params.ChainConfig { 134 return b.chainConfig 135 } 136 137 func (b *testBackend) Engine() consensus.Engine { 138 return b.engine 139 } 140 141 func (b *testBackend) ChainDb() ethdb.Database { 142 return b.chaindb 143 } 144 145 // teardown releases the associated resources. 146 func (b *testBackend) teardown() { 147 b.chain.Stop() 148 } 149 150 func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, StateReleaseFunc, error) { 151 statedb, err := b.chain.StateAt(block.Root()) 152 if err != nil { 153 return nil, nil, errStateNotFound 154 } 155 if b.refHook != nil { 156 b.refHook() 157 } 158 release := func() { 159 if b.relHook != nil { 160 b.relHook() 161 } 162 } 163 return statedb, release, nil 164 } 165 166 func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) { 167 parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) 168 if parent == nil { 169 return nil, vm.BlockContext{}, nil, nil, errBlockNotFound 170 } 171 statedb, release, err := b.StateAtBlock(ctx, parent, reexec, nil, true, false) 172 if err != nil { 173 return nil, vm.BlockContext{}, nil, nil, errStateNotFound 174 } 175 if txIndex == 0 && len(block.Transactions()) == 0 { 176 return nil, vm.BlockContext{}, statedb, release, nil 177 } 178 // Recompute transactions up to the target index. 179 signer := types.MakeSigner(b.chainConfig, block.Number(), block.Time()) 180 context := core.NewEVMBlockContext(block.Header(), b.chain, nil) 181 evm := vm.NewEVM(context, statedb, b.chainConfig, vm.Config{}) 182 for idx, tx := range block.Transactions() { 183 if idx == txIndex { 184 return tx, context, statedb, release, nil 185 } 186 msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) 187 if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { 188 return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) 189 } 190 statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number())) 191 } 192 return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) 193 } 194 195 type stateTracer struct { 196 Balance map[common.Address]*hexutil.Big 197 Nonce map[common.Address]hexutil.Uint64 198 Storage map[common.Address]map[common.Hash]common.Hash 199 } 200 201 func newStateTracer(ctx *Context, cfg json.RawMessage, chainCfg *params.ChainConfig) (*Tracer, error) { 202 t := &stateTracer{ 203 Balance: make(map[common.Address]*hexutil.Big), 204 Nonce: make(map[common.Address]hexutil.Uint64), 205 Storage: make(map[common.Address]map[common.Hash]common.Hash), 206 } 207 return &Tracer{ 208 GetResult: func() (json.RawMessage, error) { 209 return json.Marshal(t) 210 }, 211 Hooks: &tracing.Hooks{ 212 OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { 213 t.Balance[addr] = (*hexutil.Big)(new) 214 }, 215 OnNonceChange: func(addr common.Address, prev, new uint64) { 216 t.Nonce[addr] = hexutil.Uint64(new) 217 }, 218 OnStorageChange: func(addr common.Address, slot common.Hash, prev, new common.Hash) { 219 if t.Storage[addr] == nil { 220 t.Storage[addr] = make(map[common.Hash]common.Hash) 221 } 222 t.Storage[addr][slot] = new 223 }, 224 }, 225 }, nil 226 } 227 228 func TestStateHooks(t *testing.T) { 229 t.Parallel() 230 231 // Initialize test accounts 232 var ( 233 key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 234 from = crypto.PubkeyToAddress(key.PublicKey) 235 to = common.HexToAddress("0x00000000000000000000000000000000deadbeef") 236 genesis = &core.Genesis{ 237 Config: params.TestChainConfig, 238 Alloc: types.GenesisAlloc{ 239 from: {Balance: big.NewInt(params.Ether)}, 240 to: { 241 Code: []byte{ 242 byte(vm.PUSH1), 0x2a, // stack: [42] 243 byte(vm.PUSH1), 0x0, // stack: [0, 42] 244 byte(vm.SSTORE), // stack: [] 245 byte(vm.STOP), 246 }, 247 }, 248 }, 249 } 250 genBlocks = 2 251 signer = types.HomesteadSigner{} 252 nonce = uint64(0) 253 backend = newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 254 // Transfer from account[0] to account[1] 255 // value: 1000 wei 256 // fee: 0 wei 257 tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ 258 Nonce: nonce, 259 To: &to, 260 Value: big.NewInt(1000), 261 Gas: params.TxGas, 262 GasPrice: b.BaseFee(), 263 Data: nil}), 264 signer, key) 265 b.AddTx(tx) 266 nonce++ 267 }) 268 ) 269 defer backend.teardown() 270 DefaultDirectory.Register("stateTracer", newStateTracer, false) 271 api := NewAPI(backend) 272 tracer := "stateTracer" 273 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}}) 274 if err != nil { 275 t.Fatalf("failed to trace call: %v", err) 276 } 277 expected := `{"Balance":{"0x00000000000000000000000000000000deadbeef":"0x3e8","0x71562b71999873db5b286df957af199ec94617f7":"0xde0975924ed6f90"},"Nonce":{"0x71562b71999873db5b286df957af199ec94617f7":"0x3"},"Storage":{"0x00000000000000000000000000000000deadbeef":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x000000000000000000000000000000000000000000000000000000000000002a"}}}` 278 if expected != fmt.Sprintf("%s", res) { 279 t.Fatalf("unexpected trace result: have %s want %s", res, expected) 280 } 281 } 282 283 func TestTraceCall(t *testing.T) { 284 t.Parallel() 285 286 // Initialize test accounts 287 accounts := newAccounts(3) 288 genesis := &core.Genesis{ 289 Config: params.TestChainConfig, 290 Alloc: types.GenesisAlloc{ 291 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 292 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 293 accounts[2].addr: {Balance: big.NewInt(params.Ether)}, 294 }, 295 } 296 genBlocks := 10 297 signer := types.HomesteadSigner{} 298 nonce := uint64(0) 299 backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 300 // Transfer from account[0] to account[1] 301 // value: 1000 wei 302 // fee: 0 wei 303 tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ 304 Nonce: nonce, 305 To: &accounts[1].addr, 306 Value: big.NewInt(1000), 307 Gas: params.TxGas, 308 GasPrice: b.BaseFee(), 309 Data: nil}), 310 signer, accounts[0].key) 311 b.AddTx(tx) 312 nonce++ 313 314 if i == genBlocks-2 { 315 // Transfer from account[0] to account[2] 316 tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{ 317 Nonce: nonce, 318 To: &accounts[2].addr, 319 Value: big.NewInt(1000), 320 Gas: params.TxGas, 321 GasPrice: b.BaseFee(), 322 Data: nil}), 323 signer, accounts[0].key) 324 b.AddTx(tx) 325 nonce++ 326 327 // Transfer from account[0] to account[1] again 328 tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{ 329 Nonce: nonce, 330 To: &accounts[1].addr, 331 Value: big.NewInt(1000), 332 Gas: params.TxGas, 333 GasPrice: b.BaseFee(), 334 Data: nil}), 335 signer, accounts[0].key) 336 b.AddTx(tx) 337 nonce++ 338 } 339 }) 340 341 uintPtr := func(i int) *hexutil.Uint { x := hexutil.Uint(i); return &x } 342 343 defer backend.teardown() 344 api := NewAPI(backend) 345 var testSuite = []struct { 346 blockNumber rpc.BlockNumber 347 call ethapi.TransactionArgs 348 config *TraceCallConfig 349 expectErr error 350 expect string 351 }{ 352 // Standard JSON trace upon the genesis, plain transfer. 353 { 354 blockNumber: rpc.BlockNumber(0), 355 call: ethapi.TransactionArgs{ 356 From: &accounts[0].addr, 357 To: &accounts[1].addr, 358 Value: (*hexutil.Big)(big.NewInt(1000)), 359 }, 360 config: nil, 361 expectErr: nil, 362 expect: `{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}`, 363 }, 364 // Standard JSON trace upon the head, plain transfer. 365 { 366 blockNumber: rpc.BlockNumber(genBlocks), 367 call: ethapi.TransactionArgs{ 368 From: &accounts[0].addr, 369 To: &accounts[1].addr, 370 Value: (*hexutil.Big)(big.NewInt(1000)), 371 }, 372 config: nil, 373 expectErr: nil, 374 expect: `{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}`, 375 }, 376 // Upon the last state, default to the post block's state 377 { 378 blockNumber: rpc.BlockNumber(genBlocks - 1), 379 call: ethapi.TransactionArgs{ 380 From: &accounts[2].addr, 381 To: &accounts[0].addr, 382 Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), 383 }, 384 config: nil, 385 expect: `{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}`, 386 }, 387 // Before the first transaction, should be failed 388 { 389 blockNumber: rpc.BlockNumber(genBlocks - 1), 390 call: ethapi.TransactionArgs{ 391 From: &accounts[2].addr, 392 To: &accounts[0].addr, 393 Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), 394 }, 395 config: &TraceCallConfig{TxIndex: uintPtr(0)}, 396 expectErr: fmt.Errorf("tracing failed: insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr), 397 }, 398 // Before the target transaction, should be failed 399 { 400 blockNumber: rpc.BlockNumber(genBlocks - 1), 401 call: ethapi.TransactionArgs{ 402 From: &accounts[2].addr, 403 To: &accounts[0].addr, 404 Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), 405 }, 406 config: &TraceCallConfig{TxIndex: uintPtr(1)}, 407 expectErr: fmt.Errorf("tracing failed: insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr), 408 }, 409 // After the target transaction, should be succeeded 410 { 411 blockNumber: rpc.BlockNumber(genBlocks - 1), 412 call: ethapi.TransactionArgs{ 413 From: &accounts[2].addr, 414 To: &accounts[0].addr, 415 Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), 416 }, 417 config: &TraceCallConfig{TxIndex: uintPtr(2)}, 418 expectErr: nil, 419 expect: `{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}`, 420 }, 421 // Standard JSON trace upon the non-existent block, error expects 422 { 423 blockNumber: rpc.BlockNumber(genBlocks + 1), 424 call: ethapi.TransactionArgs{ 425 From: &accounts[0].addr, 426 To: &accounts[1].addr, 427 Value: (*hexutil.Big)(big.NewInt(1000)), 428 }, 429 config: nil, 430 expectErr: fmt.Errorf("block #%d not found", genBlocks+1), 431 //expect: nil, 432 }, 433 // Standard JSON trace upon the latest block 434 { 435 blockNumber: rpc.LatestBlockNumber, 436 call: ethapi.TransactionArgs{ 437 From: &accounts[0].addr, 438 To: &accounts[1].addr, 439 Value: (*hexutil.Big)(big.NewInt(1000)), 440 }, 441 config: nil, 442 expectErr: nil, 443 expect: `{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}`, 444 }, 445 // Tracing on 'pending' should fail: 446 { 447 blockNumber: rpc.PendingBlockNumber, 448 call: ethapi.TransactionArgs{ 449 From: &accounts[0].addr, 450 To: &accounts[1].addr, 451 Value: (*hexutil.Big)(big.NewInt(1000)), 452 }, 453 config: nil, 454 expectErr: errors.New("tracing on top of pending is not supported"), 455 }, 456 { 457 blockNumber: rpc.LatestBlockNumber, 458 call: ethapi.TransactionArgs{ 459 From: &accounts[0].addr, 460 Input: &hexutil.Bytes{0x43}, // blocknumber 461 }, 462 config: &TraceCallConfig{ 463 BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, 464 }, 465 expectErr: nil, 466 expect: ` {"gas":53018,"failed":false,"returnValue":"0x","structLogs":[ 467 {"pc":0,"op":"NUMBER","gas":24946984,"gasCost":2,"depth":1,"stack":[]}, 468 {"pc":1,"op":"STOP","gas":24946982,"gasCost":0,"depth":1,"stack":["0x1337"]}]}`, 469 }, 470 } 471 for i, testspec := range testSuite { 472 result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config) 473 if testspec.expectErr != nil { 474 if err == nil { 475 t.Errorf("test %d: expect error %v, got nothing", i, testspec.expectErr) 476 continue 477 } 478 if !reflect.DeepEqual(err.Error(), testspec.expectErr.Error()) { 479 t.Errorf("test %d: error mismatch, want '%v', got '%v'", i, testspec.expectErr, err) 480 } 481 } else { 482 if err != nil { 483 t.Errorf("test %d: expect no error, got %v", i, err) 484 continue 485 } 486 var have *logger.ExecutionResult 487 if err := json.Unmarshal(result.(json.RawMessage), &have); err != nil { 488 t.Errorf("test %d: failed to unmarshal result %v", i, err) 489 } 490 var want *logger.ExecutionResult 491 if err := json.Unmarshal([]byte(testspec.expect), &want); err != nil { 492 t.Errorf("test %d: failed to unmarshal result %v", i, err) 493 } 494 if !reflect.DeepEqual(have, want) { 495 t.Errorf("test %d: result mismatch, want %v, got %v", i, testspec.expect, string(result.(json.RawMessage))) 496 } 497 } 498 } 499 } 500 501 func TestTraceTransaction(t *testing.T) { 502 t.Parallel() 503 504 // Initialize test accounts 505 accounts := newAccounts(2) 506 genesis := &core.Genesis{ 507 Config: params.TestChainConfig, 508 Alloc: types.GenesisAlloc{ 509 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 510 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 511 }, 512 } 513 target := common.Hash{} 514 signer := types.HomesteadSigner{} 515 backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { 516 // Transfer from account[0] to account[1] 517 // value: 1000 wei 518 // fee: 0 wei 519 tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ 520 Nonce: uint64(i), 521 To: &accounts[1].addr, 522 Value: big.NewInt(1000), 523 Gas: params.TxGas, 524 GasPrice: b.BaseFee(), 525 Data: nil}), 526 signer, accounts[0].key) 527 b.AddTx(tx) 528 target = tx.Hash() 529 }) 530 defer backend.chain.Stop() 531 api := NewAPI(backend) 532 result, err := api.TraceTransaction(context.Background(), target, nil) 533 if err != nil { 534 t.Errorf("Failed to trace transaction %v", err) 535 } 536 var have *logger.ExecutionResult 537 if err := json.Unmarshal(result.(json.RawMessage), &have); err != nil { 538 t.Errorf("failed to unmarshal result %v", err) 539 } 540 if !reflect.DeepEqual(have, &logger.ExecutionResult{ 541 Gas: params.TxGas, 542 Failed: false, 543 ReturnValue: []byte{}, 544 StructLogs: []json.RawMessage{}, 545 }) { 546 t.Error("Transaction tracing result is different") 547 } 548 549 // Test non-existent transaction 550 _, err = api.TraceTransaction(context.Background(), common.Hash{42}, nil) 551 if !errors.Is(err, errTxNotFound) { 552 t.Fatalf("want %v, have %v", errTxNotFound, err) 553 } 554 } 555 556 func TestTraceBlock(t *testing.T) { 557 t.Parallel() 558 559 // Initialize test accounts 560 accounts := newAccounts(3) 561 genesis := &core.Genesis{ 562 Config: params.TestChainConfig, 563 Alloc: types.GenesisAlloc{ 564 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 565 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 566 accounts[2].addr: {Balance: big.NewInt(params.Ether)}, 567 }, 568 } 569 genBlocks := 10 570 signer := types.HomesteadSigner{} 571 var txHash common.Hash 572 backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 573 // Transfer from account[0] to account[1] 574 // value: 1000 wei 575 // fee: 0 wei 576 tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ 577 Nonce: uint64(i), 578 To: &accounts[1].addr, 579 Value: big.NewInt(1000), 580 Gas: params.TxGas, 581 GasPrice: b.BaseFee(), 582 Data: nil}), 583 signer, accounts[0].key) 584 b.AddTx(tx) 585 txHash = tx.Hash() 586 }) 587 defer backend.chain.Stop() 588 api := NewAPI(backend) 589 590 var testSuite = []struct { 591 blockNumber rpc.BlockNumber 592 config *TraceConfig 593 want string 594 expectErr error 595 }{ 596 // Trace genesis block, expect error 597 { 598 blockNumber: rpc.BlockNumber(0), 599 expectErr: errors.New("genesis is not traceable"), 600 }, 601 // Trace head block 602 { 603 blockNumber: rpc.BlockNumber(genBlocks), 604 want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}}]`, txHash), 605 }, 606 // Trace non-existent block 607 { 608 blockNumber: rpc.BlockNumber(genBlocks + 1), 609 expectErr: fmt.Errorf("block #%d not found", genBlocks+1), 610 }, 611 // Trace latest block 612 { 613 blockNumber: rpc.LatestBlockNumber, 614 want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}}]`, txHash), 615 }, 616 // Trace pending block 617 { 618 blockNumber: rpc.PendingBlockNumber, 619 want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}}]`, txHash), 620 }, 621 } 622 for i, tc := range testSuite { 623 result, err := api.TraceBlockByNumber(context.Background(), tc.blockNumber, tc.config) 624 if tc.expectErr != nil { 625 if err == nil { 626 t.Errorf("test %d, want error %v", i, tc.expectErr) 627 continue 628 } 629 if !reflect.DeepEqual(err, tc.expectErr) { 630 t.Errorf("test %d: error mismatch, want %v, get %v", i, tc.expectErr, err) 631 } 632 continue 633 } 634 if err != nil { 635 t.Errorf("test %d, want no error, have %v", i, err) 636 continue 637 } 638 have, _ := json.Marshal(result) 639 want := tc.want 640 if string(have) != want { 641 t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, string(have), want) 642 } 643 } 644 } 645 646 func TestTracingWithOverrides(t *testing.T) { 647 t.Parallel() 648 // Initialize test accounts 649 accounts := newAccounts(3) 650 ecRecoverAddress := common.HexToAddress("0x0000000000000000000000000000000000000001") 651 storageAccount := common.Address{0x13, 37} 652 genesis := &core.Genesis{ 653 Config: params.TestChainConfig, 654 Alloc: types.GenesisAlloc{ 655 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 656 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 657 accounts[2].addr: {Balance: big.NewInt(params.Ether)}, 658 // An account with existing storage 659 storageAccount: { 660 Balance: new(big.Int), 661 Storage: map[common.Hash]common.Hash{ 662 common.HexToHash("0x03"): common.HexToHash("0x33"), 663 common.HexToHash("0x04"): common.HexToHash("0x44"), 664 }, 665 }, 666 }, 667 } 668 genBlocks := 10 669 signer := types.HomesteadSigner{} 670 backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 671 // Transfer from account[0] to account[1] 672 // value: 1000 wei 673 // fee: 0 wei 674 tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ 675 Nonce: uint64(i), 676 To: &accounts[1].addr, 677 Value: big.NewInt(1000), 678 Gas: params.TxGas, 679 GasPrice: b.BaseFee(), 680 Data: nil}), 681 signer, accounts[0].key) 682 b.AddTx(tx) 683 }) 684 defer backend.chain.Stop() 685 api := NewAPI(backend) 686 randomAccounts := newAccounts(3) 687 type res struct { 688 Gas int 689 Failed bool 690 ReturnValue string 691 } 692 var testSuite = []struct { 693 blockNumber rpc.BlockNumber 694 call ethapi.TransactionArgs 695 config *TraceCallConfig 696 expectErr error 697 want string 698 }{ 699 // Call which can only succeed if state is state overridden 700 { 701 blockNumber: rpc.LatestBlockNumber, 702 call: ethapi.TransactionArgs{ 703 From: &randomAccounts[0].addr, 704 To: &randomAccounts[1].addr, 705 Value: (*hexutil.Big)(big.NewInt(1000)), 706 }, 707 config: &TraceCallConfig{ 708 StateOverrides: &override.StateOverride{ 709 randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, 710 }, 711 }, 712 want: `{"gas":21000,"failed":false,"returnValue":"0x"}`, 713 }, 714 // Invalid call without state overriding 715 { 716 blockNumber: rpc.LatestBlockNumber, 717 call: ethapi.TransactionArgs{ 718 From: &randomAccounts[0].addr, 719 To: &randomAccounts[1].addr, 720 Value: (*hexutil.Big)(big.NewInt(1000)), 721 }, 722 config: &TraceCallConfig{}, 723 expectErr: core.ErrInsufficientFunds, 724 }, 725 // Successful simple contract call 726 // 727 // // SPDX-License-Identifier: GPL-3.0 728 // 729 // pragma solidity >=0.7.0 <0.8.0; 730 // 731 // /** 732 // * @title Storage 733 // * @dev Store & retrieve value in a variable 734 // */ 735 // contract Storage { 736 // uint256 public number; 737 // constructor() { 738 // number = block.number; 739 // } 740 // } 741 { 742 blockNumber: rpc.LatestBlockNumber, 743 call: ethapi.TransactionArgs{ 744 From: &randomAccounts[0].addr, 745 To: &randomAccounts[2].addr, 746 Data: newRPCBytes(common.Hex2Bytes("8381f58a")), // call number() 747 }, 748 config: &TraceCallConfig{ 749 //Tracer: &tracer, 750 StateOverrides: &override.StateOverride{ 751 randomAccounts[2].addr: override.OverrideAccount{ 752 Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")), 753 StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}), 754 }, 755 }, 756 }, 757 want: `{"gas":23347,"failed":false,"returnValue":"0x000000000000000000000000000000000000000000000000000000000000007b"}`, 758 }, 759 { // Override blocknumber 760 blockNumber: rpc.LatestBlockNumber, 761 call: ethapi.TransactionArgs{ 762 From: &accounts[0].addr, 763 // BLOCKNUMBER PUSH1 MSTORE 764 Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")), 765 }, 766 config: &TraceCallConfig{ 767 BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, 768 }, 769 want: `{"gas":59537,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000001337"}`, 770 }, 771 { // Override blocknumber, and query a blockhash 772 blockNumber: rpc.LatestBlockNumber, 773 call: ethapi.TransactionArgs{ 774 From: &accounts[0].addr, 775 Input: &hexutil.Bytes{ 776 0x60, 0x00, 0x40, // BLOCKHASH(0) 777 0x60, 0x00, 0x52, // STORE memory offset 0 778 0x61, 0x13, 0x36, 0x40, // BLOCKHASH(0x1336) 779 0x60, 0x20, 0x52, // STORE memory offset 32 780 0x61, 0x13, 0x37, 0x40, // BLOCKHASH(0x1337) 781 0x60, 0x40, 0x52, // STORE memory offset 64 782 0x60, 0x60, 0x60, 0x00, 0xf3, // RETURN (0-96) 783 784 }, // blocknumber 785 }, 786 config: &TraceCallConfig{ 787 BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, 788 }, 789 want: `{"gas":72666,"failed":false,"returnValue":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`, 790 }, 791 /* 792 pragma solidity =0.8.12; 793 794 contract Test { 795 uint private x; 796 797 function test2() external { 798 x = 1337; 799 revert(); 800 } 801 802 function test() external returns (uint) { 803 x = 1; 804 try this.test2() {} catch (bytes memory) {} 805 return x; 806 } 807 } 808 */ 809 { // First with only code override, not storage override 810 blockNumber: rpc.LatestBlockNumber, 811 call: ethapi.TransactionArgs{ 812 From: &randomAccounts[0].addr, 813 To: &randomAccounts[2].addr, 814 Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // 815 }, 816 config: &TraceCallConfig{ 817 StateOverrides: &override.StateOverride{ 818 randomAccounts[2].addr: override.OverrideAccount{ 819 Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), 820 }, 821 }, 822 }, 823 want: `{"gas":44100,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000001"}`, 824 }, 825 { // Same again, this time with storage override 826 blockNumber: rpc.LatestBlockNumber, 827 call: ethapi.TransactionArgs{ 828 From: &randomAccounts[0].addr, 829 To: &randomAccounts[2].addr, 830 Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // 831 }, 832 config: &TraceCallConfig{ 833 StateOverrides: &override.StateOverride{ 834 randomAccounts[2].addr: override.OverrideAccount{ 835 Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), 836 State: newStates([]common.Hash{{}}, []common.Hash{{}}), 837 }, 838 }, 839 }, 840 //want: `{"gas":46900,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000539"}`, 841 want: `{"gas":44100,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000001"}`, 842 }, 843 { // No state override 844 blockNumber: rpc.LatestBlockNumber, 845 call: ethapi.TransactionArgs{ 846 From: &randomAccounts[0].addr, 847 To: &storageAccount, 848 Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // 849 }, 850 config: &TraceCallConfig{ 851 StateOverrides: &override.StateOverride{ 852 storageAccount: override.OverrideAccount{ 853 Code: newRPCBytes([]byte{ 854 // SLOAD(3) + SLOAD(4) (which is 0x77) 855 byte(vm.PUSH1), 0x04, 856 byte(vm.SLOAD), 857 byte(vm.PUSH1), 0x03, 858 byte(vm.SLOAD), 859 byte(vm.ADD), 860 // 0x77 -> MSTORE(0) 861 byte(vm.PUSH1), 0x00, 862 byte(vm.MSTORE), 863 // RETURN (0, 32) 864 byte(vm.PUSH1), 32, 865 byte(vm.PUSH1), 00, 866 byte(vm.RETURN), 867 }), 868 }, 869 }, 870 }, 871 want: `{"gas":25288,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000077"}`, 872 }, 873 { // Full state override 874 // The original storage is 875 // 3: 0x33 876 // 4: 0x44 877 // With a full override, where we set 3:0x11, the slot 4 should be 878 // removed. So SLOT(3)+SLOT(4) should be 0x11. 879 blockNumber: rpc.LatestBlockNumber, 880 call: ethapi.TransactionArgs{ 881 From: &randomAccounts[0].addr, 882 To: &storageAccount, 883 Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // 884 }, 885 config: &TraceCallConfig{ 886 StateOverrides: &override.StateOverride{ 887 storageAccount: override.OverrideAccount{ 888 Code: newRPCBytes([]byte{ 889 // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x00) 890 byte(vm.PUSH1), 0x04, 891 byte(vm.SLOAD), 892 byte(vm.PUSH1), 0x03, 893 byte(vm.SLOAD), 894 byte(vm.ADD), 895 // 0x11 -> MSTORE(0) 896 byte(vm.PUSH1), 0x00, 897 byte(vm.MSTORE), 898 // RETURN (0, 32) 899 byte(vm.PUSH1), 32, 900 byte(vm.PUSH1), 00, 901 byte(vm.RETURN), 902 }), 903 State: newStates( 904 []common.Hash{common.HexToHash("0x03")}, 905 []common.Hash{common.HexToHash("0x11")}), 906 }, 907 }, 908 }, 909 want: `{"gas":25288,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000011"}`, 910 }, 911 { // Partial state override 912 // The original storage is 913 // 3: 0x33 914 // 4: 0x44 915 // With a partial override, where we set 3:0x11, the slot 4 as before. 916 // So SLOT(3)+SLOT(4) should be 0x55. 917 blockNumber: rpc.LatestBlockNumber, 918 call: ethapi.TransactionArgs{ 919 From: &randomAccounts[0].addr, 920 To: &storageAccount, 921 Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // 922 }, 923 config: &TraceCallConfig{ 924 StateOverrides: &override.StateOverride{ 925 storageAccount: override.OverrideAccount{ 926 Code: newRPCBytes([]byte{ 927 // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x44) 928 byte(vm.PUSH1), 0x04, 929 byte(vm.SLOAD), 930 byte(vm.PUSH1), 0x03, 931 byte(vm.SLOAD), 932 byte(vm.ADD), 933 // 0x55 -> MSTORE(0) 934 byte(vm.PUSH1), 0x00, 935 byte(vm.MSTORE), 936 // RETURN (0, 32) 937 byte(vm.PUSH1), 32, 938 byte(vm.PUSH1), 00, 939 byte(vm.RETURN), 940 }), 941 StateDiff: map[common.Hash]common.Hash{ 942 common.HexToHash("0x03"): common.HexToHash("0x11"), 943 }, 944 }, 945 }, 946 }, 947 want: `{"gas":25288,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000055"}`, 948 }, 949 { // Call to precompile ECREC (0x01), but code was modified to add 1 to input 950 blockNumber: rpc.LatestBlockNumber, 951 call: ethapi.TransactionArgs{ 952 From: &randomAccounts[0].addr, 953 To: &ecRecoverAddress, 954 Data: newRPCBytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")), 955 }, 956 config: &TraceCallConfig{ 957 StateOverrides: &override.StateOverride{ 958 randomAccounts[0].addr: override.OverrideAccount{ 959 Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether))), 960 }, 961 ecRecoverAddress: override.OverrideAccount{ 962 // The code below adds one to input 963 Code: newRPCBytes(common.Hex2Bytes("60003560010160005260206000f3")), 964 MovePrecompileTo: &randomAccounts[2].addr, 965 }, 966 }, 967 }, 968 want: `{"gas":21167,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000002"}`, 969 }, 970 { // Call to ECREC Precompiled on a different address, expect the original behaviour of ECREC precompile 971 blockNumber: rpc.LatestBlockNumber, 972 call: ethapi.TransactionArgs{ 973 From: &randomAccounts[0].addr, 974 To: &randomAccounts[2].addr, // Moved EcRecover 975 Data: newRPCBytes(common.Hex2Bytes("82f3df49d3645876de6313df2bbe9fbce593f21341a7b03acdb9423bc171fcc9000000000000000000000000000000000000000000000000000000000000001cba13918f50da910f2d55a7ea64cf716ba31dad91856f45908dde900530377d8a112d60f36900d18eb8f9d3b4f85a697b545085614509e3520e4b762e35d0d6bd")), 976 }, 977 config: &TraceCallConfig{ 978 StateOverrides: &override.StateOverride{ 979 randomAccounts[0].addr: override.OverrideAccount{ 980 Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether))), 981 }, 982 ecRecoverAddress: override.OverrideAccount{ 983 // The code below adds one to input 984 Code: newRPCBytes(common.Hex2Bytes("60003560010160005260206000f3")), 985 MovePrecompileTo: &randomAccounts[2].addr, // Move EcRecover to this address 986 }, 987 }, 988 }, 989 want: `{"gas":25664,"failed":false,"returnValue":"0x000000000000000000000000c6e93f4c1920eaeaa1e699f76a7a8c18e3056074"}`, 990 }, 991 } 992 for i, tc := range testSuite { 993 result, err := api.TraceCall(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, tc.config) 994 if tc.expectErr != nil { 995 if err == nil { 996 t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr) 997 continue 998 } 999 if !errors.Is(err, tc.expectErr) { 1000 t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err) 1001 } 1002 continue 1003 } 1004 if err != nil { 1005 t.Errorf("test %d: want no error, have %v", i, err) 1006 continue 1007 } 1008 // Turn result into res-struct 1009 var ( 1010 have res 1011 want res 1012 ) 1013 resBytes, _ := json.Marshal(result) 1014 json.Unmarshal(resBytes, &have) 1015 json.Unmarshal([]byte(tc.want), &want) 1016 if !reflect.DeepEqual(have, want) { 1017 t.Logf("result: %v\n", string(resBytes)) 1018 t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, have, want) 1019 } 1020 } 1021 } 1022 1023 type Account struct { 1024 key *ecdsa.PrivateKey 1025 addr common.Address 1026 } 1027 1028 func newAccounts(n int) (accounts []Account) { 1029 for i := 0; i < n; i++ { 1030 key, _ := crypto.GenerateKey() 1031 addr := crypto.PubkeyToAddress(key.PublicKey) 1032 accounts = append(accounts, Account{key: key, addr: addr}) 1033 } 1034 slices.SortFunc(accounts, func(a, b Account) int { return a.addr.Cmp(b.addr) }) 1035 return accounts 1036 } 1037 1038 func newRPCBalance(balance *big.Int) *hexutil.Big { 1039 rpcBalance := (*hexutil.Big)(balance) 1040 return rpcBalance 1041 } 1042 1043 func newRPCBytes(bytes []byte) *hexutil.Bytes { 1044 rpcBytes := hexutil.Bytes(bytes) 1045 return &rpcBytes 1046 } 1047 1048 func newStates(keys []common.Hash, vals []common.Hash) map[common.Hash]common.Hash { 1049 if len(keys) != len(vals) { 1050 panic("invalid input") 1051 } 1052 m := make(map[common.Hash]common.Hash) 1053 for i := 0; i < len(keys); i++ { 1054 m[keys[i]] = vals[i] 1055 } 1056 return m 1057 } 1058 1059 func TestTraceChain(t *testing.T) { 1060 // Initialize test accounts 1061 accounts := newAccounts(3) 1062 genesis := &core.Genesis{ 1063 Config: params.TestChainConfig, 1064 Alloc: types.GenesisAlloc{ 1065 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 1066 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 1067 accounts[2].addr: {Balance: big.NewInt(params.Ether)}, 1068 }, 1069 } 1070 genBlocks := 50 1071 signer := types.HomesteadSigner{} 1072 1073 var ( 1074 ref atomic.Uint32 // total refs has made 1075 rel atomic.Uint32 // total rels has made 1076 nonce uint64 1077 ) 1078 backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 1079 // Transfer from account[0] to account[1] 1080 // value: 1000 wei 1081 // fee: 0 wei 1082 for j := 0; j < i+1; j++ { 1083 tx, _ := types.SignTx(types.NewTransaction(nonce, accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) 1084 b.AddTx(tx) 1085 nonce += 1 1086 } 1087 }) 1088 backend.refHook = func() { ref.Add(1) } 1089 backend.relHook = func() { rel.Add(1) } 1090 api := NewAPI(backend) 1091 1092 single := `{"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000","result":{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}}` 1093 var cases = []struct { 1094 start uint64 1095 end uint64 1096 config *TraceConfig 1097 }{ 1098 {0, 50, nil}, // the entire chain range, blocks [1, 50] 1099 {10, 20, nil}, // the middle chain range, blocks [11, 20] 1100 } 1101 for _, c := range cases { 1102 ref.Store(0) 1103 rel.Store(0) 1104 1105 from, _ := api.blockByNumber(context.Background(), rpc.BlockNumber(c.start)) 1106 to, _ := api.blockByNumber(context.Background(), rpc.BlockNumber(c.end)) 1107 resCh := api.traceChain(from, to, c.config, nil) 1108 1109 next := c.start + 1 1110 for result := range resCh { 1111 if have, want := uint64(result.Block), next; have != want { 1112 t.Fatalf("unexpected tracing block, have %d want %d", have, want) 1113 } 1114 if have, want := len(result.Traces), int(next); have != want { 1115 t.Fatalf("unexpected result length, have %d want %d", have, want) 1116 } 1117 for _, trace := range result.Traces { 1118 trace.TxHash = common.Hash{} 1119 blob, _ := json.Marshal(trace) 1120 if have, want := string(blob), single; have != want { 1121 t.Fatalf("unexpected tracing result, have\n%v\nwant:\n%v", have, want) 1122 } 1123 } 1124 next += 1 1125 } 1126 if next != c.end+1 { 1127 t.Error("Missing tracing block") 1128 } 1129 1130 if nref, nrel := ref.Load(), rel.Load(); nref != nrel { 1131 t.Errorf("Ref and deref actions are not equal, ref %d rel %d", nref, nrel) 1132 } 1133 } 1134 } 1135 1136 // newTestMergedBackend creates a post-merge chain 1137 func newTestMergedBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend { 1138 backend := &testBackend{ 1139 chainConfig: gspec.Config, 1140 engine: beacon.New(ethash.NewFaker()), 1141 chaindb: rawdb.NewMemoryDatabase(), 1142 } 1143 // Generate blocks for testing 1144 _, blocks, _ := core.GenerateChainWithGenesis(gspec, backend.engine, n, generator) 1145 1146 // Import the canonical chain 1147 options := &core.BlockChainConfig{ 1148 TrieCleanLimit: 256, 1149 TrieDirtyLimit: 256, 1150 TrieTimeLimit: 5 * time.Minute, 1151 SnapshotLimit: 0, 1152 ArchiveMode: true, // Archive mode 1153 } 1154 chain, err := core.NewBlockChain(backend.chaindb, gspec, backend.engine, options) 1155 if err != nil { 1156 t.Fatalf("failed to create tester chain: %v", err) 1157 } 1158 if n, err := chain.InsertChain(blocks); err != nil { 1159 t.Fatalf("block %d: failed to insert into chain: %v", n, err) 1160 } 1161 backend.chain = chain 1162 return backend 1163 } 1164 1165 func TestTraceBlockWithBasefee(t *testing.T) { 1166 t.Parallel() 1167 accounts := newAccounts(1) 1168 target := common.HexToAddress("0x1111111111111111111111111111111111111111") 1169 genesis := &core.Genesis{ 1170 Config: params.AllDevChainProtocolChanges, 1171 Alloc: types.GenesisAlloc{ 1172 accounts[0].addr: {Balance: big.NewInt(1 * params.Ether)}, 1173 target: {Nonce: 1, Code: []byte{ 1174 byte(vm.BASEFEE), byte(vm.STOP), 1175 }}, 1176 }, 1177 } 1178 genBlocks := 1 1179 signer := types.HomesteadSigner{} 1180 var txHash common.Hash 1181 var baseFee = new(big.Int) 1182 backend := newTestMergedBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 1183 tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ 1184 Nonce: uint64(i), 1185 To: &target, 1186 Value: big.NewInt(0), 1187 Gas: 5 * params.TxGas, 1188 GasPrice: b.BaseFee(), 1189 Data: nil}), 1190 signer, accounts[0].key) 1191 b.AddTx(tx) 1192 txHash = tx.Hash() 1193 baseFee.Set(b.BaseFee()) 1194 }) 1195 defer backend.chain.Stop() 1196 api := NewAPI(backend) 1197 1198 var testSuite = []struct { 1199 blockNumber rpc.BlockNumber 1200 config *TraceConfig 1201 want string 1202 }{ 1203 // Trace head block 1204 { 1205 blockNumber: rpc.BlockNumber(genBlocks), 1206 want: fmt.Sprintf(`[{"txHash":"%#x","result":{"gas":21002,"failed":false,"returnValue":"0x","structLogs":[{"pc":0,"op":"BASEFEE","gas":84000,"gasCost":2,"depth":1,"stack":[]},{"pc":1,"op":"STOP","gas":83998,"gasCost":0,"depth":1,"stack":["%#x"]}]}}]`, txHash, baseFee), 1207 }, 1208 } 1209 for i, tc := range testSuite { 1210 result, err := api.TraceBlockByNumber(context.Background(), tc.blockNumber, tc.config) 1211 if err != nil { 1212 t.Errorf("test %d, want no error, have %v", i, err) 1213 continue 1214 } 1215 have, _ := json.Marshal(result) 1216 want := tc.want 1217 if string(have) != want { 1218 t.Errorf("test %d, result mismatch\nhave: %v\nwant: %v\n", i, string(have), want) 1219 } 1220 } 1221 } 1222 1223 func TestStandardTraceBlockToFile(t *testing.T) { 1224 var ( 1225 // A sender who makes transactions, has some funds 1226 key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 1227 address = crypto.PubkeyToAddress(key.PublicKey) 1228 funds = big.NewInt(1000000000000000) 1229 1230 // first contract the sender transacts with 1231 aa = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43") 1232 aaCode = []byte{byte(vm.PUSH1), 0x00, byte(vm.POP)} 1233 1234 // second contract the sender transacts with 1235 bb = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f44") 1236 bbCode = []byte{byte(vm.PUSH2), 0x00, 0x01, byte(vm.POP)} 1237 ) 1238 1239 genesis := &core.Genesis{ 1240 Config: params.TestChainConfig, 1241 Alloc: types.GenesisAlloc{ 1242 address: {Balance: funds}, 1243 aa: { 1244 Code: aaCode, 1245 Nonce: 1, 1246 Balance: big.NewInt(0), 1247 }, 1248 bb: { 1249 Code: bbCode, 1250 Nonce: 1, 1251 Balance: big.NewInt(0), 1252 }, 1253 }, 1254 } 1255 txHashs := make([]common.Hash, 0, 2) 1256 backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { 1257 b.SetCoinbase(common.Address{1}) 1258 // first tx to aa 1259 tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ 1260 Nonce: 0, 1261 To: &aa, 1262 Value: big.NewInt(0), 1263 Gas: 50000, 1264 GasPrice: b.BaseFee(), 1265 Data: nil, 1266 }), types.HomesteadSigner{}, key) 1267 b.AddTx(tx) 1268 txHashs = append(txHashs, tx.Hash()) 1269 // second tx to bb 1270 tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{ 1271 Nonce: 1, 1272 To: &bb, 1273 Value: big.NewInt(1), 1274 Gas: 100000, 1275 GasPrice: b.BaseFee(), 1276 Data: nil, 1277 }), types.HomesteadSigner{}, key) 1278 b.AddTx(tx) 1279 txHashs = append(txHashs, tx.Hash()) 1280 }) 1281 defer backend.chain.Stop() 1282 1283 var testSuite = []struct { 1284 blockNumber rpc.BlockNumber 1285 config *StdTraceConfig 1286 want []string 1287 }{ 1288 { 1289 // test that all traces in the block were outputted if no trace config is specified 1290 blockNumber: rpc.LatestBlockNumber, 1291 config: nil, 1292 want: []string{ 1293 `{"pc":0,"op":96,"gas":"0x7148","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} 1294 {"pc":2,"op":80,"gas":"0x7145","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"POP"} 1295 {"pc":3,"op":0,"gas":"0x7143","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"} 1296 {"output":"","gasUsed":"0x5"} 1297 `, 1298 `{"pc":0,"op":97,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"} 1299 {"pc":3,"op":80,"gas":"0x13495","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"} 1300 {"pc":4,"op":0,"gas":"0x13493","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"} 1301 {"output":"","gasUsed":"0x5"} 1302 `, 1303 }, 1304 }, 1305 { 1306 // test that only a specific tx is traced if specified 1307 blockNumber: rpc.LatestBlockNumber, 1308 config: &StdTraceConfig{TxHash: txHashs[1]}, 1309 want: []string{ 1310 `{"pc":0,"op":97,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"} 1311 {"pc":3,"op":80,"gas":"0x13495","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"} 1312 {"pc":4,"op":0,"gas":"0x13493","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"} 1313 {"output":"","gasUsed":"0x5"} 1314 `, 1315 }, 1316 }, 1317 } 1318 1319 api := NewAPI(backend) 1320 for i, tc := range testSuite { 1321 block, _ := api.blockByNumber(context.Background(), tc.blockNumber) 1322 txTraces, err := api.StandardTraceBlockToFile(context.Background(), block.Hash(), tc.config) 1323 if err != nil { 1324 t.Fatalf("test index %d received error %v", i, err) 1325 } 1326 for j, traceFileName := range txTraces { 1327 traceReceived, err := os.ReadFile(traceFileName) 1328 if err != nil { 1329 t.Fatalf("could not read trace file: %v", err) 1330 } 1331 if tc.want[j] != string(traceReceived) { 1332 t.Fatalf("unexpected trace result. expected\n'%s'\n\nreceived\n'%s'\n", tc.want[j], string(traceReceived)) 1333 } 1334 } 1335 } 1336 }