github.com/juliankolbe/go-ethereum@v1.9.992/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 "errors" 24 "fmt" 25 "math/big" 26 "reflect" 27 "sort" 28 "testing" 29 "time" 30 31 "github.com/juliankolbe/go-ethereum/common" 32 "github.com/juliankolbe/go-ethereum/common/hexutil" 33 "github.com/juliankolbe/go-ethereum/consensus" 34 "github.com/juliankolbe/go-ethereum/consensus/ethash" 35 "github.com/juliankolbe/go-ethereum/core" 36 "github.com/juliankolbe/go-ethereum/core/rawdb" 37 "github.com/juliankolbe/go-ethereum/core/state" 38 "github.com/juliankolbe/go-ethereum/core/types" 39 "github.com/juliankolbe/go-ethereum/core/vm" 40 "github.com/juliankolbe/go-ethereum/crypto" 41 "github.com/juliankolbe/go-ethereum/ethdb" 42 "github.com/juliankolbe/go-ethereum/internal/ethapi" 43 "github.com/juliankolbe/go-ethereum/params" 44 "github.com/juliankolbe/go-ethereum/rpc" 45 ) 46 47 var ( 48 errStateNotFound = errors.New("state not found") 49 errBlockNotFound = errors.New("block not found") 50 errTransactionNotFound = errors.New("transaction not found") 51 ) 52 53 type testBackend struct { 54 chainConfig *params.ChainConfig 55 engine consensus.Engine 56 chaindb ethdb.Database 57 chain *core.BlockChain 58 } 59 60 func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend { 61 backend := &testBackend{ 62 chainConfig: params.TestChainConfig, 63 engine: ethash.NewFaker(), 64 chaindb: rawdb.NewMemoryDatabase(), 65 } 66 // Generate blocks for testing 67 gspec.Config = backend.chainConfig 68 var ( 69 gendb = rawdb.NewMemoryDatabase() 70 genesis = gspec.MustCommit(gendb) 71 ) 72 blocks, _ := core.GenerateChain(backend.chainConfig, genesis, backend.engine, gendb, n, generator) 73 74 // Import the canonical chain 75 gspec.MustCommit(backend.chaindb) 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, backend.chainConfig, backend.engine, vm.Config{}, nil, 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.CurrentBlock(), 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 if tx == nil { 119 return nil, common.Hash{}, 0, 0, errTransactionNotFound 120 } 121 return tx, hash, blockNumber, index, nil 122 } 123 124 func (b *testBackend) RPCGasCap() uint64 { 125 return 25000000 126 } 127 128 func (b *testBackend) ChainConfig() *params.ChainConfig { 129 return b.chainConfig 130 } 131 132 func (b *testBackend) Engine() consensus.Engine { 133 return b.engine 134 } 135 136 func (b *testBackend) ChainDb() ethdb.Database { 137 return b.chaindb 138 } 139 140 func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, func(), error) { 141 statedb, err := b.chain.StateAt(block.Root()) 142 if err != nil { 143 return nil, nil, errStateNotFound 144 } 145 return statedb, func() {}, nil 146 } 147 148 func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, func(), error) { 149 parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) 150 if parent == nil { 151 return nil, vm.BlockContext{}, nil, nil, errBlockNotFound 152 } 153 statedb, err := b.chain.StateAt(parent.Root()) 154 if err != nil { 155 return nil, vm.BlockContext{}, nil, nil, errStateNotFound 156 } 157 if txIndex == 0 && len(block.Transactions()) == 0 { 158 return nil, vm.BlockContext{}, statedb, func() {}, nil 159 } 160 // Recompute transactions up to the target index. 161 signer := types.MakeSigner(b.chainConfig, block.Number()) 162 for idx, tx := range block.Transactions() { 163 msg, _ := tx.AsMessage(signer) 164 txContext := core.NewEVMTxContext(msg) 165 context := core.NewEVMBlockContext(block.Header(), b.chain, nil) 166 if idx == txIndex { 167 return msg, context, statedb, func() {}, nil 168 } 169 vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{}) 170 if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { 171 return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) 172 } 173 statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) 174 } 175 return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) 176 } 177 178 func (b *testBackend) StatesInRange(ctx context.Context, fromBlock *types.Block, toBlock *types.Block, reexec uint64) ([]*state.StateDB, func(), error) { 179 var result []*state.StateDB 180 for number := fromBlock.NumberU64(); number <= toBlock.NumberU64(); number += 1 { 181 block := b.chain.GetBlockByNumber(number) 182 if block == nil { 183 return nil, nil, errBlockNotFound 184 } 185 statedb, err := b.chain.StateAt(block.Root()) 186 if err != nil { 187 return nil, nil, errStateNotFound 188 } 189 result = append(result, statedb) 190 } 191 return result, func() {}, nil 192 } 193 194 func TestTraceCall(t *testing.T) { 195 t.Parallel() 196 197 // Initialize test accounts 198 accounts := newAccounts(3) 199 genesis := &core.Genesis{Alloc: core.GenesisAlloc{ 200 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 201 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 202 accounts[2].addr: {Balance: big.NewInt(params.Ether)}, 203 }} 204 genBlocks := 10 205 signer := types.HomesteadSigner{} 206 api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 207 // Transfer from account[0] to account[1] 208 // value: 1000 wei 209 // fee: 0 wei 210 tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[0].key) 211 b.AddTx(tx) 212 })) 213 214 var testSuite = []struct { 215 blockNumber rpc.BlockNumber 216 call ethapi.CallArgs 217 config *TraceConfig 218 expectErr error 219 expect interface{} 220 }{ 221 // Standard JSON trace upon the genesis, plain transfer. 222 { 223 blockNumber: rpc.BlockNumber(0), 224 call: ethapi.CallArgs{ 225 From: &accounts[0].addr, 226 To: &accounts[1].addr, 227 Value: (*hexutil.Big)(big.NewInt(1000)), 228 }, 229 config: nil, 230 expectErr: nil, 231 expect: ðapi.ExecutionResult{ 232 Gas: params.TxGas, 233 Failed: false, 234 ReturnValue: "", 235 StructLogs: []ethapi.StructLogRes{}, 236 }, 237 }, 238 // Standard JSON trace upon the head, plain transfer. 239 { 240 blockNumber: rpc.BlockNumber(genBlocks), 241 call: ethapi.CallArgs{ 242 From: &accounts[0].addr, 243 To: &accounts[1].addr, 244 Value: (*hexutil.Big)(big.NewInt(1000)), 245 }, 246 config: nil, 247 expectErr: nil, 248 expect: ðapi.ExecutionResult{ 249 Gas: params.TxGas, 250 Failed: false, 251 ReturnValue: "", 252 StructLogs: []ethapi.StructLogRes{}, 253 }, 254 }, 255 // Standard JSON trace upon the non-existent block, error expects 256 { 257 blockNumber: rpc.BlockNumber(genBlocks + 1), 258 call: ethapi.CallArgs{ 259 From: &accounts[0].addr, 260 To: &accounts[1].addr, 261 Value: (*hexutil.Big)(big.NewInt(1000)), 262 }, 263 config: nil, 264 expectErr: fmt.Errorf("block #%d not found", genBlocks+1), 265 expect: nil, 266 }, 267 // Standard JSON trace upon the latest block 268 { 269 blockNumber: rpc.LatestBlockNumber, 270 call: ethapi.CallArgs{ 271 From: &accounts[0].addr, 272 To: &accounts[1].addr, 273 Value: (*hexutil.Big)(big.NewInt(1000)), 274 }, 275 config: nil, 276 expectErr: nil, 277 expect: ðapi.ExecutionResult{ 278 Gas: params.TxGas, 279 Failed: false, 280 ReturnValue: "", 281 StructLogs: []ethapi.StructLogRes{}, 282 }, 283 }, 284 // Standard JSON trace upon the pending block 285 { 286 blockNumber: rpc.PendingBlockNumber, 287 call: ethapi.CallArgs{ 288 From: &accounts[0].addr, 289 To: &accounts[1].addr, 290 Value: (*hexutil.Big)(big.NewInt(1000)), 291 }, 292 config: nil, 293 expectErr: nil, 294 expect: ðapi.ExecutionResult{ 295 Gas: params.TxGas, 296 Failed: false, 297 ReturnValue: "", 298 StructLogs: []ethapi.StructLogRes{}, 299 }, 300 }, 301 } 302 for _, testspec := range testSuite { 303 result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config) 304 if testspec.expectErr != nil { 305 if err == nil { 306 t.Errorf("Expect error %v, get nothing", testspec.expectErr) 307 continue 308 } 309 if !reflect.DeepEqual(err, testspec.expectErr) { 310 t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err) 311 } 312 } else { 313 if err != nil { 314 t.Errorf("Expect no error, get %v", err) 315 continue 316 } 317 if !reflect.DeepEqual(result, testspec.expect) { 318 t.Errorf("Result mismatch, want %v, get %v", testspec.expect, result) 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{Alloc: core.GenesisAlloc{ 330 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 331 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 332 }} 333 target := common.Hash{} 334 signer := types.HomesteadSigner{} 335 api := NewAPI(newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { 336 // Transfer from account[0] to account[1] 337 // value: 1000 wei 338 // fee: 0 wei 339 tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[0].key) 340 b.AddTx(tx) 341 target = tx.Hash() 342 })) 343 result, err := api.TraceTransaction(context.Background(), target, nil) 344 if err != nil { 345 t.Errorf("Failed to trace transaction %v", err) 346 } 347 if !reflect.DeepEqual(result, ðapi.ExecutionResult{ 348 Gas: params.TxGas, 349 Failed: false, 350 ReturnValue: "", 351 StructLogs: []ethapi.StructLogRes{}, 352 }) { 353 t.Error("Transaction tracing result is different") 354 } 355 } 356 357 func TestTraceBlock(t *testing.T) { 358 t.Parallel() 359 360 // Initialize test accounts 361 accounts := newAccounts(3) 362 genesis := &core.Genesis{Alloc: core.GenesisAlloc{ 363 accounts[0].addr: {Balance: big.NewInt(params.Ether)}, 364 accounts[1].addr: {Balance: big.NewInt(params.Ether)}, 365 accounts[2].addr: {Balance: big.NewInt(params.Ether)}, 366 }} 367 genBlocks := 10 368 signer := types.HomesteadSigner{} 369 api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { 370 // Transfer from account[0] to account[1] 371 // value: 1000 wei 372 // fee: 0 wei 373 tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[0].key) 374 b.AddTx(tx) 375 })) 376 377 var testSuite = []struct { 378 blockNumber rpc.BlockNumber 379 config *TraceConfig 380 expect interface{} 381 expectErr error 382 }{ 383 // Trace genesis block, expect error 384 { 385 blockNumber: rpc.BlockNumber(0), 386 config: nil, 387 expect: nil, 388 expectErr: errors.New("genesis is not traceable"), 389 }, 390 // Trace head block 391 { 392 blockNumber: rpc.BlockNumber(genBlocks), 393 config: nil, 394 expectErr: nil, 395 expect: []*txTraceResult{ 396 { 397 Result: ðapi.ExecutionResult{ 398 Gas: params.TxGas, 399 Failed: false, 400 ReturnValue: "", 401 StructLogs: []ethapi.StructLogRes{}, 402 }, 403 }, 404 }, 405 }, 406 // Trace non-existent block 407 { 408 blockNumber: rpc.BlockNumber(genBlocks + 1), 409 config: nil, 410 expectErr: fmt.Errorf("block #%d not found", genBlocks+1), 411 expect: nil, 412 }, 413 // Trace latest block 414 { 415 blockNumber: rpc.LatestBlockNumber, 416 config: nil, 417 expectErr: nil, 418 expect: []*txTraceResult{ 419 { 420 Result: ðapi.ExecutionResult{ 421 Gas: params.TxGas, 422 Failed: false, 423 ReturnValue: "", 424 StructLogs: []ethapi.StructLogRes{}, 425 }, 426 }, 427 }, 428 }, 429 // Trace pending block 430 { 431 blockNumber: rpc.PendingBlockNumber, 432 config: nil, 433 expectErr: nil, 434 expect: []*txTraceResult{ 435 { 436 Result: ðapi.ExecutionResult{ 437 Gas: params.TxGas, 438 Failed: false, 439 ReturnValue: "", 440 StructLogs: []ethapi.StructLogRes{}, 441 }, 442 }, 443 }, 444 }, 445 } 446 for _, testspec := range testSuite { 447 result, err := api.TraceBlockByNumber(context.Background(), testspec.blockNumber, testspec.config) 448 if testspec.expectErr != nil { 449 if err == nil { 450 t.Errorf("Expect error %v, get nothing", testspec.expectErr) 451 continue 452 } 453 if !reflect.DeepEqual(err, testspec.expectErr) { 454 t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err) 455 } 456 } else { 457 if err != nil { 458 t.Errorf("Expect no error, get %v", err) 459 continue 460 } 461 if !reflect.DeepEqual(result, testspec.expect) { 462 t.Errorf("Result mismatch, want %v, get %v", testspec.expect, result) 463 } 464 } 465 } 466 } 467 468 type Account struct { 469 key *ecdsa.PrivateKey 470 addr common.Address 471 } 472 473 type Accounts []Account 474 475 func (a Accounts) Len() int { return len(a) } 476 func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 477 func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 } 478 479 func newAccounts(n int) (accounts Accounts) { 480 for i := 0; i < n; i++ { 481 key, _ := crypto.GenerateKey() 482 addr := crypto.PubkeyToAddress(key.PublicKey) 483 accounts = append(accounts, Account{key: key, addr: addr}) 484 } 485 sort.Sort(accounts) 486 return accounts 487 }