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