github.com/jimmyx0x/go-ethereum@v1.10.28/ethclient/ethclient_test.go (about)

     1  // Copyright 2016 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 ethclient
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"errors"
    23  	"fmt"
    24  	"math/big"
    25  	"reflect"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/ethereum/go-ethereum"
    30  	"github.com/ethereum/go-ethereum/common"
    31  	"github.com/ethereum/go-ethereum/consensus/ethash"
    32  	"github.com/ethereum/go-ethereum/core"
    33  	"github.com/ethereum/go-ethereum/core/types"
    34  	"github.com/ethereum/go-ethereum/crypto"
    35  	"github.com/ethereum/go-ethereum/eth"
    36  	"github.com/ethereum/go-ethereum/eth/ethconfig"
    37  	"github.com/ethereum/go-ethereum/node"
    38  	"github.com/ethereum/go-ethereum/params"
    39  	"github.com/ethereum/go-ethereum/rpc"
    40  )
    41  
    42  // Verify that Client implements the ethereum interfaces.
    43  var (
    44  	_ = ethereum.ChainReader(&Client{})
    45  	_ = ethereum.TransactionReader(&Client{})
    46  	_ = ethereum.ChainStateReader(&Client{})
    47  	_ = ethereum.ChainSyncReader(&Client{})
    48  	_ = ethereum.ContractCaller(&Client{})
    49  	_ = ethereum.GasEstimator(&Client{})
    50  	_ = ethereum.GasPricer(&Client{})
    51  	_ = ethereum.LogFilterer(&Client{})
    52  	_ = ethereum.PendingStateReader(&Client{})
    53  	// _ = ethereum.PendingStateEventer(&Client{})
    54  	_ = ethereum.PendingContractCaller(&Client{})
    55  )
    56  
    57  func TestToFilterArg(t *testing.T) {
    58  	blockHashErr := fmt.Errorf("cannot specify both BlockHash and FromBlock/ToBlock")
    59  	addresses := []common.Address{
    60  		common.HexToAddress("0xD36722ADeC3EdCB29c8e7b5a47f352D701393462"),
    61  	}
    62  	blockHash := common.HexToHash(
    63  		"0xeb94bb7d78b73657a9d7a99792413f50c0a45c51fc62bdcb08a53f18e9a2b4eb",
    64  	)
    65  
    66  	for _, testCase := range []struct {
    67  		name   string
    68  		input  ethereum.FilterQuery
    69  		output interface{}
    70  		err    error
    71  	}{
    72  		{
    73  			"without BlockHash",
    74  			ethereum.FilterQuery{
    75  				Addresses: addresses,
    76  				FromBlock: big.NewInt(1),
    77  				ToBlock:   big.NewInt(2),
    78  				Topics:    [][]common.Hash{},
    79  			},
    80  			map[string]interface{}{
    81  				"address":   addresses,
    82  				"fromBlock": "0x1",
    83  				"toBlock":   "0x2",
    84  				"topics":    [][]common.Hash{},
    85  			},
    86  			nil,
    87  		},
    88  		{
    89  			"with nil fromBlock and nil toBlock",
    90  			ethereum.FilterQuery{
    91  				Addresses: addresses,
    92  				Topics:    [][]common.Hash{},
    93  			},
    94  			map[string]interface{}{
    95  				"address":   addresses,
    96  				"fromBlock": "0x0",
    97  				"toBlock":   "latest",
    98  				"topics":    [][]common.Hash{},
    99  			},
   100  			nil,
   101  		},
   102  		{
   103  			"with negative fromBlock and negative toBlock",
   104  			ethereum.FilterQuery{
   105  				Addresses: addresses,
   106  				FromBlock: big.NewInt(-1),
   107  				ToBlock:   big.NewInt(-1),
   108  				Topics:    [][]common.Hash{},
   109  			},
   110  			map[string]interface{}{
   111  				"address":   addresses,
   112  				"fromBlock": "pending",
   113  				"toBlock":   "pending",
   114  				"topics":    [][]common.Hash{},
   115  			},
   116  			nil,
   117  		},
   118  		{
   119  			"with blockhash",
   120  			ethereum.FilterQuery{
   121  				Addresses: addresses,
   122  				BlockHash: &blockHash,
   123  				Topics:    [][]common.Hash{},
   124  			},
   125  			map[string]interface{}{
   126  				"address":   addresses,
   127  				"blockHash": blockHash,
   128  				"topics":    [][]common.Hash{},
   129  			},
   130  			nil,
   131  		},
   132  		{
   133  			"with blockhash and from block",
   134  			ethereum.FilterQuery{
   135  				Addresses: addresses,
   136  				BlockHash: &blockHash,
   137  				FromBlock: big.NewInt(1),
   138  				Topics:    [][]common.Hash{},
   139  			},
   140  			nil,
   141  			blockHashErr,
   142  		},
   143  		{
   144  			"with blockhash and to block",
   145  			ethereum.FilterQuery{
   146  				Addresses: addresses,
   147  				BlockHash: &blockHash,
   148  				ToBlock:   big.NewInt(1),
   149  				Topics:    [][]common.Hash{},
   150  			},
   151  			nil,
   152  			blockHashErr,
   153  		},
   154  		{
   155  			"with blockhash and both from / to block",
   156  			ethereum.FilterQuery{
   157  				Addresses: addresses,
   158  				BlockHash: &blockHash,
   159  				FromBlock: big.NewInt(1),
   160  				ToBlock:   big.NewInt(2),
   161  				Topics:    [][]common.Hash{},
   162  			},
   163  			nil,
   164  			blockHashErr,
   165  		},
   166  	} {
   167  		t.Run(testCase.name, func(t *testing.T) {
   168  			output, err := toFilterArg(testCase.input)
   169  			if (testCase.err == nil) != (err == nil) {
   170  				t.Fatalf("expected error %v but got %v", testCase.err, err)
   171  			}
   172  			if testCase.err != nil {
   173  				if testCase.err.Error() != err.Error() {
   174  					t.Fatalf("expected error %v but got %v", testCase.err, err)
   175  				}
   176  			} else if !reflect.DeepEqual(testCase.output, output) {
   177  				t.Fatalf("expected filter arg %v but got %v", testCase.output, output)
   178  			}
   179  		})
   180  	}
   181  }
   182  
   183  var (
   184  	testKey, _  = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   185  	testAddr    = crypto.PubkeyToAddress(testKey.PublicKey)
   186  	testBalance = big.NewInt(2e15)
   187  )
   188  
   189  var genesis = &core.Genesis{
   190  	Config:    params.AllEthashProtocolChanges,
   191  	Alloc:     core.GenesisAlloc{testAddr: {Balance: testBalance}},
   192  	ExtraData: []byte("test genesis"),
   193  	Timestamp: 9000,
   194  	BaseFee:   big.NewInt(params.InitialBaseFee),
   195  }
   196  
   197  var testTx1 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{
   198  	Nonce:    0,
   199  	Value:    big.NewInt(12),
   200  	GasPrice: big.NewInt(params.InitialBaseFee),
   201  	Gas:      params.TxGas,
   202  	To:       &common.Address{2},
   203  })
   204  
   205  var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{
   206  	Nonce:    1,
   207  	Value:    big.NewInt(8),
   208  	GasPrice: big.NewInt(params.InitialBaseFee),
   209  	Gas:      params.TxGas,
   210  	To:       &common.Address{2},
   211  })
   212  
   213  func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
   214  	// Generate test chain.
   215  	blocks := generateTestChain()
   216  
   217  	// Create node
   218  	n, err := node.New(&node.Config{})
   219  	if err != nil {
   220  		t.Fatalf("can't create new node: %v", err)
   221  	}
   222  	// Create Ethereum Service
   223  	config := &ethconfig.Config{Genesis: genesis}
   224  	config.Ethash.PowMode = ethash.ModeFake
   225  	ethservice, err := eth.New(n, config)
   226  	if err != nil {
   227  		t.Fatalf("can't create new ethereum service: %v", err)
   228  	}
   229  	// Import the test chain.
   230  	if err := n.Start(); err != nil {
   231  		t.Fatalf("can't start test node: %v", err)
   232  	}
   233  	if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil {
   234  		t.Fatalf("can't import test blocks: %v", err)
   235  	}
   236  	return n, blocks
   237  }
   238  
   239  func generateTestChain() []*types.Block {
   240  	generate := func(i int, g *core.BlockGen) {
   241  		g.OffsetTime(5)
   242  		g.SetExtra([]byte("test"))
   243  		if i == 1 {
   244  			// Test transactions are included in block #2.
   245  			g.AddTx(testTx1)
   246  			g.AddTx(testTx2)
   247  		}
   248  	}
   249  	_, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 2, generate)
   250  	return append([]*types.Block{genesis.ToBlock()}, blocks...)
   251  }
   252  
   253  func TestEthClient(t *testing.T) {
   254  	backend, chain := newTestBackend(t)
   255  	client, _ := backend.Attach()
   256  	defer backend.Close()
   257  	defer client.Close()
   258  
   259  	tests := map[string]struct {
   260  		test func(t *testing.T)
   261  	}{
   262  		"Header": {
   263  			func(t *testing.T) { testHeader(t, chain, client) },
   264  		},
   265  		"BalanceAt": {
   266  			func(t *testing.T) { testBalanceAt(t, client) },
   267  		},
   268  		"TxInBlockInterrupted": {
   269  			func(t *testing.T) { testTransactionInBlockInterrupted(t, client) },
   270  		},
   271  		"ChainID": {
   272  			func(t *testing.T) { testChainID(t, client) },
   273  		},
   274  		"GetBlock": {
   275  			func(t *testing.T) { testGetBlock(t, client) },
   276  		},
   277  		"StatusFunctions": {
   278  			func(t *testing.T) { testStatusFunctions(t, client) },
   279  		},
   280  		"CallContract": {
   281  			func(t *testing.T) { testCallContract(t, client) },
   282  		},
   283  		"CallContractAtHash": {
   284  			func(t *testing.T) { testCallContractAtHash(t, client) },
   285  		},
   286  		"AtFunctions": {
   287  			func(t *testing.T) { testAtFunctions(t, client) },
   288  		},
   289  		"TransactionSender": {
   290  			func(t *testing.T) { testTransactionSender(t, client) },
   291  		},
   292  	}
   293  
   294  	t.Parallel()
   295  	for name, tt := range tests {
   296  		t.Run(name, tt.test)
   297  	}
   298  }
   299  
   300  func testHeader(t *testing.T, chain []*types.Block, client *rpc.Client) {
   301  	tests := map[string]struct {
   302  		block   *big.Int
   303  		want    *types.Header
   304  		wantErr error
   305  	}{
   306  		"genesis": {
   307  			block: big.NewInt(0),
   308  			want:  chain[0].Header(),
   309  		},
   310  		"first_block": {
   311  			block: big.NewInt(1),
   312  			want:  chain[1].Header(),
   313  		},
   314  		"future_block": {
   315  			block:   big.NewInt(1000000000),
   316  			want:    nil,
   317  			wantErr: ethereum.NotFound,
   318  		},
   319  	}
   320  	for name, tt := range tests {
   321  		t.Run(name, func(t *testing.T) {
   322  			ec := NewClient(client)
   323  			ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
   324  			defer cancel()
   325  
   326  			got, err := ec.HeaderByNumber(ctx, tt.block)
   327  			if !errors.Is(err, tt.wantErr) {
   328  				t.Fatalf("HeaderByNumber(%v) error = %q, want %q", tt.block, err, tt.wantErr)
   329  			}
   330  			if got != nil && got.Number != nil && got.Number.Sign() == 0 {
   331  				got.Number = big.NewInt(0) // hack to make DeepEqual work
   332  			}
   333  			if !reflect.DeepEqual(got, tt.want) {
   334  				t.Fatalf("HeaderByNumber(%v)\n   = %v\nwant %v", tt.block, got, tt.want)
   335  			}
   336  		})
   337  	}
   338  }
   339  
   340  func testBalanceAt(t *testing.T, client *rpc.Client) {
   341  	tests := map[string]struct {
   342  		account common.Address
   343  		block   *big.Int
   344  		want    *big.Int
   345  		wantErr error
   346  	}{
   347  		"valid_account_genesis": {
   348  			account: testAddr,
   349  			block:   big.NewInt(0),
   350  			want:    testBalance,
   351  		},
   352  		"valid_account": {
   353  			account: testAddr,
   354  			block:   big.NewInt(1),
   355  			want:    testBalance,
   356  		},
   357  		"non_existent_account": {
   358  			account: common.Address{1},
   359  			block:   big.NewInt(1),
   360  			want:    big.NewInt(0),
   361  		},
   362  		"future_block": {
   363  			account: testAddr,
   364  			block:   big.NewInt(1000000000),
   365  			want:    big.NewInt(0),
   366  			wantErr: errors.New("header not found"),
   367  		},
   368  	}
   369  	for name, tt := range tests {
   370  		t.Run(name, func(t *testing.T) {
   371  			ec := NewClient(client)
   372  			ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
   373  			defer cancel()
   374  
   375  			got, err := ec.BalanceAt(ctx, tt.account, tt.block)
   376  			if tt.wantErr != nil && (err == nil || err.Error() != tt.wantErr.Error()) {
   377  				t.Fatalf("BalanceAt(%x, %v) error = %q, want %q", tt.account, tt.block, err, tt.wantErr)
   378  			}
   379  			if got.Cmp(tt.want) != 0 {
   380  				t.Fatalf("BalanceAt(%x, %v) = %v, want %v", tt.account, tt.block, got, tt.want)
   381  			}
   382  		})
   383  	}
   384  }
   385  
   386  func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) {
   387  	ec := NewClient(client)
   388  
   389  	// Get current block by number.
   390  	block, err := ec.BlockByNumber(context.Background(), nil)
   391  	if err != nil {
   392  		t.Fatalf("unexpected error: %v", err)
   393  	}
   394  
   395  	// Test tx in block interrupted.
   396  	ctx, cancel := context.WithCancel(context.Background())
   397  	cancel()
   398  	tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0)
   399  	if tx != nil {
   400  		t.Fatal("transaction should be nil")
   401  	}
   402  	if err == nil || err == ethereum.NotFound {
   403  		t.Fatal("error should not be nil/notfound")
   404  	}
   405  
   406  	// Test tx in block not found.
   407  	if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 20); err != ethereum.NotFound {
   408  		t.Fatal("error should be ethereum.NotFound")
   409  	}
   410  }
   411  
   412  func testChainID(t *testing.T, client *rpc.Client) {
   413  	ec := NewClient(client)
   414  	id, err := ec.ChainID(context.Background())
   415  	if err != nil {
   416  		t.Fatalf("unexpected error: %v", err)
   417  	}
   418  	if id == nil || id.Cmp(params.AllEthashProtocolChanges.ChainID) != 0 {
   419  		t.Fatalf("ChainID returned wrong number: %+v", id)
   420  	}
   421  }
   422  
   423  func testGetBlock(t *testing.T, client *rpc.Client) {
   424  	ec := NewClient(client)
   425  
   426  	// Get current block number
   427  	blockNumber, err := ec.BlockNumber(context.Background())
   428  	if err != nil {
   429  		t.Fatalf("unexpected error: %v", err)
   430  	}
   431  	if blockNumber != 2 {
   432  		t.Fatalf("BlockNumber returned wrong number: %d", blockNumber)
   433  	}
   434  	// Get current block by number
   435  	block, err := ec.BlockByNumber(context.Background(), new(big.Int).SetUint64(blockNumber))
   436  	if err != nil {
   437  		t.Fatalf("unexpected error: %v", err)
   438  	}
   439  	if block.NumberU64() != blockNumber {
   440  		t.Fatalf("BlockByNumber returned wrong block: want %d got %d", blockNumber, block.NumberU64())
   441  	}
   442  	// Get current block by hash
   443  	blockH, err := ec.BlockByHash(context.Background(), block.Hash())
   444  	if err != nil {
   445  		t.Fatalf("unexpected error: %v", err)
   446  	}
   447  	if block.Hash() != blockH.Hash() {
   448  		t.Fatalf("BlockByHash returned wrong block: want %v got %v", block.Hash().Hex(), blockH.Hash().Hex())
   449  	}
   450  	// Get header by number
   451  	header, err := ec.HeaderByNumber(context.Background(), new(big.Int).SetUint64(blockNumber))
   452  	if err != nil {
   453  		t.Fatalf("unexpected error: %v", err)
   454  	}
   455  	if block.Header().Hash() != header.Hash() {
   456  		t.Fatalf("HeaderByNumber returned wrong header: want %v got %v", block.Header().Hash().Hex(), header.Hash().Hex())
   457  	}
   458  	// Get header by hash
   459  	headerH, err := ec.HeaderByHash(context.Background(), block.Hash())
   460  	if err != nil {
   461  		t.Fatalf("unexpected error: %v", err)
   462  	}
   463  	if block.Header().Hash() != headerH.Hash() {
   464  		t.Fatalf("HeaderByHash returned wrong header: want %v got %v", block.Header().Hash().Hex(), headerH.Hash().Hex())
   465  	}
   466  }
   467  
   468  func testStatusFunctions(t *testing.T, client *rpc.Client) {
   469  	ec := NewClient(client)
   470  
   471  	// Sync progress
   472  	progress, err := ec.SyncProgress(context.Background())
   473  	if err != nil {
   474  		t.Fatalf("unexpected error: %v", err)
   475  	}
   476  	if progress != nil {
   477  		t.Fatalf("unexpected progress: %v", progress)
   478  	}
   479  
   480  	// NetworkID
   481  	networkID, err := ec.NetworkID(context.Background())
   482  	if err != nil {
   483  		t.Fatalf("unexpected error: %v", err)
   484  	}
   485  	if networkID.Cmp(big.NewInt(0)) != 0 {
   486  		t.Fatalf("unexpected networkID: %v", networkID)
   487  	}
   488  
   489  	// SuggestGasPrice
   490  	gasPrice, err := ec.SuggestGasPrice(context.Background())
   491  	if err != nil {
   492  		t.Fatalf("unexpected error: %v", err)
   493  	}
   494  	if gasPrice.Cmp(big.NewInt(1000000000)) != 0 {
   495  		t.Fatalf("unexpected gas price: %v", gasPrice)
   496  	}
   497  
   498  	// SuggestGasTipCap
   499  	gasTipCap, err := ec.SuggestGasTipCap(context.Background())
   500  	if err != nil {
   501  		t.Fatalf("unexpected error: %v", err)
   502  	}
   503  	if gasTipCap.Cmp(big.NewInt(234375000)) != 0 {
   504  		t.Fatalf("unexpected gas tip cap: %v", gasTipCap)
   505  	}
   506  
   507  	// FeeHistory
   508  	history, err := ec.FeeHistory(context.Background(), 1, big.NewInt(2), []float64{95, 99})
   509  	if err != nil {
   510  		t.Fatalf("unexpected error: %v", err)
   511  	}
   512  	want := &ethereum.FeeHistory{
   513  		OldestBlock: big.NewInt(2),
   514  		Reward: [][]*big.Int{
   515  			{
   516  				big.NewInt(234375000),
   517  				big.NewInt(234375000),
   518  			},
   519  		},
   520  		BaseFee: []*big.Int{
   521  			big.NewInt(765625000),
   522  			big.NewInt(671627818),
   523  		},
   524  		GasUsedRatio: []float64{0.008912678667376286},
   525  	}
   526  	if !reflect.DeepEqual(history, want) {
   527  		t.Fatalf("FeeHistory result doesn't match expected: (got: %v, want: %v)", history, want)
   528  	}
   529  }
   530  
   531  func testCallContractAtHash(t *testing.T, client *rpc.Client) {
   532  	ec := NewClient(client)
   533  
   534  	// EstimateGas
   535  	msg := ethereum.CallMsg{
   536  		From:  testAddr,
   537  		To:    &common.Address{},
   538  		Gas:   21000,
   539  		Value: big.NewInt(1),
   540  	}
   541  	gas, err := ec.EstimateGas(context.Background(), msg)
   542  	if err != nil {
   543  		t.Fatalf("unexpected error: %v", err)
   544  	}
   545  	if gas != 21000 {
   546  		t.Fatalf("unexpected gas price: %v", gas)
   547  	}
   548  	block, err := ec.HeaderByNumber(context.Background(), big.NewInt(1))
   549  	if err != nil {
   550  		t.Fatalf("BlockByNumber error: %v", err)
   551  	}
   552  	// CallContract
   553  	if _, err := ec.CallContractAtHash(context.Background(), msg, block.Hash()); err != nil {
   554  		t.Fatalf("unexpected error: %v", err)
   555  	}
   556  }
   557  
   558  func testCallContract(t *testing.T, client *rpc.Client) {
   559  	ec := NewClient(client)
   560  
   561  	// EstimateGas
   562  	msg := ethereum.CallMsg{
   563  		From:  testAddr,
   564  		To:    &common.Address{},
   565  		Gas:   21000,
   566  		Value: big.NewInt(1),
   567  	}
   568  	gas, err := ec.EstimateGas(context.Background(), msg)
   569  	if err != nil {
   570  		t.Fatalf("unexpected error: %v", err)
   571  	}
   572  	if gas != 21000 {
   573  		t.Fatalf("unexpected gas price: %v", gas)
   574  	}
   575  	// CallContract
   576  	if _, err := ec.CallContract(context.Background(), msg, big.NewInt(1)); err != nil {
   577  		t.Fatalf("unexpected error: %v", err)
   578  	}
   579  	// PendingCallContract
   580  	if _, err := ec.PendingCallContract(context.Background(), msg); err != nil {
   581  		t.Fatalf("unexpected error: %v", err)
   582  	}
   583  }
   584  
   585  func testAtFunctions(t *testing.T, client *rpc.Client) {
   586  	ec := NewClient(client)
   587  
   588  	// send a transaction for some interesting pending status
   589  	sendTransaction(ec)
   590  	time.Sleep(100 * time.Millisecond)
   591  
   592  	// Check pending transaction count
   593  	pending, err := ec.PendingTransactionCount(context.Background())
   594  	if err != nil {
   595  		t.Fatalf("unexpected error: %v", err)
   596  	}
   597  	if pending != 1 {
   598  		t.Fatalf("unexpected pending, wanted 1 got: %v", pending)
   599  	}
   600  	// Query balance
   601  	balance, err := ec.BalanceAt(context.Background(), testAddr, nil)
   602  	if err != nil {
   603  		t.Fatalf("unexpected error: %v", err)
   604  	}
   605  	penBalance, err := ec.PendingBalanceAt(context.Background(), testAddr)
   606  	if err != nil {
   607  		t.Fatalf("unexpected error: %v", err)
   608  	}
   609  	if balance.Cmp(penBalance) == 0 {
   610  		t.Fatalf("unexpected balance: %v %v", balance, penBalance)
   611  	}
   612  	// NonceAt
   613  	nonce, err := ec.NonceAt(context.Background(), testAddr, nil)
   614  	if err != nil {
   615  		t.Fatalf("unexpected error: %v", err)
   616  	}
   617  	penNonce, err := ec.PendingNonceAt(context.Background(), testAddr)
   618  	if err != nil {
   619  		t.Fatalf("unexpected error: %v", err)
   620  	}
   621  	if penNonce != nonce+1 {
   622  		t.Fatalf("unexpected nonce: %v %v", nonce, penNonce)
   623  	}
   624  	// StorageAt
   625  	storage, err := ec.StorageAt(context.Background(), testAddr, common.Hash{}, nil)
   626  	if err != nil {
   627  		t.Fatalf("unexpected error: %v", err)
   628  	}
   629  	penStorage, err := ec.PendingStorageAt(context.Background(), testAddr, common.Hash{})
   630  	if err != nil {
   631  		t.Fatalf("unexpected error: %v", err)
   632  	}
   633  	if !bytes.Equal(storage, penStorage) {
   634  		t.Fatalf("unexpected storage: %v %v", storage, penStorage)
   635  	}
   636  	// CodeAt
   637  	code, err := ec.CodeAt(context.Background(), testAddr, nil)
   638  	if err != nil {
   639  		t.Fatalf("unexpected error: %v", err)
   640  	}
   641  	penCode, err := ec.PendingCodeAt(context.Background(), testAddr)
   642  	if err != nil {
   643  		t.Fatalf("unexpected error: %v", err)
   644  	}
   645  	if !bytes.Equal(code, penCode) {
   646  		t.Fatalf("unexpected code: %v %v", code, penCode)
   647  	}
   648  }
   649  
   650  func testTransactionSender(t *testing.T, client *rpc.Client) {
   651  	ec := NewClient(client)
   652  	ctx := context.Background()
   653  
   654  	// Retrieve testTx1 via RPC.
   655  	block2, err := ec.HeaderByNumber(ctx, big.NewInt(2))
   656  	if err != nil {
   657  		t.Fatal("can't get block 1:", err)
   658  	}
   659  	tx1, err := ec.TransactionInBlock(ctx, block2.Hash(), 0)
   660  	if err != nil {
   661  		t.Fatal("can't get tx:", err)
   662  	}
   663  	if tx1.Hash() != testTx1.Hash() {
   664  		t.Fatalf("wrong tx hash %v, want %v", tx1.Hash(), testTx1.Hash())
   665  	}
   666  
   667  	// The sender address is cached in tx1, so no additional RPC should be required in
   668  	// TransactionSender. Ensure the server is not asked by canceling the context here.
   669  	canceledCtx, cancel := context.WithCancel(context.Background())
   670  	cancel()
   671  	sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0)
   672  	if err != nil {
   673  		t.Fatal(err)
   674  	}
   675  	if sender1 != testAddr {
   676  		t.Fatal("wrong sender:", sender1)
   677  	}
   678  
   679  	// Now try to get the sender of testTx2, which was not fetched through RPC.
   680  	// TransactionSender should query the server here.
   681  	sender2, err := ec.TransactionSender(ctx, testTx2, block2.Hash(), 1)
   682  	if err != nil {
   683  		t.Fatal(err)
   684  	}
   685  	if sender2 != testAddr {
   686  		t.Fatal("wrong sender:", sender2)
   687  	}
   688  }
   689  
   690  func sendTransaction(ec *Client) error {
   691  	chainID, err := ec.ChainID(context.Background())
   692  	if err != nil {
   693  		return err
   694  	}
   695  	nonce, err := ec.PendingNonceAt(context.Background(), testAddr)
   696  	if err != nil {
   697  		return err
   698  	}
   699  
   700  	signer := types.LatestSignerForChainID(chainID)
   701  	tx, err := types.SignNewTx(testKey, signer, &types.LegacyTx{
   702  		Nonce:    nonce,
   703  		To:       &common.Address{2},
   704  		Value:    big.NewInt(1),
   705  		Gas:      22000,
   706  		GasPrice: big.NewInt(params.InitialBaseFee),
   707  	})
   708  	if err != nil {
   709  		return err
   710  	}
   711  	return ec.SendTransaction(context.Background(), tx)
   712  }