github.com/newbtp/btp@v0.0.0-20190709081714-e4aafa07224e/btp/handler_test.go (about)

     1  // Copyright 2015 The go-btpereum Authors
     2  // This file is part of the go-btpereum library.
     3  //
     4  // The go-btpereum 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-btpereum 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-btpereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package btp
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"math/big"
    23  	"math/rand"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/btpereum/go-btpereum/common"
    28  	"github.com/btpereum/go-btpereum/consensus/btpash"
    29  	"github.com/btpereum/go-btpereum/core"
    30  	"github.com/btpereum/go-btpereum/core/rawdb"
    31  	"github.com/btpereum/go-btpereum/core/state"
    32  	"github.com/btpereum/go-btpereum/core/types"
    33  	"github.com/btpereum/go-btpereum/core/vm"
    34  	"github.com/btpereum/go-btpereum/crypto"
    35  	"github.com/btpereum/go-btpereum/btp/downloader"
    36  	"github.com/btpereum/go-btpereum/event"
    37  	"github.com/btpereum/go-btpereum/p2p"
    38  	"github.com/btpereum/go-btpereum/params"
    39  )
    40  
    41  // Tests that block headers can be retrieved from a remote chain based on user queries.
    42  func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) }
    43  func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
    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 TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) }
   202  func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
   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  
   275  func testGetNodeData(t *testing.T, protocol int) {
   276  	// Define three accounts to simulate transactions with
   277  	acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   278  	acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
   279  	acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
   280  	acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
   281  
   282  	signer := types.HomesteadSigner{}
   283  	// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test)
   284  	generator := func(i int, block *core.BlockGen) {
   285  		switch i {
   286  		case 0:
   287  			// In block 1, the test bank sends account #1 some btper.
   288  			tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
   289  			block.AddTx(tx)
   290  		case 1:
   291  			// In block 2, the test bank sends some more btper to account #1.
   292  			// acc1Addr passes it on to account #2.
   293  			tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
   294  			tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
   295  			block.AddTx(tx1)
   296  			block.AddTx(tx2)
   297  		case 2:
   298  			// Block 3 is empty but was mined by account #2.
   299  			block.SetCoinbase(acc2Addr)
   300  			block.SetExtra([]byte("yeehaw"))
   301  		case 3:
   302  			// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
   303  			b2 := block.PrevBlock(1).Header()
   304  			b2.Extra = []byte("foo")
   305  			block.AddUncle(b2)
   306  			b3 := block.PrevBlock(2).Header()
   307  			b3.Extra = []byte("foo")
   308  			block.AddUncle(b3)
   309  		}
   310  	}
   311  	// Assemble the test environment
   312  	pm, db := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
   313  	peer, _ := newTestPeer("peer", protocol, pm, true)
   314  	defer peer.close()
   315  
   316  	// Fetch for now the entire chain db
   317  	hashes := []common.Hash{}
   318  
   319  	it := db.NewIterator()
   320  	for it.Next() {
   321  		if key := it.Key(); len(key) == common.HashLength {
   322  			hashes = append(hashes, common.BytesToHash(key))
   323  		}
   324  	}
   325  	it.Release()
   326  
   327  	p2p.Send(peer.app, 0x0d, hashes)
   328  	msg, err := peer.app.ReadMsg()
   329  	if err != nil {
   330  		t.Fatalf("failed to read node data response: %v", err)
   331  	}
   332  	if msg.Code != 0x0e {
   333  		t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c)
   334  	}
   335  	var data [][]byte
   336  	if err := msg.Decode(&data); err != nil {
   337  		t.Fatalf("failed to decode response node data: %v", err)
   338  	}
   339  	// Verify that all hashes correspond to the requested data, and reconstruct a state tree
   340  	for i, want := range hashes {
   341  		if hash := crypto.Keccak256Hash(data[i]); hash != want {
   342  			t.Errorf("data hash mismatch: have %x, want %x", hash, want)
   343  		}
   344  	}
   345  	statedb := rawdb.NewMemoryDatabase()
   346  	for i := 0; i < len(data); i++ {
   347  		statedb.Put(hashes[i].Bytes(), data[i])
   348  	}
   349  	accounts := []common.Address{testBank, acc1Addr, acc2Addr}
   350  	for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
   351  		trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb))
   352  
   353  		for j, acc := range accounts {
   354  			state, _ := pm.blockchain.State()
   355  			bw := state.GetBalance(acc)
   356  			bh := trie.GetBalance(acc)
   357  
   358  			if (bw != nil && bh == nil) || (bw == nil && bh != nil) {
   359  				t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
   360  			}
   361  			if bw != nil && bh != nil && bw.Cmp(bw) != 0 {
   362  				t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
   363  			}
   364  		}
   365  	}
   366  }
   367  
   368  // Tests that the transaction receipts can be retrieved based on hashes.
   369  func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) }
   370  
   371  func testGetReceipt(t *testing.T, protocol int) {
   372  	// Define three accounts to simulate transactions with
   373  	acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   374  	acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
   375  	acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
   376  	acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
   377  
   378  	signer := types.HomesteadSigner{}
   379  	// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test)
   380  	generator := func(i int, block *core.BlockGen) {
   381  		switch i {
   382  		case 0:
   383  			// In block 1, the test bank sends account #1 some btper.
   384  			tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
   385  			block.AddTx(tx)
   386  		case 1:
   387  			// In block 2, the test bank sends some more btper to account #1.
   388  			// acc1Addr passes it on to account #2.
   389  			tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
   390  			tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
   391  			block.AddTx(tx1)
   392  			block.AddTx(tx2)
   393  		case 2:
   394  			// Block 3 is empty but was mined by account #2.
   395  			block.SetCoinbase(acc2Addr)
   396  			block.SetExtra([]byte("yeehaw"))
   397  		case 3:
   398  			// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
   399  			b2 := block.PrevBlock(1).Header()
   400  			b2.Extra = []byte("foo")
   401  			block.AddUncle(b2)
   402  			b3 := block.PrevBlock(2).Header()
   403  			b3.Extra = []byte("foo")
   404  			block.AddUncle(b3)
   405  		}
   406  	}
   407  	// Assemble the test environment
   408  	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
   409  	peer, _ := newTestPeer("peer", protocol, pm, true)
   410  	defer peer.close()
   411  
   412  	// Collect the hashes to request, and the response to expect
   413  	hashes, receipts := []common.Hash{}, []types.Receipts{}
   414  	for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
   415  		block := pm.blockchain.GetBlockByNumber(i)
   416  
   417  		hashes = append(hashes, block.Hash())
   418  		receipts = append(receipts, pm.blockchain.GetReceiptsByHash(block.Hash()))
   419  	}
   420  	// Send the hash request and verify the response
   421  	p2p.Send(peer.app, 0x0f, hashes)
   422  	if err := p2p.ExpectMsg(peer.app, 0x10, receipts); err != nil {
   423  		t.Errorf("receipts mismatch: %v", err)
   424  	}
   425  }
   426  
   427  // Tests that post btp protocol handshake, clients perform a mutual checkpoint
   428  // challenge to validate each other's chains. Hash mismatches, or missing ones
   429  // during a fast sync should lead to the peer getting dropped.
   430  func TestCheckpointChallenge(t *testing.T) {
   431  	tests := []struct {
   432  		syncmode   downloader.SyncMode
   433  		checkpoint bool
   434  		timeout    bool
   435  		empty      bool
   436  		match      bool
   437  		drop       bool
   438  	}{
   439  		// If checkpointing is not enabled locally, don't challenge and don't drop
   440  		{downloader.FullSync, false, false, false, false, false},
   441  		{downloader.FastSync, false, false, false, false, false},
   442  
   443  		// If checkpointing is enabled locally and remote response is empty, only drop during fast sync
   444  		{downloader.FullSync, true, false, true, false, false},
   445  		{downloader.FastSync, true, false, true, false, true}, // Special case, fast sync, unsynced peer
   446  
   447  		// If checkpointing is enabled locally and remote response mismatches, always drop
   448  		{downloader.FullSync, true, false, false, false, true},
   449  		{downloader.FastSync, true, false, false, false, true},
   450  
   451  		// If checkpointing is enabled locally and remote response matches, never drop
   452  		{downloader.FullSync, true, false, false, true, false},
   453  		{downloader.FastSync, true, false, false, true, false},
   454  
   455  		// If checkpointing is enabled locally and remote times out, always drop
   456  		{downloader.FullSync, true, true, false, true, true},
   457  		{downloader.FastSync, true, true, false, true, true},
   458  	}
   459  	for _, tt := range tests {
   460  		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) {
   461  			testCheckpointChallenge(t, tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match, tt.drop)
   462  		})
   463  	}
   464  }
   465  
   466  func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) {
   467  	// Reduce the checkpoint handshake challenge timeout
   468  	defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout)
   469  	syncChallengeTimeout = 250 * time.Millisecond
   470  
   471  	// Initialize a chain and generate a fake CHT if checkpointing is enabled
   472  	var (
   473  		db     = rawdb.NewMemoryDatabase()
   474  		config = new(params.ChainConfig)
   475  	)
   476  	(&core.Genesis{Config: config}).MustCommit(db) // Commit genesis block
   477  	// If checkpointing is enabled, create and inject a fake CHT and the corresponding
   478  	// chllenge response.
   479  	var response *types.Header
   480  	var cht *params.TrustedCheckpoint
   481  	if checkpoint {
   482  		index := uint64(rand.Intn(500))
   483  		number := (index+1)*params.CHTFrequency - 1
   484  		response = &types.Header{Number: big.NewInt(int64(number)), Extra: []byte("valid")}
   485  
   486  		cht = &params.TrustedCheckpoint{
   487  			SectionIndex: index,
   488  			SectionHead:  response.Hash(),
   489  		}
   490  	}
   491  	// Create a checkpoint aware protocol manager
   492  	blockchain, err := core.NewBlockChain(db, nil, config, btpash.NewFaker(), vm.Config{}, nil)
   493  	if err != nil {
   494  		t.Fatalf("failed to create new blockchain: %v", err)
   495  	}
   496  	pm, err := NewProtocolManager(config, cht, syncmode, DefaultConfig.NetworkId, new(event.TypeMux), new(testTxPool), btpash.NewFaker(), blockchain, db, 1, nil)
   497  	if err != nil {
   498  		t.Fatalf("failed to start test protocol manager: %v", err)
   499  	}
   500  	pm.Start(1000)
   501  	defer pm.Stop()
   502  
   503  	// Connect a new peer and check that we receive the checkpoint challenge
   504  	peer, _ := newTestPeer("peer", btp63, pm, true)
   505  	defer peer.close()
   506  
   507  	if checkpoint {
   508  		challenge := &getBlockHeadersData{
   509  			Origin:  hashOrNumber{Number: response.Number.Uint64()},
   510  			Amount:  1,
   511  			Skip:    0,
   512  			Reverse: false,
   513  		}
   514  		if err := p2p.ExpectMsg(peer.app, GetBlockHeadersMsg, challenge); err != nil {
   515  			t.Fatalf("challenge mismatch: %v", err)
   516  		}
   517  		// Create a block to reply to the challenge if no timeout is simulated
   518  		if !timeout {
   519  			if empty {
   520  				if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{}); err != nil {
   521  					t.Fatalf("failed to answer challenge: %v", err)
   522  				}
   523  			} else if match {
   524  				if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{response}); err != nil {
   525  					t.Fatalf("failed to answer challenge: %v", err)
   526  				}
   527  			} else {
   528  				if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{{Number: response.Number}}); err != nil {
   529  					t.Fatalf("failed to answer challenge: %v", err)
   530  				}
   531  			}
   532  		}
   533  	}
   534  	// Wait until the test timeout passes to ensure proper cleanup
   535  	time.Sleep(syncChallengeTimeout + 100*time.Millisecond)
   536  
   537  	// Verify that the remote peer is maintained or dropped
   538  	if drop {
   539  		if peers := pm.peers.Len(); peers != 0 {
   540  			t.Fatalf("peer count mismatch: have %d, want %d", peers, 0)
   541  		}
   542  	} else {
   543  		if peers := pm.peers.Len(); peers != 1 {
   544  			t.Fatalf("peer count mismatch: have %d, want %d", peers, 1)
   545  		}
   546  	}
   547  }
   548  
   549  func TestBroadcastBlock(t *testing.T) {
   550  	var tests = []struct {
   551  		totalPeers        int
   552  		broadcastExpected int
   553  	}{
   554  		{1, 1},
   555  		{2, 2},
   556  		{3, 3},
   557  		{4, 4},
   558  		{5, 4},
   559  		{9, 4},
   560  		{12, 4},
   561  		{16, 4},
   562  		{26, 5},
   563  		{100, 10},
   564  	}
   565  	for _, test := range tests {
   566  		testBroadcastBlock(t, test.totalPeers, test.broadcastExpected)
   567  	}
   568  }
   569  
   570  func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int) {
   571  	var (
   572  		evmux   = new(event.TypeMux)
   573  		pow     = btpash.NewFaker()
   574  		db      = rawdb.NewMemoryDatabase()
   575  		config  = &params.ChainConfig{}
   576  		gspec   = &core.Genesis{Config: config}
   577  		genesis = gspec.MustCommit(db)
   578  	)
   579  	blockchain, err := core.NewBlockChain(db, nil, config, pow, vm.Config{}, nil)
   580  	if err != nil {
   581  		t.Fatalf("failed to create new blockchain: %v", err)
   582  	}
   583  	pm, err := NewProtocolManager(config, nil, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db, 1, nil)
   584  	if err != nil {
   585  		t.Fatalf("failed to start test protocol manager: %v", err)
   586  	}
   587  	pm.Start(1000)
   588  	defer pm.Stop()
   589  	var peers []*testPeer
   590  	for i := 0; i < totalPeers; i++ {
   591  		peer, _ := newTestPeer(fmt.Sprintf("peer %d", i), btp63, pm, true)
   592  		defer peer.close()
   593  		peers = append(peers, peer)
   594  	}
   595  	chain, _ := core.GenerateChain(gspec.Config, genesis, btpash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) {})
   596  	pm.BroadcastBlock(chain[0], true /*propagate*/)
   597  
   598  	errCh := make(chan error, totalPeers)
   599  	doneCh := make(chan struct{}, totalPeers)
   600  	for _, peer := range peers {
   601  		go func(p *testPeer) {
   602  			if err := p2p.ExpectMsg(p.app, NewBlockMsg, &newBlockData{Block: chain[0], TD: big.NewInt(131136)}); err != nil {
   603  				errCh <- err
   604  			} else {
   605  				doneCh <- struct{}{}
   606  			}
   607  		}(peer)
   608  	}
   609  	timeout := time.After(300 * time.Millisecond)
   610  	var receivedCount int
   611  outer:
   612  	for {
   613  		select {
   614  		case err = <-errCh:
   615  			break outer
   616  		case <-doneCh:
   617  			receivedCount++
   618  			if receivedCount == totalPeers {
   619  				break outer
   620  			}
   621  		case <-timeout:
   622  			break outer
   623  		}
   624  	}
   625  	for _, peer := range peers {
   626  		peer.app.Close()
   627  	}
   628  	if err != nil {
   629  		t.Errorf("error matching block by peer: %v", err)
   630  	}
   631  	if receivedCount != broadcastExpected {
   632  		t.Errorf("block broadcast to %d peers, expected %d", receivedCount, broadcastExpected)
   633  	}
   634  }