github.com/core-coin/go-core/v2@v2.1.9/xcb/handler_test.go (about)

     1  // Copyright 2015 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package xcb
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"math/big"
    23  	"math/rand"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/core-coin/go-core/v2/consensus/cryptore"
    28  
    29  	"github.com/core-coin/go-core/v2/common"
    30  	"github.com/core-coin/go-core/v2/core"
    31  	"github.com/core-coin/go-core/v2/core/rawdb"
    32  	"github.com/core-coin/go-core/v2/core/state"
    33  	"github.com/core-coin/go-core/v2/core/types"
    34  	"github.com/core-coin/go-core/v2/core/vm"
    35  	"github.com/core-coin/go-core/v2/crypto"
    36  	"github.com/core-coin/go-core/v2/event"
    37  	"github.com/core-coin/go-core/v2/p2p"
    38  	"github.com/core-coin/go-core/v2/params"
    39  	"github.com/core-coin/go-core/v2/xcb/downloader"
    40  )
    41  
    42  // Tests that block headers can be retrieved from a remote chain based on user queries.
    43  func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
    44  func TestGetBlockHeaders64(t *testing.T) { testGetBlockHeaders(t, 64) }
    45  
    46  func testGetBlockHeaders(t *testing.T, protocol int) {
    47  	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil)
    48  	peer, _ := newTestPeer("peer", protocol, pm, true)
    49  	defer peer.close()
    50  
    51  	// Create a "random" unknown hash for testing
    52  	var unknown common.Hash
    53  	for i := range unknown {
    54  		unknown[i] = byte(i)
    55  	}
    56  	// Create a batch of tests for various scenarios
    57  	limit := uint64(downloader.MaxHeaderFetch)
    58  	tests := []struct {
    59  		query  *getBlockHeadersData // The query to execute for header retrieval
    60  		expect []common.Hash        // The hashes of the block whose headers are expected
    61  	}{
    62  		// A single random block should be retrievable by hash and number too
    63  		{
    64  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1},
    65  			[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()},
    66  		}, {
    67  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1},
    68  			[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()},
    69  		},
    70  		// Multiple headers should be retrievable in both directions
    71  		{
    72  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3},
    73  			[]common.Hash{
    74  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
    75  				pm.blockchain.GetBlockByNumber(limit/2 + 1).Hash(),
    76  				pm.blockchain.GetBlockByNumber(limit/2 + 2).Hash(),
    77  			},
    78  		}, {
    79  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true},
    80  			[]common.Hash{
    81  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
    82  				pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(),
    83  				pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(),
    84  			},
    85  		},
    86  		// Multiple headers with skip lists should be retrievable
    87  		{
    88  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3},
    89  			[]common.Hash{
    90  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
    91  				pm.blockchain.GetBlockByNumber(limit/2 + 4).Hash(),
    92  				pm.blockchain.GetBlockByNumber(limit/2 + 8).Hash(),
    93  			},
    94  		}, {
    95  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true},
    96  			[]common.Hash{
    97  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
    98  				pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(),
    99  				pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(),
   100  			},
   101  		},
   102  		// The chain endpoints should be retrievable
   103  		{
   104  			&getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1},
   105  			[]common.Hash{pm.blockchain.GetBlockByNumber(0).Hash()},
   106  		}, {
   107  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1},
   108  			[]common.Hash{pm.blockchain.CurrentBlock().Hash()},
   109  		},
   110  		// Ensure protocol limits are honored
   111  		{
   112  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true},
   113  			pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit),
   114  		},
   115  		// Check that requesting more than available is handled gracefully
   116  		{
   117  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3},
   118  			[]common.Hash{
   119  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(),
   120  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(),
   121  			},
   122  		}, {
   123  			&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true},
   124  			[]common.Hash{
   125  				pm.blockchain.GetBlockByNumber(4).Hash(),
   126  				pm.blockchain.GetBlockByNumber(0).Hash(),
   127  			},
   128  		},
   129  		// Check that requesting more than available is handled gracefully, even if mid skip
   130  		{
   131  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3},
   132  			[]common.Hash{
   133  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(),
   134  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(),
   135  			},
   136  		}, {
   137  			&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true},
   138  			[]common.Hash{
   139  				pm.blockchain.GetBlockByNumber(4).Hash(),
   140  				pm.blockchain.GetBlockByNumber(1).Hash(),
   141  			},
   142  		},
   143  		// Check a corner case where requesting more can iterate past the endpoints
   144  		{
   145  			&getBlockHeadersData{Origin: hashOrNumber{Number: 2}, Amount: 5, Reverse: true},
   146  			[]common.Hash{
   147  				pm.blockchain.GetBlockByNumber(2).Hash(),
   148  				pm.blockchain.GetBlockByNumber(1).Hash(),
   149  				pm.blockchain.GetBlockByNumber(0).Hash(),
   150  			},
   151  		},
   152  		// Check a corner case where skipping overflow loops back into the chain start
   153  		{
   154  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1},
   155  			[]common.Hash{
   156  				pm.blockchain.GetBlockByNumber(3).Hash(),
   157  			},
   158  		},
   159  		// Check a corner case where skipping overflow loops back to the same header
   160  		{
   161  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64},
   162  			[]common.Hash{
   163  				pm.blockchain.GetBlockByNumber(1).Hash(),
   164  			},
   165  		},
   166  		// Check that non existing headers aren't returned
   167  		{
   168  			&getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1},
   169  			[]common.Hash{},
   170  		}, {
   171  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1},
   172  			[]common.Hash{},
   173  		},
   174  	}
   175  	// Run each of the tests and verify the results against the chain
   176  	for i, tt := range tests {
   177  		// Collect the headers to expect in the response
   178  		headers := []*types.Header{}
   179  		for _, hash := range tt.expect {
   180  			headers = append(headers, pm.blockchain.GetBlockByHash(hash).Header())
   181  		}
   182  		// Send the hash request and verify the response
   183  		p2p.Send(peer.app, 0x03, tt.query)
   184  		if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil {
   185  			t.Errorf("test %d: headers mismatch: %v", i, err)
   186  		}
   187  		// If the test used number origins, repeat with hashes as the too
   188  		if tt.query.Origin.Hash == (common.Hash{}) {
   189  			if origin := pm.blockchain.GetBlockByNumber(tt.query.Origin.Number); origin != nil {
   190  				tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0
   191  
   192  				p2p.Send(peer.app, 0x03, tt.query)
   193  				if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil {
   194  					t.Errorf("test %d: headers mismatch: %v", i, err)
   195  				}
   196  			}
   197  		}
   198  	}
   199  }
   200  
   201  // Tests that block contents can be retrieved from a remote chain based on their hashes.
   202  func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
   203  func TestGetBlockBodies64(t *testing.T) { testGetBlockBodies(t, 64) }
   204  
   205  func testGetBlockBodies(t *testing.T, protocol int) {
   206  	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil)
   207  	peer, _ := newTestPeer("peer", protocol, pm, true)
   208  	defer peer.close()
   209  
   210  	// Create a batch of tests for various scenarios
   211  	limit := downloader.MaxBlockFetch
   212  	tests := []struct {
   213  		random    int           // Number of blocks to fetch randomly from the chain
   214  		explicit  []common.Hash // Explicitly requested blocks
   215  		available []bool        // Availability of explicitly requested blocks
   216  		expected  int           // Total number of existing blocks to expect
   217  	}{
   218  		{1, nil, nil, 1},             // A single random block should be retrievable
   219  		{10, nil, nil, 10},           // Multiple random blocks should be retrievable
   220  		{limit, nil, nil, limit},     // The maximum possible blocks should be retrievable
   221  		{limit + 1, nil, nil, limit}, // No more than the possible block count should be returned
   222  		{0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1},      // The genesis block should be retrievable
   223  		{0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable
   224  		{0, []common.Hash{{}}, []bool{false}, 0},                                 // A non existent block should not be returned
   225  
   226  		// Existing and non-existing blocks interleaved should not cause problems
   227  		{0, []common.Hash{
   228  			{},
   229  			pm.blockchain.GetBlockByNumber(1).Hash(),
   230  			{},
   231  			pm.blockchain.GetBlockByNumber(10).Hash(),
   232  			{},
   233  			pm.blockchain.GetBlockByNumber(100).Hash(),
   234  			{},
   235  		}, []bool{false, true, false, true, false, true, false}, 3},
   236  	}
   237  	// Run each of the tests and verify the results against the chain
   238  	for i, tt := range tests {
   239  		// Collect the hashes to request, and the response to expect
   240  		hashes, seen := []common.Hash{}, make(map[int64]bool)
   241  		bodies := []*blockBody{}
   242  
   243  		for j := 0; j < tt.random; j++ {
   244  			for {
   245  				num := rand.Int63n(int64(pm.blockchain.CurrentBlock().NumberU64()))
   246  				if !seen[num] {
   247  					seen[num] = true
   248  
   249  					block := pm.blockchain.GetBlockByNumber(uint64(num))
   250  					hashes = append(hashes, block.Hash())
   251  					if len(bodies) < tt.expected {
   252  						bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()})
   253  					}
   254  					break
   255  				}
   256  			}
   257  		}
   258  		for j, hash := range tt.explicit {
   259  			hashes = append(hashes, hash)
   260  			if tt.available[j] && len(bodies) < tt.expected {
   261  				block := pm.blockchain.GetBlockByHash(hash)
   262  				bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()})
   263  			}
   264  		}
   265  		// Send the hash request and verify the response
   266  		p2p.Send(peer.app, 0x05, hashes)
   267  		if err := p2p.ExpectMsg(peer.app, 0x06, bodies); err != nil {
   268  			t.Errorf("test %d: bodies mismatch: %v", i, err)
   269  		}
   270  	}
   271  }
   272  
   273  // Tests that the node state database can be retrieved based on hashes.
   274  func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) }
   275  func TestGetNodeData64(t *testing.T) { testGetNodeData(t, 64) }
   276  
   277  func testGetNodeData(t *testing.T, protocol int) {
   278  	// Define three accounts to simulate transactions with
   279  	acc1Key, _ := crypto.UnmarshalPrivateKeyHex("7db3f59e4badffb6ca7dfafd74f079403831d43f2f2b9a32988d05e859b8ed9e98d9a22439b455bc13e852f95a6386fbf692021fbd149ad23c")
   280  	acc2Key, _ := crypto.UnmarshalPrivateKeyHex("4485aa768e5a88e04636984ced4bac88ddb67356b1ea647cf30107ebd229b04bb9bfa6674978aed2a73b8a598c84a544153628fa28ccc1c840")
   281  
   282  	signer := types.NewNucleusSigner(big.NewInt(1))
   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 core.
   288  			tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankKey.Address()), acc1Key.Address(), big.NewInt(10000), params.TxEnergy, nil, nil), signer, testBankKey)
   289  			block.AddTx(tx)
   290  		case 1:
   291  			// In block 2, the test bank sends some more core to account #1.
   292  			// acc1Key.Address() passes it on to account #2.
   293  			tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankKey.Address()), acc1Key.Address(), big.NewInt(1000), params.TxEnergy, nil, nil), signer, testBankKey)
   294  			tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Key.Address()), acc2Key.Address(), big.NewInt(1000), params.TxEnergy, 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(acc2Key.Address())
   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(nil, nil)
   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.SHA3Hash(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{testBankKey.Address(), acc1Key.Address(), acc2Key.Address()}
   350  	for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
   351  		trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb), nil)
   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  func TestGetReceipt64(t *testing.T) { testGetReceipt(t, 64) }
   371  
   372  func testGetReceipt(t *testing.T, protocol int) {
   373  	// Define three accounts to simulate transactions with
   374  	acc1Key, _ := crypto.UnmarshalPrivateKeyHex("7db3f59e4badffb6ca7dfafd74f079403831d43f2f2b9a32988d05e859b8ed9e98d9a22439b455bc13e852f95a6386fbf692021fbd149ad23c")
   375  	acc2Key, _ := crypto.UnmarshalPrivateKeyHex("4485aa768e5a88e04636984ced4bac88ddb67356b1ea647cf30107ebd229b04bb9bfa6674978aed2a73b8a598c84a544153628fa28ccc1c840")
   376  
   377  	signer := types.NewNucleusSigner(big.NewInt(1))
   378  	// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test)
   379  	generator := func(i int, block *core.BlockGen) {
   380  		switch i {
   381  		case 0:
   382  			// In block 1, the test bank sends account #1 some core.
   383  			tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankKey.Address()), acc1Key.Address(), big.NewInt(10000), params.TxEnergy, nil, nil), signer, testBankKey)
   384  			block.AddTx(tx)
   385  		case 1:
   386  			// In block 2, the test bank sends some more core to account #1.
   387  			// acc1Key.Address() passes it on to account #2.
   388  			tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankKey.Address()), acc1Key.Address(), big.NewInt(1000), params.TxEnergy, nil, nil), signer, testBankKey)
   389  			tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Key.Address()), acc2Key.Address(), big.NewInt(1000), params.TxEnergy, nil, nil), signer, acc1Key)
   390  			block.AddTx(tx1)
   391  			block.AddTx(tx2)
   392  		case 2:
   393  			// Block 3 is empty but was mined by account #2.
   394  			block.SetCoinbase(acc2Key.Address())
   395  			block.SetExtra([]byte("yeehaw"))
   396  		case 3:
   397  			// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
   398  			b2 := block.PrevBlock(1).Header()
   399  			b2.Extra = []byte("foo")
   400  			block.AddUncle(b2)
   401  			b3 := block.PrevBlock(2).Header()
   402  			b3.Extra = []byte("foo")
   403  			block.AddUncle(b3)
   404  		}
   405  	}
   406  	// Assemble the test environment
   407  	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
   408  	peer, _ := newTestPeer("peer", protocol, pm, true)
   409  	defer peer.close()
   410  
   411  	// Collect the hashes to request, and the response to expect
   412  	hashes, receipts := []common.Hash{}, []types.Receipts{}
   413  	for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
   414  		block := pm.blockchain.GetBlockByNumber(i)
   415  
   416  		hashes = append(hashes, block.Hash())
   417  		receipts = append(receipts, pm.blockchain.GetReceiptsByHash(block.Hash()))
   418  	}
   419  	// Send the hash request and verify the response
   420  	p2p.Send(peer.app, 0x0f, hashes)
   421  	if err := p2p.ExpectMsg(peer.app, 0x10, receipts); err != nil {
   422  		t.Errorf("receipts mismatch: %v", err)
   423  	}
   424  }
   425  
   426  // Tests that post xcb protocol handshake, clients perform a mutual checkpoint
   427  // challenge to validate each other's chains. Hash mismatches, or missing ones
   428  // during a fast sync should lead to the peer getting dropped.
   429  func TestCheckpointChallenge(t *testing.T) {
   430  	tests := []struct {
   431  		syncmode   downloader.SyncMode
   432  		checkpoint bool
   433  		timeout    bool
   434  		empty      bool
   435  		match      bool
   436  		drop       bool
   437  	}{
   438  		// If checkpointing is not enabled locally, don't challenge and don't drop
   439  		{downloader.FullSync, false, false, false, false, false},
   440  		{downloader.FastSync, false, false, false, false, false},
   441  
   442  		// If checkpointing is enabled locally and remote response is empty, only drop during fast sync
   443  		{downloader.FullSync, true, false, true, false, false},
   444  		{downloader.FastSync, true, false, true, false, true}, // Special case, fast sync, unsynced peer
   445  
   446  		// If checkpointing is enabled locally and remote response mismatches, always drop
   447  		{downloader.FullSync, true, false, false, false, true},
   448  		{downloader.FastSync, true, false, false, false, true},
   449  
   450  		// If checkpointing is enabled locally and remote response matches, never drop
   451  		{downloader.FullSync, true, false, false, true, false},
   452  		{downloader.FastSync, true, false, false, true, false},
   453  
   454  		// If checkpointing is enabled locally and remote times out, always drop
   455  		{downloader.FullSync, true, true, false, true, true},
   456  		{downloader.FastSync, true, true, false, true, true},
   457  	}
   458  	for _, tt := range tests {
   459  		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) {
   460  			testCheckpointChallenge(t, tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match, tt.drop)
   461  		})
   462  	}
   463  }
   464  
   465  func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) {
   466  	// Reduce the checkpoint handshake challenge timeout
   467  	defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout)
   468  	syncChallengeTimeout = 250 * time.Millisecond
   469  
   470  	// Initialize a chain and generate a fake CHT if checkpointing is enabled
   471  	var (
   472  		db     = rawdb.NewMemoryDatabase()
   473  		config = new(params.ChainConfig)
   474  	)
   475  	(&core.Genesis{Config: config}).MustCommit(db) // Commit genesis block
   476  	// If checkpointing is enabled, create and inject a fake CHT and the corresponding
   477  	// chllenge response.
   478  	var response *types.Header
   479  	var cht *params.TrustedCheckpoint
   480  	if checkpoint {
   481  		index := uint64(rand.Intn(500))
   482  		number := (index+1)*params.CHTFrequency - 1
   483  		response = &types.Header{Number: big.NewInt(int64(number)), Extra: []byte("valid")}
   484  
   485  		cht = &params.TrustedCheckpoint{
   486  			SectionIndex: index,
   487  			SectionHead:  response.Hash(),
   488  		}
   489  	}
   490  	// Create a checkpoint aware protocol manager
   491  	blockchain, err := core.NewBlockChain(db, nil, config, cryptore.NewFaker(), vm.Config{}, nil, nil)
   492  	if err != nil {
   493  		t.Fatalf("failed to create new blockchain: %v", err)
   494  	}
   495  	pm, err := NewProtocolManager(config, cht, syncmode, DefaultConfig.NetworkId, new(event.TypeMux), &testTxPool{pool: make(map[common.Hash]*types.Transaction)}, cryptore.NewFaker(), blockchain, db, 1, nil, false)
   496  	if err != nil {
   497  		t.Fatalf("failed to start test protocol manager: %v", err)
   498  	}
   499  	pm.Start(1000)
   500  	defer pm.Stop()
   501  
   502  	// Connect a new peer and check that we receive the checkpoint challenge
   503  	peer, _ := newTestPeer("peer", xcb63, pm, true)
   504  	defer peer.close()
   505  
   506  	if checkpoint {
   507  		challenge := &getBlockHeadersData{
   508  			Origin:  hashOrNumber{Number: response.Number.Uint64()},
   509  			Amount:  1,
   510  			Skip:    0,
   511  			Reverse: false,
   512  		}
   513  		if err := p2p.ExpectMsg(peer.app, GetBlockHeadersMsg, challenge); err != nil {
   514  			t.Fatalf("challenge mismatch: %v", err)
   515  		}
   516  		// Create a block to reply to the challenge if no timeout is simulated
   517  		if !timeout {
   518  			if empty {
   519  				if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{}); err != nil {
   520  					t.Fatalf("failed to answer challenge: %v", err)
   521  				}
   522  			} else if match {
   523  				if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{response}); err != nil {
   524  					t.Fatalf("failed to answer challenge: %v", err)
   525  				}
   526  			} else {
   527  				if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{{Number: response.Number}}); err != nil {
   528  					t.Fatalf("failed to answer challenge: %v", err)
   529  				}
   530  			}
   531  		}
   532  	}
   533  	// Wait until the test timeout passes to ensure proper cleanup
   534  	time.Sleep(syncChallengeTimeout + 300*time.Millisecond)
   535  
   536  	// Verify that the remote peer is maintained or dropped
   537  	if drop {
   538  		if peers := pm.peers.Len(); peers != 0 {
   539  			t.Fatalf("peer count mismatch: have %d, want %d", peers, 0)
   540  		}
   541  	} else {
   542  		if peers := pm.peers.Len(); peers != 1 {
   543  			t.Fatalf("peer count mismatch: have %d, want %d", peers, 1)
   544  		}
   545  	}
   546  }
   547  
   548  func TestBroadcastBlock(t *testing.T) {
   549  	var tests = []struct {
   550  		totalPeers        int
   551  		broadcastExpected int
   552  	}{
   553  		{1, 1},
   554  		{2, 1},
   555  		{3, 1},
   556  		{4, 2},
   557  		{5, 2},
   558  		{9, 3},
   559  		{12, 3},
   560  		{16, 4},
   561  		{26, 5},
   562  		{100, 10},
   563  	}
   564  	for _, test := range tests {
   565  		testBroadcastBlock(t, test.totalPeers, test.broadcastExpected, false)
   566  	}
   567  }
   568  
   569  func TestBroadcastBlockBTTP(t *testing.T) {
   570  	var tests = []struct {
   571  		totalPeers        int
   572  		broadcastExpected int
   573  	}{
   574  		{2, 2},
   575  		{3, 3},
   576  		{4, 4},
   577  		{5, 5},
   578  		{9, 9},
   579  		{12, 12},
   580  		{16, 16},
   581  		{26, 26},
   582  		{100, 100},
   583  	}
   584  	for _, test := range tests {
   585  		testBroadcastBlock(t, test.totalPeers, test.broadcastExpected, true)
   586  	}
   587  }
   588  
   589  func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int, bttp bool) {
   590  	var (
   591  		cvmux   = new(event.TypeMux)
   592  		pow     = cryptore.NewFaker()
   593  		db      = rawdb.NewMemoryDatabase()
   594  		config  = &params.ChainConfig{NetworkID: big.NewInt(1)}
   595  		gspec   = &core.Genesis{Config: config}
   596  		genesis = gspec.MustCommit(db)
   597  	)
   598  	blockchain, err := core.NewBlockChain(db, nil, config, pow, vm.Config{}, nil, nil)
   599  	if err != nil {
   600  		t.Fatalf("failed to create new blockchain: %v", err)
   601  	}
   602  	pm, err := NewProtocolManager(config, nil, downloader.FullSync, DefaultConfig.NetworkId, cvmux, &testTxPool{pool: make(map[common.Hash]*types.Transaction)}, pow, blockchain, db, 1, nil, bttp)
   603  	if err != nil {
   604  		t.Fatalf("failed to start test protocol manager: %v", err)
   605  	}
   606  	pm.Start(1000)
   607  	defer pm.Stop()
   608  	var peers []*testPeer
   609  	for i := 0; i < totalPeers; i++ {
   610  		peer, _ := newTestPeer(fmt.Sprintf("peer %d", i), xcb63, pm, true)
   611  		defer peer.close()
   612  
   613  		peers = append(peers, peer)
   614  	}
   615  	chain, _ := core.GenerateChain(gspec.Config, genesis, cryptore.NewFaker(), db, 1, func(i int, gen *core.BlockGen) {})
   616  	pm.BroadcastBlock(chain[0], true /*propagate*/)
   617  
   618  	errCh := make(chan error, totalPeers)
   619  	doneCh := make(chan struct{}, totalPeers)
   620  	for _, peer := range peers {
   621  		go func(p *testPeer) {
   622  			if err := p2p.ExpectMsg(p.app, NewBlockMsg, &newBlockData{Block: chain[0], TD: big.NewInt(0x2000)}); err != nil {
   623  				errCh <- err
   624  			} else {
   625  				doneCh <- struct{}{}
   626  			}
   627  		}(peer)
   628  	}
   629  	var received int
   630  	for {
   631  		select {
   632  		case <-doneCh:
   633  			received++
   634  			if received > broadcastExpected {
   635  				// We can bail early here
   636  				t.Errorf("broadcast count mismatch: have %d > want %d", received, broadcastExpected)
   637  				return
   638  			}
   639  		case <-time.After(3 * time.Second):
   640  			if received != broadcastExpected {
   641  				t.Errorf("broadcast count mismatch: have %d, want %d", received, broadcastExpected)
   642  			}
   643  			return
   644  		case err = <-errCh:
   645  			t.Fatalf("broadcast failed: %v", err)
   646  		}
   647  	}
   648  
   649  }
   650  
   651  // Tests that a propagated malformed block (uncles or transactions don't match
   652  // with the hashes in the header) gets discarded and not broadcast forward.
   653  func TestBroadcastMalformedBlock(t *testing.T) {
   654  	// Create a live node to test propagation with
   655  	var (
   656  		engine  = cryptore.NewFaker()
   657  		db      = rawdb.NewMemoryDatabase()
   658  		config  = &params.ChainConfig{}
   659  		gspec   = &core.Genesis{Config: config}
   660  		genesis = gspec.MustCommit(db)
   661  	)
   662  	blockchain, err := core.NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil)
   663  	if err != nil {
   664  		t.Fatalf("failed to create new blockchain: %v", err)
   665  	}
   666  	pm, err := NewProtocolManager(config, nil, downloader.FullSync, DefaultConfig.NetworkId, new(event.TypeMux), new(testTxPool), engine, blockchain, db, 1, nil, false)
   667  	if err != nil {
   668  		t.Fatalf("failed to start test protocol manager: %v", err)
   669  	}
   670  	pm.Start(2)
   671  	defer pm.Stop()
   672  
   673  	// Create two peers, one to send the malformed block with and one to check
   674  	// propagation
   675  	source, _ := newTestPeer("source", xcb63, pm, true)
   676  	defer source.close()
   677  
   678  	sink, _ := newTestPeer("sink", xcb63, pm, true)
   679  	defer sink.close()
   680  
   681  	// Create various combinations of malformed blocks
   682  	chain, _ := core.GenerateChain(gspec.Config, genesis, cryptore.NewFaker(), db, 1, func(i int, gen *core.BlockGen) {})
   683  
   684  	malformedUncles := chain[0].Header()
   685  	malformedUncles.UncleHash[0]++
   686  	malformedTransactions := chain[0].Header()
   687  	malformedTransactions.TxHash[0]++
   688  	malformedEverything := chain[0].Header()
   689  	malformedEverything.UncleHash[0]++
   690  	malformedEverything.TxHash[0]++
   691  
   692  	// Keep listening to broadcasts and notify if any arrives
   693  	notify := make(chan struct{}, 1)
   694  	go func() {
   695  		if _, err := sink.app.ReadMsg(); err == nil {
   696  			notify <- struct{}{}
   697  		}
   698  	}()
   699  	// Try to broadcast all malformations and ensure they all get discarded
   700  	for _, header := range []*types.Header{malformedUncles, malformedTransactions, malformedEverything} {
   701  		block := types.NewBlockWithHeader(header).WithBody(chain[0].Transactions(), chain[0].Uncles())
   702  		if err := p2p.Send(source.app, NewBlockMsg, []interface{}{block, big.NewInt(131136)}); err != nil {
   703  			t.Fatalf("failed to broadcast block: %v", err)
   704  		}
   705  		select {
   706  		case <-notify:
   707  			t.Fatalf("malformed block forwarded")
   708  		case <-time.After(100 * time.Millisecond):
   709  		}
   710  	}
   711  }