github.com/ethereumproject/go-ethereum@v5.5.2+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  	"math"
    21  	"math/big"
    22  	"math/rand"
    23  	"testing"
    24  
    25  	"github.com/ethereumproject/go-ethereum/common"
    26  	"github.com/ethereumproject/go-ethereum/core"
    27  	"github.com/ethereumproject/go-ethereum/core/state"
    28  	"github.com/ethereumproject/go-ethereum/core/types"
    29  	"github.com/ethereumproject/go-ethereum/crypto"
    30  	"github.com/ethereumproject/go-ethereum/eth/downloader"
    31  	"github.com/ethereumproject/go-ethereum/ethdb"
    32  	"github.com/ethereumproject/go-ethereum/p2p"
    33  )
    34  
    35  // Tests that protocol versions and modes of operations are matched up properly.
    36  func TestProtocolCompatibility(t *testing.T) {
    37  	// Define the compatibility chart
    38  	tests := []struct {
    39  		version    uint
    40  		mode       downloader.SyncMode
    41  		compatible bool
    42  	}{
    43  		{61, downloader.FullSync, true}, {62, downloader.FullSync, true}, {63, downloader.FullSync, true},
    44  		{61, downloader.FastSync, false}, {62, downloader.FastSync, false}, {63, downloader.FastSync, true},
    45  	}
    46  	// Make sure anything we screw up is restored
    47  	backup := ProtocolVersions
    48  	defer func() { ProtocolVersions = backup }()
    49  
    50  	// Try all available compatibility configs and check for errors
    51  	for i, tt := range tests {
    52  		ProtocolVersions = []uint{tt.version}
    53  
    54  		pm, _, err := newTestProtocolManager(tt.mode, 0, nil, nil)
    55  		if pm != nil {
    56  			defer pm.Stop()
    57  		}
    58  		if (err == nil && !tt.compatible) || (err != nil && tt.compatible) {
    59  			t.Errorf("test %d: compatibility mismatch: have error %v, want compatibility %v", i, err, tt.compatible)
    60  		}
    61  	}
    62  }
    63  
    64  // Tests that block headers can be retrieved from a remote chain based on user queries.
    65  func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) }
    66  func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
    67  
    68  func testGetBlockHeaders(t *testing.T, protocol int) {
    69  	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil)
    70  	peer, _ := newTestPeer("peer", protocol, pm, true)
    71  	defer peer.close()
    72  
    73  	// Create a "random" unknown hash for testing
    74  	var unknown common.Hash
    75  	for i := range unknown {
    76  		unknown[i] = byte(i)
    77  	}
    78  	// Create a batch of tests for various scenarios
    79  	limit := uint64(downloader.MaxHeaderFetch)
    80  	tests := []struct {
    81  		query  *getBlockHeadersData // The query to execute for header retrieval
    82  		expect []common.Hash        // The hashes of the block whose headers are expected
    83  	}{
    84  		// A single random block should be retrievable by hash and number too
    85  		{
    86  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1},
    87  			[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()},
    88  		}, {
    89  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1},
    90  			[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()},
    91  		},
    92  		// Multiple headers should be retrievable in both directions
    93  		{
    94  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3},
    95  			[]common.Hash{
    96  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
    97  				pm.blockchain.GetBlockByNumber(limit/2 + 1).Hash(),
    98  				pm.blockchain.GetBlockByNumber(limit/2 + 2).Hash(),
    99  			},
   100  		}, {
   101  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true},
   102  			[]common.Hash{
   103  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
   104  				pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(),
   105  				pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(),
   106  			},
   107  		},
   108  		// Multiple headers with skip lists should be retrievable
   109  		{
   110  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3},
   111  			[]common.Hash{
   112  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
   113  				pm.blockchain.GetBlockByNumber(limit/2 + 4).Hash(),
   114  				pm.blockchain.GetBlockByNumber(limit/2 + 8).Hash(),
   115  			},
   116  		}, {
   117  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true},
   118  			[]common.Hash{
   119  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
   120  				pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(),
   121  				pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(),
   122  			},
   123  		},
   124  		// The chain endpoints should be retrievable
   125  		{
   126  			&getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1},
   127  			[]common.Hash{pm.blockchain.GetBlockByNumber(0).Hash()},
   128  		}, {
   129  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1},
   130  			[]common.Hash{pm.blockchain.CurrentBlock().Hash()},
   131  		},
   132  		// Ensure protocol limits are honored
   133  		{
   134  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true},
   135  			pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit),
   136  		},
   137  		// Check that requesting more than available is handled gracefully
   138  		{
   139  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3},
   140  			[]common.Hash{
   141  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(),
   142  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(),
   143  			},
   144  		}, {
   145  			&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true},
   146  			[]common.Hash{
   147  				pm.blockchain.GetBlockByNumber(4).Hash(),
   148  				pm.blockchain.GetBlockByNumber(0).Hash(),
   149  			},
   150  		},
   151  		// Check that requesting more than available is handled gracefully, even if mid skip
   152  		{
   153  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3},
   154  			[]common.Hash{
   155  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(),
   156  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(),
   157  			},
   158  		}, {
   159  			&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true},
   160  			[]common.Hash{
   161  				pm.blockchain.GetBlockByNumber(4).Hash(),
   162  				pm.blockchain.GetBlockByNumber(1).Hash(),
   163  			},
   164  		},
   165  		// Check a corner case where requesting more can iterate past the endpoints
   166  		{
   167  			&getBlockHeadersData{Origin: hashOrNumber{Number: 2}, Amount: 5, Reverse: true},
   168  			[]common.Hash{
   169  				pm.blockchain.GetBlockByNumber(2).Hash(),
   170  				pm.blockchain.GetBlockByNumber(1).Hash(),
   171  				pm.blockchain.GetBlockByNumber(0).Hash(),
   172  			},
   173  		},
   174  		// Check a corner case where skipping overflow loops back into the chain start
   175  		{
   176  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1},
   177  			[]common.Hash{
   178  				pm.blockchain.GetBlockByNumber(3).Hash(),
   179  			},
   180  		},
   181  		// Check a corner case where skipping overflow loops back to the same header
   182  		{
   183  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64},
   184  			[]common.Hash{
   185  				pm.blockchain.GetBlockByNumber(1).Hash(),
   186  			},
   187  		},
   188  		// Check that non existing headers aren't returned
   189  		{
   190  			&getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1},
   191  			[]common.Hash{},
   192  		}, {
   193  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1},
   194  			[]common.Hash{},
   195  		},
   196  	}
   197  	// Run each of the tests and verify the results against the chain
   198  	for i, tt := range tests {
   199  		// Collect the headers to expect in the response
   200  		headers := []*types.Header{}
   201  		for _, hash := range tt.expect {
   202  			headers = append(headers, pm.blockchain.GetBlock(hash).Header())
   203  		}
   204  		// Send the hash request and verify the response
   205  		s, _ := p2p.Send(peer.app, GetBlockHeadersMsg, tt.query)
   206  		if s == 0 {
   207  			t.Errorf("got: %v, want: >0", s)
   208  		}
   209  		if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, headers); err != nil {
   210  			t.Errorf("test %d: headers mismatch: %v", i, err)
   211  		}
   212  		// If the test used number origins, repeat with hashes as the too
   213  		if tt.query.Origin.Hash == (common.Hash{}) {
   214  			if origin := pm.blockchain.GetBlockByNumber(tt.query.Origin.Number); origin != nil {
   215  				tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0
   216  
   217  				s, _ := p2p.Send(peer.app, GetBlockHeadersMsg, tt.query)
   218  				if s == 0 {
   219  					t.Errorf("got: %v, want: >0", s)
   220  				}
   221  				if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, 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.GetBlock(hash)
   290  				bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()})
   291  			}
   292  		}
   293  		// Send the hash request and verify the response
   294  		s, _ := p2p.Send(peer.app, GetBlockBodiesMsg, hashes)
   295  		if s == 0 {
   296  			t.Errorf("got: %v, want: >0", s)
   297  		}
   298  		if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, bodies); err != nil {
   299  			t.Errorf("test %d: bodies mismatch: %v", i, err)
   300  		}
   301  	}
   302  }
   303  
   304  // Tests that the node state database can be retrieved based on hashes.
   305  func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) }
   306  
   307  func testGetNodeData(t *testing.T, protocol int) {
   308  	// Define three accounts to simulate transactions with
   309  	acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   310  	acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
   311  	acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
   312  	acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
   313  
   314  	// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test)
   315  	generator := func(i int, block *core.BlockGen) {
   316  		switch i {
   317  		case 0:
   318  			// In block 1, the test bank sends account #1 some ether.
   319  			tx, _ := types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), core.TxGas, nil, nil).SignECDSA(testBankKey)
   320  			block.AddTx(tx)
   321  		case 1:
   322  			// In block 2, the test bank sends some more ether to account #1.
   323  			// acc1Addr passes it on to account #2.
   324  			tx1, _ := types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), core.TxGas, nil, nil).SignECDSA(testBankKey)
   325  			tx2, _ := types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), core.TxGas, nil, nil).SignECDSA(acc1Key)
   326  			block.AddTx(tx1)
   327  			block.AddTx(tx2)
   328  		case 2:
   329  			// Block 3 is empty but was mined by account #2.
   330  			block.SetCoinbase(acc2Addr)
   331  			block.SetExtra([]byte("yeehaw"))
   332  		case 3:
   333  			// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
   334  			b2 := block.PrevBlock(1).Header()
   335  			b2.Extra = []byte("foo")
   336  			block.AddUncle(b2)
   337  			b3 := block.PrevBlock(2).Header()
   338  			b3.Extra = []byte("foo")
   339  			block.AddUncle(b3)
   340  		}
   341  	}
   342  	// Assemble the test environment
   343  	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
   344  	peer, _ := newTestPeer("peer", protocol, pm, true)
   345  	defer peer.close()
   346  
   347  	// Fetch for now the entire chain db
   348  	hashes := []common.Hash{}
   349  	for _, key := range pm.chaindb.(*ethdb.MemDatabase).Keys() {
   350  		if len(key) == len(common.Hash{}) {
   351  			hashes = append(hashes, common.BytesToHash(key))
   352  		}
   353  	}
   354  	s, _ := p2p.Send(peer.app, GetNodeDataMsg, hashes)
   355  	if s == 0 {
   356  		t.Errorf("got: %v, want: >0", s)
   357  	}
   358  	msg, err := peer.app.ReadMsg()
   359  	if err != nil {
   360  		t.Fatalf("failed to read node data response: %v", err)
   361  	}
   362  	if msg.Code != NodeDataMsg {
   363  		t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c)
   364  	}
   365  	var data [][]byte
   366  	if err := msg.Decode(&data); err != nil {
   367  		t.Fatalf("failed to decode response node data: %v", err)
   368  	}
   369  	// Verify that all hashes correspond to the requested data, and reconstruct a state tree
   370  	for i, want := range hashes {
   371  		if hash := crypto.Keccak256Hash(data[i]); hash != want {
   372  			t.Errorf("data hash mismatch: have %x, want %x", hash, want)
   373  		}
   374  	}
   375  	statedb, _ := ethdb.NewMemDatabase()
   376  	for i := 0; i < len(data); i++ {
   377  		statedb.Put(hashes[i].Bytes(), data[i])
   378  	}
   379  	accounts := []common.Address{testBank.Address, acc1Addr, acc2Addr}
   380  	for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
   381  		trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb))
   382  
   383  		for j, acc := range accounts {
   384  			state, _ := pm.blockchain.State()
   385  			bw := state.GetBalance(acc)
   386  			bh := trie.GetBalance(acc)
   387  
   388  			if (bw != nil && bh == nil) || (bw == nil && bh != nil) {
   389  				t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
   390  			}
   391  			if bw != nil && bh != nil && bw.Cmp(bw) != 0 {
   392  				t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
   393  			}
   394  		}
   395  	}
   396  }
   397  
   398  // Tests that the transaction receipts can be retrieved based on hashes.
   399  func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) }
   400  
   401  func testGetReceipt(t *testing.T, protocol int) {
   402  	// Define three accounts to simulate transactions with
   403  	acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   404  	acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
   405  	acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
   406  	acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
   407  
   408  	// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test)
   409  	generator := func(i int, block *core.BlockGen) {
   410  		switch i {
   411  		case 0:
   412  			// In block 1, the test bank sends account #1 some ether.
   413  			tx, _ := types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), core.TxGas, nil, nil).SignECDSA(testBankKey)
   414  			block.AddTx(tx)
   415  		case 1:
   416  			// In block 2, the test bank sends some more ether to account #1.
   417  			// acc1Addr passes it on to account #2.
   418  			tx1, _ := types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), core.TxGas, nil, nil).SignECDSA(testBankKey)
   419  			tx2, _ := types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), core.TxGas, nil, nil).SignECDSA(acc1Key)
   420  			block.AddTx(tx1)
   421  			block.AddTx(tx2)
   422  		case 2:
   423  			// Block 3 is empty but was mined by account #2.
   424  			block.SetCoinbase(acc2Addr)
   425  			block.SetExtra([]byte("yeehaw"))
   426  		case 3:
   427  			// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
   428  			b2 := block.PrevBlock(1).Header()
   429  			b2.Extra = []byte("foo")
   430  			block.AddUncle(b2)
   431  			b3 := block.PrevBlock(2).Header()
   432  			b3.Extra = []byte("foo")
   433  			block.AddUncle(b3)
   434  		}
   435  	}
   436  	// Assemble the test environment
   437  	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
   438  	peer, _ := newTestPeer("peer", protocol, pm, true)
   439  	defer peer.close()
   440  
   441  	// Collect the hashes to request, and the response to expect
   442  	hashes, receipts := []common.Hash{}, []types.Receipts{}
   443  	for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
   444  		block := pm.blockchain.GetBlockByNumber(i)
   445  
   446  		hashes = append(hashes, block.Hash())
   447  		receipts = append(receipts, core.GetBlockReceipts(pm.chaindb, block.Hash()))
   448  	}
   449  	// Send the hash request and verify the response
   450  	s, _ := p2p.Send(peer.app, GetReceiptsMsg, hashes)
   451  	if s <= 0 {
   452  		t.Errorf("got: %v, want: >0", s)
   453  	}
   454  	if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, receipts); err != nil {
   455  		t.Errorf("receipts mismatch: %v", err)
   456  	}
   457  }