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