chain_test.go raw

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