github.com/Aodurkeen/go-ubiq@v2.3.0+incompatible/eth/handler_test.go (about)

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