github.com/decred/dcrd/blockchain@v1.2.1/chain_test.go (about)

     1  // Copyright (c) 2013-2016 The btcsuite developers
     2  // Copyright (c) 2015-2019 The Decred developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package blockchain
     7  
     8  import (
     9  	"bytes"
    10  	"compress/bzip2"
    11  	"encoding/gob"
    12  	"fmt"
    13  	"os"
    14  	"path/filepath"
    15  	"reflect"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/decred/dcrd/blockchain/chaingen"
    20  	"github.com/decred/dcrd/chaincfg"
    21  	"github.com/decred/dcrd/chaincfg/chainhash"
    22  	"github.com/decred/dcrd/dcrutil"
    23  	"github.com/decred/dcrd/wire"
    24  )
    25  
    26  // cloneParams returns a deep copy of the provided parameters so the caller is
    27  // free to modify them without worrying about interfering with other tests.
    28  func cloneParams(params *chaincfg.Params) *chaincfg.Params {
    29  	// Encode via gob.
    30  	buf := new(bytes.Buffer)
    31  	enc := gob.NewEncoder(buf)
    32  	enc.Encode(params)
    33  
    34  	// Decode via gob to make a deep copy.
    35  	var paramsCopy chaincfg.Params
    36  	dec := gob.NewDecoder(buf)
    37  	dec.Decode(&paramsCopy)
    38  	return &paramsCopy
    39  }
    40  
    41  // TestBlockchainFunction tests the various blockchain API to ensure proper
    42  // functionality.
    43  func TestBlockchainFunctions(t *testing.T) {
    44  	// Update parameters to reflect what is expected by the legacy data.
    45  	params := cloneParams(&chaincfg.RegNetParams)
    46  	params.GenesisBlock.Header.MerkleRoot = *mustParseHash("a216ea043f0d481a072424af646787794c32bcefd3ed181a090319bbf8a37105")
    47  	params.GenesisBlock.Header.Timestamp = time.Unix(1401292357, 0)
    48  	params.GenesisBlock.Transactions[0].TxIn[0].ValueIn = 0
    49  	params.PubKeyHashAddrID = [2]byte{0x0e, 0x91}
    50  	params.StakeBaseSigScript = []byte{0xde, 0xad, 0xbe, 0xef}
    51  	params.OrganizationPkScript = hexToBytes("a914cbb08d6ca783b533b2c7d24a51fbca92d937bf9987")
    52  	params.BlockOneLedger = []*chaincfg.TokenPayout{
    53  		{Address: "Sshw6S86G2bV6W32cbc7EhtFy8f93rU6pae", Amount: 100000 * 1e8},
    54  		{Address: "SsjXRK6Xz6CFuBt6PugBvrkdAa4xGbcZ18w", Amount: 100000 * 1e8},
    55  		{Address: "SsfXiYkYkCoo31CuVQw428N6wWKus2ZEw5X", Amount: 100000 * 1e8},
    56  	}
    57  	genesisHash := params.GenesisBlock.BlockHash()
    58  	params.GenesisHash = &genesisHash
    59  
    60  	// Create a new database and chain instance to run tests against.
    61  	chain, teardownFunc, err := chainSetup("validateunittests", params)
    62  	if err != nil {
    63  		t.Errorf("Failed to setup chain instance: %v", err)
    64  		return
    65  	}
    66  	defer teardownFunc()
    67  
    68  	// Load up the rest of the blocks up to HEAD~1.
    69  	filename := filepath.Join("testdata", "blocks0to168.bz2")
    70  	fi, err := os.Open(filename)
    71  	if err != nil {
    72  		t.Errorf("Unable to open %s: %v", filename, err)
    73  	}
    74  	bcStream := bzip2.NewReader(fi)
    75  	defer fi.Close()
    76  
    77  	// Create a buffer of the read file.
    78  	bcBuf := new(bytes.Buffer)
    79  	bcBuf.ReadFrom(bcStream)
    80  
    81  	// Create decoder from the buffer and a map to store the data.
    82  	bcDecoder := gob.NewDecoder(bcBuf)
    83  	blockChain := make(map[int64][]byte)
    84  
    85  	// Decode the blockchain into the map.
    86  	if err := bcDecoder.Decode(&blockChain); err != nil {
    87  		t.Errorf("error decoding test blockchain: %v", err.Error())
    88  	}
    89  
    90  	// Insert blocks 1 to 168 and perform various tests.
    91  	for i := 1; i <= 168; i++ {
    92  		bl, err := dcrutil.NewBlockFromBytes(blockChain[int64(i)])
    93  		if err != nil {
    94  			t.Errorf("NewBlockFromBytes error: %v", err.Error())
    95  		}
    96  
    97  		_, _, err = chain.ProcessBlock(bl, BFNone)
    98  		if err != nil {
    99  			t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error())
   100  		}
   101  	}
   102  
   103  	val, err := chain.TicketPoolValue()
   104  	if err != nil {
   105  		t.Errorf("Failed to get ticket pool value: %v", err)
   106  	}
   107  	expectedVal := dcrutil.Amount(3495091704)
   108  	if val != expectedVal {
   109  		t.Errorf("Failed to get correct result for ticket pool value; "+
   110  			"want %v, got %v", expectedVal, val)
   111  	}
   112  
   113  	a, _ := dcrutil.DecodeAddress("SsbKpMkPnadDcZFFZqRPY8nvdFagrktKuzB")
   114  	hs, err := chain.TicketsWithAddress(a)
   115  	if err != nil {
   116  		t.Errorf("Failed to do TicketsWithAddress: %v", err)
   117  	}
   118  	expectedLen := 223
   119  	if len(hs) != expectedLen {
   120  		t.Errorf("Failed to get correct number of tickets for "+
   121  			"TicketsWithAddress; want %v, got %v", expectedLen, len(hs))
   122  	}
   123  
   124  	totalSubsidy := chain.TotalSubsidy()
   125  	expectedSubsidy := int64(35783267326630)
   126  	if expectedSubsidy != totalSubsidy {
   127  		t.Errorf("Failed to get correct total subsidy for "+
   128  			"TotalSubsidy; want %v, got %v", expectedSubsidy,
   129  			totalSubsidy)
   130  	}
   131  }
   132  
   133  // TestForceHeadReorg ensures forcing header reorganization works as expected.
   134  func TestForceHeadReorg(t *testing.T) {
   135  	// Create a test harness initialized with the genesis block as the tip.
   136  	params := &chaincfg.RegNetParams
   137  	g, teardownFunc := newChaingenHarness(t, params, "forceheadreorgtest")
   138  	defer teardownFunc()
   139  
   140  	// Define some additional convenience helper functions to process the
   141  	// current tip block associated with the generator.
   142  	//
   143  	// rejectForceTipReorg forces the chain instance to reorganize the
   144  	// current tip of the main chain from the given block to the given
   145  	// block and expected it to be rejected with the provided error code.
   146  	rejectForceTipReorg := func(fromTipName, toTipName string, code ErrorCode) {
   147  		from := g.BlockByName(fromTipName)
   148  		to := g.BlockByName(toTipName)
   149  		t.Logf("Testing forced reorg from %s (hash %s, height %d) "+
   150  			"to %s (hash %s, height %d)", fromTipName,
   151  			from.BlockHash(), from.Header.Height, toTipName,
   152  			to.BlockHash(), to.Header.Height)
   153  
   154  		err := g.chain.ForceHeadReorganization(from.BlockHash(), to.BlockHash())
   155  		if err == nil {
   156  			t.Fatalf("forced header reorg from block %q (hash %s, "+
   157  				"height %d) to block %q (hash %s, height %d) "+
   158  				"should have failed", fromTipName, from.BlockHash(),
   159  				from.Header.Height, toTipName, to.BlockHash(),
   160  				to.Header.Height)
   161  		}
   162  
   163  		// Ensure the error code is of the expected type and the reject
   164  		// code matches the value specified in the test instance.
   165  		rerr, ok := err.(RuleError)
   166  		if !ok {
   167  			t.Fatalf("forced header reorg from block %q (hash %s, "+
   168  				"height %d) to block %q (hash %s, height %d) "+
   169  				"returned unexpected error type -- got %T, "+
   170  				"want blockchain.RuleError", fromTipName,
   171  				from.BlockHash(), from.Header.Height, toTipName,
   172  				to.BlockHash(), to.Header.Height, err)
   173  		}
   174  		if rerr.ErrorCode != code {
   175  			t.Fatalf("forced header reorg from block %q (hash %s, "+
   176  				"height %d) to block %q (hash %s, height %d) "+
   177  				"does not have expected reject code -- got %v, "+
   178  				"want %v", fromTipName,
   179  				from.BlockHash(), from.Header.Height, toTipName,
   180  				to.BlockHash(), to.Header.Height, rerr.ErrorCode,
   181  				code)
   182  		}
   183  	}
   184  
   185  	// Shorter versions of useful params for convenience.
   186  	coinbaseMaturity := params.CoinbaseMaturity
   187  	stakeValidationHeight := params.StakeValidationHeight
   188  
   189  	// ---------------------------------------------------------------------
   190  	// Generate and accept enough blocks to reach stake validation height.
   191  	// ---------------------------------------------------------------------
   192  
   193  	g.AdvanceToStakeValidationHeight()
   194  
   195  	// ---------------------------------------------------------------------
   196  	// Generate enough blocks to have a known distance to the first mature
   197  	// coinbase outputs for all tests that follow.  These blocks continue
   198  	// to purchase tickets to avoid running out of votes.
   199  	//
   200  	//   ... -> bsv# -> bbm0 -> bbm1 -> ... -> bbm#
   201  	// ---------------------------------------------------------------------
   202  
   203  	for i := uint16(0); i < coinbaseMaturity; i++ {
   204  		outs := g.OldestCoinbaseOuts()
   205  		blockName := fmt.Sprintf("bbm%d", i)
   206  		g.NextBlock(blockName, nil, outs[1:])
   207  		g.SaveTipCoinbaseOuts()
   208  		g.AcceptTipBlock()
   209  	}
   210  	g.AssertTipHeight(uint32(stakeValidationHeight) + uint32(coinbaseMaturity))
   211  
   212  	// Collect spendable outputs into two different slices.  The outs slice
   213  	// is intended to be used for regular transactions that spend from the
   214  	// output, while the ticketOuts slice is intended to be used for stake
   215  	// ticket purchases.
   216  	var outs []*chaingen.SpendableOut
   217  	var ticketOuts [][]chaingen.SpendableOut
   218  	for i := uint16(0); i < coinbaseMaturity; i++ {
   219  		coinbaseOuts := g.OldestCoinbaseOuts()
   220  		outs = append(outs, &coinbaseOuts[0])
   221  		ticketOuts = append(ticketOuts, coinbaseOuts[1:])
   222  	}
   223  
   224  	// ---------------------------------------------------------------------
   225  	// Forced header reorganization test.
   226  	// ---------------------------------------------------------------------
   227  
   228  	// Start by building a block at current tip (value in parens is which
   229  	// output is spent):
   230  	//
   231  	//   ... -> b1(0)
   232  	g.NextBlock("b1", outs[0], ticketOuts[0])
   233  	g.AcceptTipBlock()
   234  
   235  	// Create a fork from b1 with an invalid block due to committing to an
   236  	// invalid number of votes.  Since verifying the header commitment is a
   237  	// basic sanity check, no entry will be added to the block index.
   238  	//
   239  	//   ... -> b1(0) -> b2(1)
   240  	//               \-> b2bad0(1)
   241  	g.SetTip("b1")
   242  	g.NextBlock("b2bad0", outs[1], ticketOuts[1], func(b *wire.MsgBlock) {
   243  		b.Header.Voters++
   244  	})
   245  	g.RejectTipBlock(ErrTooManyVotes)
   246  
   247  	// Create a fork from b1 with an invalid block due to committing to an
   248  	// invalid input amount.  Since verifying the fraud proof necessarily
   249  	// requires access to the inputs, this will trigger the failure late
   250  	// enough to ensure an entry is added to the block index.  Further,
   251  	// since the block is attempting to extend the main chain it will also
   252  	// be fully checked and thus will be known invalid.
   253  	//
   254  	//   ... -> b1(0)
   255  	//               \-> b2bad1(1)
   256  	g.SetTip("b1")
   257  	g.NextBlock("b2bad1", outs[1], ticketOuts[1], func(b *wire.MsgBlock) {
   258  		b.Transactions[1].TxIn[0].ValueIn--
   259  	})
   260  	g.RejectTipBlock(ErrFraudAmountIn)
   261  
   262  	// Create some forks from b1.  There should not be a reorg since b1 is
   263  	// the current tip and b2 is seen first.
   264  	//
   265  	//   ... -> b1(0) -> b2(1)
   266  	//               \-> b3(1)
   267  	//               \-> b4(1)
   268  	//               \-> b5(1)
   269  	//               \-> b2bad0(1)
   270  	//               \-> b2bad1(1)
   271  	g.SetTip("b1")
   272  	g.NextBlock("b2", outs[1], ticketOuts[1])
   273  	g.AcceptTipBlock()
   274  
   275  	g.SetTip("b1")
   276  	g.NextBlock("b3", outs[1], ticketOuts[1])
   277  	g.AcceptedToSideChainWithExpectedTip("b2")
   278  
   279  	g.SetTip("b1")
   280  	g.NextBlock("b4", outs[1], ticketOuts[1])
   281  	g.AcceptedToSideChainWithExpectedTip("b2")
   282  
   283  	g.SetTip("b1")
   284  	g.NextBlock("b5", outs[1], ticketOuts[1])
   285  	g.AcceptedToSideChainWithExpectedTip("b2")
   286  
   287  	// Create a fork from b1 with an invalid block due to committing to an
   288  	// invalid input amount.  Since verifying the fraud proof necessarily
   289  	// requires access to the inputs, this will trigger the failure late
   290  	// enough to ensure an entry is added to the block index.  Further,
   291  	// since the block is not attempting to extend the main chain it will
   292  	// not be fully checked and thus will not yet have a known validation
   293  	// status.
   294  	//
   295  	//   ... -> b1(0) -> b2(1)
   296  	//               \-> b3(1)
   297  	//               \-> b4(1)
   298  	//               \-> b5(1)
   299  	//               \-> b2bad0(1)
   300  	//               \-> b2bad1(1)
   301  	//               \-> b2bad2(1)
   302  	g.SetTip("b1")
   303  	g.NextBlock("b2bad2", outs[1], ticketOuts[1], func(b *wire.MsgBlock) {
   304  		b.Transactions[1].TxIn[0].ValueIn--
   305  	})
   306  	g.AcceptedToSideChainWithExpectedTip("b2")
   307  
   308  	// Force tip reorganization to b3.
   309  	//
   310  	//   ... -> b1(0) -> b3(1)
   311  	//               \-> b2(1)
   312  	//               \-> b4(1)
   313  	//               \-> b5(1)
   314  	//               \-> b2bad0(1)
   315  	//               \-> b2bad1(1)
   316  	//               \-> b2bad2(1)
   317  	g.SetTip("b1")
   318  	g.ForceTipReorg("b2", "b3")
   319  	g.ExpectTip("b3")
   320  
   321  	// Force tip reorganization to b4.
   322  	//
   323  	//   ... -> b1(0) -> b4(1)
   324  	//               \-> b2(1)
   325  	//               \-> b3(1)
   326  	//               \-> b5(1)
   327  	//               \-> b2bad0(1)
   328  	//               \-> b2bad1(1)
   329  	//               \-> b2bad2(1)
   330  	g.ForceTipReorg("b3", "b4")
   331  	g.ExpectTip("b4")
   332  
   333  	// Force tip reorganization to b5.
   334  	//
   335  	//   ... -> b1(0) -> b5(1)
   336  	//               \-> b2(1)
   337  	//               \-> b3(1)
   338  	//               \-> b4(1)
   339  	//               \-> b2bad0(1)
   340  	//               \-> b2bad1(1)
   341  	//               \-> b2bad2(1)
   342  	g.ForceTipReorg("b4", "b5")
   343  	g.ExpectTip("b5")
   344  
   345  	// Force tip reorganization back to b3 to ensure cached validation
   346  	// results are exercised.
   347  	//
   348  	//   ... -> b1(0) -> b3(1)
   349  	//               \-> b2(1)
   350  	//               \-> b4(1)
   351  	//               \-> b5(1)
   352  	//               \-> b2bad0(1)
   353  	//               \-> b2bad1(1)
   354  	//               \-> b2bad2(1)
   355  	g.ForceTipReorg("b5", "b3")
   356  	g.ExpectTip("b3")
   357  
   358  	// Attempt to force tip reorganization from a block that is not the
   359  	// current tip.  This should fail since that is not allowed.
   360  	//
   361  	//   ... -> b1(0) -> b3(1)
   362  	//               \-> b2(1)
   363  	//               \-> b4(1)
   364  	//               \-> b5(1)
   365  	//               \-> b2bad0(1)
   366  	//               \-> b2bad1(1)
   367  	//               \-> b2bad2(1)
   368  	rejectForceTipReorg("b2", "b4", ErrForceReorgWrongChain)
   369  	g.ExpectTip("b3")
   370  
   371  	// Attempt to force tip reorganization to an invalid block that does
   372  	// not have an entry in the block index.
   373  	//
   374  	//   ... -> b1(0) -> b3(1)
   375  	//               \-> b2(1)
   376  	//               \-> b4(1)
   377  	//               \-> b5(1)
   378  	//               \-> b2bad0(1)
   379  	//               \-> b2bad1(1)
   380  	//               \-> b2bad2(1)
   381  	rejectForceTipReorg("b3", "b2bad0", ErrForceReorgMissingChild)
   382  	g.ExpectTip("b3")
   383  
   384  	// Attempt to force tip reorganization to an invalid block that has an
   385  	// entry in the block index and is already known to be invalid.
   386  	//
   387  	//   ... -> b1(0) -> b3(1)
   388  	//               \-> b2(1)
   389  	//               \-> b4(1)
   390  	//               \-> b5(1)
   391  	//               \-> b2bad0(1)
   392  	//               \-> b2bad1(1)
   393  	//               \-> b2bad2(1)
   394  	rejectForceTipReorg("b3", "b2bad1", ErrKnownInvalidBlock)
   395  	g.ExpectTip("b3")
   396  
   397  	// Attempt to force tip reorganization to an invalid block that has an
   398  	// entry in the block index, but is not already known to be invalid.
   399  	//
   400  	//
   401  	//   ... -> b1(0) -> b3(1)
   402  	//               \-> b2(1)
   403  	//               \-> b4(1)
   404  	//               \-> b5(1)
   405  	//               \-> b2bad0(1)
   406  	//               \-> b2bad1(1)
   407  	//               \-> b2bad2(1)
   408  	rejectForceTipReorg("b3", "b2bad2", ErrFraudAmountIn)
   409  	g.ExpectTip("b3")
   410  }
   411  
   412  // locatorHashes is a convenience function that returns the hashes for all of
   413  // the passed indexes of the provided nodes.  It is used to construct expected
   414  // block locators in the tests.
   415  func locatorHashes(nodes []*blockNode, indexes ...int) BlockLocator {
   416  	hashes := make(BlockLocator, 0, len(indexes))
   417  	for _, idx := range indexes {
   418  		hashes = append(hashes, &nodes[idx].hash)
   419  	}
   420  	return hashes
   421  }
   422  
   423  // nodeHashes is a convenience function that returns the hashes for all of the
   424  // passed indexes of the provided nodes.  It is used to construct expected hash
   425  // slices in the tests.
   426  func nodeHashes(nodes []*blockNode, indexes ...int) []chainhash.Hash {
   427  	hashes := make([]chainhash.Hash, 0, len(indexes))
   428  	for _, idx := range indexes {
   429  		hashes = append(hashes, nodes[idx].hash)
   430  	}
   431  	return hashes
   432  }
   433  
   434  // nodeHeaders is a convenience function that returns the headers for all of
   435  // the passed indexes of the provided nodes.  It is used to construct expected
   436  // located headers in the tests.
   437  func nodeHeaders(nodes []*blockNode, indexes ...int) []wire.BlockHeader {
   438  	headers := make([]wire.BlockHeader, 0, len(indexes))
   439  	for _, idx := range indexes {
   440  		headers = append(headers, nodes[idx].Header())
   441  	}
   442  	return headers
   443  }
   444  
   445  // TestLocateInventory ensures that locating inventory via the LocateHeaders and
   446  // LocateBlocks functions behaves as expected.
   447  func TestLocateInventory(t *testing.T) {
   448  	// Construct a synthetic block chain with a block index consisting of
   449  	// the following structure.
   450  	// 	genesis -> 1 -> 2 -> ... -> 15 -> 16  -> 17  -> 18
   451  	// 	                              \-> 16a -> 17a
   452  	tip := branchTip
   453  	chain := newFakeChain(&chaincfg.MainNetParams)
   454  	branch0Nodes := chainedFakeNodes(chain.bestChain.Genesis(), 18)
   455  	branch1Nodes := chainedFakeNodes(branch0Nodes[14], 2)
   456  	for _, node := range branch0Nodes {
   457  		chain.index.AddNode(node)
   458  	}
   459  	for _, node := range branch1Nodes {
   460  		chain.index.AddNode(node)
   461  	}
   462  	chain.bestChain.SetTip(tip(branch0Nodes))
   463  
   464  	// Create chain views for different branches of the overall chain to
   465  	// simulate a local and remote node on different parts of the chain.
   466  	localView := newChainView(tip(branch0Nodes))
   467  	remoteView := newChainView(tip(branch1Nodes))
   468  
   469  	// Create a chain view for a completely unrelated block chain to
   470  	// simulate a remote node on a totally different chain.
   471  	unrelatedBranchNodes := chainedFakeNodes(nil, 5)
   472  	unrelatedView := newChainView(tip(unrelatedBranchNodes))
   473  
   474  	tests := []struct {
   475  		name       string
   476  		locator    BlockLocator       // locator for requested inventory
   477  		hashStop   chainhash.Hash     // stop hash for locator
   478  		maxAllowed uint32             // max to locate, 0 = wire const
   479  		headers    []wire.BlockHeader // expected located headers
   480  		hashes     []chainhash.Hash   // expected located hashes
   481  	}{
   482  		{
   483  			// Empty block locators and unknown stop hash.  No
   484  			// inventory should be located.
   485  			name:     "no locators, no stop",
   486  			locator:  nil,
   487  			hashStop: chainhash.Hash{},
   488  			headers:  nil,
   489  			hashes:   nil,
   490  		},
   491  		{
   492  			// Empty block locators and stop hash in side chain.
   493  			// The expected result is the requested block.
   494  			name:     "no locators, stop in side",
   495  			locator:  nil,
   496  			hashStop: tip(branch1Nodes).hash,
   497  			headers:  nodeHeaders(branch1Nodes, 1),
   498  			hashes:   nodeHashes(branch1Nodes, 1),
   499  		},
   500  		{
   501  			// Empty block locators and stop hash in main chain.
   502  			// The expected result is the requested block.
   503  			name:     "no locators, stop in main",
   504  			locator:  nil,
   505  			hashStop: branch0Nodes[12].hash,
   506  			headers:  nodeHeaders(branch0Nodes, 12),
   507  			hashes:   nodeHashes(branch0Nodes, 12),
   508  		},
   509  		{
   510  			// Locators based on remote being on side chain and a
   511  			// stop hash local node doesn't know about.  The
   512  			// expected result is the blocks after the fork point in
   513  			// the main chain and the stop hash has no effect.
   514  			name:     "remote side chain, unknown stop",
   515  			locator:  remoteView.BlockLocator(nil),
   516  			hashStop: chainhash.Hash{0x01},
   517  			headers:  nodeHeaders(branch0Nodes, 15, 16, 17),
   518  			hashes:   nodeHashes(branch0Nodes, 15, 16, 17),
   519  		},
   520  		{
   521  			// Locators based on remote being on side chain and a
   522  			// stop hash in side chain.  The expected result is the
   523  			// blocks after the fork point in the main chain and the
   524  			// stop hash has no effect.
   525  			name:     "remote side chain, stop in side",
   526  			locator:  remoteView.BlockLocator(nil),
   527  			hashStop: tip(branch1Nodes).hash,
   528  			headers:  nodeHeaders(branch0Nodes, 15, 16, 17),
   529  			hashes:   nodeHashes(branch0Nodes, 15, 16, 17),
   530  		},
   531  		{
   532  			// Locators based on remote being on side chain and a
   533  			// stop hash in main chain, but before fork point.  The
   534  			// expected result is the blocks after the fork point in
   535  			// the main chain and the stop hash has no effect.
   536  			name:     "remote side chain, stop in main before",
   537  			locator:  remoteView.BlockLocator(nil),
   538  			hashStop: branch0Nodes[13].hash,
   539  			headers:  nodeHeaders(branch0Nodes, 15, 16, 17),
   540  			hashes:   nodeHashes(branch0Nodes, 15, 16, 17),
   541  		},
   542  		{
   543  			// Locators based on remote being on side chain and a
   544  			// stop hash in main chain, but exactly at the fork
   545  			// point.  The expected result is the blocks after the
   546  			// fork point in the main chain and the stop hash has no
   547  			// effect.
   548  			name:     "remote side chain, stop in main exact",
   549  			locator:  remoteView.BlockLocator(nil),
   550  			hashStop: branch0Nodes[14].hash,
   551  			headers:  nodeHeaders(branch0Nodes, 15, 16, 17),
   552  			hashes:   nodeHashes(branch0Nodes, 15, 16, 17),
   553  		},
   554  		{
   555  			// Locators based on remote being on side chain and a
   556  			// stop hash in main chain just after the fork point.
   557  			// The expected result is the blocks after the fork
   558  			// point in the main chain up to and including the stop
   559  			// hash.
   560  			name:     "remote side chain, stop in main after",
   561  			locator:  remoteView.BlockLocator(nil),
   562  			hashStop: branch0Nodes[15].hash,
   563  			headers:  nodeHeaders(branch0Nodes, 15),
   564  			hashes:   nodeHashes(branch0Nodes, 15),
   565  		},
   566  		{
   567  			// Locators based on remote being on side chain and a
   568  			// stop hash in main chain some time after the fork
   569  			// point.  The expected result is the blocks after the
   570  			// fork point in the main chain up to and including the
   571  			// stop hash.
   572  			name:     "remote side chain, stop in main after more",
   573  			locator:  remoteView.BlockLocator(nil),
   574  			hashStop: branch0Nodes[16].hash,
   575  			headers:  nodeHeaders(branch0Nodes, 15, 16),
   576  			hashes:   nodeHashes(branch0Nodes, 15, 16),
   577  		},
   578  		{
   579  			// Locators based on remote being on main chain in the
   580  			// past and a stop hash local node doesn't know about.
   581  			// The expected result is the blocks after the known
   582  			// point in the main chain and the stop hash has no
   583  			// effect.
   584  			name:     "remote main chain past, unknown stop",
   585  			locator:  localView.BlockLocator(branch0Nodes[12]),
   586  			hashStop: chainhash.Hash{0x01},
   587  			headers:  nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
   588  			hashes:   nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
   589  		},
   590  		{
   591  			// Locators based on remote being on main chain in the
   592  			// past and a stop hash in a side chain.  The expected
   593  			// result is the blocks after the known point in the
   594  			// main chain and the stop hash has no effect.
   595  			name:     "remote main chain past, stop in side",
   596  			locator:  localView.BlockLocator(branch0Nodes[12]),
   597  			hashStop: tip(branch1Nodes).hash,
   598  			headers:  nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
   599  			hashes:   nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
   600  		},
   601  		{
   602  			// Locators based on remote being on main chain in the
   603  			// past and a stop hash in the main chain before that
   604  			// point.  The expected result is the blocks after the
   605  			// known point in the main chain and the stop hash has
   606  			// no effect.
   607  			name:     "remote main chain past, stop in main before",
   608  			locator:  localView.BlockLocator(branch0Nodes[12]),
   609  			hashStop: branch0Nodes[11].hash,
   610  			headers:  nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
   611  			hashes:   nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
   612  		},
   613  		{
   614  			// Locators based on remote being on main chain in the
   615  			// past and a stop hash in the main chain exactly at that
   616  			// point.  The expected result is the blocks after the
   617  			// known point in the main chain and the stop hash has
   618  			// no effect.
   619  			name:     "remote main chain past, stop in main exact",
   620  			locator:  localView.BlockLocator(branch0Nodes[12]),
   621  			hashStop: branch0Nodes[12].hash,
   622  			headers:  nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
   623  			hashes:   nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
   624  		},
   625  		{
   626  			// Locators based on remote being on main chain in the
   627  			// past and a stop hash in the main chain just after
   628  			// that point.  The expected result is the blocks after
   629  			// the known point in the main chain and the stop hash
   630  			// has no effect.
   631  			name:     "remote main chain past, stop in main after",
   632  			locator:  localView.BlockLocator(branch0Nodes[12]),
   633  			hashStop: branch0Nodes[13].hash,
   634  			headers:  nodeHeaders(branch0Nodes, 13),
   635  			hashes:   nodeHashes(branch0Nodes, 13),
   636  		},
   637  		{
   638  			// Locators based on remote being on main chain in the
   639  			// past and a stop hash in the main chain some time
   640  			// after that point.  The expected result is the blocks
   641  			// after the known point in the main chain and the stop
   642  			// hash has no effect.
   643  			name:     "remote main chain past, stop in main after more",
   644  			locator:  localView.BlockLocator(branch0Nodes[12]),
   645  			hashStop: branch0Nodes[15].hash,
   646  			headers:  nodeHeaders(branch0Nodes, 13, 14, 15),
   647  			hashes:   nodeHashes(branch0Nodes, 13, 14, 15),
   648  		},
   649  		{
   650  			// Locators based on remote being at exactly the same
   651  			// point in the main chain and a stop hash local node
   652  			// doesn't know about.  The expected result is no
   653  			// located inventory.
   654  			name:     "remote main chain same, unknown stop",
   655  			locator:  localView.BlockLocator(nil),
   656  			hashStop: chainhash.Hash{0x01},
   657  			headers:  nil,
   658  			hashes:   nil,
   659  		},
   660  		{
   661  			// Locators based on remote being at exactly the same
   662  			// point in the main chain and a stop hash at exactly
   663  			// the same point.  The expected result is no located
   664  			// inventory.
   665  			name:     "remote main chain same, stop same point",
   666  			locator:  localView.BlockLocator(nil),
   667  			hashStop: tip(branch0Nodes).hash,
   668  			headers:  nil,
   669  			hashes:   nil,
   670  		},
   671  		{
   672  			// Locators from remote that don't include any blocks
   673  			// the local node knows.  This would happen if the
   674  			// remote node is on a completely separate chain that
   675  			// isn't rooted with the same genesis block.  The
   676  			// expected result is the blocks after the genesis
   677  			// block.
   678  			name:     "remote unrelated chain",
   679  			locator:  unrelatedView.BlockLocator(nil),
   680  			hashStop: chainhash.Hash{},
   681  			headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   682  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   683  			hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   684  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   685  		},
   686  		{
   687  			// Locators from remote for second block in main chain
   688  			// and no stop hash, but with an overridden max limit.
   689  			// The expected result is the blocks after the second
   690  			// block limited by the max.
   691  			name:       "remote genesis",
   692  			locator:    locatorHashes(branch0Nodes, 0),
   693  			hashStop:   chainhash.Hash{},
   694  			maxAllowed: 3,
   695  			headers:    nodeHeaders(branch0Nodes, 1, 2, 3),
   696  			hashes:     nodeHashes(branch0Nodes, 1, 2, 3),
   697  		},
   698  		{
   699  			// Poorly formed locator.
   700  			//
   701  			// Locator from remote that only includes a single
   702  			// block on a side chain the local node knows.  The
   703  			// expected result is the blocks after the genesis
   704  			// block since even though the block is known, it is on
   705  			// a side chain and there are no more locators to find
   706  			// the fork point.
   707  			name:     "weak locator, single known side block",
   708  			locator:  locatorHashes(branch1Nodes, 1),
   709  			hashStop: chainhash.Hash{},
   710  			headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   711  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   712  			hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   713  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   714  		},
   715  		{
   716  			// Poorly formed locator.
   717  			//
   718  			// Locator from remote that only includes multiple
   719  			// blocks on a side chain the local node knows however
   720  			// none in the main chain.  The expected result is the
   721  			// blocks after the genesis block since even though the
   722  			// blocks are known, they are all on a side chain and
   723  			// there are no more locators to find the fork point.
   724  			name:     "weak locator, multiple known side blocks",
   725  			locator:  locatorHashes(branch1Nodes, 1),
   726  			hashStop: chainhash.Hash{},
   727  			headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   728  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   729  			hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   730  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   731  		},
   732  		{
   733  			// Poorly formed locator.
   734  			//
   735  			// Locator from remote that only includes multiple
   736  			// blocks on a side chain the local node knows however
   737  			// none in the main chain but includes a stop hash in
   738  			// the main chain.  The expected result is the blocks
   739  			// after the genesis block up to the stop hash since
   740  			// even though the blocks are known, they are all on a
   741  			// side chain and there are no more locators to find the
   742  			// fork point.
   743  			name:     "weak locator, multiple known side blocks, stop in main",
   744  			locator:  locatorHashes(branch1Nodes, 1),
   745  			hashStop: branch0Nodes[5].hash,
   746  			headers:  nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5),
   747  			hashes:   nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5),
   748  		},
   749  	}
   750  	for _, test := range tests {
   751  		// Ensure the expected headers are located.
   752  		var headers []wire.BlockHeader
   753  		if test.maxAllowed != 0 {
   754  			// Need to use the unexported function to override the
   755  			// max allowed for headers.
   756  			chain.chainLock.RLock()
   757  			headers = chain.locateHeaders(test.locator,
   758  				&test.hashStop, test.maxAllowed)
   759  			chain.chainLock.RUnlock()
   760  		} else {
   761  			headers = chain.LocateHeaders(test.locator,
   762  				&test.hashStop)
   763  		}
   764  		if !reflect.DeepEqual(headers, test.headers) {
   765  			t.Errorf("%s: unxpected headers -- got %v, want %v",
   766  				test.name, headers, test.headers)
   767  			continue
   768  		}
   769  
   770  		// Ensure the expected block hashes are located.
   771  		maxAllowed := uint32(wire.MaxBlocksPerMsg)
   772  		if test.maxAllowed != 0 {
   773  			maxAllowed = test.maxAllowed
   774  		}
   775  		hashes := chain.LocateBlocks(test.locator, &test.hashStop,
   776  			maxAllowed)
   777  		if !reflect.DeepEqual(hashes, test.hashes) {
   778  			t.Errorf("%s: unxpected hashes -- got %v, want %v",
   779  				test.name, hashes, test.hashes)
   780  			continue
   781  		}
   782  	}
   783  }