github.com/coltonfike/e2c@v21.1.0+incompatible/eth/handler_test.go (about)

     1  // Copyright 2015 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 eth
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"math/big"
    23  	"math/rand"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/consensus"
    29  	"github.com/ethereum/go-ethereum/consensus/ethash"
    30  	"github.com/ethereum/go-ethereum/core"
    31  	"github.com/ethereum/go-ethereum/core/rawdb"
    32  	"github.com/ethereum/go-ethereum/core/state"
    33  	"github.com/ethereum/go-ethereum/core/types"
    34  	"github.com/ethereum/go-ethereum/core/vm"
    35  	"github.com/ethereum/go-ethereum/crypto"
    36  	"github.com/ethereum/go-ethereum/eth/downloader"
    37  	"github.com/ethereum/go-ethereum/event"
    38  	"github.com/ethereum/go-ethereum/p2p"
    39  	"github.com/ethereum/go-ethereum/params"
    40  )
    41  
    42  // Tests that correct consensus mechanism details are returned in NodeInfo.
    43  func TestNodeInfo(t *testing.T) {
    44  
    45  	// Define the tests to be run
    46  	tests := []struct {
    47  		consensus      string
    48  		cliqueConfig   *params.CliqueConfig
    49  		istanbulConfig *params.IstanbulConfig
    50  		raftMode       bool
    51  	}{
    52  		{"ethash", nil, nil, false},
    53  		{"raft", nil, nil, true},
    54  		{"istanbul", nil, &params.IstanbulConfig{1, 1, big.NewInt(0)}, false},
    55  		{"clique", &params.CliqueConfig{1, 1, 0}, nil, false},
    56  	}
    57  
    58  	// Make sure anything we screw up is restored
    59  	backup := consensus.EthProtocol.Versions
    60  	defer func() { consensus.EthProtocol.Versions = backup }()
    61  
    62  	// Try all available consensus mechanisms and check for errors
    63  	for i, tt := range tests {
    64  
    65  		pm, _, err := newTestProtocolManagerConsensus(tt.consensus, tt.cliqueConfig, tt.istanbulConfig, tt.raftMode)
    66  
    67  		if pm != nil {
    68  			defer pm.Stop()
    69  		}
    70  		if err == nil {
    71  			pmConsensus := pm.getConsensusAlgorithm()
    72  			if tt.consensus != pmConsensus {
    73  				t.Errorf("test %d: consensus type error, wanted %v but got %v", i, tt.consensus, pmConsensus)
    74  			}
    75  		} else {
    76  			t.Errorf("test %d: consensus type error %v", i, err)
    77  		}
    78  	}
    79  }
    80  
    81  // Tests that block headers can be retrieved from a remote chain based on user queries.
    82  func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
    83  func TestGetBlockHeaders64(t *testing.T) { testGetBlockHeaders(t, 64) }
    84  
    85  func testGetBlockHeaders(t *testing.T, protocol int) {
    86  	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil)
    87  	peer, _ := newTestPeer("peer", protocol, pm, true)
    88  	defer peer.close()
    89  
    90  	// Create a "random" unknown hash for testing
    91  	var unknown common.Hash
    92  	for i := range unknown {
    93  		unknown[i] = byte(i)
    94  	}
    95  	// Create a batch of tests for various scenarios
    96  	limit := uint64(downloader.MaxHeaderFetch)
    97  	tests := []struct {
    98  		query  *getBlockHeadersData // The query to execute for header retrieval
    99  		expect []common.Hash        // The hashes of the block whose headers are expected
   100  	}{
   101  		// A single random block should be retrievable by hash and number too
   102  		{
   103  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1},
   104  			[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()},
   105  		}, {
   106  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1},
   107  			[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()},
   108  		},
   109  		// Multiple headers should be retrievable in both directions
   110  		{
   111  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3},
   112  			[]common.Hash{
   113  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
   114  				pm.blockchain.GetBlockByNumber(limit/2 + 1).Hash(),
   115  				pm.blockchain.GetBlockByNumber(limit/2 + 2).Hash(),
   116  			},
   117  		}, {
   118  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true},
   119  			[]common.Hash{
   120  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
   121  				pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(),
   122  				pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(),
   123  			},
   124  		},
   125  		// Multiple headers with skip lists should be retrievable
   126  		{
   127  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3},
   128  			[]common.Hash{
   129  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
   130  				pm.blockchain.GetBlockByNumber(limit/2 + 4).Hash(),
   131  				pm.blockchain.GetBlockByNumber(limit/2 + 8).Hash(),
   132  			},
   133  		}, {
   134  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true},
   135  			[]common.Hash{
   136  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
   137  				pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(),
   138  				pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(),
   139  			},
   140  		},
   141  		// The chain endpoints should be retrievable
   142  		{
   143  			&getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1},
   144  			[]common.Hash{pm.blockchain.GetBlockByNumber(0).Hash()},
   145  		}, {
   146  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1},
   147  			[]common.Hash{pm.blockchain.CurrentBlock().Hash()},
   148  		},
   149  		// Ensure protocol limits are honored
   150  		{
   151  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true},
   152  			pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit),
   153  		},
   154  		// Check that requesting more than available is handled gracefully
   155  		{
   156  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3},
   157  			[]common.Hash{
   158  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(),
   159  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(),
   160  			},
   161  		}, {
   162  			&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true},
   163  			[]common.Hash{
   164  				pm.blockchain.GetBlockByNumber(4).Hash(),
   165  				pm.blockchain.GetBlockByNumber(0).Hash(),
   166  			},
   167  		},
   168  		// Check that requesting more than available is handled gracefully, even if mid skip
   169  		{
   170  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3},
   171  			[]common.Hash{
   172  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(),
   173  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(),
   174  			},
   175  		}, {
   176  			&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true},
   177  			[]common.Hash{
   178  				pm.blockchain.GetBlockByNumber(4).Hash(),
   179  				pm.blockchain.GetBlockByNumber(1).Hash(),
   180  			},
   181  		},
   182  		// Check a corner case where requesting more can iterate past the endpoints
   183  		{
   184  			&getBlockHeadersData{Origin: hashOrNumber{Number: 2}, Amount: 5, Reverse: true},
   185  			[]common.Hash{
   186  				pm.blockchain.GetBlockByNumber(2).Hash(),
   187  				pm.blockchain.GetBlockByNumber(1).Hash(),
   188  				pm.blockchain.GetBlockByNumber(0).Hash(),
   189  			},
   190  		},
   191  		// Check a corner case where skipping overflow loops back into the chain start
   192  		{
   193  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1},
   194  			[]common.Hash{
   195  				pm.blockchain.GetBlockByNumber(3).Hash(),
   196  			},
   197  		},
   198  		// Check a corner case where skipping overflow loops back to the same header
   199  		{
   200  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64},
   201  			[]common.Hash{
   202  				pm.blockchain.GetBlockByNumber(1).Hash(),
   203  			},
   204  		},
   205  		// Check that non existing headers aren't returned
   206  		{
   207  			&getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1},
   208  			[]common.Hash{},
   209  		}, {
   210  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1},
   211  			[]common.Hash{},
   212  		},
   213  	}
   214  	// Run each of the tests and verify the results against the chain
   215  	for i, tt := range tests {
   216  		// Collect the headers to expect in the response
   217  		headers := []*types.Header{}
   218  		for _, hash := range tt.expect {
   219  			headers = append(headers, pm.blockchain.GetBlockByHash(hash).Header())
   220  		}
   221  		// Send the hash request and verify the response
   222  		p2p.Send(peer.app, 0x03, tt.query)
   223  		if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil {
   224  			t.Errorf("test %d: headers mismatch: %v", i, err)
   225  		}
   226  		// If the test used number origins, repeat with hashes as the too
   227  		if tt.query.Origin.Hash == (common.Hash{}) {
   228  			if origin := pm.blockchain.GetBlockByNumber(tt.query.Origin.Number); origin != nil {
   229  				tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0
   230  
   231  				p2p.Send(peer.app, 0x03, tt.query)
   232  				if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil {
   233  					t.Errorf("test %d: headers mismatch: %v", i, err)
   234  				}
   235  			}
   236  		}
   237  	}
   238  }
   239  
   240  // Tests that block contents can be retrieved from a remote chain based on their hashes.
   241  func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
   242  func TestGetBlockBodies64(t *testing.T) { testGetBlockBodies(t, 64) }
   243  
   244  func testGetBlockBodies(t *testing.T, protocol int) {
   245  	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil)
   246  	peer, _ := newTestPeer("peer", protocol, pm, true)
   247  	defer peer.close()
   248  
   249  	// Create a batch of tests for various scenarios
   250  	limit := downloader.MaxBlockFetch
   251  	tests := []struct {
   252  		random    int           // Number of blocks to fetch randomly from the chain
   253  		explicit  []common.Hash // Explicitly requested blocks
   254  		available []bool        // Availability of explicitly requested blocks
   255  		expected  int           // Total number of existing blocks to expect
   256  	}{
   257  		{1, nil, nil, 1},             // A single random block should be retrievable
   258  		{10, nil, nil, 10},           // Multiple random blocks should be retrievable
   259  		{limit, nil, nil, limit},     // The maximum possible blocks should be retrievable
   260  		{limit + 1, nil, nil, limit}, // No more than the possible block count should be returned
   261  		{0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1},      // The genesis block should be retrievable
   262  		{0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable
   263  		{0, []common.Hash{{}}, []bool{false}, 0},                                 // A non existent block should not be returned
   264  
   265  		// Existing and non-existing blocks interleaved should not cause problems
   266  		{0, []common.Hash{
   267  			{},
   268  			pm.blockchain.GetBlockByNumber(1).Hash(),
   269  			{},
   270  			pm.blockchain.GetBlockByNumber(10).Hash(),
   271  			{},
   272  			pm.blockchain.GetBlockByNumber(100).Hash(),
   273  			{},
   274  		}, []bool{false, true, false, true, false, true, false}, 3},
   275  	}
   276  	// Run each of the tests and verify the results against the chain
   277  	for i, tt := range tests {
   278  		// Collect the hashes to request, and the response to expect
   279  		hashes, seen := []common.Hash{}, make(map[int64]bool)
   280  		bodies := []*blockBody{}
   281  
   282  		for j := 0; j < tt.random; j++ {
   283  			for {
   284  				num := rand.Int63n(int64(pm.blockchain.CurrentBlock().NumberU64()))
   285  				if !seen[num] {
   286  					seen[num] = true
   287  
   288  					block := pm.blockchain.GetBlockByNumber(uint64(num))
   289  					hashes = append(hashes, block.Hash())
   290  					if len(bodies) < tt.expected {
   291  						bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()})
   292  					}
   293  					break
   294  				}
   295  			}
   296  		}
   297  		for j, hash := range tt.explicit {
   298  			hashes = append(hashes, hash)
   299  			if tt.available[j] && len(bodies) < tt.expected {
   300  				block := pm.blockchain.GetBlockByHash(hash)
   301  				bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()})
   302  			}
   303  		}
   304  		// Send the hash request and verify the response
   305  		p2p.Send(peer.app, 0x05, hashes)
   306  		if err := p2p.ExpectMsg(peer.app, 0x06, bodies); err != nil {
   307  			t.Errorf("test %d: bodies mismatch: %v", i, err)
   308  		}
   309  	}
   310  }
   311  
   312  // Tests that the node state database can be retrieved based on hashes.
   313  func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) }
   314  func TestGetNodeData64(t *testing.T) { testGetNodeData(t, 64) }
   315  
   316  func testGetNodeData(t *testing.T, protocol int) {
   317  	// Define three accounts to simulate transactions with
   318  	acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   319  	acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
   320  	acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
   321  	acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
   322  
   323  	signer := types.HomesteadSigner{}
   324  	// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test)
   325  	generator := func(i int, block *core.BlockGen) {
   326  		switch i {
   327  		case 0:
   328  			// In block 1, the test bank sends account #1 some ether.
   329  			tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
   330  			block.AddTx(tx)
   331  		case 1:
   332  			// In block 2, the test bank sends some more ether to account #1.
   333  			// acc1Addr passes it on to account #2.
   334  			tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
   335  			tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
   336  			block.AddTx(tx1)
   337  			block.AddTx(tx2)
   338  		case 2:
   339  			// Block 3 is empty but was mined by account #2.
   340  			block.SetCoinbase(acc2Addr)
   341  			block.SetExtra([]byte("yeehaw"))
   342  		case 3:
   343  			// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
   344  			b2 := block.PrevBlock(1).Header()
   345  			b2.Extra = []byte("foo")
   346  			block.AddUncle(b2)
   347  			b3 := block.PrevBlock(2).Header()
   348  			b3.Extra = []byte("foo")
   349  			block.AddUncle(b3)
   350  		}
   351  	}
   352  	// Assemble the test environment
   353  	pm, db := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
   354  	peer, _ := newTestPeer("peer", protocol, pm, true)
   355  	defer peer.close()
   356  
   357  	// Fetch for now the entire chain db
   358  	hashes := []common.Hash{}
   359  
   360  	it := db.NewIterator()
   361  	for it.Next() {
   362  		if key := it.Key(); len(key) == common.HashLength {
   363  			hashes = append(hashes, common.BytesToHash(key))
   364  		}
   365  	}
   366  	it.Release()
   367  
   368  	p2p.Send(peer.app, 0x0d, hashes)
   369  	msg, err := peer.app.ReadMsg()
   370  	if err != nil {
   371  		t.Fatalf("failed to read node data response: %v", err)
   372  	}
   373  	if msg.Code != 0x0e {
   374  		t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c)
   375  	}
   376  	var data [][]byte
   377  	if err := msg.Decode(&data); err != nil {
   378  		t.Fatalf("failed to decode response node data: %v", err)
   379  	}
   380  	// Verify that all hashes correspond to the requested data, and reconstruct a state tree
   381  	for i, want := range hashes {
   382  		if hash := crypto.Keccak256Hash(data[i]); hash != want {
   383  			t.Errorf("data hash mismatch: have %x, want %x", hash, want)
   384  		}
   385  	}
   386  	statedb := rawdb.NewMemoryDatabase()
   387  	for i := 0; i < len(data); i++ {
   388  		statedb.Put(hashes[i].Bytes(), data[i])
   389  	}
   390  	accounts := []common.Address{testBank, acc1Addr, acc2Addr}
   391  	for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
   392  		trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb))
   393  
   394  		for j, acc := range accounts {
   395  			state, _, _ := pm.blockchain.State()
   396  			bw := state.GetBalance(acc)
   397  			bh := trie.GetBalance(acc)
   398  
   399  			if (bw != nil && bh == nil) || (bw == nil && bh != nil) {
   400  				t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
   401  			}
   402  			if bw != nil && bh != nil && bw.Cmp(bw) != 0 {
   403  				t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
   404  			}
   405  		}
   406  	}
   407  }
   408  
   409  // Tests that the transaction receipts can be retrieved based on hashes.
   410  func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) }
   411  func TestGetReceipt64(t *testing.T) { testGetReceipt(t, 64) }
   412  
   413  func testGetReceipt(t *testing.T, protocol int) {
   414  	// Define three accounts to simulate transactions with
   415  	acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   416  	acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
   417  	acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
   418  	acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
   419  
   420  	signer := types.HomesteadSigner{}
   421  	// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test)
   422  	generator := func(i int, block *core.BlockGen) {
   423  		switch i {
   424  		case 0:
   425  			// In block 1, the test bank sends account #1 some ether.
   426  			tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
   427  			block.AddTx(tx)
   428  		case 1:
   429  			// In block 2, the test bank sends some more ether to account #1.
   430  			// acc1Addr passes it on to account #2.
   431  			tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
   432  			tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
   433  			block.AddTx(tx1)
   434  			block.AddTx(tx2)
   435  		case 2:
   436  			// Block 3 is empty but was mined by account #2.
   437  			block.SetCoinbase(acc2Addr)
   438  			block.SetExtra([]byte("yeehaw"))
   439  		case 3:
   440  			// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
   441  			b2 := block.PrevBlock(1).Header()
   442  			b2.Extra = []byte("foo")
   443  			block.AddUncle(b2)
   444  			b3 := block.PrevBlock(2).Header()
   445  			b3.Extra = []byte("foo")
   446  			block.AddUncle(b3)
   447  		}
   448  	}
   449  	// Assemble the test environment
   450  	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
   451  	peer, _ := newTestPeer("peer", protocol, pm, true)
   452  	defer peer.close()
   453  
   454  	// Collect the hashes to request, and the response to expect
   455  	hashes, receipts := []common.Hash{}, []types.Receipts{}
   456  	for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
   457  		block := pm.blockchain.GetBlockByNumber(i)
   458  
   459  		hashes = append(hashes, block.Hash())
   460  		receipts = append(receipts, pm.blockchain.GetReceiptsByHash(block.Hash()))
   461  	}
   462  	// Send the hash request and verify the response
   463  	p2p.Send(peer.app, 0x0f, hashes)
   464  	if err := p2p.ExpectMsg(peer.app, 0x10, receipts); err != nil {
   465  		t.Errorf("receipts mismatch: %v", err)
   466  	}
   467  }
   468  
   469  // Tests that post eth protocol handshake, clients perform a mutual checkpoint
   470  // challenge to validate each other's chains. Hash mismatches, or missing ones
   471  // during a fast sync should lead to the peer getting dropped.
   472  func TestCheckpointChallenge(t *testing.T) {
   473  	tests := []struct {
   474  		syncmode   downloader.SyncMode
   475  		checkpoint bool
   476  		timeout    bool
   477  		empty      bool
   478  		match      bool
   479  		drop       bool
   480  	}{
   481  		// If checkpointing is not enabled locally, don't challenge and don't drop
   482  		{downloader.FullSync, false, false, false, false, false},
   483  		{downloader.FastSync, false, false, false, false, false},
   484  
   485  		// If checkpointing is enabled locally and remote response is empty, only drop during fast sync
   486  		{downloader.FullSync, true, false, true, false, false},
   487  		{downloader.FastSync, true, false, true, false, true}, // Special case, fast sync, unsynced peer
   488  
   489  		// If checkpointing is enabled locally and remote response mismatches, always drop
   490  		{downloader.FullSync, true, false, false, false, true},
   491  		{downloader.FastSync, true, false, false, false, true},
   492  
   493  		// If checkpointing is enabled locally and remote response matches, never drop
   494  		{downloader.FullSync, true, false, false, true, false},
   495  		{downloader.FastSync, true, false, false, true, false},
   496  
   497  		// If checkpointing is enabled locally and remote times out, always drop
   498  		{downloader.FullSync, true, true, false, true, true},
   499  		{downloader.FastSync, true, true, false, true, true},
   500  	}
   501  	for _, tt := range tests {
   502  		t.Run(fmt.Sprintf("sync %v checkpoint %v timeout %v empty %v match %v", tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match), func(t *testing.T) {
   503  			testCheckpointChallenge(t, tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match, tt.drop)
   504  		})
   505  	}
   506  }
   507  
   508  func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) {
   509  	// Reduce the checkpoint handshake challenge timeout
   510  	defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout)
   511  	syncChallengeTimeout = 250 * time.Millisecond
   512  
   513  	// Initialize a chain and generate a fake CHT if checkpointing is enabled
   514  	var (
   515  		db     = rawdb.NewMemoryDatabase()
   516  		config = new(params.ChainConfig)
   517  	)
   518  	(&core.Genesis{Config: config}).MustCommit(db) // Commit genesis block
   519  	// If checkpointing is enabled, create and inject a fake CHT and the corresponding
   520  	// chllenge response.
   521  	var response *types.Header
   522  	var cht *params.TrustedCheckpoint
   523  	if checkpoint {
   524  		index := uint64(rand.Intn(500))
   525  		number := (index+1)*params.CHTFrequency - 1
   526  		response = &types.Header{Number: big.NewInt(int64(number)), Extra: []byte("valid")}
   527  
   528  		cht = &params.TrustedCheckpoint{
   529  			SectionIndex: index,
   530  			SectionHead:  response.Hash(),
   531  		}
   532  	}
   533  	// Create a checkpoint aware protocol manager
   534  	blockchain, err := core.NewBlockChain(db, nil, config, ethash.NewFaker(), vm.Config{}, nil)
   535  	if err != nil {
   536  		t.Fatalf("failed to create new blockchain: %v", err)
   537  	}
   538  	pm, err := NewProtocolManager(config, cht, syncmode, DefaultConfig.NetworkId, new(event.TypeMux), new(testTxPool), ethash.NewFaker(), blockchain, db, 1, nil, false)
   539  	if err != nil {
   540  		t.Fatalf("failed to start test protocol manager: %v", err)
   541  	}
   542  	pm.Start(1000)
   543  	defer pm.Stop()
   544  
   545  	// Connect a new peer and check that we receive the checkpoint challenge
   546  	peer, _ := newTestPeer("peer", eth63, pm, true)
   547  	defer peer.close()
   548  
   549  	if checkpoint {
   550  		challenge := &getBlockHeadersData{
   551  			Origin:  hashOrNumber{Number: response.Number.Uint64()},
   552  			Amount:  1,
   553  			Skip:    0,
   554  			Reverse: false,
   555  		}
   556  		if err := p2p.ExpectMsg(peer.app, GetBlockHeadersMsg, challenge); err != nil {
   557  			t.Fatalf("challenge mismatch: %v", err)
   558  		}
   559  		// Create a block to reply to the challenge if no timeout is simulated
   560  		if !timeout {
   561  			if empty {
   562  				if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{}); err != nil {
   563  					t.Fatalf("failed to answer challenge: %v", err)
   564  				}
   565  			} else if match {
   566  				if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{response}); err != nil {
   567  					t.Fatalf("failed to answer challenge: %v", err)
   568  				}
   569  			} else {
   570  				if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{{Number: response.Number}}); err != nil {
   571  					t.Fatalf("failed to answer challenge: %v", err)
   572  				}
   573  			}
   574  		}
   575  	}
   576  	// Wait until the test timeout passes to ensure proper cleanup
   577  	time.Sleep(syncChallengeTimeout + 100*time.Millisecond)
   578  
   579  	// Verify that the remote peer is maintained or dropped
   580  	if drop {
   581  		if peers := pm.peers.Len(); peers != 0 {
   582  			t.Fatalf("peer count mismatch: have %d, want %d", peers, 0)
   583  		}
   584  	} else {
   585  		if peers := pm.peers.Len(); peers != 1 {
   586  			t.Fatalf("peer count mismatch: have %d, want %d", peers, 1)
   587  		}
   588  	}
   589  }
   590  
   591  func TestBroadcastBlock(t *testing.T) {
   592  	var tests = []struct {
   593  		totalPeers        int
   594  		broadcastExpected int
   595  	}{
   596  		{1, 1},
   597  		{2, 2},
   598  		{3, 3},
   599  		{4, 4},
   600  		{5, 4},
   601  		{9, 4},
   602  		{12, 4},
   603  		{16, 4},
   604  		{26, 5},
   605  		{100, 10},
   606  	}
   607  	for _, test := range tests {
   608  		testBroadcastBlock(t, test.totalPeers, test.broadcastExpected)
   609  	}
   610  }
   611  
   612  func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int) {
   613  	var (
   614  		evmux   = new(event.TypeMux)
   615  		pow     = ethash.NewFaker()
   616  		db      = rawdb.NewMemoryDatabase()
   617  		config  = &params.ChainConfig{}
   618  		gspec   = &core.Genesis{Config: config}
   619  		genesis = gspec.MustCommit(db)
   620  	)
   621  	blockchain, err := core.NewBlockChain(db, nil, config, pow, vm.Config{}, nil)
   622  	if err != nil {
   623  		t.Fatalf("failed to create new blockchain: %v", err)
   624  	}
   625  	pm, err := NewProtocolManager(config, nil, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db, 1, nil, false)
   626  	if err != nil {
   627  		t.Fatalf("failed to start test protocol manager: %v", err)
   628  	}
   629  	pm.Start(1000)
   630  	defer pm.Stop()
   631  	var peers []*testPeer
   632  	for i := 0; i < totalPeers; i++ {
   633  		peer, _ := newTestPeer(fmt.Sprintf("peer %d", i), eth63, pm, true)
   634  		defer peer.close()
   635  		peers = append(peers, peer)
   636  	}
   637  	chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) {})
   638  	pm.BroadcastBlock(chain[0], true /*propagate*/)
   639  
   640  	errCh := make(chan error, totalPeers)
   641  	doneCh := make(chan struct{}, totalPeers)
   642  	for _, peer := range peers {
   643  		go func(p *testPeer) {
   644  			if err := p2p.ExpectMsg(p.app, NewBlockMsg, &newBlockData{Block: chain[0], TD: big.NewInt(131136)}); err != nil {
   645  				errCh <- err
   646  			} else {
   647  				doneCh <- struct{}{}
   648  			}
   649  		}(peer)
   650  	}
   651  	timeout := time.After(300 * time.Millisecond)
   652  	var receivedCount int
   653  outer:
   654  	for {
   655  		select {
   656  		case err = <-errCh:
   657  			break outer
   658  		case <-doneCh:
   659  			receivedCount++
   660  			if receivedCount == totalPeers {
   661  				break outer
   662  			}
   663  		case <-timeout:
   664  			break outer
   665  		}
   666  	}
   667  	for _, peer := range peers {
   668  		peer.app.Close()
   669  	}
   670  	if err != nil {
   671  		t.Errorf("error matching block by peer: %v", err)
   672  	}
   673  	if receivedCount != broadcastExpected {
   674  		t.Errorf("block broadcast to %d peers, expected %d", receivedCount, broadcastExpected)
   675  	}
   676  }