github.com/codingfuture/orig-energi3@v0.8.4/eth/handler_test.go (about)

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