github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/eth/handler_test.go (about)

     1  // Copyright 2015 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum 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 Spectrum 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 Spectrum 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  	"time"
    25  
    26  	"github.com/SmartMeshFoundation/Spectrum/common"
    27  	"github.com/SmartMeshFoundation/Spectrum/consensus/ethash"
    28  	"github.com/SmartMeshFoundation/Spectrum/core"
    29  	"github.com/SmartMeshFoundation/Spectrum/core/state"
    30  	"github.com/SmartMeshFoundation/Spectrum/core/types"
    31  	"github.com/SmartMeshFoundation/Spectrum/core/vm"
    32  	"github.com/SmartMeshFoundation/Spectrum/crypto"
    33  	"github.com/SmartMeshFoundation/Spectrum/eth/downloader"
    34  	"github.com/SmartMeshFoundation/Spectrum/ethdb"
    35  	"github.com/SmartMeshFoundation/Spectrum/event"
    36  	"github.com/SmartMeshFoundation/Spectrum/p2p"
    37  	"github.com/SmartMeshFoundation/Spectrum/params"
    38  )
    39  
    40  var bigTxGas = new(big.Int).SetUint64(params.TxGas)
    41  
    42  // Tests that protocol versions and modes of operations are matched up properly.
    43  func TestProtocolCompatibility(t *testing.T) {
    44  	// Define the compatibility chart
    45  	tests := []struct {
    46  		version    uint
    47  		mode       downloader.SyncMode
    48  		compatible bool
    49  	}{
    50  		{61, downloader.FullSync, true}, {62, downloader.FullSync, true}, {63, downloader.FullSync, true},
    51  		{61, downloader.FastSync, false}, {62, downloader.FastSync, false}, {63, downloader.FastSync, true},
    52  	}
    53  	// Make sure anything we screw up is restored
    54  	backup := ProtocolVersions
    55  	defer func() { ProtocolVersions = backup }()
    56  
    57  	// Try all available compatibility configs and check for errors
    58  	for i, tt := range tests {
    59  		ProtocolVersions = []uint{tt.version}
    60  
    61  		pm, err := newTestProtocolManager(tt.mode, 0, nil, nil)
    62  		if pm != nil {
    63  			defer pm.Stop()
    64  		}
    65  		if (err == nil && !tt.compatible) || (err != nil && tt.compatible) {
    66  			t.Errorf("test %d: compatibility mismatch: have error %v, want compatibility %v", i, err, tt.compatible)
    67  		}
    68  	}
    69  }
    70  
    71  // Tests that block headers can be retrieved from a remote chain based on user queries.
    72  func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) }
    73  func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
    74  
    75  func testGetBlockHeaders(t *testing.T, protocol int) {
    76  	pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil)
    77  	peer, _ := newTestPeer("peer", protocol, pm, true)
    78  	defer peer.close()
    79  
    80  	// Create a "random" unknown hash for testing
    81  	var unknown common.Hash
    82  	for i := range unknown {
    83  		unknown[i] = byte(i)
    84  	}
    85  	// Create a batch of tests for various scenarios
    86  	limit := uint64(downloader.MaxHeaderFetch)
    87  	tests := []struct {
    88  		query  *getBlockHeadersData // The query to execute for header retrieval
    89  		expect []common.Hash        // The hashes of the block whose headers are expected
    90  	}{
    91  		// A single random block should be retrievable by hash and number too
    92  		{
    93  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1},
    94  			[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()},
    95  		}, {
    96  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1},
    97  			[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()},
    98  		},
    99  		// Multiple headers should be retrievable in both directions
   100  		{
   101  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3},
   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  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true},
   109  			[]common.Hash{
   110  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
   111  				pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(),
   112  				pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(),
   113  			},
   114  		},
   115  		// Multiple headers with skip lists should be retrievable
   116  		{
   117  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3},
   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  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true},
   125  			[]common.Hash{
   126  				pm.blockchain.GetBlockByNumber(limit / 2).Hash(),
   127  				pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(),
   128  				pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(),
   129  			},
   130  		},
   131  		// The chain endpoints should be retrievable
   132  		{
   133  			&getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1},
   134  			[]common.Hash{pm.blockchain.GetBlockByNumber(0).Hash()},
   135  		}, {
   136  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1},
   137  			[]common.Hash{pm.blockchain.CurrentBlock().Hash()},
   138  		},
   139  		// Ensure protocol limits are honored
   140  		{
   141  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true},
   142  			pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit),
   143  		},
   144  		// Check that requesting more than available is handled gracefully
   145  		{
   146  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3},
   147  			[]common.Hash{
   148  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(),
   149  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(),
   150  			},
   151  		}, {
   152  			&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true},
   153  			[]common.Hash{
   154  				pm.blockchain.GetBlockByNumber(4).Hash(),
   155  				pm.blockchain.GetBlockByNumber(0).Hash(),
   156  			},
   157  		},
   158  		// Check that requesting more than available is handled gracefully, even if mid skip
   159  		{
   160  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3},
   161  			[]common.Hash{
   162  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(),
   163  				pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(),
   164  			},
   165  		}, {
   166  			&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true},
   167  			[]common.Hash{
   168  				pm.blockchain.GetBlockByNumber(4).Hash(),
   169  				pm.blockchain.GetBlockByNumber(1).Hash(),
   170  			},
   171  		},
   172  		// Check a corner case where requesting more can iterate past the endpoints
   173  		{
   174  			&getBlockHeadersData{Origin: hashOrNumber{Number: 2}, Amount: 5, Reverse: true},
   175  			[]common.Hash{
   176  				pm.blockchain.GetBlockByNumber(2).Hash(),
   177  				pm.blockchain.GetBlockByNumber(1).Hash(),
   178  				pm.blockchain.GetBlockByNumber(0).Hash(),
   179  			},
   180  		},
   181  		// Check a corner case where skipping overflow loops back into the chain start
   182  		{
   183  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1},
   184  			[]common.Hash{
   185  				pm.blockchain.GetBlockByNumber(3).Hash(),
   186  			},
   187  		},
   188  		// Check a corner case where skipping overflow loops back to the same header
   189  		{
   190  			&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64},
   191  			[]common.Hash{
   192  				pm.blockchain.GetBlockByNumber(1).Hash(),
   193  			},
   194  		},
   195  		// Check that non existing headers aren't returned
   196  		{
   197  			&getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1},
   198  			[]common.Hash{},
   199  		}, {
   200  			&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1},
   201  			[]common.Hash{},
   202  		},
   203  	}
   204  	// Run each of the tests and verify the results against the chain
   205  	for i, tt := range tests {
   206  		// Collect the headers to expect in the response
   207  		headers := []*types.Header{}
   208  		for _, hash := range tt.expect {
   209  			headers = append(headers, pm.blockchain.GetBlockByHash(hash).Header())
   210  		}
   211  		// Send the hash request and verify the response
   212  		p2p.Send(peer.app, 0x03, tt.query)
   213  		if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil {
   214  			t.Errorf("test %d: headers mismatch: %v", i, err)
   215  		}
   216  		// If the test used number origins, repeat with hashes as the too
   217  		if tt.query.Origin.Hash == (common.Hash{}) {
   218  			if origin := pm.blockchain.GetBlockByNumber(tt.query.Origin.Number); origin != nil {
   219  				tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0
   220  
   221  				p2p.Send(peer.app, 0x03, tt.query)
   222  				if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil {
   223  					t.Errorf("test %d: headers mismatch: %v", i, err)
   224  				}
   225  			}
   226  		}
   227  	}
   228  }
   229  
   230  // Tests that block contents can be retrieved from a remote chain based on their hashes.
   231  func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) }
   232  func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
   233  
   234  func testGetBlockBodies(t *testing.T, protocol int) {
   235  	pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil)
   236  	peer, _ := newTestPeer("peer", protocol, pm, true)
   237  	defer peer.close()
   238  
   239  	// Create a batch of tests for various scenarios
   240  	limit := downloader.MaxBlockFetch
   241  	tests := []struct {
   242  		random    int           // Number of blocks to fetch randomly from the chain
   243  		explicit  []common.Hash // Explicitly requested blocks
   244  		available []bool        // Availability of explicitly requested blocks
   245  		expected  int           // Total number of existing blocks to expect
   246  	}{
   247  		{1, nil, nil, 1},             // A single random block should be retrievable
   248  		{10, nil, nil, 10},           // Multiple random blocks should be retrievable
   249  		{limit, nil, nil, limit},     // The maximum possible blocks should be retrievable
   250  		{limit + 1, nil, nil, limit}, // No more than the possible block count should be returned
   251  		{0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1},      // The genesis block should be retrievable
   252  		{0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable
   253  		{0, []common.Hash{{}}, []bool{false}, 0},                                 // A non existent block should not be returned
   254  
   255  		// Existing and non-existing blocks interleaved should not cause problems
   256  		{0, []common.Hash{
   257  			{},
   258  			pm.blockchain.GetBlockByNumber(1).Hash(),
   259  			{},
   260  			pm.blockchain.GetBlockByNumber(10).Hash(),
   261  			{},
   262  			pm.blockchain.GetBlockByNumber(100).Hash(),
   263  			{},
   264  		}, []bool{false, true, false, true, false, true, false}, 3},
   265  	}
   266  	// Run each of the tests and verify the results against the chain
   267  	for i, tt := range tests {
   268  		// Collect the hashes to request, and the response to expect
   269  		hashes, seen := []common.Hash{}, make(map[int64]bool)
   270  		bodies := []*blockBody{}
   271  
   272  		for j := 0; j < tt.random; j++ {
   273  			for {
   274  				num := rand.Int63n(int64(pm.blockchain.CurrentBlock().NumberU64()))
   275  				if !seen[num] {
   276  					seen[num] = true
   277  
   278  					block := pm.blockchain.GetBlockByNumber(uint64(num))
   279  					hashes = append(hashes, block.Hash())
   280  					if len(bodies) < tt.expected {
   281  						bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()})
   282  					}
   283  					break
   284  				}
   285  			}
   286  		}
   287  		for j, hash := range tt.explicit {
   288  			hashes = append(hashes, hash)
   289  			if tt.available[j] && len(bodies) < tt.expected {
   290  				block := pm.blockchain.GetBlockByHash(hash)
   291  				bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()})
   292  			}
   293  		}
   294  		// Send the hash request and verify the response
   295  		p2p.Send(peer.app, 0x05, hashes)
   296  		if err := p2p.ExpectMsg(peer.app, 0x06, bodies); err != nil {
   297  			t.Errorf("test %d: bodies mismatch: %v", i, err)
   298  		}
   299  	}
   300  }
   301  
   302  // Tests that the node state database can be retrieved based on hashes.
   303  func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) }
   304  
   305  func testGetNodeData(t *testing.T, protocol int) {
   306  	// Define three accounts to simulate transactions with
   307  	acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   308  	acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
   309  	acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
   310  	acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
   311  
   312  	signer := types.HomesteadSigner{}
   313  	// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test)
   314  	generator := func(i int, block *core.BlockGen) {
   315  		switch i {
   316  		case 0:
   317  			// In block 1, the test bank sends account #1 some ether.
   318  			tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey)
   319  			block.AddTx(tx)
   320  		case 1:
   321  			// In block 2, the test bank sends some more ether to account #1.
   322  			// acc1Addr passes it on to account #2.
   323  			tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey)
   324  			tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key)
   325  			block.AddTx(tx1)
   326  			block.AddTx(tx2)
   327  		case 2:
   328  			// Block 3 is empty but was mined by account #2.
   329  			block.SetCoinbase(acc2Addr)
   330  			block.SetExtra([]byte("yeehaw"))
   331  		case 3:
   332  			// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
   333  			b2 := block.PrevBlock(1).Header()
   334  			b2.Extra = []byte("foo")
   335  			block.AddUncle(b2)
   336  			b3 := block.PrevBlock(2).Header()
   337  			b3.Extra = []byte("foo")
   338  			block.AddUncle(b3)
   339  		}
   340  	}
   341  	// Assemble the test environment
   342  	pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
   343  	peer, _ := newTestPeer("peer", protocol, pm, true)
   344  	defer peer.close()
   345  
   346  	// Fetch for now the entire chain db
   347  	hashes := []common.Hash{}
   348  	for _, key := range pm.chaindb.(*ethdb.MemDatabase).Keys() {
   349  		if len(key) == len(common.Hash{}) {
   350  			hashes = append(hashes, common.BytesToHash(key))
   351  		}
   352  	}
   353  	p2p.Send(peer.app, 0x0d, hashes)
   354  	msg, err := peer.app.ReadMsg()
   355  	if err != nil {
   356  		t.Fatalf("failed to read node data response: %v", err)
   357  	}
   358  	if msg.Code != 0x0e {
   359  		t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c)
   360  	}
   361  	var data [][]byte
   362  	if err := msg.Decode(&data); err != nil {
   363  		t.Fatalf("failed to decode response node data: %v", err)
   364  	}
   365  	// Verify that all hashes correspond to the requested data, and reconstruct a state tree
   366  	for i, want := range hashes {
   367  		if hash := crypto.Keccak256Hash(data[i]); hash != want {
   368  			t.Errorf("data hash mismatch: have %x, want %x", hash, want)
   369  		}
   370  	}
   371  	statedb, _ := ethdb.NewMemDatabase()
   372  	for i := 0; i < len(data); i++ {
   373  		statedb.Put(hashes[i].Bytes(), data[i])
   374  	}
   375  	accounts := []common.Address{testBank, acc1Addr, acc2Addr}
   376  	for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
   377  		trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb))
   378  
   379  		for j, acc := range accounts {
   380  			state, _ := pm.blockchain.State()
   381  			bw := state.GetBalance(acc)
   382  			bh := trie.GetBalance(acc)
   383  
   384  			if (bw != nil && bh == nil) || (bw == nil && bh != nil) {
   385  				t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
   386  			}
   387  			if bw != nil && bh != nil && bw.Cmp(bw) != 0 {
   388  				t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
   389  			}
   390  		}
   391  	}
   392  }
   393  
   394  // Tests that the transaction receipts can be retrieved based on hashes.
   395  func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) }
   396  
   397  func testGetReceipt(t *testing.T, protocol int) {
   398  	// Define three accounts to simulate transactions with
   399  	acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   400  	acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
   401  	acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
   402  	acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
   403  
   404  	signer := types.HomesteadSigner{}
   405  	// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test)
   406  	generator := func(i int, block *core.BlockGen) {
   407  		switch i {
   408  		case 0:
   409  			// In block 1, the test bank sends account #1 some ether.
   410  			tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey)
   411  			block.AddTx(tx)
   412  		case 1:
   413  			// In block 2, the test bank sends some more ether to account #1.
   414  			// acc1Addr passes it on to account #2.
   415  			tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey)
   416  			tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key)
   417  			block.AddTx(tx1)
   418  			block.AddTx(tx2)
   419  		case 2:
   420  			// Block 3 is empty but was mined by account #2.
   421  			block.SetCoinbase(acc2Addr)
   422  			block.SetExtra([]byte("yeehaw"))
   423  		case 3:
   424  			// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
   425  			b2 := block.PrevBlock(1).Header()
   426  			b2.Extra = []byte("foo")
   427  			block.AddUncle(b2)
   428  			b3 := block.PrevBlock(2).Header()
   429  			b3.Extra = []byte("foo")
   430  			block.AddUncle(b3)
   431  		}
   432  	}
   433  	// Assemble the test environment
   434  	pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
   435  	peer, _ := newTestPeer("peer", protocol, pm, true)
   436  	defer peer.close()
   437  
   438  	// Collect the hashes to request, and the response to expect
   439  	hashes, receipts := []common.Hash{}, []types.Receipts{}
   440  	for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
   441  		block := pm.blockchain.GetBlockByNumber(i)
   442  
   443  		hashes = append(hashes, block.Hash())
   444  		receipts = append(receipts, core.GetBlockReceipts(pm.chaindb, block.Hash(), block.NumberU64()))
   445  	}
   446  	// Send the hash request and verify the response
   447  	p2p.Send(peer.app, 0x0f, hashes)
   448  	if err := p2p.ExpectMsg(peer.app, 0x10, receipts); err != nil {
   449  		t.Errorf("receipts mismatch: %v", err)
   450  	}
   451  }
   452  
   453  // Tests that post eth protocol handshake, DAO fork-enabled clients also execute
   454  // a DAO "challenge" verifying each others' DAO fork headers to ensure they're on
   455  // compatible chains.
   456  func TestDAOChallengeNoVsNo(t *testing.T)       { testDAOChallenge(t, false, false, false) }
   457  func TestDAOChallengeNoVsPro(t *testing.T)      { testDAOChallenge(t, false, true, false) }
   458  func TestDAOChallengeProVsNo(t *testing.T)      { testDAOChallenge(t, true, false, false) }
   459  func TestDAOChallengeProVsPro(t *testing.T)     { testDAOChallenge(t, true, true, false) }
   460  func TestDAOChallengeNoVsTimeout(t *testing.T)  { testDAOChallenge(t, false, false, true) }
   461  func TestDAOChallengeProVsTimeout(t *testing.T) { testDAOChallenge(t, true, true, true) }
   462  
   463  func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool) {
   464  	// Reduce the DAO handshake challenge timeout
   465  	if timeout {
   466  		defer func(old time.Duration) { daoChallengeTimeout = old }(daoChallengeTimeout)
   467  		daoChallengeTimeout = 500 * time.Millisecond
   468  	}
   469  	// Create a DAO aware protocol manager
   470  	var (
   471  		evmux         = new(event.TypeMux)
   472  		pow           = ethash.NewFaker()
   473  		db, _         = ethdb.NewMemDatabase()
   474  		config        = &params.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked}
   475  		gspec         = &core.Genesis{Config: config}
   476  		genesis       = gspec.MustCommit(db)
   477  		blockchain, _ = core.NewBlockChain(db, config, pow, vm.Config{})
   478  	)
   479  	pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db)
   480  	if err != nil {
   481  		t.Fatalf("failed to start test protocol manager: %v", err)
   482  	}
   483  	pm.Start(1000)
   484  	defer pm.Stop()
   485  
   486  	// Connect a new peer and check that we receive the DAO challenge
   487  	peer, _ := newTestPeer("peer", eth63, pm, true)
   488  	defer peer.close()
   489  
   490  	challenge := &getBlockHeadersData{
   491  		Origin:  hashOrNumber{Number: config.DAOForkBlock.Uint64()},
   492  		Amount:  1,
   493  		Skip:    0,
   494  		Reverse: false,
   495  	}
   496  	if err := p2p.ExpectMsg(peer.app, GetBlockHeadersMsg, challenge); err != nil {
   497  		t.Fatalf("challenge mismatch: %v", err)
   498  	}
   499  	// Create a block to reply to the challenge if no timeout is simulated
   500  	if !timeout {
   501  		blocks, _ := core.GenerateChain(&params.ChainConfig{}, genesis, ethash.NewFaker(), db, 1, func(i int, block *core.BlockGen) {
   502  			if remoteForked {
   503  				block.SetExtra(params.DAOForkBlockExtra)
   504  			}
   505  		})
   506  		if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{blocks[0].Header()}); err != nil {
   507  			t.Fatalf("failed to answer challenge: %v", err)
   508  		}
   509  		time.Sleep(100 * time.Millisecond) // Sleep to avoid the verification racing with the drops
   510  	} else {
   511  		// Otherwise wait until the test timeout passes
   512  		time.Sleep(daoChallengeTimeout + 500*time.Millisecond)
   513  	}
   514  	// Verify that depending on fork side, the remote peer is maintained or dropped
   515  	if localForked == remoteForked && !timeout {
   516  		if peers := pm.peers.Len(); peers != 1 {
   517  			t.Fatalf("peer count mismatch: have %d, want %d", peers, 1)
   518  		}
   519  	} else {
   520  		if peers := pm.peers.Len(); peers != 0 {
   521  			t.Fatalf("peer count mismatch: have %d, want %d", peers, 0)
   522  		}
   523  	}
   524  }