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