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 }