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