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: &ethapi.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: &ethapi.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: &ethapi.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: &ethapi.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, &ethapi.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: &ethapi.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: &ethapi.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: &ethapi.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  }