github.com/palcoin-project/palcd@v1.0.0/blockchain/chain_test.go (about)

     1  // Copyright (c) 2013-2017 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"reflect"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/palcoin-project/palcd/chaincfg"
    13  	"github.com/palcoin-project/palcd/chaincfg/chainhash"
    14  	"github.com/palcoin-project/palcd/wire"
    15  	"github.com/palcoin-project/palcutil"
    16  )
    17  
    18  // TestHaveBlock tests the HaveBlock API to ensure proper functionality.
    19  func TestHaveBlock(t *testing.T) {
    20  	// Load up blocks such that there is a side chain.
    21  	// (genesis block) -> 1 -> 2 -> 3 -> 4
    22  	//                          \-> 3a
    23  	testFiles := []string{
    24  		"blk_0_to_4.dat.bz2",
    25  		"blk_3A.dat.bz2",
    26  	}
    27  
    28  	var blocks []*palcutil.Block
    29  	for _, file := range testFiles {
    30  		blockTmp, err := loadBlocks(file)
    31  		if err != nil {
    32  			t.Errorf("Error loading file: %v\n", err)
    33  			return
    34  		}
    35  		blocks = append(blocks, blockTmp...)
    36  	}
    37  
    38  	// Create a new database and chain instance to run tests against.
    39  	chain, teardownFunc, err := chainSetup("haveblock",
    40  		&chaincfg.MainNetParams)
    41  	if err != nil {
    42  		t.Errorf("Failed to setup chain instance: %v", err)
    43  		return
    44  	}
    45  	defer teardownFunc()
    46  
    47  	// Since we're not dealing with the real block chain, set the coinbase
    48  	// maturity to 1.
    49  	chain.TstSetCoinbaseMaturity(1)
    50  
    51  	for i := 1; i < len(blocks); i++ {
    52  		_, isOrphan, err := chain.ProcessBlock(blocks[i], BFNone)
    53  		if err != nil {
    54  			t.Errorf("ProcessBlock fail on block %v: %v\n", i, err)
    55  			return
    56  		}
    57  		if isOrphan {
    58  			t.Errorf("ProcessBlock incorrectly returned block %v "+
    59  				"is an orphan\n", i)
    60  			return
    61  		}
    62  	}
    63  
    64  	// Insert an orphan block.
    65  	_, isOrphan, err := chain.ProcessBlock(palcutil.NewBlock(&Block100000),
    66  		BFNone)
    67  	if err != nil {
    68  		t.Errorf("Unable to process block: %v", err)
    69  		return
    70  	}
    71  	if !isOrphan {
    72  		t.Errorf("ProcessBlock indicated block is an not orphan when " +
    73  			"it should be\n")
    74  		return
    75  	}
    76  
    77  	tests := []struct {
    78  		hash string
    79  		want bool
    80  	}{
    81  		// Genesis block should be present (in the main chain).
    82  		{hash: chaincfg.MainNetParams.GenesisHash.String(), want: true},
    83  
    84  		// Block 3a should be present (on a side chain).
    85  		{hash: "00000000474284d20067a4d33f6a02284e6ef70764a3a26d6a5b9df52ef663dd", want: true},
    86  
    87  		// Block 100000 should be present (as an orphan).
    88  		{hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506", want: true},
    89  
    90  		// Random hashes should not be available.
    91  		{hash: "123", want: false},
    92  	}
    93  
    94  	for i, test := range tests {
    95  		hash, err := chainhash.NewHashFromStr(test.hash)
    96  		if err != nil {
    97  			t.Errorf("NewHashFromStr: %v", err)
    98  			continue
    99  		}
   100  
   101  		result, err := chain.HaveBlock(hash)
   102  		if err != nil {
   103  			t.Errorf("HaveBlock #%d unexpected error: %v", i, err)
   104  			return
   105  		}
   106  		if result != test.want {
   107  			t.Errorf("HaveBlock #%d got %v want %v", i, result,
   108  				test.want)
   109  			continue
   110  		}
   111  	}
   112  }
   113  
   114  // TestCalcSequenceLock tests the LockTimeToSequence function, and the
   115  // CalcSequenceLock method of a Chain instance. The tests exercise several
   116  // combinations of inputs to the CalcSequenceLock function in order to ensure
   117  // the returned SequenceLocks are correct for each test instance.
   118  func TestCalcSequenceLock(t *testing.T) {
   119  	netParams := &chaincfg.SimNetParams
   120  
   121  	// We need to activate CSV in order to test the processing logic, so
   122  	// manually craft the block version that's used to signal the soft-fork
   123  	// activation.
   124  	csvBit := netParams.Deployments[chaincfg.DeploymentCSV].BitNumber
   125  	blockVersion := int32(0x20000000 | (uint32(1) << csvBit))
   126  
   127  	// Generate enough synthetic blocks to activate CSV.
   128  	chain := newFakeChain(netParams)
   129  	node := chain.bestChain.Tip()
   130  	blockTime := node.Header().Timestamp
   131  	numBlocksToActivate := (netParams.MinerConfirmationWindow * 3)
   132  	for i := uint32(0); i < numBlocksToActivate; i++ {
   133  		blockTime = blockTime.Add(time.Second)
   134  		node = newFakeNode(node, blockVersion, 0, blockTime)
   135  		chain.index.AddNode(node)
   136  		chain.bestChain.SetTip(node)
   137  	}
   138  
   139  	// Create a utxo view with a fake utxo for the inputs used in the
   140  	// transactions created below.  This utxo is added such that it has an
   141  	// age of 4 blocks.
   142  	targetTx := palcutil.NewTx(&wire.MsgTx{
   143  		TxOut: []*wire.TxOut{{
   144  			PkScript: nil,
   145  			Value:    10,
   146  		}},
   147  	})
   148  	utxoView := NewUtxoViewpoint()
   149  	utxoView.AddTxOuts(targetTx, int32(numBlocksToActivate)-4)
   150  	utxoView.SetBestHash(&node.hash)
   151  
   152  	// Create a utxo that spends the fake utxo created above for use in the
   153  	// transactions created in the tests.  It has an age of 4 blocks.  Note
   154  	// that the sequence lock heights are always calculated from the same
   155  	// point of view that they were originally calculated from for a given
   156  	// utxo.  That is to say, the height prior to it.
   157  	utxo := wire.OutPoint{
   158  		Hash:  *targetTx.Hash(),
   159  		Index: 0,
   160  	}
   161  	prevUtxoHeight := int32(numBlocksToActivate) - 4
   162  
   163  	// Obtain the median time past from the PoV of the input created above.
   164  	// The MTP for the input is the MTP from the PoV of the block *prior*
   165  	// to the one that included it.
   166  	medianTime := node.RelativeAncestor(5).CalcPastMedianTime().Unix()
   167  
   168  	// The median time calculated from the PoV of the best block in the
   169  	// test chain.  For unconfirmed inputs, this value will be used since
   170  	// the MTP will be calculated from the PoV of the yet-to-be-mined
   171  	// block.
   172  	nextMedianTime := node.CalcPastMedianTime().Unix()
   173  	nextBlockHeight := int32(numBlocksToActivate) + 1
   174  
   175  	// Add an additional transaction which will serve as our unconfirmed
   176  	// output.
   177  	unConfTx := &wire.MsgTx{
   178  		TxOut: []*wire.TxOut{{
   179  			PkScript: nil,
   180  			Value:    5,
   181  		}},
   182  	}
   183  	unConfUtxo := wire.OutPoint{
   184  		Hash:  unConfTx.TxHash(),
   185  		Index: 0,
   186  	}
   187  
   188  	// Adding a utxo with a height of 0x7fffffff indicates that the output
   189  	// is currently unmined.
   190  	utxoView.AddTxOuts(palcutil.NewTx(unConfTx), 0x7fffffff)
   191  
   192  	tests := []struct {
   193  		tx      *wire.MsgTx
   194  		view    *UtxoViewpoint
   195  		mempool bool
   196  		want    *SequenceLock
   197  	}{
   198  		// A transaction of version one should disable sequence locks
   199  		// as the new sequence number semantics only apply to
   200  		// transactions version 2 or higher.
   201  		{
   202  			tx: &wire.MsgTx{
   203  				Version: 1,
   204  				TxIn: []*wire.TxIn{{
   205  					PreviousOutPoint: utxo,
   206  					Sequence:         LockTimeToSequence(false, 3),
   207  				}},
   208  			},
   209  			view: utxoView,
   210  			want: &SequenceLock{
   211  				Seconds:     -1,
   212  				BlockHeight: -1,
   213  			},
   214  		},
   215  		// A transaction with a single input with max sequence number.
   216  		// This sequence number has the high bit set, so sequence locks
   217  		// should be disabled.
   218  		{
   219  			tx: &wire.MsgTx{
   220  				Version: 2,
   221  				TxIn: []*wire.TxIn{{
   222  					PreviousOutPoint: utxo,
   223  					Sequence:         wire.MaxTxInSequenceNum,
   224  				}},
   225  			},
   226  			view: utxoView,
   227  			want: &SequenceLock{
   228  				Seconds:     -1,
   229  				BlockHeight: -1,
   230  			},
   231  		},
   232  		// A transaction with a single input whose lock time is
   233  		// expressed in seconds.  However, the specified lock time is
   234  		// below the required floor for time based lock times since
   235  		// they have time granularity of 512 seconds.  As a result, the
   236  		// seconds lock-time should be just before the median time of
   237  		// the targeted block.
   238  		{
   239  			tx: &wire.MsgTx{
   240  				Version: 2,
   241  				TxIn: []*wire.TxIn{{
   242  					PreviousOutPoint: utxo,
   243  					Sequence:         LockTimeToSequence(true, 2),
   244  				}},
   245  			},
   246  			view: utxoView,
   247  			want: &SequenceLock{
   248  				Seconds:     medianTime - 1,
   249  				BlockHeight: -1,
   250  			},
   251  		},
   252  		// A transaction with a single input whose lock time is
   253  		// expressed in seconds.  The number of seconds should be 1023
   254  		// seconds after the median past time of the last block in the
   255  		// chain.
   256  		{
   257  			tx: &wire.MsgTx{
   258  				Version: 2,
   259  				TxIn: []*wire.TxIn{{
   260  					PreviousOutPoint: utxo,
   261  					Sequence:         LockTimeToSequence(true, 1024),
   262  				}},
   263  			},
   264  			view: utxoView,
   265  			want: &SequenceLock{
   266  				Seconds:     medianTime + 1023,
   267  				BlockHeight: -1,
   268  			},
   269  		},
   270  		// A transaction with multiple inputs.  The first input has a
   271  		// lock time expressed in seconds.  The second input has a
   272  		// sequence lock in blocks with a value of 4.  The last input
   273  		// has a sequence number with a value of 5, but has the disable
   274  		// bit set.  So the first lock should be selected as it's the
   275  		// latest lock that isn't disabled.
   276  		{
   277  			tx: &wire.MsgTx{
   278  				Version: 2,
   279  				TxIn: []*wire.TxIn{{
   280  					PreviousOutPoint: utxo,
   281  					Sequence:         LockTimeToSequence(true, 2560),
   282  				}, {
   283  					PreviousOutPoint: utxo,
   284  					Sequence:         LockTimeToSequence(false, 4),
   285  				}, {
   286  					PreviousOutPoint: utxo,
   287  					Sequence: LockTimeToSequence(false, 5) |
   288  						wire.SequenceLockTimeDisabled,
   289  				}},
   290  			},
   291  			view: utxoView,
   292  			want: &SequenceLock{
   293  				Seconds:     medianTime + (5 << wire.SequenceLockTimeGranularity) - 1,
   294  				BlockHeight: prevUtxoHeight + 3,
   295  			},
   296  		},
   297  		// Transaction with a single input.  The input's sequence number
   298  		// encodes a relative lock-time in blocks (3 blocks).  The
   299  		// sequence lock should  have a value of -1 for seconds, but a
   300  		// height of 2 meaning it can be included at height 3.
   301  		{
   302  			tx: &wire.MsgTx{
   303  				Version: 2,
   304  				TxIn: []*wire.TxIn{{
   305  					PreviousOutPoint: utxo,
   306  					Sequence:         LockTimeToSequence(false, 3),
   307  				}},
   308  			},
   309  			view: utxoView,
   310  			want: &SequenceLock{
   311  				Seconds:     -1,
   312  				BlockHeight: prevUtxoHeight + 2,
   313  			},
   314  		},
   315  		// A transaction with two inputs with lock times expressed in
   316  		// seconds.  The selected sequence lock value for seconds should
   317  		// be the time further in the future.
   318  		{
   319  			tx: &wire.MsgTx{
   320  				Version: 2,
   321  				TxIn: []*wire.TxIn{{
   322  					PreviousOutPoint: utxo,
   323  					Sequence:         LockTimeToSequence(true, 5120),
   324  				}, {
   325  					PreviousOutPoint: utxo,
   326  					Sequence:         LockTimeToSequence(true, 2560),
   327  				}},
   328  			},
   329  			view: utxoView,
   330  			want: &SequenceLock{
   331  				Seconds:     medianTime + (10 << wire.SequenceLockTimeGranularity) - 1,
   332  				BlockHeight: -1,
   333  			},
   334  		},
   335  		// A transaction with two inputs with lock times expressed in
   336  		// blocks.  The selected sequence lock value for blocks should
   337  		// be the height further in the future, so a height of 10
   338  		// indicating it can be included at height 11.
   339  		{
   340  			tx: &wire.MsgTx{
   341  				Version: 2,
   342  				TxIn: []*wire.TxIn{{
   343  					PreviousOutPoint: utxo,
   344  					Sequence:         LockTimeToSequence(false, 1),
   345  				}, {
   346  					PreviousOutPoint: utxo,
   347  					Sequence:         LockTimeToSequence(false, 11),
   348  				}},
   349  			},
   350  			view: utxoView,
   351  			want: &SequenceLock{
   352  				Seconds:     -1,
   353  				BlockHeight: prevUtxoHeight + 10,
   354  			},
   355  		},
   356  		// A transaction with multiple inputs.  Two inputs are time
   357  		// based, and the other two are block based. The lock lying
   358  		// further into the future for both inputs should be chosen.
   359  		{
   360  			tx: &wire.MsgTx{
   361  				Version: 2,
   362  				TxIn: []*wire.TxIn{{
   363  					PreviousOutPoint: utxo,
   364  					Sequence:         LockTimeToSequence(true, 2560),
   365  				}, {
   366  					PreviousOutPoint: utxo,
   367  					Sequence:         LockTimeToSequence(true, 6656),
   368  				}, {
   369  					PreviousOutPoint: utxo,
   370  					Sequence:         LockTimeToSequence(false, 3),
   371  				}, {
   372  					PreviousOutPoint: utxo,
   373  					Sequence:         LockTimeToSequence(false, 9),
   374  				}},
   375  			},
   376  			view: utxoView,
   377  			want: &SequenceLock{
   378  				Seconds:     medianTime + (13 << wire.SequenceLockTimeGranularity) - 1,
   379  				BlockHeight: prevUtxoHeight + 8,
   380  			},
   381  		},
   382  		// A transaction with a single unconfirmed input.  As the input
   383  		// is confirmed, the height of the input should be interpreted
   384  		// as the height of the *next* block.  So, a 2 block relative
   385  		// lock means the sequence lock should be for 1 block after the
   386  		// *next* block height, indicating it can be included 2 blocks
   387  		// after that.
   388  		{
   389  			tx: &wire.MsgTx{
   390  				Version: 2,
   391  				TxIn: []*wire.TxIn{{
   392  					PreviousOutPoint: unConfUtxo,
   393  					Sequence:         LockTimeToSequence(false, 2),
   394  				}},
   395  			},
   396  			view:    utxoView,
   397  			mempool: true,
   398  			want: &SequenceLock{
   399  				Seconds:     -1,
   400  				BlockHeight: nextBlockHeight + 1,
   401  			},
   402  		},
   403  		// A transaction with a single unconfirmed input.  The input has
   404  		// a time based lock, so the lock time should be based off the
   405  		// MTP of the *next* block.
   406  		{
   407  			tx: &wire.MsgTx{
   408  				Version: 2,
   409  				TxIn: []*wire.TxIn{{
   410  					PreviousOutPoint: unConfUtxo,
   411  					Sequence:         LockTimeToSequence(true, 1024),
   412  				}},
   413  			},
   414  			view:    utxoView,
   415  			mempool: true,
   416  			want: &SequenceLock{
   417  				Seconds:     nextMedianTime + 1023,
   418  				BlockHeight: -1,
   419  			},
   420  		},
   421  	}
   422  
   423  	t.Logf("Running %v SequenceLock tests", len(tests))
   424  	for i, test := range tests {
   425  		utilTx := palcutil.NewTx(test.tx)
   426  		seqLock, err := chain.CalcSequenceLock(utilTx, test.view, test.mempool)
   427  		if err != nil {
   428  			t.Fatalf("test #%d, unable to calc sequence lock: %v", i, err)
   429  		}
   430  
   431  		if seqLock.Seconds != test.want.Seconds {
   432  			t.Fatalf("test #%d got %v seconds want %v seconds",
   433  				i, seqLock.Seconds, test.want.Seconds)
   434  		}
   435  		if seqLock.BlockHeight != test.want.BlockHeight {
   436  			t.Fatalf("test #%d got height of %v want height of %v ",
   437  				i, seqLock.BlockHeight, test.want.BlockHeight)
   438  		}
   439  	}
   440  }
   441  
   442  // nodeHashes is a convenience function that returns the hashes for all of the
   443  // passed indexes of the provided nodes.  It is used to construct expected hash
   444  // slices in the tests.
   445  func nodeHashes(nodes []*blockNode, indexes ...int) []chainhash.Hash {
   446  	hashes := make([]chainhash.Hash, 0, len(indexes))
   447  	for _, idx := range indexes {
   448  		hashes = append(hashes, nodes[idx].hash)
   449  	}
   450  	return hashes
   451  }
   452  
   453  // nodeHeaders is a convenience function that returns the headers for all of
   454  // the passed indexes of the provided nodes.  It is used to construct expected
   455  // located headers in the tests.
   456  func nodeHeaders(nodes []*blockNode, indexes ...int) []wire.BlockHeader {
   457  	headers := make([]wire.BlockHeader, 0, len(indexes))
   458  	for _, idx := range indexes {
   459  		headers = append(headers, nodes[idx].Header())
   460  	}
   461  	return headers
   462  }
   463  
   464  // TestLocateInventory ensures that locating inventory via the LocateHeaders and
   465  // LocateBlocks functions behaves as expected.
   466  func TestLocateInventory(t *testing.T) {
   467  	// Construct a synthetic block chain with a block index consisting of
   468  	// the following structure.
   469  	// 	genesis -> 1 -> 2 -> ... -> 15 -> 16  -> 17  -> 18
   470  	// 	                              \-> 16a -> 17a
   471  	tip := tstTip
   472  	chain := newFakeChain(&chaincfg.MainNetParams)
   473  	branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18)
   474  	branch1Nodes := chainedNodes(branch0Nodes[14], 2)
   475  	for _, node := range branch0Nodes {
   476  		chain.index.AddNode(node)
   477  	}
   478  	for _, node := range branch1Nodes {
   479  		chain.index.AddNode(node)
   480  	}
   481  	chain.bestChain.SetTip(tip(branch0Nodes))
   482  
   483  	// Create chain views for different branches of the overall chain to
   484  	// simulate a local and remote node on different parts of the chain.
   485  	localView := newChainView(tip(branch0Nodes))
   486  	remoteView := newChainView(tip(branch1Nodes))
   487  
   488  	// Create a chain view for a completely unrelated block chain to
   489  	// simulate a remote node on a totally different chain.
   490  	unrelatedBranchNodes := chainedNodes(nil, 5)
   491  	unrelatedView := newChainView(tip(unrelatedBranchNodes))
   492  
   493  	tests := []struct {
   494  		name       string
   495  		locator    BlockLocator       // locator for requested inventory
   496  		hashStop   chainhash.Hash     // stop hash for locator
   497  		maxAllowed uint32             // max to locate, 0 = wire const
   498  		headers    []wire.BlockHeader // expected located headers
   499  		hashes     []chainhash.Hash   // expected located hashes
   500  	}{
   501  		{
   502  			// Empty block locators and unknown stop hash.  No
   503  			// inventory should be located.
   504  			name:     "no locators, no stop",
   505  			locator:  nil,
   506  			hashStop: chainhash.Hash{},
   507  			headers:  nil,
   508  			hashes:   nil,
   509  		},
   510  		{
   511  			// Empty block locators and stop hash in side chain.
   512  			// The expected result is the requested block.
   513  			name:     "no locators, stop in side",
   514  			locator:  nil,
   515  			hashStop: tip(branch1Nodes).hash,
   516  			headers:  nodeHeaders(branch1Nodes, 1),
   517  			hashes:   nodeHashes(branch1Nodes, 1),
   518  		},
   519  		{
   520  			// Empty block locators and stop hash in main chain.
   521  			// The expected result is the requested block.
   522  			name:     "no locators, stop in main",
   523  			locator:  nil,
   524  			hashStop: branch0Nodes[12].hash,
   525  			headers:  nodeHeaders(branch0Nodes, 12),
   526  			hashes:   nodeHashes(branch0Nodes, 12),
   527  		},
   528  		{
   529  			// Locators based on remote being on side chain and a
   530  			// stop hash local node doesn't know about.  The
   531  			// expected result is the blocks after the fork point in
   532  			// the main chain and the stop hash has no effect.
   533  			name:     "remote side chain, unknown stop",
   534  			locator:  remoteView.BlockLocator(nil),
   535  			hashStop: chainhash.Hash{0x01},
   536  			headers:  nodeHeaders(branch0Nodes, 15, 16, 17),
   537  			hashes:   nodeHashes(branch0Nodes, 15, 16, 17),
   538  		},
   539  		{
   540  			// Locators based on remote being on side chain and a
   541  			// stop hash in side chain.  The expected result is the
   542  			// blocks after the fork point in the main chain and the
   543  			// stop hash has no effect.
   544  			name:     "remote side chain, stop in side",
   545  			locator:  remoteView.BlockLocator(nil),
   546  			hashStop: tip(branch1Nodes).hash,
   547  			headers:  nodeHeaders(branch0Nodes, 15, 16, 17),
   548  			hashes:   nodeHashes(branch0Nodes, 15, 16, 17),
   549  		},
   550  		{
   551  			// Locators based on remote being on side chain and a
   552  			// stop hash in main chain, but before fork point.  The
   553  			// expected result is the blocks after the fork point in
   554  			// the main chain and the stop hash has no effect.
   555  			name:     "remote side chain, stop in main before",
   556  			locator:  remoteView.BlockLocator(nil),
   557  			hashStop: branch0Nodes[13].hash,
   558  			headers:  nodeHeaders(branch0Nodes, 15, 16, 17),
   559  			hashes:   nodeHashes(branch0Nodes, 15, 16, 17),
   560  		},
   561  		{
   562  			// Locators based on remote being on side chain and a
   563  			// stop hash in main chain, but exactly at the fork
   564  			// point.  The expected result is the blocks after the
   565  			// fork point in the main chain and the stop hash has no
   566  			// effect.
   567  			name:     "remote side chain, stop in main exact",
   568  			locator:  remoteView.BlockLocator(nil),
   569  			hashStop: branch0Nodes[14].hash,
   570  			headers:  nodeHeaders(branch0Nodes, 15, 16, 17),
   571  			hashes:   nodeHashes(branch0Nodes, 15, 16, 17),
   572  		},
   573  		{
   574  			// Locators based on remote being on side chain and a
   575  			// stop hash in main chain just after the fork point.
   576  			// The expected result is the blocks after the fork
   577  			// point in the main chain up to and including the stop
   578  			// hash.
   579  			name:     "remote side chain, stop in main after",
   580  			locator:  remoteView.BlockLocator(nil),
   581  			hashStop: branch0Nodes[15].hash,
   582  			headers:  nodeHeaders(branch0Nodes, 15),
   583  			hashes:   nodeHashes(branch0Nodes, 15),
   584  		},
   585  		{
   586  			// Locators based on remote being on side chain and a
   587  			// stop hash in main chain some time after the fork
   588  			// point.  The expected result is the blocks after the
   589  			// fork point in the main chain up to and including the
   590  			// stop hash.
   591  			name:     "remote side chain, stop in main after more",
   592  			locator:  remoteView.BlockLocator(nil),
   593  			hashStop: branch0Nodes[16].hash,
   594  			headers:  nodeHeaders(branch0Nodes, 15, 16),
   595  			hashes:   nodeHashes(branch0Nodes, 15, 16),
   596  		},
   597  		{
   598  			// Locators based on remote being on main chain in the
   599  			// past and a stop hash local node doesn't know about.
   600  			// The expected result is the blocks after the known
   601  			// point in the main chain and the stop hash has no
   602  			// effect.
   603  			name:     "remote main chain past, unknown stop",
   604  			locator:  localView.BlockLocator(branch0Nodes[12]),
   605  			hashStop: chainhash.Hash{0x01},
   606  			headers:  nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
   607  			hashes:   nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
   608  		},
   609  		{
   610  			// Locators based on remote being on main chain in the
   611  			// past and a stop hash in a side chain.  The expected
   612  			// result is the blocks after the known point in the
   613  			// main chain and the stop hash has no effect.
   614  			name:     "remote main chain past, stop in side",
   615  			locator:  localView.BlockLocator(branch0Nodes[12]),
   616  			hashStop: tip(branch1Nodes).hash,
   617  			headers:  nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
   618  			hashes:   nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
   619  		},
   620  		{
   621  			// Locators based on remote being on main chain in the
   622  			// past and a stop hash in the main chain before that
   623  			// point.  The expected result is the blocks after the
   624  			// known point in the main chain and the stop hash has
   625  			// no effect.
   626  			name:     "remote main chain past, stop in main before",
   627  			locator:  localView.BlockLocator(branch0Nodes[12]),
   628  			hashStop: branch0Nodes[11].hash,
   629  			headers:  nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
   630  			hashes:   nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
   631  		},
   632  		{
   633  			// Locators based on remote being on main chain in the
   634  			// past and a stop hash in the main chain exactly at that
   635  			// point.  The expected result is the blocks after the
   636  			// known point in the main chain and the stop hash has
   637  			// no effect.
   638  			name:     "remote main chain past, stop in main exact",
   639  			locator:  localView.BlockLocator(branch0Nodes[12]),
   640  			hashStop: branch0Nodes[12].hash,
   641  			headers:  nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
   642  			hashes:   nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
   643  		},
   644  		{
   645  			// Locators based on remote being on main chain in the
   646  			// past and a stop hash in the main chain just after
   647  			// that point.  The expected result is the blocks after
   648  			// the known point in the main chain and the stop hash
   649  			// has no effect.
   650  			name:     "remote main chain past, stop in main after",
   651  			locator:  localView.BlockLocator(branch0Nodes[12]),
   652  			hashStop: branch0Nodes[13].hash,
   653  			headers:  nodeHeaders(branch0Nodes, 13),
   654  			hashes:   nodeHashes(branch0Nodes, 13),
   655  		},
   656  		{
   657  			// Locators based on remote being on main chain in the
   658  			// past and a stop hash in the main chain some time
   659  			// after that point.  The expected result is the blocks
   660  			// after the known point in the main chain and the stop
   661  			// hash has no effect.
   662  			name:     "remote main chain past, stop in main after more",
   663  			locator:  localView.BlockLocator(branch0Nodes[12]),
   664  			hashStop: branch0Nodes[15].hash,
   665  			headers:  nodeHeaders(branch0Nodes, 13, 14, 15),
   666  			hashes:   nodeHashes(branch0Nodes, 13, 14, 15),
   667  		},
   668  		{
   669  			// Locators based on remote being at exactly the same
   670  			// point in the main chain and a stop hash local node
   671  			// doesn't know about.  The expected result is no
   672  			// located inventory.
   673  			name:     "remote main chain same, unknown stop",
   674  			locator:  localView.BlockLocator(nil),
   675  			hashStop: chainhash.Hash{0x01},
   676  			headers:  nil,
   677  			hashes:   nil,
   678  		},
   679  		{
   680  			// Locators based on remote being at exactly the same
   681  			// point in the main chain and a stop hash at exactly
   682  			// the same point.  The expected result is no located
   683  			// inventory.
   684  			name:     "remote main chain same, stop same point",
   685  			locator:  localView.BlockLocator(nil),
   686  			hashStop: tip(branch0Nodes).hash,
   687  			headers:  nil,
   688  			hashes:   nil,
   689  		},
   690  		{
   691  			// Locators from remote that don't include any blocks
   692  			// the local node knows.  This would happen if the
   693  			// remote node is on a completely separate chain that
   694  			// isn't rooted with the same genesis block.  The
   695  			// expected result is the blocks after the genesis
   696  			// block.
   697  			name:     "remote unrelated chain",
   698  			locator:  unrelatedView.BlockLocator(nil),
   699  			hashStop: chainhash.Hash{},
   700  			headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   701  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   702  			hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   703  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   704  		},
   705  		{
   706  			// Locators from remote for second block in main chain
   707  			// and no stop hash, but with an overridden max limit.
   708  			// The expected result is the blocks after the second
   709  			// block limited by the max.
   710  			name:       "remote genesis",
   711  			locator:    locatorHashes(branch0Nodes, 0),
   712  			hashStop:   chainhash.Hash{},
   713  			maxAllowed: 3,
   714  			headers:    nodeHeaders(branch0Nodes, 1, 2, 3),
   715  			hashes:     nodeHashes(branch0Nodes, 1, 2, 3),
   716  		},
   717  		{
   718  			// Poorly formed locator.
   719  			//
   720  			// Locator from remote that only includes a single
   721  			// block on a side chain the local node knows.  The
   722  			// expected result is the blocks after the genesis
   723  			// block since even though the block is known, it is on
   724  			// a side chain and there are no more locators to find
   725  			// the fork point.
   726  			name:     "weak locator, single known side block",
   727  			locator:  locatorHashes(branch1Nodes, 1),
   728  			hashStop: chainhash.Hash{},
   729  			headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   730  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   731  			hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   732  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   733  		},
   734  		{
   735  			// Poorly formed locator.
   736  			//
   737  			// Locator from remote that only includes multiple
   738  			// blocks on a side chain the local node knows however
   739  			// none in the main chain.  The expected result is the
   740  			// blocks after the genesis block since even though the
   741  			// blocks are known, they are all on a side chain and
   742  			// there are no more locators to find the fork point.
   743  			name:     "weak locator, multiple known side blocks",
   744  			locator:  locatorHashes(branch1Nodes, 1),
   745  			hashStop: chainhash.Hash{},
   746  			headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   747  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   748  			hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
   749  				7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
   750  		},
   751  		{
   752  			// Poorly formed locator.
   753  			//
   754  			// Locator from remote that only includes multiple
   755  			// blocks on a side chain the local node knows however
   756  			// none in the main chain but includes a stop hash in
   757  			// the main chain.  The expected result is the blocks
   758  			// after the genesis block up to the stop hash since
   759  			// even though the blocks are known, they are all on a
   760  			// side chain and there are no more locators to find the
   761  			// fork point.
   762  			name:     "weak locator, multiple known side blocks, stop in main",
   763  			locator:  locatorHashes(branch1Nodes, 1),
   764  			hashStop: branch0Nodes[5].hash,
   765  			headers:  nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5),
   766  			hashes:   nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5),
   767  		},
   768  	}
   769  	for _, test := range tests {
   770  		// Ensure the expected headers are located.
   771  		var headers []wire.BlockHeader
   772  		if test.maxAllowed != 0 {
   773  			// Need to use the unexported function to override the
   774  			// max allowed for headers.
   775  			chain.chainLock.RLock()
   776  			headers = chain.locateHeaders(test.locator,
   777  				&test.hashStop, test.maxAllowed)
   778  			chain.chainLock.RUnlock()
   779  		} else {
   780  			headers = chain.LocateHeaders(test.locator,
   781  				&test.hashStop)
   782  		}
   783  		if !reflect.DeepEqual(headers, test.headers) {
   784  			t.Errorf("%s: unxpected headers -- got %v, want %v",
   785  				test.name, headers, test.headers)
   786  			continue
   787  		}
   788  
   789  		// Ensure the expected block hashes are located.
   790  		maxAllowed := uint32(wire.MaxBlocksPerMsg)
   791  		if test.maxAllowed != 0 {
   792  			maxAllowed = test.maxAllowed
   793  		}
   794  		hashes := chain.LocateBlocks(test.locator, &test.hashStop,
   795  			maxAllowed)
   796  		if !reflect.DeepEqual(hashes, test.hashes) {
   797  			t.Errorf("%s: unxpected hashes -- got %v, want %v",
   798  				test.name, hashes, test.hashes)
   799  			continue
   800  		}
   801  	}
   802  }
   803  
   804  // TestHeightToHashRange ensures that fetching a range of block hashes by start
   805  // height and end hash works as expected.
   806  func TestHeightToHashRange(t *testing.T) {
   807  	// Construct a synthetic block chain with a block index consisting of
   808  	// the following structure.
   809  	// 	genesis -> 1 -> 2 -> ... -> 15 -> 16  -> 17  -> 18
   810  	// 	                              \-> 16a -> 17a -> 18a (unvalidated)
   811  	tip := tstTip
   812  	chain := newFakeChain(&chaincfg.MainNetParams)
   813  	branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18)
   814  	branch1Nodes := chainedNodes(branch0Nodes[14], 3)
   815  	for _, node := range branch0Nodes {
   816  		chain.index.SetStatusFlags(node, statusValid)
   817  		chain.index.AddNode(node)
   818  	}
   819  	for _, node := range branch1Nodes {
   820  		if node.height < 18 {
   821  			chain.index.SetStatusFlags(node, statusValid)
   822  		}
   823  		chain.index.AddNode(node)
   824  	}
   825  	chain.bestChain.SetTip(tip(branch0Nodes))
   826  
   827  	tests := []struct {
   828  		name        string
   829  		startHeight int32            // locator for requested inventory
   830  		endHash     chainhash.Hash   // stop hash for locator
   831  		maxResults  int              // max to locate, 0 = wire const
   832  		hashes      []chainhash.Hash // expected located hashes
   833  		expectError bool
   834  	}{
   835  		{
   836  			name:        "blocks below tip",
   837  			startHeight: 11,
   838  			endHash:     branch0Nodes[14].hash,
   839  			maxResults:  10,
   840  			hashes:      nodeHashes(branch0Nodes, 10, 11, 12, 13, 14),
   841  		},
   842  		{
   843  			name:        "blocks on main chain",
   844  			startHeight: 15,
   845  			endHash:     branch0Nodes[17].hash,
   846  			maxResults:  10,
   847  			hashes:      nodeHashes(branch0Nodes, 14, 15, 16, 17),
   848  		},
   849  		{
   850  			name:        "blocks on stale chain",
   851  			startHeight: 15,
   852  			endHash:     branch1Nodes[1].hash,
   853  			maxResults:  10,
   854  			hashes: append(nodeHashes(branch0Nodes, 14),
   855  				nodeHashes(branch1Nodes, 0, 1)...),
   856  		},
   857  		{
   858  			name:        "invalid start height",
   859  			startHeight: 19,
   860  			endHash:     branch0Nodes[17].hash,
   861  			maxResults:  10,
   862  			expectError: true,
   863  		},
   864  		{
   865  			name:        "too many results",
   866  			startHeight: 1,
   867  			endHash:     branch0Nodes[17].hash,
   868  			maxResults:  10,
   869  			expectError: true,
   870  		},
   871  		{
   872  			name:        "unvalidated block",
   873  			startHeight: 15,
   874  			endHash:     branch1Nodes[2].hash,
   875  			maxResults:  10,
   876  			expectError: true,
   877  		},
   878  	}
   879  	for _, test := range tests {
   880  		hashes, err := chain.HeightToHashRange(test.startHeight, &test.endHash,
   881  			test.maxResults)
   882  		if err != nil {
   883  			if !test.expectError {
   884  				t.Errorf("%s: unexpected error: %v", test.name, err)
   885  			}
   886  			continue
   887  		}
   888  
   889  		if !reflect.DeepEqual(hashes, test.hashes) {
   890  			t.Errorf("%s: unxpected hashes -- got %v, want %v",
   891  				test.name, hashes, test.hashes)
   892  		}
   893  	}
   894  }
   895  
   896  // TestIntervalBlockHashes ensures that fetching block hashes at specified
   897  // intervals by end hash works as expected.
   898  func TestIntervalBlockHashes(t *testing.T) {
   899  	// Construct a synthetic block chain with a block index consisting of
   900  	// the following structure.
   901  	// 	genesis -> 1 -> 2 -> ... -> 15 -> 16  -> 17  -> 18
   902  	// 	                              \-> 16a -> 17a -> 18a (unvalidated)
   903  	tip := tstTip
   904  	chain := newFakeChain(&chaincfg.MainNetParams)
   905  	branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18)
   906  	branch1Nodes := chainedNodes(branch0Nodes[14], 3)
   907  	for _, node := range branch0Nodes {
   908  		chain.index.SetStatusFlags(node, statusValid)
   909  		chain.index.AddNode(node)
   910  	}
   911  	for _, node := range branch1Nodes {
   912  		if node.height < 18 {
   913  			chain.index.SetStatusFlags(node, statusValid)
   914  		}
   915  		chain.index.AddNode(node)
   916  	}
   917  	chain.bestChain.SetTip(tip(branch0Nodes))
   918  
   919  	tests := []struct {
   920  		name        string
   921  		endHash     chainhash.Hash
   922  		interval    int
   923  		hashes      []chainhash.Hash
   924  		expectError bool
   925  	}{
   926  		{
   927  			name:     "blocks on main chain",
   928  			endHash:  branch0Nodes[17].hash,
   929  			interval: 8,
   930  			hashes:   nodeHashes(branch0Nodes, 7, 15),
   931  		},
   932  		{
   933  			name:     "blocks on stale chain",
   934  			endHash:  branch1Nodes[1].hash,
   935  			interval: 8,
   936  			hashes: append(nodeHashes(branch0Nodes, 7),
   937  				nodeHashes(branch1Nodes, 0)...),
   938  		},
   939  		{
   940  			name:     "no results",
   941  			endHash:  branch0Nodes[17].hash,
   942  			interval: 20,
   943  			hashes:   []chainhash.Hash{},
   944  		},
   945  		{
   946  			name:        "unvalidated block",
   947  			endHash:     branch1Nodes[2].hash,
   948  			interval:    8,
   949  			expectError: true,
   950  		},
   951  	}
   952  	for _, test := range tests {
   953  		hashes, err := chain.IntervalBlockHashes(&test.endHash, test.interval)
   954  		if err != nil {
   955  			if !test.expectError {
   956  				t.Errorf("%s: unexpected error: %v", test.name, err)
   957  			}
   958  			continue
   959  		}
   960  
   961  		if !reflect.DeepEqual(hashes, test.hashes) {
   962  			t.Errorf("%s: unxpected hashes -- got %v, want %v",
   963  				test.name, hashes, test.hashes)
   964  		}
   965  	}
   966  }