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  }