github.com/decred/dcrd/blockchain@v1.2.1/chain_test.go (about) 1 // Copyright (c) 2013-2016 The btcsuite developers 2 // Copyright (c) 2015-2019 The Decred developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package blockchain 7 8 import ( 9 "bytes" 10 "compress/bzip2" 11 "encoding/gob" 12 "fmt" 13 "os" 14 "path/filepath" 15 "reflect" 16 "testing" 17 "time" 18 19 "github.com/decred/dcrd/blockchain/chaingen" 20 "github.com/decred/dcrd/chaincfg" 21 "github.com/decred/dcrd/chaincfg/chainhash" 22 "github.com/decred/dcrd/dcrutil" 23 "github.com/decred/dcrd/wire" 24 ) 25 26 // cloneParams returns a deep copy of the provided parameters so the caller is 27 // free to modify them without worrying about interfering with other tests. 28 func cloneParams(params *chaincfg.Params) *chaincfg.Params { 29 // Encode via gob. 30 buf := new(bytes.Buffer) 31 enc := gob.NewEncoder(buf) 32 enc.Encode(params) 33 34 // Decode via gob to make a deep copy. 35 var paramsCopy chaincfg.Params 36 dec := gob.NewDecoder(buf) 37 dec.Decode(¶msCopy) 38 return ¶msCopy 39 } 40 41 // TestBlockchainFunction tests the various blockchain API to ensure proper 42 // functionality. 43 func TestBlockchainFunctions(t *testing.T) { 44 // Update parameters to reflect what is expected by the legacy data. 45 params := cloneParams(&chaincfg.RegNetParams) 46 params.GenesisBlock.Header.MerkleRoot = *mustParseHash("a216ea043f0d481a072424af646787794c32bcefd3ed181a090319bbf8a37105") 47 params.GenesisBlock.Header.Timestamp = time.Unix(1401292357, 0) 48 params.GenesisBlock.Transactions[0].TxIn[0].ValueIn = 0 49 params.PubKeyHashAddrID = [2]byte{0x0e, 0x91} 50 params.StakeBaseSigScript = []byte{0xde, 0xad, 0xbe, 0xef} 51 params.OrganizationPkScript = hexToBytes("a914cbb08d6ca783b533b2c7d24a51fbca92d937bf9987") 52 params.BlockOneLedger = []*chaincfg.TokenPayout{ 53 {Address: "Sshw6S86G2bV6W32cbc7EhtFy8f93rU6pae", Amount: 100000 * 1e8}, 54 {Address: "SsjXRK6Xz6CFuBt6PugBvrkdAa4xGbcZ18w", Amount: 100000 * 1e8}, 55 {Address: "SsfXiYkYkCoo31CuVQw428N6wWKus2ZEw5X", Amount: 100000 * 1e8}, 56 } 57 genesisHash := params.GenesisBlock.BlockHash() 58 params.GenesisHash = &genesisHash 59 60 // Create a new database and chain instance to run tests against. 61 chain, teardownFunc, err := chainSetup("validateunittests", params) 62 if err != nil { 63 t.Errorf("Failed to setup chain instance: %v", err) 64 return 65 } 66 defer teardownFunc() 67 68 // Load up the rest of the blocks up to HEAD~1. 69 filename := filepath.Join("testdata", "blocks0to168.bz2") 70 fi, err := os.Open(filename) 71 if err != nil { 72 t.Errorf("Unable to open %s: %v", filename, err) 73 } 74 bcStream := bzip2.NewReader(fi) 75 defer fi.Close() 76 77 // Create a buffer of the read file. 78 bcBuf := new(bytes.Buffer) 79 bcBuf.ReadFrom(bcStream) 80 81 // Create decoder from the buffer and a map to store the data. 82 bcDecoder := gob.NewDecoder(bcBuf) 83 blockChain := make(map[int64][]byte) 84 85 // Decode the blockchain into the map. 86 if err := bcDecoder.Decode(&blockChain); err != nil { 87 t.Errorf("error decoding test blockchain: %v", err.Error()) 88 } 89 90 // Insert blocks 1 to 168 and perform various tests. 91 for i := 1; i <= 168; i++ { 92 bl, err := dcrutil.NewBlockFromBytes(blockChain[int64(i)]) 93 if err != nil { 94 t.Errorf("NewBlockFromBytes error: %v", err.Error()) 95 } 96 97 _, _, err = chain.ProcessBlock(bl, BFNone) 98 if err != nil { 99 t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error()) 100 } 101 } 102 103 val, err := chain.TicketPoolValue() 104 if err != nil { 105 t.Errorf("Failed to get ticket pool value: %v", err) 106 } 107 expectedVal := dcrutil.Amount(3495091704) 108 if val != expectedVal { 109 t.Errorf("Failed to get correct result for ticket pool value; "+ 110 "want %v, got %v", expectedVal, val) 111 } 112 113 a, _ := dcrutil.DecodeAddress("SsbKpMkPnadDcZFFZqRPY8nvdFagrktKuzB") 114 hs, err := chain.TicketsWithAddress(a) 115 if err != nil { 116 t.Errorf("Failed to do TicketsWithAddress: %v", err) 117 } 118 expectedLen := 223 119 if len(hs) != expectedLen { 120 t.Errorf("Failed to get correct number of tickets for "+ 121 "TicketsWithAddress; want %v, got %v", expectedLen, len(hs)) 122 } 123 124 totalSubsidy := chain.TotalSubsidy() 125 expectedSubsidy := int64(35783267326630) 126 if expectedSubsidy != totalSubsidy { 127 t.Errorf("Failed to get correct total subsidy for "+ 128 "TotalSubsidy; want %v, got %v", expectedSubsidy, 129 totalSubsidy) 130 } 131 } 132 133 // TestForceHeadReorg ensures forcing header reorganization works as expected. 134 func TestForceHeadReorg(t *testing.T) { 135 // Create a test harness initialized with the genesis block as the tip. 136 params := &chaincfg.RegNetParams 137 g, teardownFunc := newChaingenHarness(t, params, "forceheadreorgtest") 138 defer teardownFunc() 139 140 // Define some additional convenience helper functions to process the 141 // current tip block associated with the generator. 142 // 143 // rejectForceTipReorg forces the chain instance to reorganize the 144 // current tip of the main chain from the given block to the given 145 // block and expected it to be rejected with the provided error code. 146 rejectForceTipReorg := func(fromTipName, toTipName string, code ErrorCode) { 147 from := g.BlockByName(fromTipName) 148 to := g.BlockByName(toTipName) 149 t.Logf("Testing forced reorg from %s (hash %s, height %d) "+ 150 "to %s (hash %s, height %d)", fromTipName, 151 from.BlockHash(), from.Header.Height, toTipName, 152 to.BlockHash(), to.Header.Height) 153 154 err := g.chain.ForceHeadReorganization(from.BlockHash(), to.BlockHash()) 155 if err == nil { 156 t.Fatalf("forced header reorg from block %q (hash %s, "+ 157 "height %d) to block %q (hash %s, height %d) "+ 158 "should have failed", fromTipName, from.BlockHash(), 159 from.Header.Height, toTipName, to.BlockHash(), 160 to.Header.Height) 161 } 162 163 // Ensure the error code is of the expected type and the reject 164 // code matches the value specified in the test instance. 165 rerr, ok := err.(RuleError) 166 if !ok { 167 t.Fatalf("forced header reorg from block %q (hash %s, "+ 168 "height %d) to block %q (hash %s, height %d) "+ 169 "returned unexpected error type -- got %T, "+ 170 "want blockchain.RuleError", fromTipName, 171 from.BlockHash(), from.Header.Height, toTipName, 172 to.BlockHash(), to.Header.Height, err) 173 } 174 if rerr.ErrorCode != code { 175 t.Fatalf("forced header reorg from block %q (hash %s, "+ 176 "height %d) to block %q (hash %s, height %d) "+ 177 "does not have expected reject code -- got %v, "+ 178 "want %v", fromTipName, 179 from.BlockHash(), from.Header.Height, toTipName, 180 to.BlockHash(), to.Header.Height, rerr.ErrorCode, 181 code) 182 } 183 } 184 185 // Shorter versions of useful params for convenience. 186 coinbaseMaturity := params.CoinbaseMaturity 187 stakeValidationHeight := params.StakeValidationHeight 188 189 // --------------------------------------------------------------------- 190 // Generate and accept enough blocks to reach stake validation height. 191 // --------------------------------------------------------------------- 192 193 g.AdvanceToStakeValidationHeight() 194 195 // --------------------------------------------------------------------- 196 // Generate enough blocks to have a known distance to the first mature 197 // coinbase outputs for all tests that follow. These blocks continue 198 // to purchase tickets to avoid running out of votes. 199 // 200 // ... -> bsv# -> bbm0 -> bbm1 -> ... -> bbm# 201 // --------------------------------------------------------------------- 202 203 for i := uint16(0); i < coinbaseMaturity; i++ { 204 outs := g.OldestCoinbaseOuts() 205 blockName := fmt.Sprintf("bbm%d", i) 206 g.NextBlock(blockName, nil, outs[1:]) 207 g.SaveTipCoinbaseOuts() 208 g.AcceptTipBlock() 209 } 210 g.AssertTipHeight(uint32(stakeValidationHeight) + uint32(coinbaseMaturity)) 211 212 // Collect spendable outputs into two different slices. The outs slice 213 // is intended to be used for regular transactions that spend from the 214 // output, while the ticketOuts slice is intended to be used for stake 215 // ticket purchases. 216 var outs []*chaingen.SpendableOut 217 var ticketOuts [][]chaingen.SpendableOut 218 for i := uint16(0); i < coinbaseMaturity; i++ { 219 coinbaseOuts := g.OldestCoinbaseOuts() 220 outs = append(outs, &coinbaseOuts[0]) 221 ticketOuts = append(ticketOuts, coinbaseOuts[1:]) 222 } 223 224 // --------------------------------------------------------------------- 225 // Forced header reorganization test. 226 // --------------------------------------------------------------------- 227 228 // Start by building a block at current tip (value in parens is which 229 // output is spent): 230 // 231 // ... -> b1(0) 232 g.NextBlock("b1", outs[0], ticketOuts[0]) 233 g.AcceptTipBlock() 234 235 // Create a fork from b1 with an invalid block due to committing to an 236 // invalid number of votes. Since verifying the header commitment is a 237 // basic sanity check, no entry will be added to the block index. 238 // 239 // ... -> b1(0) -> b2(1) 240 // \-> b2bad0(1) 241 g.SetTip("b1") 242 g.NextBlock("b2bad0", outs[1], ticketOuts[1], func(b *wire.MsgBlock) { 243 b.Header.Voters++ 244 }) 245 g.RejectTipBlock(ErrTooManyVotes) 246 247 // Create a fork from b1 with an invalid block due to committing to an 248 // invalid input amount. Since verifying the fraud proof necessarily 249 // requires access to the inputs, this will trigger the failure late 250 // enough to ensure an entry is added to the block index. Further, 251 // since the block is attempting to extend the main chain it will also 252 // be fully checked and thus will be known invalid. 253 // 254 // ... -> b1(0) 255 // \-> b2bad1(1) 256 g.SetTip("b1") 257 g.NextBlock("b2bad1", outs[1], ticketOuts[1], func(b *wire.MsgBlock) { 258 b.Transactions[1].TxIn[0].ValueIn-- 259 }) 260 g.RejectTipBlock(ErrFraudAmountIn) 261 262 // Create some forks from b1. There should not be a reorg since b1 is 263 // the current tip and b2 is seen first. 264 // 265 // ... -> b1(0) -> b2(1) 266 // \-> b3(1) 267 // \-> b4(1) 268 // \-> b5(1) 269 // \-> b2bad0(1) 270 // \-> b2bad1(1) 271 g.SetTip("b1") 272 g.NextBlock("b2", outs[1], ticketOuts[1]) 273 g.AcceptTipBlock() 274 275 g.SetTip("b1") 276 g.NextBlock("b3", outs[1], ticketOuts[1]) 277 g.AcceptedToSideChainWithExpectedTip("b2") 278 279 g.SetTip("b1") 280 g.NextBlock("b4", outs[1], ticketOuts[1]) 281 g.AcceptedToSideChainWithExpectedTip("b2") 282 283 g.SetTip("b1") 284 g.NextBlock("b5", outs[1], ticketOuts[1]) 285 g.AcceptedToSideChainWithExpectedTip("b2") 286 287 // Create a fork from b1 with an invalid block due to committing to an 288 // invalid input amount. Since verifying the fraud proof necessarily 289 // requires access to the inputs, this will trigger the failure late 290 // enough to ensure an entry is added to the block index. Further, 291 // since the block is not attempting to extend the main chain it will 292 // not be fully checked and thus will not yet have a known validation 293 // status. 294 // 295 // ... -> b1(0) -> b2(1) 296 // \-> b3(1) 297 // \-> b4(1) 298 // \-> b5(1) 299 // \-> b2bad0(1) 300 // \-> b2bad1(1) 301 // \-> b2bad2(1) 302 g.SetTip("b1") 303 g.NextBlock("b2bad2", outs[1], ticketOuts[1], func(b *wire.MsgBlock) { 304 b.Transactions[1].TxIn[0].ValueIn-- 305 }) 306 g.AcceptedToSideChainWithExpectedTip("b2") 307 308 // Force tip reorganization to b3. 309 // 310 // ... -> b1(0) -> b3(1) 311 // \-> b2(1) 312 // \-> b4(1) 313 // \-> b5(1) 314 // \-> b2bad0(1) 315 // \-> b2bad1(1) 316 // \-> b2bad2(1) 317 g.SetTip("b1") 318 g.ForceTipReorg("b2", "b3") 319 g.ExpectTip("b3") 320 321 // Force tip reorganization to b4. 322 // 323 // ... -> b1(0) -> b4(1) 324 // \-> b2(1) 325 // \-> b3(1) 326 // \-> b5(1) 327 // \-> b2bad0(1) 328 // \-> b2bad1(1) 329 // \-> b2bad2(1) 330 g.ForceTipReorg("b3", "b4") 331 g.ExpectTip("b4") 332 333 // Force tip reorganization to b5. 334 // 335 // ... -> b1(0) -> b5(1) 336 // \-> b2(1) 337 // \-> b3(1) 338 // \-> b4(1) 339 // \-> b2bad0(1) 340 // \-> b2bad1(1) 341 // \-> b2bad2(1) 342 g.ForceTipReorg("b4", "b5") 343 g.ExpectTip("b5") 344 345 // Force tip reorganization back to b3 to ensure cached validation 346 // results are exercised. 347 // 348 // ... -> b1(0) -> b3(1) 349 // \-> b2(1) 350 // \-> b4(1) 351 // \-> b5(1) 352 // \-> b2bad0(1) 353 // \-> b2bad1(1) 354 // \-> b2bad2(1) 355 g.ForceTipReorg("b5", "b3") 356 g.ExpectTip("b3") 357 358 // Attempt to force tip reorganization from a block that is not the 359 // current tip. This should fail since that is not allowed. 360 // 361 // ... -> b1(0) -> b3(1) 362 // \-> b2(1) 363 // \-> b4(1) 364 // \-> b5(1) 365 // \-> b2bad0(1) 366 // \-> b2bad1(1) 367 // \-> b2bad2(1) 368 rejectForceTipReorg("b2", "b4", ErrForceReorgWrongChain) 369 g.ExpectTip("b3") 370 371 // Attempt to force tip reorganization to an invalid block that does 372 // not have an entry in the block index. 373 // 374 // ... -> b1(0) -> b3(1) 375 // \-> b2(1) 376 // \-> b4(1) 377 // \-> b5(1) 378 // \-> b2bad0(1) 379 // \-> b2bad1(1) 380 // \-> b2bad2(1) 381 rejectForceTipReorg("b3", "b2bad0", ErrForceReorgMissingChild) 382 g.ExpectTip("b3") 383 384 // Attempt to force tip reorganization to an invalid block that has an 385 // entry in the block index and is already known to be invalid. 386 // 387 // ... -> b1(0) -> b3(1) 388 // \-> b2(1) 389 // \-> b4(1) 390 // \-> b5(1) 391 // \-> b2bad0(1) 392 // \-> b2bad1(1) 393 // \-> b2bad2(1) 394 rejectForceTipReorg("b3", "b2bad1", ErrKnownInvalidBlock) 395 g.ExpectTip("b3") 396 397 // Attempt to force tip reorganization to an invalid block that has an 398 // entry in the block index, but is not already known to be invalid. 399 // 400 // 401 // ... -> b1(0) -> b3(1) 402 // \-> b2(1) 403 // \-> b4(1) 404 // \-> b5(1) 405 // \-> b2bad0(1) 406 // \-> b2bad1(1) 407 // \-> b2bad2(1) 408 rejectForceTipReorg("b3", "b2bad2", ErrFraudAmountIn) 409 g.ExpectTip("b3") 410 } 411 412 // locatorHashes is a convenience function that returns the hashes for all of 413 // the passed indexes of the provided nodes. It is used to construct expected 414 // block locators in the tests. 415 func locatorHashes(nodes []*blockNode, indexes ...int) BlockLocator { 416 hashes := make(BlockLocator, 0, len(indexes)) 417 for _, idx := range indexes { 418 hashes = append(hashes, &nodes[idx].hash) 419 } 420 return hashes 421 } 422 423 // nodeHashes is a convenience function that returns the hashes for all of the 424 // passed indexes of the provided nodes. It is used to construct expected hash 425 // slices in the tests. 426 func nodeHashes(nodes []*blockNode, indexes ...int) []chainhash.Hash { 427 hashes := make([]chainhash.Hash, 0, len(indexes)) 428 for _, idx := range indexes { 429 hashes = append(hashes, nodes[idx].hash) 430 } 431 return hashes 432 } 433 434 // nodeHeaders is a convenience function that returns the headers for all of 435 // the passed indexes of the provided nodes. It is used to construct expected 436 // located headers in the tests. 437 func nodeHeaders(nodes []*blockNode, indexes ...int) []wire.BlockHeader { 438 headers := make([]wire.BlockHeader, 0, len(indexes)) 439 for _, idx := range indexes { 440 headers = append(headers, nodes[idx].Header()) 441 } 442 return headers 443 } 444 445 // TestLocateInventory ensures that locating inventory via the LocateHeaders and 446 // LocateBlocks functions behaves as expected. 447 func TestLocateInventory(t *testing.T) { 448 // Construct a synthetic block chain with a block index consisting of 449 // the following structure. 450 // genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18 451 // \-> 16a -> 17a 452 tip := branchTip 453 chain := newFakeChain(&chaincfg.MainNetParams) 454 branch0Nodes := chainedFakeNodes(chain.bestChain.Genesis(), 18) 455 branch1Nodes := chainedFakeNodes(branch0Nodes[14], 2) 456 for _, node := range branch0Nodes { 457 chain.index.AddNode(node) 458 } 459 for _, node := range branch1Nodes { 460 chain.index.AddNode(node) 461 } 462 chain.bestChain.SetTip(tip(branch0Nodes)) 463 464 // Create chain views for different branches of the overall chain to 465 // simulate a local and remote node on different parts of the chain. 466 localView := newChainView(tip(branch0Nodes)) 467 remoteView := newChainView(tip(branch1Nodes)) 468 469 // Create a chain view for a completely unrelated block chain to 470 // simulate a remote node on a totally different chain. 471 unrelatedBranchNodes := chainedFakeNodes(nil, 5) 472 unrelatedView := newChainView(tip(unrelatedBranchNodes)) 473 474 tests := []struct { 475 name string 476 locator BlockLocator // locator for requested inventory 477 hashStop chainhash.Hash // stop hash for locator 478 maxAllowed uint32 // max to locate, 0 = wire const 479 headers []wire.BlockHeader // expected located headers 480 hashes []chainhash.Hash // expected located hashes 481 }{ 482 { 483 // Empty block locators and unknown stop hash. No 484 // inventory should be located. 485 name: "no locators, no stop", 486 locator: nil, 487 hashStop: chainhash.Hash{}, 488 headers: nil, 489 hashes: nil, 490 }, 491 { 492 // Empty block locators and stop hash in side chain. 493 // The expected result is the requested block. 494 name: "no locators, stop in side", 495 locator: nil, 496 hashStop: tip(branch1Nodes).hash, 497 headers: nodeHeaders(branch1Nodes, 1), 498 hashes: nodeHashes(branch1Nodes, 1), 499 }, 500 { 501 // Empty block locators and stop hash in main chain. 502 // The expected result is the requested block. 503 name: "no locators, stop in main", 504 locator: nil, 505 hashStop: branch0Nodes[12].hash, 506 headers: nodeHeaders(branch0Nodes, 12), 507 hashes: nodeHashes(branch0Nodes, 12), 508 }, 509 { 510 // Locators based on remote being on side chain and a 511 // stop hash local node doesn't know about. The 512 // expected result is the blocks after the fork point in 513 // the main chain and the stop hash has no effect. 514 name: "remote side chain, unknown stop", 515 locator: remoteView.BlockLocator(nil), 516 hashStop: chainhash.Hash{0x01}, 517 headers: nodeHeaders(branch0Nodes, 15, 16, 17), 518 hashes: nodeHashes(branch0Nodes, 15, 16, 17), 519 }, 520 { 521 // Locators based on remote being on side chain and a 522 // stop hash in side chain. The expected result is the 523 // blocks after the fork point in the main chain and the 524 // stop hash has no effect. 525 name: "remote side chain, stop in side", 526 locator: remoteView.BlockLocator(nil), 527 hashStop: tip(branch1Nodes).hash, 528 headers: nodeHeaders(branch0Nodes, 15, 16, 17), 529 hashes: nodeHashes(branch0Nodes, 15, 16, 17), 530 }, 531 { 532 // Locators based on remote being on side chain and a 533 // stop hash in main chain, but before fork point. The 534 // expected result is the blocks after the fork point in 535 // the main chain and the stop hash has no effect. 536 name: "remote side chain, stop in main before", 537 locator: remoteView.BlockLocator(nil), 538 hashStop: branch0Nodes[13].hash, 539 headers: nodeHeaders(branch0Nodes, 15, 16, 17), 540 hashes: nodeHashes(branch0Nodes, 15, 16, 17), 541 }, 542 { 543 // Locators based on remote being on side chain and a 544 // stop hash in main chain, but exactly at the fork 545 // point. The expected result is the blocks after the 546 // fork point in the main chain and the stop hash has no 547 // effect. 548 name: "remote side chain, stop in main exact", 549 locator: remoteView.BlockLocator(nil), 550 hashStop: branch0Nodes[14].hash, 551 headers: nodeHeaders(branch0Nodes, 15, 16, 17), 552 hashes: nodeHashes(branch0Nodes, 15, 16, 17), 553 }, 554 { 555 // Locators based on remote being on side chain and a 556 // stop hash in main chain just after the fork point. 557 // The expected result is the blocks after the fork 558 // point in the main chain up to and including the stop 559 // hash. 560 name: "remote side chain, stop in main after", 561 locator: remoteView.BlockLocator(nil), 562 hashStop: branch0Nodes[15].hash, 563 headers: nodeHeaders(branch0Nodes, 15), 564 hashes: nodeHashes(branch0Nodes, 15), 565 }, 566 { 567 // Locators based on remote being on side chain and a 568 // stop hash in main chain some time after the fork 569 // point. The expected result is the blocks after the 570 // fork point in the main chain up to and including the 571 // stop hash. 572 name: "remote side chain, stop in main after more", 573 locator: remoteView.BlockLocator(nil), 574 hashStop: branch0Nodes[16].hash, 575 headers: nodeHeaders(branch0Nodes, 15, 16), 576 hashes: nodeHashes(branch0Nodes, 15, 16), 577 }, 578 { 579 // Locators based on remote being on main chain in the 580 // past and a stop hash local node doesn't know about. 581 // The expected result is the blocks after the known 582 // point in the main chain and the stop hash has no 583 // effect. 584 name: "remote main chain past, unknown stop", 585 locator: localView.BlockLocator(branch0Nodes[12]), 586 hashStop: chainhash.Hash{0x01}, 587 headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17), 588 hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17), 589 }, 590 { 591 // Locators based on remote being on main chain in the 592 // past and a stop hash in a side chain. The expected 593 // result is the blocks after the known point in the 594 // main chain and the stop hash has no effect. 595 name: "remote main chain past, stop in side", 596 locator: localView.BlockLocator(branch0Nodes[12]), 597 hashStop: tip(branch1Nodes).hash, 598 headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17), 599 hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17), 600 }, 601 { 602 // Locators based on remote being on main chain in the 603 // past and a stop hash in the main chain before that 604 // point. The expected result is the blocks after the 605 // known point in the main chain and the stop hash has 606 // no effect. 607 name: "remote main chain past, stop in main before", 608 locator: localView.BlockLocator(branch0Nodes[12]), 609 hashStop: branch0Nodes[11].hash, 610 headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17), 611 hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17), 612 }, 613 { 614 // Locators based on remote being on main chain in the 615 // past and a stop hash in the main chain exactly at that 616 // point. The expected result is the blocks after the 617 // known point in the main chain and the stop hash has 618 // no effect. 619 name: "remote main chain past, stop in main exact", 620 locator: localView.BlockLocator(branch0Nodes[12]), 621 hashStop: branch0Nodes[12].hash, 622 headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17), 623 hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17), 624 }, 625 { 626 // Locators based on remote being on main chain in the 627 // past and a stop hash in the main chain just after 628 // that point. The expected result is the blocks after 629 // the known point in the main chain and the stop hash 630 // has no effect. 631 name: "remote main chain past, stop in main after", 632 locator: localView.BlockLocator(branch0Nodes[12]), 633 hashStop: branch0Nodes[13].hash, 634 headers: nodeHeaders(branch0Nodes, 13), 635 hashes: nodeHashes(branch0Nodes, 13), 636 }, 637 { 638 // Locators based on remote being on main chain in the 639 // past and a stop hash in the main chain some time 640 // after that point. The expected result is the blocks 641 // after the known point in the main chain and the stop 642 // hash has no effect. 643 name: "remote main chain past, stop in main after more", 644 locator: localView.BlockLocator(branch0Nodes[12]), 645 hashStop: branch0Nodes[15].hash, 646 headers: nodeHeaders(branch0Nodes, 13, 14, 15), 647 hashes: nodeHashes(branch0Nodes, 13, 14, 15), 648 }, 649 { 650 // Locators based on remote being at exactly the same 651 // point in the main chain and a stop hash local node 652 // doesn't know about. The expected result is no 653 // located inventory. 654 name: "remote main chain same, unknown stop", 655 locator: localView.BlockLocator(nil), 656 hashStop: chainhash.Hash{0x01}, 657 headers: nil, 658 hashes: nil, 659 }, 660 { 661 // Locators based on remote being at exactly the same 662 // point in the main chain and a stop hash at exactly 663 // the same point. The expected result is no located 664 // inventory. 665 name: "remote main chain same, stop same point", 666 locator: localView.BlockLocator(nil), 667 hashStop: tip(branch0Nodes).hash, 668 headers: nil, 669 hashes: nil, 670 }, 671 { 672 // Locators from remote that don't include any blocks 673 // the local node knows. This would happen if the 674 // remote node is on a completely separate chain that 675 // isn't rooted with the same genesis block. The 676 // expected result is the blocks after the genesis 677 // block. 678 name: "remote unrelated chain", 679 locator: unrelatedView.BlockLocator(nil), 680 hashStop: chainhash.Hash{}, 681 headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6, 682 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), 683 hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6, 684 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), 685 }, 686 { 687 // Locators from remote for second block in main chain 688 // and no stop hash, but with an overridden max limit. 689 // The expected result is the blocks after the second 690 // block limited by the max. 691 name: "remote genesis", 692 locator: locatorHashes(branch0Nodes, 0), 693 hashStop: chainhash.Hash{}, 694 maxAllowed: 3, 695 headers: nodeHeaders(branch0Nodes, 1, 2, 3), 696 hashes: nodeHashes(branch0Nodes, 1, 2, 3), 697 }, 698 { 699 // Poorly formed locator. 700 // 701 // Locator from remote that only includes a single 702 // block on a side chain the local node knows. The 703 // expected result is the blocks after the genesis 704 // block since even though the block is known, it is on 705 // a side chain and there are no more locators to find 706 // the fork point. 707 name: "weak locator, single known side block", 708 locator: locatorHashes(branch1Nodes, 1), 709 hashStop: chainhash.Hash{}, 710 headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6, 711 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), 712 hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6, 713 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), 714 }, 715 { 716 // Poorly formed locator. 717 // 718 // Locator from remote that only includes multiple 719 // blocks on a side chain the local node knows however 720 // none in the main chain. The expected result is the 721 // blocks after the genesis block since even though the 722 // blocks are known, they are all on a side chain and 723 // there are no more locators to find the fork point. 724 name: "weak locator, multiple known side blocks", 725 locator: locatorHashes(branch1Nodes, 1), 726 hashStop: chainhash.Hash{}, 727 headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6, 728 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), 729 hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6, 730 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), 731 }, 732 { 733 // Poorly formed locator. 734 // 735 // Locator from remote that only includes multiple 736 // blocks on a side chain the local node knows however 737 // none in the main chain but includes a stop hash in 738 // the main chain. The expected result is the blocks 739 // after the genesis block up to the stop hash since 740 // even though the blocks are known, they are all on a 741 // side chain and there are no more locators to find the 742 // fork point. 743 name: "weak locator, multiple known side blocks, stop in main", 744 locator: locatorHashes(branch1Nodes, 1), 745 hashStop: branch0Nodes[5].hash, 746 headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5), 747 hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5), 748 }, 749 } 750 for _, test := range tests { 751 // Ensure the expected headers are located. 752 var headers []wire.BlockHeader 753 if test.maxAllowed != 0 { 754 // Need to use the unexported function to override the 755 // max allowed for headers. 756 chain.chainLock.RLock() 757 headers = chain.locateHeaders(test.locator, 758 &test.hashStop, test.maxAllowed) 759 chain.chainLock.RUnlock() 760 } else { 761 headers = chain.LocateHeaders(test.locator, 762 &test.hashStop) 763 } 764 if !reflect.DeepEqual(headers, test.headers) { 765 t.Errorf("%s: unxpected headers -- got %v, want %v", 766 test.name, headers, test.headers) 767 continue 768 } 769 770 // Ensure the expected block hashes are located. 771 maxAllowed := uint32(wire.MaxBlocksPerMsg) 772 if test.maxAllowed != 0 { 773 maxAllowed = test.maxAllowed 774 } 775 hashes := chain.LocateBlocks(test.locator, &test.hashStop, 776 maxAllowed) 777 if !reflect.DeepEqual(hashes, test.hashes) { 778 t.Errorf("%s: unxpected hashes -- got %v, want %v", 779 test.name, hashes, test.hashes) 780 continue 781 } 782 } 783 }