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