github.com/klaytn/klaytn@v1.12.1/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/common/hexutil" 40 "github.com/klaytn/klaytn/consensus" 41 "github.com/klaytn/klaytn/consensus/gxhash" 42 "github.com/klaytn/klaytn/crypto" 43 "github.com/klaytn/klaytn/networks/rpc" 44 "github.com/klaytn/klaytn/params" 45 "github.com/klaytn/klaytn/storage/database" 46 "github.com/klaytn/klaytn/storage/statedb" 47 "github.com/stretchr/testify/assert" 48 ) 49 50 var ( 51 errStateNotFound = errors.New("state not found") 52 errBlockNotFound = errors.New("block not found") 53 errTransactionNotFound = errors.New("transaction not found") 54 ) 55 56 type testBackend struct { 57 chainConfig *params.ChainConfig 58 engine consensus.Engine 59 chaindb database.DBManager 60 chain *blockchain.BlockChain 61 } 62 63 func newTestBackend(t *testing.T, n int, gspec *blockchain.Genesis, generator func(i int, b *blockchain.BlockGen)) *testBackend { 64 backend := &testBackend{ 65 chainConfig: params.TestChainConfig, 66 engine: gxhash.NewFaker(), 67 chaindb: database.NewMemoryDBManager(), 68 } 69 // Generate blocks for testing 70 gspec.Config = backend.chainConfig 71 var ( 72 gendb = database.NewMemoryDBManager() 73 genesis = gspec.MustCommit(gendb) 74 ) 75 blocks, _ := blockchain.GenerateChain(backend.chainConfig, genesis, backend.engine, gendb, n, generator) 76 // Import the canonical chain 77 gspec.MustCommit(backend.chaindb) 78 cacheConfig := &blockchain.CacheConfig{ 79 CacheSize: 512, 80 BlockInterval: blockchain.DefaultBlockInterval, 81 TriesInMemory: blockchain.DefaultTriesInMemory, 82 TrieNodeCacheConfig: statedb.GetEmptyTrieNodeCacheConfig(), 83 SnapshotCacheSize: 512, 84 ArchiveMode: true, // Archive mode 85 } 86 chain, err := blockchain.NewBlockChain(backend.chaindb, cacheConfig, backend.chainConfig, backend.engine, vm.Config{}) 87 if err != nil { 88 t.Fatalf("failed to create tester chain: %v", err) 89 } 90 if n, err := chain.InsertChain(blocks); err != nil { 91 t.Fatalf("block %d: failed to insert into chain: %v", n, err) 92 } 93 backend.chain = chain 94 return backend 95 } 96 97 func (b *testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { 98 return b.chain.GetHeaderByHash(hash), nil 99 } 100 101 func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { 102 if number == rpc.PendingBlockNumber || number == rpc.LatestBlockNumber { 103 return b.chain.CurrentHeader(), nil 104 } 105 return b.chain.GetHeaderByNumber(uint64(number)), nil 106 } 107 108 func (b *testBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { 109 return b.chain.GetBlockByHash(hash), nil 110 } 111 112 func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { 113 if number == rpc.PendingBlockNumber || number == rpc.LatestBlockNumber { 114 return b.chain.CurrentBlock(), nil 115 } 116 block := b.chain.GetBlockByNumber(uint64(number)) 117 if block == nil { 118 return nil, fmt.Errorf("the block does not exist (block number: %d)", number) 119 } 120 return block, nil 121 } 122 123 func (b *testBackend) GetTxAndLookupInfo(txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { 124 tx, hash, blockNumber, index := b.chain.GetTxAndLookupInfoInCache(txHash) 125 if tx == nil { 126 return nil, common.Hash{}, 0, 0 127 } 128 return tx, hash, blockNumber, index 129 } 130 131 func (b *testBackend) RPCGasCap() *big.Int { 132 return big.NewInt(250 * params.Ston) 133 } 134 135 func (b *testBackend) ChainConfig() *params.ChainConfig { 136 return b.chainConfig 137 } 138 139 func (b *testBackend) Engine() consensus.Engine { 140 return b.engine 141 } 142 143 func (b *testBackend) ChainDB() database.DBManager { 144 return b.chaindb 145 } 146 147 func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (*state.StateDB, error) { 148 statedb, err := b.chain.StateAt(block.Root()) 149 if err != nil { 150 return nil, errStateNotFound 151 } 152 return statedb, nil 153 } 154 155 func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (blockchain.Message, vm.BlockContext, vm.TxContext, *state.StateDB, error) { 156 parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) 157 if parent == nil { 158 return nil, vm.BlockContext{}, vm.TxContext{}, nil, errBlockNotFound 159 } 160 statedb, err := b.chain.StateAt(parent.Root()) 161 if err != nil { 162 return nil, vm.BlockContext{}, vm.TxContext{}, nil, errStateNotFound 163 } 164 if txIndex == 0 && len(block.Transactions()) == 0 { 165 return nil, vm.BlockContext{}, vm.TxContext{}, statedb, nil 166 } 167 // Recompute transactions up to the target index. 168 signer := types.MakeSigner(b.chainConfig, block.Number()) 169 for idx, tx := range block.Transactions() { 170 msg, _ := tx.AsMessageWithAccountKeyPicker(signer, statedb, block.NumberU64()) 171 txContext := blockchain.NewEVMTxContext(msg, block.Header()) 172 blockContext := blockchain.NewEVMBlockContext(block.Header(), b.chain, nil) 173 if idx == txIndex { 174 return msg, blockContext, txContext, statedb, nil 175 } 176 vmenv := vm.NewEVM(blockContext, txContext, statedb, b.chainConfig, &vm.Config{Debug: true, EnableInternalTxTracing: true}) 177 if _, err := blockchain.ApplyMessage(vmenv, msg); err != nil { 178 return nil, vm.BlockContext{}, vm.TxContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) 179 } 180 statedb.Finalise(true, true) 181 } 182 return nil, vm.BlockContext{}, vm.TxContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) 183 } 184 185 func TestTraceCall(t *testing.T) { 186 t.Parallel() 187 188 // Initialize test accounts 189 accounts := newAccounts(3) 190 genesis := &blockchain.Genesis{Alloc: blockchain.GenesisAlloc{ 191 accounts[0].addr: {Balance: big.NewInt(0)}, 192 accounts[1].addr: {Balance: big.NewInt(1000 * 10)}, 193 accounts[2].addr: {Balance: big.NewInt(0)}, 194 }} 195 genBlocks := 10 196 signer := types.LatestSignerForChainID(params.TestChainConfig.ChainID) 197 api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *blockchain.BlockGen) { 198 // Transfer from account[1] to account[0] 199 // value: 1000 peb 200 // fee: 0 peb 201 tx, err := types.SignTx(types.NewTransaction(uint64(i), accounts[0].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[1].key) 202 assert.NoError(t, err) 203 b.AddTx(tx) 204 })) 205 206 testSuite := []struct { 207 blockNumber rpc.BlockNumber 208 call klaytnapi.CallArgs 209 config *TraceConfig 210 expectErr error 211 expect interface{} 212 }{ 213 // Standard JSON trace upon the genesis, plain transfer. 214 { 215 blockNumber: rpc.BlockNumber(0), 216 call: klaytnapi.CallArgs{ 217 From: accounts[0].addr, 218 To: &accounts[1].addr, 219 Value: (hexutil.Big)(*big.NewInt(1000)), 220 }, 221 config: nil, 222 expectErr: errors.New("tracing failed: insufficient balance for transfer"), 223 expect: nil, 224 }, 225 // Standard JSON trace upon the head, plain transfer. 226 { 227 blockNumber: rpc.BlockNumber(genBlocks), 228 call: klaytnapi.CallArgs{ 229 From: accounts[0].addr, 230 To: &accounts[1].addr, 231 Value: (hexutil.Big)(*big.NewInt(1000)), 232 }, 233 config: nil, 234 expectErr: nil, 235 expect: &klaytnapi.ExecutionResult{ 236 Gas: params.TxGas, 237 Failed: false, 238 ReturnValue: "", 239 StructLogs: []klaytnapi.StructLogRes{}, 240 }, 241 }, 242 // Standard JSON trace upon the non-existent block, error expects 243 { 244 blockNumber: rpc.BlockNumber(genBlocks + 1), 245 call: klaytnapi.CallArgs{ 246 From: accounts[0].addr, 247 To: &accounts[1].addr, 248 Value: (hexutil.Big)(*big.NewInt(1000)), 249 }, 250 config: nil, 251 expectErr: fmt.Errorf("the block does not exist (block number: %d)", genBlocks+1), 252 expect: nil, 253 }, 254 // Standard JSON trace upon the latest block 255 { 256 blockNumber: rpc.LatestBlockNumber, 257 call: klaytnapi.CallArgs{ 258 From: accounts[0].addr, 259 To: &accounts[1].addr, 260 Value: (hexutil.Big)(*big.NewInt(1000)), 261 }, 262 config: nil, 263 expectErr: nil, 264 expect: &klaytnapi.ExecutionResult{ 265 Gas: params.TxGas, 266 Failed: false, 267 ReturnValue: "", 268 StructLogs: []klaytnapi.StructLogRes{}, 269 }, 270 }, 271 // Standard JSON trace upon the pending block 272 { 273 blockNumber: rpc.PendingBlockNumber, 274 call: klaytnapi.CallArgs{ 275 From: accounts[0].addr, 276 To: &accounts[1].addr, 277 Value: (hexutil.Big)(*big.NewInt(1000)), 278 }, 279 config: nil, 280 expectErr: nil, 281 expect: &klaytnapi.ExecutionResult{ 282 Gas: params.TxGas, 283 Failed: false, 284 ReturnValue: "", 285 StructLogs: []klaytnapi.StructLogRes{}, 286 }, 287 }, 288 } 289 for _, testspec := range testSuite { 290 result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config) 291 assert.Equal(t, err, testspec.expectErr) 292 assert.Equal(t, result, testspec.expect) 293 } 294 } 295 296 func TestTraceTransaction(t *testing.T) { 297 t.Parallel() 298 299 // Initialize test accounts 300 accounts := newAccounts(2) 301 genesis := &blockchain.Genesis{Alloc: blockchain.GenesisAlloc{ 302 accounts[0].addr: {Balance: big.NewInt(params.KLAY)}, 303 accounts[1].addr: {Balance: big.NewInt(params.KLAY)}, 304 }} 305 target := common.Hash{} 306 signer := types.LatestSignerForChainID(params.TestChainConfig.ChainID) 307 api := NewAPI(newTestBackend(t, 1, genesis, func(i int, b *blockchain.BlockGen) { 308 // Transfer from account[0] to account[1] 309 // value: 1000 peb 310 // fee: 0 peb 311 tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil), signer, accounts[0].key) 312 b.AddTx(tx) 313 target = tx.Hash() 314 })) 315 result, err := api.TraceTransaction(context.Background(), target, nil) 316 if err != nil { 317 t.Errorf("Failed to trace transaction %v", err) 318 } 319 if !reflect.DeepEqual(result, &klaytnapi.ExecutionResult{ 320 Gas: params.TxGas, 321 Failed: false, 322 ReturnValue: "", 323 StructLogs: []klaytnapi.StructLogRes{}, 324 }) { 325 t.Error("Transaction tracing result is different") 326 } 327 } 328 329 func TestTraceBlock(t *testing.T) { 330 t.Parallel() 331 332 // Initialize test accounts 333 accounts := newAccounts(3) 334 genesis := &blockchain.Genesis{Alloc: blockchain.GenesisAlloc{ 335 accounts[0].addr: {Balance: big.NewInt(params.KLAY)}, 336 accounts[1].addr: {Balance: big.NewInt(params.KLAY)}, 337 accounts[2].addr: {Balance: big.NewInt(params.KLAY)}, 338 }} 339 genBlocks := 10 340 signer := types.LatestSignerForChainID(params.TestChainConfig.ChainID) 341 api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *blockchain.BlockGen) { 342 // Transfer from account[0] to account[1] 343 // value: 1000 peb 344 // fee: 0 peb 345 tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[0].key) 346 b.AddTx(tx) 347 })) 348 349 testSuite := []struct { 350 blockNumber rpc.BlockNumber 351 config *TraceConfig 352 expect interface{} 353 expectErr error 354 }{ 355 // Trace genesis block, expect error 356 { 357 blockNumber: rpc.BlockNumber(0), 358 config: nil, 359 expect: nil, 360 expectErr: errors.New("genesis is not traceable"), 361 }, 362 // Trace head block 363 { 364 blockNumber: rpc.BlockNumber(genBlocks), 365 config: nil, 366 expectErr: nil, 367 expect: []*txTraceResult{ 368 { 369 Result: &klaytnapi.ExecutionResult{ 370 Gas: params.TxGas, 371 Failed: false, 372 ReturnValue: "", 373 StructLogs: []klaytnapi.StructLogRes{}, 374 }, 375 }, 376 }, 377 }, 378 // Trace non-existent block 379 { 380 blockNumber: rpc.BlockNumber(genBlocks + 1), 381 config: nil, 382 expectErr: fmt.Errorf("the block does not exist (block number: %d)", genBlocks+1), 383 expect: nil, 384 }, 385 // Trace latest block 386 { 387 blockNumber: rpc.LatestBlockNumber, 388 config: nil, 389 expectErr: nil, 390 expect: []*txTraceResult{ 391 { 392 Result: &klaytnapi.ExecutionResult{ 393 Gas: params.TxGas, 394 Failed: false, 395 ReturnValue: "", 396 StructLogs: []klaytnapi.StructLogRes{}, 397 }, 398 }, 399 }, 400 }, 401 // Trace pending block 402 { 403 blockNumber: rpc.PendingBlockNumber, 404 config: nil, 405 expectErr: nil, 406 expect: []*txTraceResult{ 407 { 408 Result: &klaytnapi.ExecutionResult{ 409 Gas: params.TxGas, 410 Failed: false, 411 ReturnValue: "", 412 StructLogs: []klaytnapi.StructLogRes{}, 413 }, 414 }, 415 }, 416 }, 417 } 418 for _, testspec := range testSuite { 419 result, err := api.TraceBlockByNumber(context.Background(), testspec.blockNumber, testspec.config) 420 if testspec.expectErr != nil { 421 if err == nil { 422 t.Errorf("Expect error %v, get nothing", testspec.expectErr) 423 continue 424 } 425 if !reflect.DeepEqual(err, testspec.expectErr) { 426 t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err) 427 } 428 } else { 429 if err != nil { 430 t.Errorf("Expect no error, get %v", err) 431 continue 432 } 433 if len(result) != len(testspec.expect.([]*txTraceResult)) { 434 t.Errorf("Result length mismatch, want %v, get %v", len(result), len(testspec.expect.([]*txTraceResult))) 435 } 436 for idx, r := range result { 437 if !reflect.DeepEqual(r.Result, testspec.expect.([]*txTraceResult)[idx].Result) { 438 t.Errorf("Result mismatch, want %v, get %v", testspec.expect, result) 439 } 440 } 441 } 442 } 443 } 444 445 type Account struct { 446 key *ecdsa.PrivateKey 447 addr common.Address 448 } 449 450 type Accounts []Account 451 452 func (a Accounts) Len() int { return len(a) } 453 func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 454 func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 } 455 456 func newAccounts(n int) (accounts Accounts) { 457 for i := 0; i < n; i++ { 458 key, _ := crypto.GenerateKey() 459 addr := crypto.PubkeyToAddress(key.PublicKey) 460 accounts = append(accounts, Account{key: key, addr: addr}) 461 } 462 sort.Sort(accounts) 463 return accounts 464 }