github.com/snowblossomcoin/go-ethereum@v1.9.25/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  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"math/big"
    24  	"reflect"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum"
    29  	"github.com/ethereum/go-ethereum/common"
    30  	"github.com/ethereum/go-ethereum/consensus/ethash"
    31  	"github.com/ethereum/go-ethereum/core"
    32  	"github.com/ethereum/go-ethereum/core/rawdb"
    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/node"
    37  	"github.com/ethereum/go-ethereum/params"
    38  )
    39  
    40  // Verify that Client implements the ethereum interfaces.
    41  var (
    42  	_ = ethereum.ChainReader(&Client{})
    43  	_ = ethereum.TransactionReader(&Client{})
    44  	_ = ethereum.ChainStateReader(&Client{})
    45  	_ = ethereum.ChainSyncReader(&Client{})
    46  	_ = ethereum.ContractCaller(&Client{})
    47  	_ = ethereum.GasEstimator(&Client{})
    48  	_ = ethereum.GasPricer(&Client{})
    49  	_ = ethereum.LogFilterer(&Client{})
    50  	_ = ethereum.PendingStateReader(&Client{})
    51  	// _ = ethereum.PendingStateEventer(&Client{})
    52  	_ = ethereum.PendingContractCaller(&Client{})
    53  )
    54  
    55  func TestToFilterArg(t *testing.T) {
    56  	blockHashErr := fmt.Errorf("cannot specify both BlockHash and FromBlock/ToBlock")
    57  	addresses := []common.Address{
    58  		common.HexToAddress("0xD36722ADeC3EdCB29c8e7b5a47f352D701393462"),
    59  	}
    60  	blockHash := common.HexToHash(
    61  		"0xeb94bb7d78b73657a9d7a99792413f50c0a45c51fc62bdcb08a53f18e9a2b4eb",
    62  	)
    63  
    64  	for _, testCase := range []struct {
    65  		name   string
    66  		input  ethereum.FilterQuery
    67  		output interface{}
    68  		err    error
    69  	}{
    70  		{
    71  			"without BlockHash",
    72  			ethereum.FilterQuery{
    73  				Addresses: addresses,
    74  				FromBlock: big.NewInt(1),
    75  				ToBlock:   big.NewInt(2),
    76  				Topics:    [][]common.Hash{},
    77  			},
    78  			map[string]interface{}{
    79  				"address":   addresses,
    80  				"fromBlock": "0x1",
    81  				"toBlock":   "0x2",
    82  				"topics":    [][]common.Hash{},
    83  			},
    84  			nil,
    85  		},
    86  		{
    87  			"with nil fromBlock and nil toBlock",
    88  			ethereum.FilterQuery{
    89  				Addresses: addresses,
    90  				Topics:    [][]common.Hash{},
    91  			},
    92  			map[string]interface{}{
    93  				"address":   addresses,
    94  				"fromBlock": "0x0",
    95  				"toBlock":   "latest",
    96  				"topics":    [][]common.Hash{},
    97  			},
    98  			nil,
    99  		},
   100  		{
   101  			"with negative fromBlock and negative toBlock",
   102  			ethereum.FilterQuery{
   103  				Addresses: addresses,
   104  				FromBlock: big.NewInt(-1),
   105  				ToBlock:   big.NewInt(-1),
   106  				Topics:    [][]common.Hash{},
   107  			},
   108  			map[string]interface{}{
   109  				"address":   addresses,
   110  				"fromBlock": "pending",
   111  				"toBlock":   "pending",
   112  				"topics":    [][]common.Hash{},
   113  			},
   114  			nil,
   115  		},
   116  		{
   117  			"with blockhash",
   118  			ethereum.FilterQuery{
   119  				Addresses: addresses,
   120  				BlockHash: &blockHash,
   121  				Topics:    [][]common.Hash{},
   122  			},
   123  			map[string]interface{}{
   124  				"address":   addresses,
   125  				"blockHash": blockHash,
   126  				"topics":    [][]common.Hash{},
   127  			},
   128  			nil,
   129  		},
   130  		{
   131  			"with blockhash and from block",
   132  			ethereum.FilterQuery{
   133  				Addresses: addresses,
   134  				BlockHash: &blockHash,
   135  				FromBlock: big.NewInt(1),
   136  				Topics:    [][]common.Hash{},
   137  			},
   138  			nil,
   139  			blockHashErr,
   140  		},
   141  		{
   142  			"with blockhash and to block",
   143  			ethereum.FilterQuery{
   144  				Addresses: addresses,
   145  				BlockHash: &blockHash,
   146  				ToBlock:   big.NewInt(1),
   147  				Topics:    [][]common.Hash{},
   148  			},
   149  			nil,
   150  			blockHashErr,
   151  		},
   152  		{
   153  			"with blockhash and both from / to block",
   154  			ethereum.FilterQuery{
   155  				Addresses: addresses,
   156  				BlockHash: &blockHash,
   157  				FromBlock: big.NewInt(1),
   158  				ToBlock:   big.NewInt(2),
   159  				Topics:    [][]common.Hash{},
   160  			},
   161  			nil,
   162  			blockHashErr,
   163  		},
   164  	} {
   165  		t.Run(testCase.name, func(t *testing.T) {
   166  			output, err := toFilterArg(testCase.input)
   167  			if (testCase.err == nil) != (err == nil) {
   168  				t.Fatalf("expected error %v but got %v", testCase.err, err)
   169  			}
   170  			if testCase.err != nil {
   171  				if testCase.err.Error() != err.Error() {
   172  					t.Fatalf("expected error %v but got %v", testCase.err, err)
   173  				}
   174  			} else if !reflect.DeepEqual(testCase.output, output) {
   175  				t.Fatalf("expected filter arg %v but got %v", testCase.output, output)
   176  			}
   177  		})
   178  	}
   179  }
   180  
   181  var (
   182  	testKey, _  = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   183  	testAddr    = crypto.PubkeyToAddress(testKey.PublicKey)
   184  	testBalance = big.NewInt(2e10)
   185  )
   186  
   187  func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
   188  	// Generate test chain.
   189  	genesis, blocks := generateTestChain()
   190  	// Create node
   191  	n, err := node.New(&node.Config{})
   192  	if err != nil {
   193  		t.Fatalf("can't create new node: %v", err)
   194  	}
   195  	// Create Ethereum Service
   196  	config := &eth.Config{Genesis: genesis}
   197  	config.Ethash.PowMode = ethash.ModeFake
   198  	ethservice, err := eth.New(n, config)
   199  	if err != nil {
   200  		t.Fatalf("can't create new ethereum service: %v", err)
   201  	}
   202  	// Import the test chain.
   203  	if err := n.Start(); err != nil {
   204  		t.Fatalf("can't start test node: %v", err)
   205  	}
   206  	if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil {
   207  		t.Fatalf("can't import test blocks: %v", err)
   208  	}
   209  	return n, blocks
   210  }
   211  
   212  func generateTestChain() (*core.Genesis, []*types.Block) {
   213  	db := rawdb.NewMemoryDatabase()
   214  	config := params.AllEthashProtocolChanges
   215  	genesis := &core.Genesis{
   216  		Config:    config,
   217  		Alloc:     core.GenesisAlloc{testAddr: {Balance: testBalance}},
   218  		ExtraData: []byte("test genesis"),
   219  		Timestamp: 9000,
   220  	}
   221  	generate := func(i int, g *core.BlockGen) {
   222  		g.OffsetTime(5)
   223  		g.SetExtra([]byte("test"))
   224  	}
   225  	gblock := genesis.ToBlock(db)
   226  	engine := ethash.NewFaker()
   227  	blocks, _ := core.GenerateChain(config, gblock, engine, db, 1, generate)
   228  	blocks = append([]*types.Block{gblock}, blocks...)
   229  	return genesis, blocks
   230  }
   231  
   232  func TestHeader(t *testing.T) {
   233  	backend, chain := newTestBackend(t)
   234  	client, _ := backend.Attach()
   235  	defer backend.Close()
   236  	defer client.Close()
   237  
   238  	tests := map[string]struct {
   239  		block   *big.Int
   240  		want    *types.Header
   241  		wantErr error
   242  	}{
   243  		"genesis": {
   244  			block: big.NewInt(0),
   245  			want:  chain[0].Header(),
   246  		},
   247  		"first_block": {
   248  			block: big.NewInt(1),
   249  			want:  chain[1].Header(),
   250  		},
   251  		"future_block": {
   252  			block: big.NewInt(1000000000),
   253  			want:  nil,
   254  		},
   255  	}
   256  	for name, tt := range tests {
   257  		t.Run(name, func(t *testing.T) {
   258  			ec := NewClient(client)
   259  			ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
   260  			defer cancel()
   261  
   262  			got, err := ec.HeaderByNumber(ctx, tt.block)
   263  			if tt.wantErr != nil && (err == nil || err.Error() != tt.wantErr.Error()) {
   264  				t.Fatalf("HeaderByNumber(%v) error = %q, want %q", tt.block, err, tt.wantErr)
   265  			}
   266  			if got != nil && got.Number.Sign() == 0 {
   267  				got.Number = big.NewInt(0) // hack to make DeepEqual work
   268  			}
   269  			if !reflect.DeepEqual(got, tt.want) {
   270  				t.Fatalf("HeaderByNumber(%v)\n   = %v\nwant %v", tt.block, got, tt.want)
   271  			}
   272  		})
   273  	}
   274  }
   275  
   276  func TestBalanceAt(t *testing.T) {
   277  	backend, _ := newTestBackend(t)
   278  	client, _ := backend.Attach()
   279  	defer backend.Close()
   280  	defer client.Close()
   281  
   282  	tests := map[string]struct {
   283  		account common.Address
   284  		block   *big.Int
   285  		want    *big.Int
   286  		wantErr error
   287  	}{
   288  		"valid_account": {
   289  			account: testAddr,
   290  			block:   big.NewInt(1),
   291  			want:    testBalance,
   292  		},
   293  		"non_existent_account": {
   294  			account: common.Address{1},
   295  			block:   big.NewInt(1),
   296  			want:    big.NewInt(0),
   297  		},
   298  		"future_block": {
   299  			account: testAddr,
   300  			block:   big.NewInt(1000000000),
   301  			want:    big.NewInt(0),
   302  			wantErr: errors.New("header not found"),
   303  		},
   304  	}
   305  	for name, tt := range tests {
   306  		t.Run(name, func(t *testing.T) {
   307  			ec := NewClient(client)
   308  			ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
   309  			defer cancel()
   310  
   311  			got, err := ec.BalanceAt(ctx, tt.account, tt.block)
   312  			if tt.wantErr != nil && (err == nil || err.Error() != tt.wantErr.Error()) {
   313  				t.Fatalf("BalanceAt(%x, %v) error = %q, want %q", tt.account, tt.block, err, tt.wantErr)
   314  			}
   315  			if got.Cmp(tt.want) != 0 {
   316  				t.Fatalf("BalanceAt(%x, %v) = %v, want %v", tt.account, tt.block, got, tt.want)
   317  			}
   318  		})
   319  	}
   320  }
   321  
   322  func TestTransactionInBlockInterrupted(t *testing.T) {
   323  	backend, _ := newTestBackend(t)
   324  	client, _ := backend.Attach()
   325  	defer backend.Close()
   326  	defer client.Close()
   327  
   328  	ec := NewClient(client)
   329  	ctx, cancel := context.WithCancel(context.Background())
   330  	cancel()
   331  	tx, err := ec.TransactionInBlock(ctx, common.Hash{1}, 1)
   332  	if tx != nil {
   333  		t.Fatal("transaction should be nil")
   334  	}
   335  	if err == nil {
   336  		t.Fatal("error should not be nil")
   337  	}
   338  }
   339  
   340  func TestChainID(t *testing.T) {
   341  	backend, _ := newTestBackend(t)
   342  	client, _ := backend.Attach()
   343  	defer backend.Close()
   344  	defer client.Close()
   345  	ec := NewClient(client)
   346  
   347  	id, err := ec.ChainID(context.Background())
   348  	if err != nil {
   349  		t.Fatalf("unexpected error: %v", err)
   350  	}
   351  	if id == nil || id.Cmp(params.AllEthashProtocolChanges.ChainID) != 0 {
   352  		t.Fatalf("ChainID returned wrong number: %+v", id)
   353  	}
   354  }
   355  
   356  func TestBlockNumber(t *testing.T) {
   357  	backend, _ := newTestBackend(t)
   358  	client, _ := backend.Attach()
   359  	defer backend.Close()
   360  	defer client.Close()
   361  	ec := NewClient(client)
   362  
   363  	blockNumber, err := ec.BlockNumber(context.Background())
   364  	if err != nil {
   365  		t.Fatalf("unexpected error: %v", err)
   366  	}
   367  	if blockNumber != 1 {
   368  		t.Fatalf("BlockNumber returned wrong number: %d", blockNumber)
   369  	}
   370  }