github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/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 "bytes" 21 "context" 22 "crypto/ecdsa" 23 "encoding/json" 24 "errors" 25 "fmt" 26 "math/big" 27 "reflect" 28 "sort" 29 "sync/atomic" 30 "testing" 31 "time" 32 33 "github.com/tacshi/go-ethereum/common" 34 "github.com/tacshi/go-ethereum/common/hexutil" 35 "github.com/tacshi/go-ethereum/consensus" 36 "github.com/tacshi/go-ethereum/consensus/ethash" 37 "github.com/tacshi/go-ethereum/core" 38 "github.com/tacshi/go-ethereum/core/rawdb" 39 "github.com/tacshi/go-ethereum/core/state" 40 "github.com/tacshi/go-ethereum/core/types" 41 "github.com/tacshi/go-ethereum/core/vm" 42 "github.com/tacshi/go-ethereum/crypto" 43 "github.com/tacshi/go-ethereum/eth/tracers/logger" 44 "github.com/tacshi/go-ethereum/ethdb" 45 "github.com/tacshi/go-ethereum/internal/ethapi" 46 "github.com/tacshi/go-ethereum/params" 47 "github.com/tacshi/go-ethereum/rpc" 48 ) 49 50 var ( 51 errStateNotFound = errors.New("state not found") 52 errBlockNotFound = errors.New("block not found") 53 ) 54 55 type testBackend struct { 56 chainConfig *params.ChainConfig 57 engine consensus.Engine 58 chaindb ethdb.Database 59 chain *core.BlockChain 60 61 refHook func() // Hook is invoked when the requested state is referenced 62 relHook func() // Hook is invoked when the requested state is released 63 } 64 65 // testBackend creates a new test backend. OBS: After test is done, teardown must be 66 // invoked in order to release associated resources. 67 func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend { 68 backend := &testBackend{ 69 chainConfig: gspec.Config, 70 engine: ethash.NewFaker(), 71 chaindb: rawdb.NewMemoryDatabase(), 72 } 73 // Generate blocks for testing 74 _, blocks, _ := core.GenerateChainWithGenesis(gspec, backend.engine, n, generator) 75 76 // Import the canonical chain 77 cacheConfig := &core.CacheConfig{ 78 79 // Arbitrum 80 TriesInMemory: 128, 81 TrieRetention: 30 * time.Minute, 82 83 TrieCleanLimit: 256, 84 TrieDirtyLimit: 256, 85 TrieTimeLimit: 5 * time.Minute, 86 SnapshotLimit: 0, 87 TrieDirtyDisabled: true, // Archive mode 88 } 89 chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, nil, gspec, nil, backend.engine, vm.Config{}, nil, nil) 90 if err != nil { 91 t.Fatalf("failed to create tester chain: %v", err) 92 } 93 if n, err := chain.InsertChain(blocks); err != nil { 94 t.Fatalf("block %d: failed to insert into chain: %v", n, err) 95 } 96 backend.chain = chain 97 return backend 98 } 99 100 func (b *testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { 101 return b.chain.GetHeaderByHash(hash), nil 102 } 103 104 func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { 105 if number == rpc.PendingBlockNumber || number == rpc.LatestBlockNumber { 106 return b.chain.CurrentHeader(), nil 107 } 108 return b.chain.GetHeaderByNumber(uint64(number)), nil 109 } 110 111 func (b *testBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { 112 return b.chain.GetBlockByHash(hash), nil 113 } 114 115 func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { 116 if number == rpc.PendingBlockNumber || number == rpc.LatestBlockNumber { 117 return b.chain.GetBlockByNumber(b.chain.CurrentBlock().Number.Uint64()), nil 118 } 119 return b.chain.GetBlockByNumber(uint64(number)), nil 120 } 121 122 func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { 123 tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash) 124 return tx, hash, blockNumber, index, nil 125 } 126 127 func (b *testBackend) RPCGasCap() uint64 { 128 return 25000000 129 } 130 131 func (b *testBackend) ChainConfig() *params.ChainConfig { 132 return b.chainConfig 133 } 134 135 func (b *testBackend) Engine() consensus.Engine { 136 return b.engine 137 } 138 139 func (b *testBackend) ChainDb() ethdb.Database { 140 return b.chaindb 141 } 142 143 // teardown releases the associated resources. 144 func (b *testBackend) teardown() { 145 b.chain.Stop() 146 } 147 148 func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, StateReleaseFunc, error) { 149 statedb, err := b.chain.StateAt(block.Root()) 150 if err != nil { 151 return nil, nil, errStateNotFound 152 } 153 if b.refHook != nil { 154 b.refHook() 155 } 156 release := func() { 157 if b.relHook != nil { 158 b.relHook() 159 } 160 } 161 return statedb, release, nil 162 } 163 164 func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) { 165 parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) 166 if parent == nil { 167 return nil, vm.BlockContext{}, nil, nil, errBlockNotFound 168 } 169 statedb, release, err := b.StateAtBlock(ctx, parent, reexec, nil, true, false) 170 if err != nil { 171 return nil, vm.BlockContext{}, nil, nil, errStateNotFound 172 } 173 if txIndex == 0 && len(block.Transactions()) == 0 { 174 return nil, vm.BlockContext{}, statedb, release, nil 175 } 176 // Recompute transactions up to the target index. 177 signer := types.MakeSigner(b.chainConfig, block.Number()) 178 for idx, tx := range block.Transactions() { 179 msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) 180 txContext := core.NewEVMTxContext(msg) 181 context := core.NewEVMBlockContext(block.Header(), b.chain, nil) 182 if idx == txIndex { 183 return msg, context, statedb, release, nil 184 } 185 vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{}) 186 if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { 187 return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) 188 } 189 statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) 190 } 191 return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) 192 } 193 194 func TestTraceCall(t *testing.T) { 195 t.Parallel() 196 197 // Initialize test accounts 198 accounts := newAccounts(3) 199 genesis := &core.Genesis{ 200 Config: params.TestChainConfig, 201 Alloc: core.GenesisAlloc{ 202 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 203 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 204 accounts[2].addr: {Balance: big.NewInt(params.Ether)}, 205 }, 206 } 207 genBlocks := 10 208 signer := types.HomesteadSigner{} 209 backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 210 // Transfer from account[0] to account[1] 211 // value: 1000 wei 212 // fee: 0 wei 213 tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) 214 b.AddTx(tx) 215 }) 216 defer backend.teardown() 217 api := NewAPI(backend) 218 var testSuite = []struct { 219 blockNumber rpc.BlockNumber 220 call ethapi.TransactionArgs 221 config *TraceCallConfig 222 expectErr error 223 expect string 224 }{ 225 // Standard JSON trace upon the genesis, plain transfer. 226 { 227 blockNumber: rpc.BlockNumber(0), 228 call: ethapi.TransactionArgs{ 229 From: &accounts[0].addr, 230 To: &accounts[1].addr, 231 Value: (*hexutil.Big)(big.NewInt(1000)), 232 }, 233 config: nil, 234 expectErr: nil, 235 expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, 236 }, 237 // Standard JSON trace upon the head, plain transfer. 238 { 239 blockNumber: rpc.BlockNumber(genBlocks), 240 call: ethapi.TransactionArgs{ 241 From: &accounts[0].addr, 242 To: &accounts[1].addr, 243 Value: (*hexutil.Big)(big.NewInt(1000)), 244 }, 245 config: nil, 246 expectErr: nil, 247 expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, 248 }, 249 // Standard JSON trace upon the non-existent block, error expects 250 { 251 blockNumber: rpc.BlockNumber(genBlocks + 1), 252 call: ethapi.TransactionArgs{ 253 From: &accounts[0].addr, 254 To: &accounts[1].addr, 255 Value: (*hexutil.Big)(big.NewInt(1000)), 256 }, 257 config: nil, 258 expectErr: fmt.Errorf("block #%d not found", genBlocks+1), 259 //expect: nil, 260 }, 261 // Standard JSON trace upon the latest block 262 { 263 blockNumber: rpc.LatestBlockNumber, 264 call: ethapi.TransactionArgs{ 265 From: &accounts[0].addr, 266 To: &accounts[1].addr, 267 Value: (*hexutil.Big)(big.NewInt(1000)), 268 }, 269 config: nil, 270 expectErr: nil, 271 expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, 272 }, 273 // Tracing on 'pending' should fail: 274 { 275 blockNumber: rpc.PendingBlockNumber, 276 call: ethapi.TransactionArgs{ 277 From: &accounts[0].addr, 278 To: &accounts[1].addr, 279 Value: (*hexutil.Big)(big.NewInt(1000)), 280 }, 281 config: nil, 282 expectErr: errors.New("tracing on top of pending is not supported"), 283 }, 284 { 285 blockNumber: rpc.LatestBlockNumber, 286 call: ethapi.TransactionArgs{ 287 From: &accounts[0].addr, 288 Input: &hexutil.Bytes{0x43}, // blocknumber 289 }, 290 config: &TraceCallConfig{ 291 BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, 292 }, 293 expectErr: nil, 294 expect: ` {"gas":53018,"failed":false,"returnValue":"","structLogs":[ 295 {"pc":0,"op":"NUMBER","gas":24946984,"gasCost":2,"depth":1,"stack":[]}, 296 {"pc":1,"op":"STOP","gas":24946982,"gasCost":0,"depth":1,"stack":["0x1337"]}]}`, 297 }, 298 } 299 for i, testspec := range testSuite { 300 result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config) 301 if testspec.expectErr != nil { 302 if err == nil { 303 t.Errorf("test %d: expect error %v, got nothing", i, testspec.expectErr) 304 continue 305 } 306 if !reflect.DeepEqual(err, testspec.expectErr) { 307 t.Errorf("test %d: error mismatch, want %v, git %v", i, testspec.expectErr, err) 308 } 309 } else { 310 if err != nil { 311 t.Errorf("test %d: expect no error, got %v", i, err) 312 continue 313 } 314 var have *logger.ExecutionResult 315 if err := json.Unmarshal(result.(json.RawMessage), &have); err != nil { 316 t.Errorf("test %d: failed to unmarshal result %v", i, err) 317 } 318 var want *logger.ExecutionResult 319 if err := json.Unmarshal([]byte(testspec.expect), &want); err != nil { 320 t.Errorf("test %d: failed to unmarshal result %v", i, err) 321 } 322 if !reflect.DeepEqual(have, want) { 323 t.Errorf("test %d: result mismatch, want %v, got %v", i, testspec.expect, string(result.(json.RawMessage))) 324 } 325 } 326 } 327 } 328 329 func TestTraceTransaction(t *testing.T) { 330 t.Parallel() 331 332 // Initialize test accounts 333 accounts := newAccounts(2) 334 genesis := &core.Genesis{ 335 Config: params.TestChainConfig, 336 Alloc: core.GenesisAlloc{ 337 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 338 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 339 }, 340 } 341 target := common.Hash{} 342 signer := types.HomesteadSigner{} 343 backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { 344 // Transfer from account[0] to account[1] 345 // value: 1000 wei 346 // fee: 0 wei 347 tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) 348 b.AddTx(tx) 349 target = tx.Hash() 350 }) 351 defer backend.chain.Stop() 352 api := NewAPI(backend) 353 result, err := api.TraceTransaction(context.Background(), target, nil) 354 if err != nil { 355 t.Errorf("Failed to trace transaction %v", err) 356 } 357 var have *logger.ExecutionResult 358 if err := json.Unmarshal(result.(json.RawMessage), &have); err != nil { 359 t.Errorf("failed to unmarshal result %v", err) 360 } 361 if !reflect.DeepEqual(have, &logger.ExecutionResult{ 362 Gas: params.TxGas, 363 Failed: false, 364 ReturnValue: "", 365 StructLogs: []logger.StructLogRes{}, 366 }) { 367 t.Error("Transaction tracing result is different") 368 } 369 370 // Test non-existent transaction 371 _, err = api.TraceTransaction(context.Background(), common.Hash{42}, nil) 372 if !errors.Is(err, errTxNotFound) { 373 t.Fatalf("want %v, have %v", errTxNotFound, err) 374 } 375 } 376 377 func TestTraceBlock(t *testing.T) { 378 t.Parallel() 379 380 // Initialize test accounts 381 accounts := newAccounts(3) 382 genesis := &core.Genesis{ 383 Config: params.TestChainConfig, 384 Alloc: core.GenesisAlloc{ 385 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 386 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 387 accounts[2].addr: {Balance: big.NewInt(params.Ether)}, 388 }, 389 } 390 genBlocks := 10 391 signer := types.HomesteadSigner{} 392 backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 393 // Transfer from account[0] to account[1] 394 // value: 1000 wei 395 // fee: 0 wei 396 tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) 397 b.AddTx(tx) 398 }) 399 defer backend.chain.Stop() 400 api := NewAPI(backend) 401 402 var testSuite = []struct { 403 blockNumber rpc.BlockNumber 404 config *TraceConfig 405 want string 406 expectErr error 407 }{ 408 // Trace genesis block, expect error 409 { 410 blockNumber: rpc.BlockNumber(0), 411 expectErr: errors.New("genesis is not traceable"), 412 }, 413 // Trace head block 414 { 415 blockNumber: rpc.BlockNumber(genBlocks), 416 want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, 417 }, 418 // Trace non-existent block 419 { 420 blockNumber: rpc.BlockNumber(genBlocks + 1), 421 expectErr: fmt.Errorf("block #%d not found", genBlocks+1), 422 }, 423 // Trace latest block 424 { 425 blockNumber: rpc.LatestBlockNumber, 426 want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, 427 }, 428 // Trace pending block 429 { 430 blockNumber: rpc.PendingBlockNumber, 431 want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, 432 }, 433 } 434 for i, tc := range testSuite { 435 result, err := api.TraceBlockByNumber(context.Background(), tc.blockNumber, tc.config) 436 if tc.expectErr != nil { 437 if err == nil { 438 t.Errorf("test %d, want error %v", i, tc.expectErr) 439 continue 440 } 441 if !reflect.DeepEqual(err, tc.expectErr) { 442 t.Errorf("test %d: error mismatch, want %v, get %v", i, tc.expectErr, err) 443 } 444 continue 445 } 446 if err != nil { 447 t.Errorf("test %d, want no error, have %v", i, err) 448 continue 449 } 450 have, _ := json.Marshal(result) 451 want := tc.want 452 if string(have) != want { 453 t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, string(have), want) 454 } 455 } 456 } 457 458 func TestTracingWithOverrides(t *testing.T) { 459 t.Parallel() 460 // Initialize test accounts 461 accounts := newAccounts(3) 462 storageAccount := common.Address{0x13, 37} 463 genesis := &core.Genesis{ 464 Config: params.TestChainConfig, 465 Alloc: core.GenesisAlloc{ 466 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 467 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 468 accounts[2].addr: {Balance: big.NewInt(params.Ether)}, 469 // An account with existing storage 470 storageAccount: { 471 Balance: new(big.Int), 472 Storage: map[common.Hash]common.Hash{ 473 common.HexToHash("0x03"): common.HexToHash("0x33"), 474 common.HexToHash("0x04"): common.HexToHash("0x44"), 475 }, 476 }, 477 }, 478 } 479 genBlocks := 10 480 signer := types.HomesteadSigner{} 481 backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 482 // Transfer from account[0] to account[1] 483 // value: 1000 wei 484 // fee: 0 wei 485 tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) 486 b.AddTx(tx) 487 }) 488 defer backend.chain.Stop() 489 api := NewAPI(backend) 490 randomAccounts := newAccounts(3) 491 type res struct { 492 Gas int 493 Failed bool 494 ReturnValue string 495 } 496 var testSuite = []struct { 497 blockNumber rpc.BlockNumber 498 call ethapi.TransactionArgs 499 config *TraceCallConfig 500 expectErr error 501 want string 502 }{ 503 // Call which can only succeed if state is state overridden 504 { 505 blockNumber: rpc.LatestBlockNumber, 506 call: ethapi.TransactionArgs{ 507 From: &randomAccounts[0].addr, 508 To: &randomAccounts[1].addr, 509 Value: (*hexutil.Big)(big.NewInt(1000)), 510 }, 511 config: &TraceCallConfig{ 512 StateOverrides: ðapi.StateOverride{ 513 randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, 514 }, 515 }, 516 want: `{"gas":21000,"failed":false,"returnValue":""}`, 517 }, 518 // Invalid call without state overriding 519 { 520 blockNumber: rpc.LatestBlockNumber, 521 call: ethapi.TransactionArgs{ 522 From: &randomAccounts[0].addr, 523 To: &randomAccounts[1].addr, 524 Value: (*hexutil.Big)(big.NewInt(1000)), 525 }, 526 config: &TraceCallConfig{}, 527 expectErr: core.ErrInsufficientFunds, 528 }, 529 // Successful simple contract call 530 // 531 // // SPDX-License-Identifier: GPL-3.0 532 // 533 // pragma solidity >=0.7.0 <0.8.0; 534 // 535 // /** 536 // * @title Storage 537 // * @dev Store & retrieve value in a variable 538 // */ 539 // contract Storage { 540 // uint256 public number; 541 // constructor() { 542 // number = block.number; 543 // } 544 // } 545 { 546 blockNumber: rpc.LatestBlockNumber, 547 call: ethapi.TransactionArgs{ 548 From: &randomAccounts[0].addr, 549 To: &randomAccounts[2].addr, 550 Data: newRPCBytes(common.Hex2Bytes("8381f58a")), // call number() 551 }, 552 config: &TraceCallConfig{ 553 //Tracer: &tracer, 554 StateOverrides: ðapi.StateOverride{ 555 randomAccounts[2].addr: ethapi.OverrideAccount{ 556 Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")), 557 StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}), 558 }, 559 }, 560 }, 561 want: `{"gas":23347,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000007b"}`, 562 }, 563 { // Override blocknumber 564 blockNumber: rpc.LatestBlockNumber, 565 call: ethapi.TransactionArgs{ 566 From: &accounts[0].addr, 567 // BLOCKNUMBER PUSH1 MSTORE 568 Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")), 569 //&hexutil.Bytes{0x43}, // blocknumber 570 }, 571 config: &TraceCallConfig{ 572 BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, 573 }, 574 want: `{"gas":59537,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000001337"}`, 575 }, 576 { // Override blocknumber, and query a blockhash 577 blockNumber: rpc.LatestBlockNumber, 578 call: ethapi.TransactionArgs{ 579 From: &accounts[0].addr, 580 Input: &hexutil.Bytes{ 581 0x60, 0x00, 0x40, // BLOCKHASH(0) 582 0x60, 0x00, 0x52, // STORE memory offset 0 583 0x61, 0x13, 0x36, 0x40, // BLOCKHASH(0x1336) 584 0x60, 0x20, 0x52, // STORE memory offset 32 585 0x61, 0x13, 0x37, 0x40, // BLOCKHASH(0x1337) 586 0x60, 0x40, 0x52, // STORE memory offset 64 587 0x60, 0x60, 0x60, 0x00, 0xf3, // RETURN (0-96) 588 589 }, // blocknumber 590 }, 591 config: &TraceCallConfig{ 592 BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, 593 }, 594 want: `{"gas":72666,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`, 595 }, 596 /* 597 pragma solidity =0.8.12; 598 599 contract Test { 600 uint private x; 601 602 function test2() external { 603 x = 1337; 604 revert(); 605 } 606 607 function test() external returns (uint) { 608 x = 1; 609 try this.test2() {} catch (bytes memory) {} 610 return x; 611 } 612 } 613 */ 614 { // First with only code override, not storage override 615 blockNumber: rpc.LatestBlockNumber, 616 call: ethapi.TransactionArgs{ 617 From: &randomAccounts[0].addr, 618 To: &randomAccounts[2].addr, 619 Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // 620 }, 621 config: &TraceCallConfig{ 622 StateOverrides: ðapi.StateOverride{ 623 randomAccounts[2].addr: ethapi.OverrideAccount{ 624 Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), 625 }, 626 }, 627 }, 628 want: `{"gas":44100,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000001"}`, 629 }, 630 { // Same again, this time with storage override 631 blockNumber: rpc.LatestBlockNumber, 632 call: ethapi.TransactionArgs{ 633 From: &randomAccounts[0].addr, 634 To: &randomAccounts[2].addr, 635 Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // 636 }, 637 config: &TraceCallConfig{ 638 StateOverrides: ðapi.StateOverride{ 639 randomAccounts[2].addr: ethapi.OverrideAccount{ 640 Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), 641 State: newStates([]common.Hash{{}}, []common.Hash{{}}), 642 }, 643 }, 644 }, 645 //want: `{"gas":46900,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000539"}`, 646 want: `{"gas":44100,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000001"}`, 647 }, 648 { // No state override 649 blockNumber: rpc.LatestBlockNumber, 650 call: ethapi.TransactionArgs{ 651 From: &randomAccounts[0].addr, 652 To: &storageAccount, 653 Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // 654 }, 655 config: &TraceCallConfig{ 656 StateOverrides: ðapi.StateOverride{ 657 storageAccount: ethapi.OverrideAccount{ 658 Code: newRPCBytes([]byte{ 659 // SLOAD(3) + SLOAD(4) (which is 0x77) 660 byte(vm.PUSH1), 0x04, 661 byte(vm.SLOAD), 662 byte(vm.PUSH1), 0x03, 663 byte(vm.SLOAD), 664 byte(vm.ADD), 665 // 0x77 -> MSTORE(0) 666 byte(vm.PUSH1), 0x00, 667 byte(vm.MSTORE), 668 // RETURN (0, 32) 669 byte(vm.PUSH1), 32, 670 byte(vm.PUSH1), 00, 671 byte(vm.RETURN), 672 }), 673 }, 674 }, 675 }, 676 want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000077"}`, 677 }, 678 { // Full state override 679 // The original storage is 680 // 3: 0x33 681 // 4: 0x44 682 // With a full override, where we set 3:0x11, the slot 4 should be 683 // removed. So SLOT(3)+SLOT(4) should be 0x11. 684 blockNumber: rpc.LatestBlockNumber, 685 call: ethapi.TransactionArgs{ 686 From: &randomAccounts[0].addr, 687 To: &storageAccount, 688 Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // 689 }, 690 config: &TraceCallConfig{ 691 StateOverrides: ðapi.StateOverride{ 692 storageAccount: ethapi.OverrideAccount{ 693 Code: newRPCBytes([]byte{ 694 // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x00) 695 byte(vm.PUSH1), 0x04, 696 byte(vm.SLOAD), 697 byte(vm.PUSH1), 0x03, 698 byte(vm.SLOAD), 699 byte(vm.ADD), 700 // 0x11 -> MSTORE(0) 701 byte(vm.PUSH1), 0x00, 702 byte(vm.MSTORE), 703 // RETURN (0, 32) 704 byte(vm.PUSH1), 32, 705 byte(vm.PUSH1), 00, 706 byte(vm.RETURN), 707 }), 708 State: newStates( 709 []common.Hash{common.HexToHash("0x03")}, 710 []common.Hash{common.HexToHash("0x11")}), 711 }, 712 }, 713 }, 714 want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000011"}`, 715 }, 716 { // Partial state override 717 // The original storage is 718 // 3: 0x33 719 // 4: 0x44 720 // With a partial override, where we set 3:0x11, the slot 4 as before. 721 // So SLOT(3)+SLOT(4) should be 0x55. 722 blockNumber: rpc.LatestBlockNumber, 723 call: ethapi.TransactionArgs{ 724 From: &randomAccounts[0].addr, 725 To: &storageAccount, 726 Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // 727 }, 728 config: &TraceCallConfig{ 729 StateOverrides: ðapi.StateOverride{ 730 storageAccount: ethapi.OverrideAccount{ 731 Code: newRPCBytes([]byte{ 732 // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x44) 733 byte(vm.PUSH1), 0x04, 734 byte(vm.SLOAD), 735 byte(vm.PUSH1), 0x03, 736 byte(vm.SLOAD), 737 byte(vm.ADD), 738 // 0x55 -> MSTORE(0) 739 byte(vm.PUSH1), 0x00, 740 byte(vm.MSTORE), 741 // RETURN (0, 32) 742 byte(vm.PUSH1), 32, 743 byte(vm.PUSH1), 00, 744 byte(vm.RETURN), 745 }), 746 StateDiff: &map[common.Hash]common.Hash{ 747 common.HexToHash("0x03"): common.HexToHash("0x11"), 748 }, 749 }, 750 }, 751 }, 752 want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000055"}`, 753 }, 754 } 755 for i, tc := range testSuite { 756 result, err := api.TraceCall(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, tc.config) 757 if tc.expectErr != nil { 758 if err == nil { 759 t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr) 760 continue 761 } 762 if !errors.Is(err, tc.expectErr) { 763 t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err) 764 } 765 continue 766 } 767 if err != nil { 768 t.Errorf("test %d: want no error, have %v", i, err) 769 continue 770 } 771 // Turn result into res-struct 772 var ( 773 have res 774 want res 775 ) 776 resBytes, _ := json.Marshal(result) 777 json.Unmarshal(resBytes, &have) 778 json.Unmarshal([]byte(tc.want), &want) 779 if !reflect.DeepEqual(have, want) { 780 t.Logf("result: %v\n", string(resBytes)) 781 t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, have, want) 782 } 783 } 784 } 785 786 type Account struct { 787 key *ecdsa.PrivateKey 788 addr common.Address 789 } 790 791 type Accounts []Account 792 793 func (a Accounts) Len() int { return len(a) } 794 func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 795 func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 } 796 797 func newAccounts(n int) (accounts Accounts) { 798 for i := 0; i < n; i++ { 799 key, _ := crypto.GenerateKey() 800 addr := crypto.PubkeyToAddress(key.PublicKey) 801 accounts = append(accounts, Account{key: key, addr: addr}) 802 } 803 sort.Sort(accounts) 804 return accounts 805 } 806 807 func newRPCBalance(balance *big.Int) **hexutil.Big { 808 rpcBalance := (*hexutil.Big)(balance) 809 return &rpcBalance 810 } 811 812 func newRPCBytes(bytes []byte) *hexutil.Bytes { 813 rpcBytes := hexutil.Bytes(bytes) 814 return &rpcBytes 815 } 816 817 func newStates(keys []common.Hash, vals []common.Hash) *map[common.Hash]common.Hash { 818 if len(keys) != len(vals) { 819 panic("invalid input") 820 } 821 m := make(map[common.Hash]common.Hash) 822 for i := 0; i < len(keys); i++ { 823 m[keys[i]] = vals[i] 824 } 825 return &m 826 } 827 828 func TestTraceChain(t *testing.T) { 829 // Initialize test accounts 830 accounts := newAccounts(3) 831 genesis := &core.Genesis{ 832 Config: params.TestChainConfig, 833 Alloc: core.GenesisAlloc{ 834 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 835 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 836 accounts[2].addr: {Balance: big.NewInt(params.Ether)}, 837 }, 838 } 839 genBlocks := 50 840 signer := types.HomesteadSigner{} 841 842 var ( 843 ref uint32 // total refs has made 844 rel uint32 // total rels has made 845 nonce uint64 846 ) 847 backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 848 // Transfer from account[0] to account[1] 849 // value: 1000 wei 850 // fee: 0 wei 851 for j := 0; j < i+1; j++ { 852 tx, _ := types.SignTx(types.NewTransaction(nonce, accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) 853 b.AddTx(tx) 854 nonce += 1 855 } 856 }) 857 backend.refHook = func() { atomic.AddUint32(&ref, 1) } 858 backend.relHook = func() { atomic.AddUint32(&rel, 1) } 859 api := NewAPI(backend) 860 861 single := `{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}` 862 var cases = []struct { 863 start uint64 864 end uint64 865 config *TraceConfig 866 }{ 867 {0, 50, nil}, // the entire chain range, blocks [1, 50] 868 {10, 20, nil}, // the middle chain range, blocks [11, 20] 869 } 870 for _, c := range cases { 871 ref, rel = 0, 0 // clean up the counters 872 873 from, _ := api.blockByNumber(context.Background(), rpc.BlockNumber(c.start)) 874 to, _ := api.blockByNumber(context.Background(), rpc.BlockNumber(c.end)) 875 resCh := api.traceChain(from, to, c.config, nil) 876 877 next := c.start + 1 878 for result := range resCh { 879 if next != uint64(result.Block) { 880 t.Error("Unexpected tracing block") 881 } 882 if len(result.Traces) != int(next) { 883 t.Error("Unexpected tracing result") 884 } 885 for _, trace := range result.Traces { 886 blob, _ := json.Marshal(trace) 887 if string(blob) != single { 888 t.Error("Unexpected tracing result") 889 } 890 } 891 next += 1 892 } 893 if next != c.end+1 { 894 t.Error("Missing tracing block") 895 } 896 if ref != rel { 897 t.Errorf("Ref and deref actions are not equal, ref %d rel %d", ref, rel) 898 } 899 } 900 }