github.com/klaytn/klaytn@v1.10.2/node/cn/tracers/api_test.go (about) 1 // Modifications Copyright 2022 The klaytn Authors 2 // Copyright 2021 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from eth/tracers/api_test.go (2022/08/08). 19 // Modified and improved for the klaytn development. 20 package tracers 21 22 import ( 23 "bytes" 24 "context" 25 "crypto/ecdsa" 26 "errors" 27 "fmt" 28 "math/big" 29 "reflect" 30 "sort" 31 "testing" 32 33 klaytnapi "github.com/klaytn/klaytn/api" 34 "github.com/klaytn/klaytn/blockchain" 35 "github.com/klaytn/klaytn/blockchain/state" 36 "github.com/klaytn/klaytn/blockchain/types" 37 "github.com/klaytn/klaytn/blockchain/vm" 38 "github.com/klaytn/klaytn/common" 39 "github.com/klaytn/klaytn/consensus" 40 "github.com/klaytn/klaytn/consensus/gxhash" 41 "github.com/klaytn/klaytn/crypto" 42 "github.com/klaytn/klaytn/networks/rpc" 43 "github.com/klaytn/klaytn/params" 44 "github.com/klaytn/klaytn/storage/database" 45 "github.com/klaytn/klaytn/storage/statedb" 46 ) 47 48 var ( 49 errStateNotFound = errors.New("state not found") 50 errBlockNotFound = errors.New("block not found") 51 errTransactionNotFound = errors.New("transaction not found") 52 ) 53 54 type testBackend struct { 55 chainConfig *params.ChainConfig 56 engine consensus.Engine 57 chaindb database.DBManager 58 chain *blockchain.BlockChain 59 } 60 61 func newTestBackend(t *testing.T, n int, gspec *blockchain.Genesis, generator func(i int, b *blockchain.BlockGen)) *testBackend { 62 backend := &testBackend{ 63 chainConfig: params.TestChainConfig, 64 engine: gxhash.NewFaker(), 65 chaindb: database.NewMemoryDBManager(), 66 } 67 // Generate blocks for testing 68 gspec.Config = backend.chainConfig 69 var ( 70 gendb = database.NewMemoryDBManager() 71 genesis = gspec.MustCommit(gendb) 72 ) 73 blocks, _ := blockchain.GenerateChain(backend.chainConfig, genesis, backend.engine, gendb, n, generator) 74 // Import the canonical chain 75 gspec.MustCommit(backend.chaindb) 76 cacheConfig := &blockchain.CacheConfig{ 77 CacheSize: 512, 78 BlockInterval: blockchain.DefaultBlockInterval, 79 TriesInMemory: blockchain.DefaultTriesInMemory, 80 TrieNodeCacheConfig: statedb.GetEmptyTrieNodeCacheConfig(), 81 SnapshotCacheSize: 512, 82 ArchiveMode: true, // Archive mode 83 } 84 chain, err := blockchain.NewBlockChain(backend.chaindb, cacheConfig, backend.chainConfig, backend.engine, vm.Config{}) 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.CurrentBlock(), nil 113 } 114 block := b.chain.GetBlockByNumber(uint64(number)) 115 if block == nil { 116 return nil, fmt.Errorf("the block does not exist (block number: %d)", number) 117 } 118 return block, nil 119 } 120 121 func (b *testBackend) GetTxAndLookupInfo(txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { 122 tx, hash, blockNumber, index := b.chain.GetTxAndLookupInfoInCache(txHash) 123 if tx == nil { 124 return nil, common.Hash{}, 0, 0 125 } 126 return tx, hash, blockNumber, index 127 } 128 129 func (b *testBackend) RPCGasCap() *big.Int { 130 return big.NewInt(250 * params.Ston) 131 } 132 133 func (b *testBackend) ChainConfig() *params.ChainConfig { 134 return b.chainConfig 135 } 136 137 func (b *testBackend) Engine() consensus.Engine { 138 return b.engine 139 } 140 141 func (b *testBackend) ChainDB() database.DBManager { 142 return b.chaindb 143 } 144 145 func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (*state.StateDB, error) { 146 statedb, err := b.chain.StateAt(block.Root()) 147 if err != nil { 148 return nil, errStateNotFound 149 } 150 return statedb, nil 151 } 152 153 func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (blockchain.Message, vm.Context, *state.StateDB, error) { 154 parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) 155 if parent == nil { 156 return nil, vm.Context{}, nil, errBlockNotFound 157 } 158 statedb, err := b.chain.StateAt(parent.Root()) 159 if err != nil { 160 return nil, vm.Context{}, nil, errStateNotFound 161 } 162 if txIndex == 0 && len(block.Transactions()) == 0 { 163 return nil, vm.Context{}, statedb, nil 164 } 165 // Recompute transactions up to the target index. 166 signer := types.MakeSigner(b.chainConfig, block.Number()) 167 for idx, tx := range block.Transactions() { 168 msg, _ := tx.AsMessageWithAccountKeyPicker(signer, statedb, block.NumberU64()) 169 context := blockchain.NewEVMContext(msg, block.Header(), b.chain, nil) 170 if idx == txIndex { 171 return msg, context, statedb, nil 172 } 173 vmenv := vm.NewEVM(context, statedb, b.chainConfig, &vm.Config{Debug: true, EnableInternalTxTracing: true}) 174 if _, _, kerr := blockchain.ApplyMessage(vmenv, msg); kerr.ErrTxInvalid != nil { 175 return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), kerr.ErrTxInvalid) 176 } 177 statedb.Finalise(true, true) 178 } 179 return nil, vm.Context{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) 180 } 181 182 // TODO-tracer: implement TraceCall 183 //func TestTraceCall(t *testing.T) { 184 // t.Parallel() 185 // 186 // // Initialize test accounts 187 // accounts := newAccounts(3) 188 // genesis := &blockchain.Genesis{Alloc: blockchain.GenesisAlloc{ 189 // accounts[0].addr: {Balance: big.NewInt(params.KLAY)}, 190 // accounts[1].addr: {Balance: big.NewInt(params.KLAY)}, 191 // accounts[2].addr: {Balance: big.NewInt(params.KLAY)}, 192 // }} 193 // genBlocks := 10 194 // signer := types.HomesteadSigner{} 195 // api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *blockchain.BlockGen) { 196 // // Transfer from account[0] to account[1] 197 // // value: 1000 peb 198 // // fee: 0 peb 199 // tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[0].key) 200 // b.AddTx(tx) 201 // })) 202 // 203 // var testSuite = []struct { 204 // blockNumber rpc.BlockNumber 205 // call klaytnapi.CallArgs 206 // config *TraceConfig 207 // expectErr error 208 // expect interface{} 209 // }{ 210 // // Standard JSON trace upon the genesis, plain transfer. 211 // { 212 // blockNumber: rpc.BlockNumber(0), 213 // call: klaytnapi.CallArgs{ 214 // From: accounts[0].addr, 215 // To: &accounts[1].addr, 216 // Value: (hexutil.Big)(*big.NewInt(1000)), 217 // }, 218 // config: nil, 219 // expectErr: nil, 220 // expect: &klaytnapi.ExecutionResult{ 221 // Gas: params.TxGas, 222 // Failed: false, 223 // ReturnValue: "", 224 // StructLogs: []klaytnapi.StructLogRes{}, 225 // }, 226 // }, 227 // // Standard JSON trace upon the head, plain transfer. 228 // { 229 // blockNumber: rpc.BlockNumber(genBlocks), 230 // call: klaytnapi.CallArgs{ 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: &klaytnapi.ExecutionResult{ 238 // Gas: params.TxGas, 239 // Failed: false, 240 // ReturnValue: "", 241 // StructLogs: []klaytnapi.StructLogRes{}, 242 // }, 243 // }, 244 // // Standard JSON trace upon the non-existent block, error expects 245 // { 246 // blockNumber: rpc.BlockNumber(genBlocks + 1), 247 // call: klaytnapi.CallArgs{ 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("the block does not exist (block number: %d)", genBlocks+1), 254 // expect: nil, 255 // }, 256 // // Standard JSON trace upon the latest block 257 // { 258 // blockNumber: rpc.LatestBlockNumber, 259 // call: klaytnapi.CallArgs{ 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: &klaytnapi.ExecutionResult{ 267 // Gas: params.TxGas, 268 // Failed: false, 269 // ReturnValue: "", 270 // StructLogs: []klaytnapi.StructLogRes{}, 271 // }, 272 // }, 273 // // Standard JSON trace upon the pending block 274 // { 275 // blockNumber: rpc.PendingBlockNumber, 276 // call: klaytnapi.CallArgs{ 277 // From: accounts[0].addr, 278 // To: &accounts[1].addr, 279 // Value: (hexutil.Big)(*big.NewInt(1000)), 280 // }, 281 // config: nil, 282 // expectErr: nil, 283 // expect: &klaytnapi.ExecutionResult{ 284 // Gas: params.TxGas, 285 // Failed: false, 286 // ReturnValue: "", 287 // StructLogs: []klaytnapi.StructLogRes{}, 288 // }, 289 // }, 290 // } 291 // for _, testspec := range testSuite { 292 // result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config) 293 // if testspec.expectErr != nil { 294 // if err == nil { 295 // t.Errorf("Expect error %v, get nothing", testspec.expectErr) 296 // continue 297 // } 298 // if !reflect.DeepEqual(err, testspec.expectErr) { 299 // t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err) 300 // } 301 // } else { 302 // if err != nil { 303 // t.Errorf("Expect no error, get %v", err) 304 // continue 305 // } 306 // if !reflect.DeepEqual(result, testspec.expect) { 307 // t.Errorf("Result mismatch, want %v, get %v", testspec.expect, result) 308 // } 309 // } 310 // } 311 //} 312 313 func TestTraceTransaction(t *testing.T) { 314 t.Parallel() 315 316 // Initialize test accounts 317 accounts := newAccounts(2) 318 genesis := &blockchain.Genesis{Alloc: blockchain.GenesisAlloc{ 319 accounts[0].addr: {Balance: big.NewInt(params.KLAY)}, 320 accounts[1].addr: {Balance: big.NewInt(params.KLAY)}, 321 }} 322 target := common.Hash{} 323 signer := types.LatestSignerForChainID(params.TestChainConfig.ChainID) 324 api := NewAPI(newTestBackend(t, 1, genesis, func(i int, b *blockchain.BlockGen) { 325 // Transfer from account[0] to account[1] 326 // value: 1000 peb 327 // fee: 0 peb 328 tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil), signer, accounts[0].key) 329 b.AddTx(tx) 330 target = tx.Hash() 331 })) 332 result, err := api.TraceTransaction(context.Background(), target, nil) 333 if err != nil { 334 t.Errorf("Failed to trace transaction %v", err) 335 } 336 if !reflect.DeepEqual(result, &klaytnapi.ExecutionResult{ 337 Gas: params.TxGas, 338 Failed: false, 339 ReturnValue: "", 340 StructLogs: []klaytnapi.StructLogRes{}, 341 }) { 342 t.Error("Transaction tracing result is different") 343 } 344 } 345 346 func TestTraceBlock(t *testing.T) { 347 t.Parallel() 348 349 // Initialize test accounts 350 accounts := newAccounts(3) 351 genesis := &blockchain.Genesis{Alloc: blockchain.GenesisAlloc{ 352 accounts[0].addr: {Balance: big.NewInt(params.KLAY)}, 353 accounts[1].addr: {Balance: big.NewInt(params.KLAY)}, 354 accounts[2].addr: {Balance: big.NewInt(params.KLAY)}, 355 }} 356 genBlocks := 10 357 signer := types.LatestSignerForChainID(params.TestChainConfig.ChainID) 358 api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *blockchain.BlockGen) { 359 // Transfer from account[0] to account[1] 360 // value: 1000 peb 361 // fee: 0 peb 362 tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[0].key) 363 b.AddTx(tx) 364 })) 365 366 testSuite := []struct { 367 blockNumber rpc.BlockNumber 368 config *TraceConfig 369 expect interface{} 370 expectErr error 371 }{ 372 // Trace genesis block, expect error 373 { 374 blockNumber: rpc.BlockNumber(0), 375 config: nil, 376 expect: nil, 377 expectErr: errors.New("genesis is not traceable"), 378 }, 379 // Trace head block 380 { 381 blockNumber: rpc.BlockNumber(genBlocks), 382 config: nil, 383 expectErr: nil, 384 expect: []*txTraceResult{ 385 { 386 Result: &klaytnapi.ExecutionResult{ 387 Gas: params.TxGas, 388 Failed: false, 389 ReturnValue: "", 390 StructLogs: []klaytnapi.StructLogRes{}, 391 }, 392 }, 393 }, 394 }, 395 // Trace non-existent block 396 { 397 blockNumber: rpc.BlockNumber(genBlocks + 1), 398 config: nil, 399 expectErr: fmt.Errorf("the block does not exist (block number: %d)", genBlocks+1), 400 expect: nil, 401 }, 402 // Trace latest block 403 { 404 blockNumber: rpc.LatestBlockNumber, 405 config: nil, 406 expectErr: nil, 407 expect: []*txTraceResult{ 408 { 409 Result: &klaytnapi.ExecutionResult{ 410 Gas: params.TxGas, 411 Failed: false, 412 ReturnValue: "", 413 StructLogs: []klaytnapi.StructLogRes{}, 414 }, 415 }, 416 }, 417 }, 418 // Trace pending block 419 { 420 blockNumber: rpc.PendingBlockNumber, 421 config: nil, 422 expectErr: nil, 423 expect: []*txTraceResult{ 424 { 425 Result: &klaytnapi.ExecutionResult{ 426 Gas: params.TxGas, 427 Failed: false, 428 ReturnValue: "", 429 StructLogs: []klaytnapi.StructLogRes{}, 430 }, 431 }, 432 }, 433 }, 434 } 435 for _, testspec := range testSuite { 436 result, err := api.TraceBlockByNumber(context.Background(), testspec.blockNumber, testspec.config) 437 if testspec.expectErr != nil { 438 if err == nil { 439 t.Errorf("Expect error %v, get nothing", testspec.expectErr) 440 continue 441 } 442 if !reflect.DeepEqual(err, testspec.expectErr) { 443 t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err) 444 } 445 } else { 446 if err != nil { 447 t.Errorf("Expect no error, get %v", err) 448 continue 449 } 450 if len(result) != len(testspec.expect.([]*txTraceResult)) { 451 t.Errorf("Result length mismatch, want %v, get %v", len(result), len(testspec.expect.([]*txTraceResult))) 452 } 453 for idx, r := range result { 454 if !reflect.DeepEqual(r.Result, testspec.expect.([]*txTraceResult)[idx].Result) { 455 t.Errorf("Result mismatch, want %v, get %v", testspec.expect, result) 456 } 457 } 458 } 459 } 460 } 461 462 type Account struct { 463 key *ecdsa.PrivateKey 464 addr common.Address 465 } 466 467 type Accounts []Account 468 469 func (a Accounts) Len() int { return len(a) } 470 func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 471 func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 } 472 473 func newAccounts(n int) (accounts Accounts) { 474 for i := 0; i < n; i++ { 475 key, _ := crypto.GenerateKey() 476 addr := crypto.PubkeyToAddress(key.PublicKey) 477 accounts = append(accounts, Account{key: key, addr: addr}) 478 } 479 sort.Sort(accounts) 480 return accounts 481 }