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