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