github.com/decred/dcrd/blockchain@v1.2.1/validate_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 mrand "math/rand" 14 "os" 15 "path/filepath" 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/database" 23 "github.com/decred/dcrd/dcrutil" 24 "github.com/decred/dcrd/wire" 25 ) 26 27 // TestBlockchainSpendJournal tests for whether or not the spend journal is being 28 // written to disk correctly on a live blockchain. 29 func TestBlockchainSpendJournal(t *testing.T) { 30 // Update parameters to reflect what is expected by the legacy data. 31 params := cloneParams(&chaincfg.RegNetParams) 32 params.GenesisBlock.Header.MerkleRoot = *mustParseHash("a216ea043f0d481a072424af646787794c32bcefd3ed181a090319bbf8a37105") 33 params.GenesisBlock.Header.Timestamp = time.Unix(1401292357, 0) 34 params.GenesisBlock.Transactions[0].TxIn[0].ValueIn = 0 35 params.PubKeyHashAddrID = [2]byte{0x0e, 0x91} 36 params.StakeBaseSigScript = []byte{0xde, 0xad, 0xbe, 0xef} 37 params.OrganizationPkScript = hexToBytes("a914cbb08d6ca783b533b2c7d24a51fbca92d937bf9987") 38 params.BlockOneLedger = []*chaincfg.TokenPayout{ 39 {Address: "Sshw6S86G2bV6W32cbc7EhtFy8f93rU6pae", Amount: 100000 * 1e8}, 40 {Address: "SsjXRK6Xz6CFuBt6PugBvrkdAa4xGbcZ18w", Amount: 100000 * 1e8}, 41 {Address: "SsfXiYkYkCoo31CuVQw428N6wWKus2ZEw5X", Amount: 100000 * 1e8}, 42 } 43 genesisHash := params.GenesisBlock.BlockHash() 44 params.GenesisHash = &genesisHash 45 46 // Create a new database and chain instance to run tests against. 47 chain, teardownFunc, err := chainSetup("spendjournalunittest", params) 48 if err != nil { 49 t.Errorf("Failed to setup chain instance: %v", err) 50 return 51 } 52 defer teardownFunc() 53 54 // Load up the rest of the blocks up to HEAD. 55 filename := filepath.Join("testdata", "reorgto179.bz2") 56 fi, err := os.Open(filename) 57 if err != nil { 58 t.Errorf("Failed to open %s: %v", filename, err) 59 } 60 bcStream := bzip2.NewReader(fi) 61 defer fi.Close() 62 63 // Create a buffer of the read file 64 bcBuf := new(bytes.Buffer) 65 bcBuf.ReadFrom(bcStream) 66 67 // Create decoder from the buffer and a map to store the data 68 bcDecoder := gob.NewDecoder(bcBuf) 69 blockChain := make(map[int64][]byte) 70 71 // Decode the blockchain into the map 72 if err := bcDecoder.Decode(&blockChain); err != nil { 73 t.Errorf("error decoding test blockchain: %v", err.Error()) 74 } 75 76 // Load up the short chain 77 finalIdx1 := 179 78 for i := 1; i < finalIdx1+1; i++ { 79 bl, err := dcrutil.NewBlockFromBytes(blockChain[int64(i)]) 80 if err != nil { 81 t.Fatalf("NewBlockFromBytes error: %v", err.Error()) 82 } 83 84 forkLen, isOrphan, err := chain.ProcessBlock(bl, BFNone) 85 if err != nil { 86 t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error()) 87 } 88 isMainChain := !isOrphan && forkLen == 0 89 if !isMainChain { 90 t.Fatalf("block %s (height %d) should have been "+ 91 "accepted to the main chain", bl.Hash(), 92 bl.MsgBlock().Header.Height) 93 } 94 } 95 96 // Loop through all of the blocks and ensure the number of spent outputs 97 // matches up with the information loaded from the spend journal. 98 err = chain.db.View(func(dbTx database.Tx) error { 99 for i := int64(2); i <= chain.bestChain.Tip().height; i++ { 100 node := chain.bestChain.NodeByHeight(i) 101 if node == nil { 102 str := fmt.Sprintf("no block at height %d exists", i) 103 return errNotInMainChain(str) 104 } 105 block, err := dbFetchBlockByNode(dbTx, node) 106 if err != nil { 107 return err 108 } 109 110 ntx := countSpentOutputs(block) 111 stxos, err := dbFetchSpendJournalEntry(dbTx, block) 112 if err != nil { 113 return err 114 } 115 116 if ntx != len(stxos) { 117 return fmt.Errorf("bad number of stxos "+ 118 "calculated at "+"height %v, got %v "+ 119 "expected %v", i, len(stxos), ntx) 120 } 121 } 122 123 return nil 124 }) 125 if err != nil { 126 t.Errorf("unexpected error: %v", err) 127 } 128 } 129 130 // TestSequenceLocksActive ensure the sequence locks are detected as active or 131 // not as expected in all possible scenarios. 132 func TestSequenceLocksActive(t *testing.T) { 133 now := time.Now().Unix() 134 tests := []struct { 135 name string 136 seqLockHeight int64 137 seqLockTime int64 138 blockHeight int64 139 medianTime int64 140 want bool 141 }{ 142 { 143 // Block based sequence lock with height at min 144 // required. 145 name: "min active block height", 146 seqLockHeight: 1000, 147 seqLockTime: -1, 148 blockHeight: 1001, 149 medianTime: now + 31, 150 want: true, 151 }, 152 { 153 // Time based sequence lock with relative time at min 154 // required. 155 name: "min active median time", 156 seqLockHeight: -1, 157 seqLockTime: now + 30, 158 blockHeight: 1001, 159 medianTime: now + 31, 160 want: true, 161 }, 162 { 163 // Block based sequence lock at same height. 164 name: "same height", 165 seqLockHeight: 1000, 166 seqLockTime: -1, 167 blockHeight: 1000, 168 medianTime: now + 31, 169 want: false, 170 }, 171 { 172 // Time based sequence lock with relative time equal to 173 // lock time. 174 name: "same median time", 175 seqLockHeight: -1, 176 seqLockTime: now + 30, 177 blockHeight: 1001, 178 medianTime: now + 30, 179 want: false, 180 }, 181 { 182 // Block based sequence lock with relative height below 183 // required. 184 name: "height below required", 185 seqLockHeight: 1000, 186 seqLockTime: -1, 187 blockHeight: 999, 188 medianTime: now + 31, 189 want: false, 190 }, 191 { 192 // Time based sequence lock with relative time before 193 // required. 194 name: "median time before required", 195 seqLockHeight: -1, 196 seqLockTime: now + 30, 197 blockHeight: 1001, 198 medianTime: now + 29, 199 want: false, 200 }, 201 } 202 203 for _, test := range tests { 204 seqLock := SequenceLock{ 205 MinHeight: test.seqLockHeight, 206 MinTime: test.seqLockTime, 207 } 208 got := SequenceLockActive(&seqLock, test.blockHeight, 209 time.Unix(test.medianTime, 0)) 210 if got != test.want { 211 t.Errorf("%s: mismatched seqence lock status - got %v, "+ 212 "want %v", test.name, got, test.want) 213 continue 214 } 215 } 216 } 217 218 // quickVoteActivationParams returns a set of test chain parameters which allow 219 // for quicker vote activation as compared to various existing network params by 220 // reducing the required maturities, the ticket pool size, the stake enabled and 221 // validation heights, the proof-of-work block version upgrade window, the stake 222 // version interval, and the rule change activation interval. 223 func quickVoteActivationParams() *chaincfg.Params { 224 params := cloneParams(&chaincfg.RegNetParams) 225 params.WorkDiffWindowSize = 200000 226 params.WorkDiffWindows = 1 227 params.TargetTimespan = params.TargetTimePerBlock * 228 time.Duration(params.WorkDiffWindowSize) 229 params.CoinbaseMaturity = 2 230 params.BlockEnforceNumRequired = 5 231 params.BlockRejectNumRequired = 7 232 params.BlockUpgradeNumToCheck = 10 233 params.TicketMaturity = 2 234 params.TicketPoolSize = 4 235 params.TicketExpiry = 6 * uint32(params.TicketPoolSize) 236 params.StakeEnabledHeight = int64(params.CoinbaseMaturity) + 237 int64(params.TicketMaturity) 238 params.StakeValidationHeight = int64(params.CoinbaseMaturity) + 239 int64(params.TicketPoolSize)*2 240 params.StakeVersionInterval = 10 241 params.RuleChangeActivationInterval = uint32(params.TicketPoolSize) * 242 uint32(params.TicketsPerBlock) 243 params.RuleChangeActivationQuorum = params.RuleChangeActivationInterval * 244 uint32(params.TicketsPerBlock*100) / 1000 245 246 return params 247 } 248 249 // TestLegacySequenceLocks ensure that sequence locks within blocks behave as 250 // expected according to the legacy semantics in previous version of the 251 // software. 252 func TestLegacySequenceLocks(t *testing.T) { 253 // Use a set of test chain parameters which allow for quicker vote 254 // activation as compared to various existing network params. 255 params := quickVoteActivationParams() 256 257 // Clone the parameters so they can be mutated, find the correct deployment 258 // for the LN features agenda, and, finally, ensure it is always available 259 // to vote by removing the time constraints to prevent test failures when 260 // the real expiration time passes. 261 const lnfVoteID = chaincfg.VoteIDLNFeatures 262 params = cloneParams(params) 263 lnfVersion, deployment, err := findDeployment(params, lnfVoteID) 264 if err != nil { 265 t.Fatal(err) 266 } 267 removeDeploymentTimeConstraints(deployment) 268 269 // Create a test harness initialized with the genesis block as the tip. 270 g, teardownFunc := newChaingenHarness(t, params, "seqlocksoldsemanticstest") 271 defer teardownFunc() 272 273 // replaceLNFeaturesVersions is a munge function which modifies the provided 274 // block by replacing the block, stake, and vote versions with the LN 275 // features deployment version. 276 replaceLNFeaturesVersions := func(b *wire.MsgBlock) { 277 chaingen.ReplaceBlockVersion(int32(lnfVersion))(b) 278 chaingen.ReplaceStakeVersion(lnfVersion)(b) 279 chaingen.ReplaceVoteVersions(lnfVersion)(b) 280 } 281 282 // --------------------------------------------------------------------- 283 // Generate and accept enough blocks with the appropriate vote bits set 284 // to reach one block prior to the LN features agenda becoming active. 285 // --------------------------------------------------------------------- 286 287 g.AdvanceToStakeValidationHeight() 288 g.AdvanceFromSVHToActiveAgenda(lnfVoteID) 289 290 // --------------------------------------------------------------------- 291 // Perform a series of sequence lock tests now that ln feature 292 // enforcement is active. 293 // --------------------------------------------------------------------- 294 295 // enableSeqLocks modifies the passed transaction to enable sequence locks 296 // for the provided input. 297 enableSeqLocks := func(tx *wire.MsgTx, txInIdx int) { 298 tx.Version = 2 299 tx.TxIn[txInIdx].Sequence = 0 300 } 301 302 // --------------------------------------------------------------------- 303 // Create block that has a transaction with an input shared with a 304 // transaction in the stake tree and has several outputs used in 305 // subsequent blocks. Also, enable sequence locks for the first of 306 // those outputs. 307 // 308 // ... -> b0 309 // --------------------------------------------------------------------- 310 311 outs := g.OldestCoinbaseOuts() 312 b0 := g.NextBlock("b0", &outs[0], outs[1:], replaceLNFeaturesVersions, 313 func(b *wire.MsgBlock) { 314 // Save the current outputs of the spend tx and clear them. 315 tx := b.Transactions[1] 316 origOut := tx.TxOut[0] 317 origOpReturnOut := tx.TxOut[1] 318 tx.TxOut = tx.TxOut[:0] 319 320 // Evenly split the original output amount over multiple outputs. 321 const numOutputs = 6 322 amount := origOut.Value / numOutputs 323 for i := 0; i < numOutputs; i++ { 324 if i == numOutputs-1 { 325 amount = origOut.Value - amount*(numOutputs-1) 326 } 327 tx.AddTxOut(wire.NewTxOut(amount, origOut.PkScript)) 328 } 329 330 // Add the original op return back to the outputs and enable 331 // sequence locks for the first output. 332 tx.AddTxOut(origOpReturnOut) 333 enableSeqLocks(tx, 0) 334 }) 335 g.SaveTipCoinbaseOuts() 336 g.AcceptTipBlock() 337 338 // --------------------------------------------------------------------- 339 // Create block that spends from an output created in the previous 340 // block. 341 // 342 // ... -> b0 -> b1a 343 // --------------------------------------------------------------------- 344 345 outs = g.OldestCoinbaseOuts() 346 g.NextBlock("b1a", nil, outs[1:], replaceLNFeaturesVersions, 347 func(b *wire.MsgBlock) { 348 spend := chaingen.MakeSpendableOut(b0, 1, 0) 349 tx := g.CreateSpendTx(&spend, dcrutil.Amount(1)) 350 enableSeqLocks(tx, 0) 351 b.AddTransaction(tx) 352 }) 353 g.AcceptTipBlock() 354 355 // --------------------------------------------------------------------- 356 // Create block that involves reorganize to a sequence lock spending 357 // from an output created in a block prior to the parent also spent on 358 // on the side chain. 359 // 360 // ... -> b0 -> b1 -> b2 361 // \-> b1a 362 // --------------------------------------------------------------------- 363 g.SetTip("b0") 364 g.NextBlock("b1", nil, outs[1:], replaceLNFeaturesVersions) 365 g.SaveTipCoinbaseOuts() 366 g.AcceptedToSideChainWithExpectedTip("b1a") 367 368 outs = g.OldestCoinbaseOuts() 369 g.NextBlock("b2", nil, outs[1:], replaceLNFeaturesVersions, 370 func(b *wire.MsgBlock) { 371 spend := chaingen.MakeSpendableOut(b0, 1, 0) 372 tx := g.CreateSpendTx(&spend, dcrutil.Amount(1)) 373 enableSeqLocks(tx, 0) 374 b.AddTransaction(tx) 375 }) 376 g.SaveTipCoinbaseOuts() 377 g.AcceptTipBlock() 378 g.ExpectTip("b2") 379 380 // --------------------------------------------------------------------- 381 // Create block that involves a sequence lock on a vote. 382 // 383 // ... -> b2 -> b3 384 // --------------------------------------------------------------------- 385 386 outs = g.OldestCoinbaseOuts() 387 g.NextBlock("b3", nil, outs[1:], replaceLNFeaturesVersions, 388 func(b *wire.MsgBlock) { 389 enableSeqLocks(b.STransactions[0], 0) 390 }) 391 g.SaveTipCoinbaseOuts() 392 g.AcceptTipBlock() 393 394 // --------------------------------------------------------------------- 395 // Create block that involves a sequence lock on a ticket. 396 // 397 // ... -> b3 -> b4 398 // --------------------------------------------------------------------- 399 400 outs = g.OldestCoinbaseOuts() 401 g.NextBlock("b4", nil, outs[1:], replaceLNFeaturesVersions, 402 func(b *wire.MsgBlock) { 403 enableSeqLocks(b.STransactions[5], 0) 404 }) 405 g.SaveTipCoinbaseOuts() 406 g.AcceptTipBlock() 407 408 // --------------------------------------------------------------------- 409 // Create two blocks such that the tip block involves a sequence lock 410 // spending from a different output of a transaction the parent block 411 // also spends from. 412 // 413 // ... -> b4 -> b5 -> b6 414 // --------------------------------------------------------------------- 415 416 outs = g.OldestCoinbaseOuts() 417 g.NextBlock("b5", nil, outs[1:], replaceLNFeaturesVersions, 418 func(b *wire.MsgBlock) { 419 spend := chaingen.MakeSpendableOut(b0, 1, 1) 420 tx := g.CreateSpendTx(&spend, dcrutil.Amount(1)) 421 b.AddTransaction(tx) 422 }) 423 g.SaveTipCoinbaseOuts() 424 g.AcceptTipBlock() 425 426 outs = g.OldestCoinbaseOuts() 427 g.NextBlock("b6", nil, outs[1:], replaceLNFeaturesVersions, 428 func(b *wire.MsgBlock) { 429 spend := chaingen.MakeSpendableOut(b0, 1, 2) 430 tx := g.CreateSpendTx(&spend, dcrutil.Amount(1)) 431 enableSeqLocks(tx, 0) 432 b.AddTransaction(tx) 433 }) 434 g.SaveTipCoinbaseOuts() 435 g.AcceptTipBlock() 436 437 // --------------------------------------------------------------------- 438 // Create block that involves a sequence lock spending from a regular 439 // tree transaction earlier in the block. It should be rejected due 440 // to a consensus bug. 441 // 442 // ... -> b6 443 // \-> b7 444 // --------------------------------------------------------------------- 445 446 outs = g.OldestCoinbaseOuts() 447 g.NextBlock("b7", &outs[0], outs[1:], replaceLNFeaturesVersions, 448 func(b *wire.MsgBlock) { 449 spend := chaingen.MakeSpendableOut(b, 1, 0) 450 tx := g.CreateSpendTx(&spend, dcrutil.Amount(1)) 451 enableSeqLocks(tx, 0) 452 b.AddTransaction(tx) 453 }) 454 g.RejectTipBlock(ErrMissingTxOut) 455 456 // --------------------------------------------------------------------- 457 // Create block that involves a sequence lock spending from a block 458 // prior to the parent. It should be rejected due to a consensus bug. 459 // 460 // ... -> b6 -> b8 461 // \-> b9 462 // --------------------------------------------------------------------- 463 464 g.SetTip("b6") 465 g.NextBlock("b8", nil, outs[1:], replaceLNFeaturesVersions) 466 g.SaveTipCoinbaseOuts() 467 g.AcceptTipBlock() 468 469 outs = g.OldestCoinbaseOuts() 470 g.NextBlock("b9", nil, outs[1:], replaceLNFeaturesVersions, 471 func(b *wire.MsgBlock) { 472 spend := chaingen.MakeSpendableOut(b0, 1, 3) 473 tx := g.CreateSpendTx(&spend, dcrutil.Amount(1)) 474 enableSeqLocks(tx, 0) 475 b.AddTransaction(tx) 476 }) 477 g.RejectTipBlock(ErrMissingTxOut) 478 479 // --------------------------------------------------------------------- 480 // Create two blocks such that the tip block involves a sequence lock 481 // spending from a different output of a transaction the parent block 482 // also spends from when the parent block has been disapproved. It 483 // should be rejected due to a consensus bug. 484 // 485 // ... -> b8 -> b10 486 // \-> b11 487 // --------------------------------------------------------------------- 488 489 const ( 490 // vbDisapprovePrev and vbApprovePrev represent no and yes votes, 491 // respectively, on whether or not to approve the previous block. 492 vbDisapprovePrev = 0x0000 493 vbApprovePrev = 0x0001 494 ) 495 496 g.SetTip("b8") 497 g.NextBlock("b10", nil, outs[1:], replaceLNFeaturesVersions, 498 func(b *wire.MsgBlock) { 499 spend := chaingen.MakeSpendableOut(b0, 1, 4) 500 tx := g.CreateSpendTx(&spend, dcrutil.Amount(1)) 501 b.AddTransaction(tx) 502 }) 503 g.SaveTipCoinbaseOuts() 504 g.AcceptTipBlock() 505 506 outs = g.OldestCoinbaseOuts() 507 g.NextBlock("b11", nil, outs[1:], replaceLNFeaturesVersions, 508 chaingen.ReplaceVotes(vbDisapprovePrev, lnfVersion), 509 func(b *wire.MsgBlock) { 510 b.Header.VoteBits &^= vbApprovePrev 511 spend := chaingen.MakeSpendableOut(b0, 1, 5) 512 tx := g.CreateSpendTx(&spend, dcrutil.Amount(1)) 513 enableSeqLocks(tx, 0) 514 b.AddTransaction(tx) 515 }) 516 g.RejectTipBlock(ErrMissingTxOut) 517 } 518 519 // TestCheckBlockSanity tests the context free block sanity checks with blocks 520 // not on a chain. 521 func TestCheckBlockSanity(t *testing.T) { 522 params := &chaincfg.RegNetParams 523 timeSource := NewMedianTime() 524 block := dcrutil.NewBlock(&badBlock) 525 err := CheckBlockSanity(block, timeSource, params) 526 if err == nil { 527 t.Fatalf("block should fail.\n") 528 } 529 } 530 531 // TestCheckBlockHeaderContext tests that genesis block passes context headers 532 // because its parent is nil. 533 func TestCheckBlockHeaderContext(t *testing.T) { 534 // Create a new database for the blocks. 535 params := &chaincfg.RegNetParams 536 dbPath := filepath.Join(os.TempDir(), "examplecheckheadercontext") 537 _ = os.RemoveAll(dbPath) 538 db, err := database.Create("ffldb", dbPath, params.Net) 539 if err != nil { 540 t.Fatalf("Failed to create database: %v\n", err) 541 return 542 } 543 defer os.RemoveAll(dbPath) 544 defer db.Close() 545 546 // Create a new BlockChain instance using the underlying database for 547 // the simnet network. 548 chain, err := New(&Config{ 549 DB: db, 550 ChainParams: params, 551 TimeSource: NewMedianTime(), 552 }) 553 if err != nil { 554 t.Fatalf("Failed to create chain instance: %v\n", err) 555 return 556 } 557 558 err = chain.checkBlockHeaderContext(¶ms.GenesisBlock.Header, nil, BFNone) 559 if err != nil { 560 t.Fatalf("genesisblock should pass just by definition: %v\n", err) 561 return 562 } 563 564 // Test failing checkBlockHeaderContext when calcNextRequiredDifficulty 565 // fails. 566 block := dcrutil.NewBlock(&badBlock) 567 newNode := newBlockNode(&block.MsgBlock().Header, nil) 568 err = chain.checkBlockHeaderContext(&block.MsgBlock().Header, newNode, BFNone) 569 if err == nil { 570 t.Fatalf("Should fail due to bad diff in newNode\n") 571 return 572 } 573 } 574 575 // TestTxValidationErrors ensures certain malformed freestanding transactions 576 // are rejected as as expected. 577 func TestTxValidationErrors(t *testing.T) { 578 // Create a transaction that is too large 579 tx := wire.NewMsgTx() 580 prevOut := wire.NewOutPoint(&chainhash.Hash{0x01}, 0, wire.TxTreeRegular) 581 tx.AddTxIn(wire.NewTxIn(prevOut, 0, nil)) 582 pkScript := bytes.Repeat([]byte{0x00}, wire.MaxBlockPayload) 583 tx.AddTxOut(wire.NewTxOut(0, pkScript)) 584 585 // Assert the transaction is larger than the max allowed size. 586 txSize := tx.SerializeSize() 587 if txSize <= wire.MaxBlockPayload { 588 t.Fatalf("generated transaction is not large enough -- got "+ 589 "%d, want > %d", txSize, wire.MaxBlockPayload) 590 } 591 592 // Ensure transaction is rejected due to being too large. 593 err := CheckTransactionSanity(tx, &chaincfg.MainNetParams) 594 rerr, ok := err.(RuleError) 595 if !ok { 596 t.Fatalf("CheckTransactionSanity: unexpected error type for "+ 597 "transaction that is too large -- got %T", err) 598 } 599 if rerr.ErrorCode != ErrTxTooBig { 600 t.Fatalf("CheckTransactionSanity: unexpected error code for "+ 601 "transaction that is too large -- got %v, want %v", 602 rerr.ErrorCode, ErrTxTooBig) 603 } 604 } 605 606 // badBlock is an intentionally bad block that should fail the context-less 607 // sanity checks. 608 var badBlock = wire.MsgBlock{ 609 Header: wire.BlockHeader{ 610 Version: 1, 611 MerkleRoot: chaincfg.RegNetParams.GenesisBlock.Header.MerkleRoot, 612 VoteBits: uint16(0x0000), 613 FinalState: [6]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 614 Voters: uint16(0x0000), 615 FreshStake: uint8(0x00), 616 Revocations: uint8(0x00), 617 Timestamp: time.Unix(1401292357, 0), // 2009-01-08 20:54:25 -0600 CST 618 PoolSize: uint32(0), 619 Bits: 0x207fffff, // 545259519 620 SBits: int64(0x0000000000000000), 621 Nonce: 0x37580963, 622 StakeVersion: uint32(0), 623 Height: uint32(0), 624 }, 625 Transactions: []*wire.MsgTx{}, 626 STransactions: []*wire.MsgTx{}, 627 } 628 629 // TestCheckConnectBlockTemplate ensures that the code which deals with 630 // checking block templates works as expected. 631 func TestCheckConnectBlockTemplate(t *testing.T) { 632 // Create a test harness initialized with the genesis block as the tip. 633 params := &chaincfg.RegNetParams 634 g, teardownFunc := newChaingenHarness(t, params, "connectblktemplatetest") 635 defer teardownFunc() 636 637 // Define some additional convenience helper functions to process the 638 // current tip block associated with the generator. 639 // 640 // acceptedBlockTemplate expected the block to considered a valid block 641 // template. 642 // 643 // rejectedBlockTemplate expects the block to be considered an invalid 644 // block template due to the provided error code. 645 acceptedBlockTemplate := func() { 646 msgBlock := g.Tip() 647 blockHeight := msgBlock.Header.Height 648 block := dcrutil.NewBlock(msgBlock) 649 t.Logf("Testing block template %s (hash %s, height %d)", 650 g.TipName(), block.Hash(), blockHeight) 651 652 err := g.chain.CheckConnectBlockTemplate(block) 653 if err != nil { 654 t.Fatalf("block template %q (hash %s, height %d) should "+ 655 "have been accepted: %v", g.TipName(), 656 block.Hash(), blockHeight, err) 657 } 658 } 659 rejectedBlockTemplate := func(code ErrorCode) { 660 msgBlock := g.Tip() 661 blockHeight := msgBlock.Header.Height 662 block := dcrutil.NewBlock(msgBlock) 663 t.Logf("Testing block template %s (hash %s, height %d)", 664 g.TipName(), block.Hash(), blockHeight) 665 666 err := g.chain.CheckConnectBlockTemplate(block) 667 if err == nil { 668 t.Fatalf("block template %q (hash %s, height %d) should "+ 669 "not have been accepted", g.TipName(), block.Hash(), 670 blockHeight) 671 } 672 673 // Ensure the error code is of the expected type and the reject 674 // code matches the value specified in the test instance. 675 rerr, ok := err.(RuleError) 676 if !ok { 677 t.Fatalf("block template %q (hash %s, height %d) "+ 678 "returned unexpected error type -- got %T, want "+ 679 "blockchain.RuleError", g.TipName(), 680 block.Hash(), blockHeight, err) 681 } 682 if rerr.ErrorCode != code { 683 t.Fatalf("block template %q (hash %s, height %d) does "+ 684 "not have expected reject code -- got %v, want %v", 685 g.TipName(), block.Hash(), blockHeight, 686 rerr.ErrorCode, code) 687 } 688 } 689 690 // changeNonce is a munger that modifies the block by changing the header 691 // nonce to a pseudo-random value. 692 prng := mrand.New(mrand.NewSource(0)) 693 changeNonce := func(b *wire.MsgBlock) { 694 // Change the nonce so the block isn't actively solved. 695 b.Header.Nonce = prng.Uint32() 696 } 697 698 // Shorter versions of useful params for convenience. 699 ticketsPerBlock := params.TicketsPerBlock 700 coinbaseMaturity := params.CoinbaseMaturity 701 stakeEnabledHeight := params.StakeEnabledHeight 702 stakeValidationHeight := params.StakeValidationHeight 703 704 // --------------------------------------------------------------------- 705 // First block templates. 706 // 707 // NOTE: The advance funcs on the harness are intentionally not used in 708 // these tests since they need to manually test block templates at all 709 // heights. 710 // --------------------------------------------------------------------- 711 712 // Produce an initial block with too much coinbase and ensure the block 713 // template is rejected. 714 // 715 // genesis 716 // \-> bfbbad 717 g.CreatePremineBlock("bfbbad", 1) 718 g.AssertTipHeight(1) 719 rejectedBlockTemplate(ErrBadCoinbaseValue) 720 721 // Produce a valid, but unsolved initial block and ensure the block template 722 // is accepted while the unsolved block is rejected. 723 // 724 // genesis 725 // \-> bfbunsolved 726 g.SetTip("genesis") 727 bfbunsolved := g.CreatePremineBlock("bfbunsolved", 0, changeNonce) 728 // Since the difficulty is so low in the tests, the block might still 729 // end up being inadvertently solved. It can't be checked inside the 730 // munger because the block is finalized after the function returns and 731 // those changes could also inadvertently solve the block. Thus, just 732 // increment the nonce until it's not solved and then replace it in the 733 // generator's state. 734 { 735 origHash := bfbunsolved.BlockHash() 736 for chaingen.IsSolved(&bfbunsolved.Header) { 737 bfbunsolved.Header.Nonce++ 738 } 739 g.UpdateBlockState("bfbunsolved", origHash, "bfbunsolved", bfbunsolved) 740 } 741 g.AssertTipHeight(1) 742 acceptedBlockTemplate() 743 g.RejectTipBlock(ErrHighHash) 744 g.ExpectTip("genesis") 745 746 // Produce a valid and solved initial block. 747 // 748 // genesis -> bfb 749 g.SetTip("genesis") 750 g.CreatePremineBlock("bfb", 0) 751 g.AssertTipHeight(1) 752 g.AcceptTipBlock() 753 754 // --------------------------------------------------------------------- 755 // Generate enough blocks to have mature coinbase outputs to work with. 756 // 757 // Also, ensure that each block is considered a valid template along the 758 // way. 759 // 760 // genesis -> bfb -> bm0 -> bm1 -> ... -> bm# 761 // --------------------------------------------------------------------- 762 763 var tipName string 764 for i := uint16(0); i < coinbaseMaturity; i++ { 765 blockName := fmt.Sprintf("bm%d", i) 766 g.NextBlock(blockName, nil, nil) 767 g.SaveTipCoinbaseOuts() 768 acceptedBlockTemplate() 769 g.AcceptTipBlock() 770 tipName = blockName 771 } 772 g.AssertTipHeight(uint32(coinbaseMaturity) + 1) 773 774 // --------------------------------------------------------------------- 775 // Generate block templates that include invalid ticket purchases. 776 // --------------------------------------------------------------------- 777 778 // Create a block template with a ticket that claims too much input 779 // amount. 780 // 781 // ... -> bm# 782 // \-> btixt1 783 tempOuts := g.OldestCoinbaseOuts() 784 tempTicketOuts := tempOuts[1:] 785 g.NextBlock("btixt1", nil, tempTicketOuts, func(b *wire.MsgBlock) { 786 changeNonce(b) 787 b.STransactions[3].TxIn[0].ValueIn-- 788 }) 789 rejectedBlockTemplate(ErrFraudAmountIn) 790 791 // Create a block template with a ticket that does not pay enough. 792 // 793 // ... -> bm# 794 // \-> btixt2 795 g.SetTip(tipName) 796 g.NextBlock("btixt2", nil, tempTicketOuts, func(b *wire.MsgBlock) { 797 changeNonce(b) 798 b.STransactions[2].TxOut[0].Value-- 799 }) 800 rejectedBlockTemplate(ErrNotEnoughStake) 801 802 // --------------------------------------------------------------------- 803 // Generate enough blocks to reach the stake enabled height while 804 // creating ticket purchases that spend from the coinbases matured 805 // above. This will also populate the pool of immature tickets. 806 // 807 // Also, ensure that each block is considered a valid template along the 808 // way. 809 // 810 // ... -> bm# ... -> bse0 -> bse1 -> ... -> bse# 811 // --------------------------------------------------------------------- 812 813 // Use the already popped outputs. 814 g.SetTip(tipName) 815 g.NextBlock("bse0", nil, tempTicketOuts) 816 g.SaveTipCoinbaseOuts() 817 acceptedBlockTemplate() 818 g.AcceptTipBlock() 819 820 var ticketsPurchased int 821 for i := int64(1); int64(g.Tip().Header.Height) < stakeEnabledHeight; i++ { 822 outs := g.OldestCoinbaseOuts() 823 ticketOuts := outs[1:] 824 ticketsPurchased += len(ticketOuts) 825 blockName := fmt.Sprintf("bse%d", i) 826 g.NextBlock(blockName, nil, ticketOuts) 827 g.SaveTipCoinbaseOuts() 828 acceptedBlockTemplate() 829 g.AcceptTipBlock() 830 } 831 g.AssertTipHeight(uint32(stakeEnabledHeight)) 832 833 // --------------------------------------------------------------------- 834 // Generate enough blocks to reach the stake validation height while 835 // continuing to purchase tickets using the coinbases matured above and 836 // allowing the immature tickets to mature and thus become live. 837 // 838 // Also, ensure that each block is considered a valid template along the 839 // way. 840 // 841 // ... -> bse# -> bsv0 -> bsv1 -> ... -> bsv# 842 // --------------------------------------------------------------------- 843 844 targetPoolSize := g.Params().TicketPoolSize * ticketsPerBlock 845 for i := int64(0); int64(g.Tip().Header.Height) < stakeValidationHeight; i++ { 846 // Only purchase tickets until the target ticket pool size is 847 // reached. 848 outs := g.OldestCoinbaseOuts() 849 ticketOuts := outs[1:] 850 if ticketsPurchased+len(ticketOuts) > int(targetPoolSize) { 851 ticketsNeeded := int(targetPoolSize) - ticketsPurchased 852 if ticketsNeeded > 0 { 853 ticketOuts = ticketOuts[1 : ticketsNeeded+1] 854 } else { 855 ticketOuts = nil 856 } 857 } 858 ticketsPurchased += len(ticketOuts) 859 860 blockName := fmt.Sprintf("bsv%d", i) 861 g.NextBlock(blockName, nil, ticketOuts) 862 g.SaveTipCoinbaseOuts() 863 acceptedBlockTemplate() 864 g.AcceptTipBlock() 865 } 866 g.AssertTipHeight(uint32(stakeValidationHeight)) 867 868 // --------------------------------------------------------------------- 869 // Generate enough blocks to have a known distance to the first mature 870 // coinbase outputs for all tests that follow. These blocks continue 871 // to purchase tickets to avoid running out of votes. 872 // 873 // Also, ensure that each block is considered a valid template along the 874 // way. 875 // 876 // ... -> bsv# -> bbm0 -> bbm1 -> ... -> bbm# 877 // --------------------------------------------------------------------- 878 879 for i := uint16(0); i < coinbaseMaturity; i++ { 880 outs := g.OldestCoinbaseOuts() 881 blockName := fmt.Sprintf("bbm%d", i) 882 g.NextBlock(blockName, nil, outs[1:]) 883 g.SaveTipCoinbaseOuts() 884 acceptedBlockTemplate() 885 g.AcceptTipBlock() 886 } 887 g.AssertTipHeight(uint32(stakeValidationHeight) + uint32(coinbaseMaturity)) 888 889 // Collect spendable outputs into two different slices. The outs slice 890 // is intended to be used for regular transactions that spend from the 891 // output, while the ticketOuts slice is intended to be used for stake 892 // ticket purchases. 893 var outs []*chaingen.SpendableOut 894 var ticketOuts [][]chaingen.SpendableOut 895 for i := uint16(0); i < coinbaseMaturity; i++ { 896 coinbaseOuts := g.OldestCoinbaseOuts() 897 outs = append(outs, &coinbaseOuts[0]) 898 ticketOuts = append(ticketOuts, coinbaseOuts[1:]) 899 } 900 901 // --------------------------------------------------------------------- 902 // Generate block templates that build on ancestors of the tip. 903 // --------------------------------------------------------------------- 904 905 // Start by building a few of blocks at current tip (value in parens 906 // is which output is spent): 907 // 908 // ... -> b1(0) -> b2(1) -> b3(2) 909 g.NextBlock("b1", outs[0], ticketOuts[0]) 910 g.AcceptTipBlock() 911 912 g.NextBlock("b2", outs[1], ticketOuts[1]) 913 g.AcceptTipBlock() 914 915 g.NextBlock("b3", outs[2], ticketOuts[2]) 916 g.AcceptTipBlock() 917 918 // Create a block template that forks from b1. It should not be allowed 919 // since it is not the current tip or its parent. 920 // 921 // ... -> b1(0) -> b2(1) -> b3(2) 922 // \-> b2at(1) 923 g.SetTip("b1") 924 g.NextBlock("b2at", outs[1], ticketOuts[1], changeNonce) 925 rejectedBlockTemplate(ErrInvalidTemplateParent) 926 927 // Create a block template that forks from b2. It should be accepted 928 // because it is the current tip's parent. 929 // 930 // ... -> b2(1) -> b3(2) 931 // \-> b3at(2) 932 g.SetTip("b2") 933 g.NextBlock("b3at", outs[2], ticketOuts[2], changeNonce) 934 acceptedBlockTemplate() 935 936 // --------------------------------------------------------------------- 937 // Generate block templates that build on the tip's parent, but include 938 // invalid votes. 939 // --------------------------------------------------------------------- 940 941 // Create a block template that forks from b2 (the tip's parent) with 942 // votes that spend invalid tickets. 943 // 944 // ... -> b2(1) -> b3(2) 945 // \-> b3bt(2) 946 g.SetTip("b2") 947 g.NextBlock("b3bt", outs[2], ticketOuts[1], changeNonce) 948 rejectedBlockTemplate(ErrMissingTxOut) 949 950 // Same as before but based on the current tip. 951 // 952 // ... -> b2(1) -> b3(2) 953 // \-> b4at(3) 954 g.SetTip("b3") 955 g.NextBlock("b4at", outs[3], ticketOuts[2], changeNonce) 956 rejectedBlockTemplate(ErrMissingTxOut) 957 958 // Create a block template that forks from b2 (the tip's parent) with 959 // a vote that pays too much. 960 // 961 // ... -> b2(1) -> b3(2) 962 // \-> b3ct(2) 963 g.SetTip("b2") 964 g.NextBlock("b3ct", outs[2], ticketOuts[2], func(b *wire.MsgBlock) { 965 changeNonce(b) 966 b.STransactions[0].TxOut[0].Value++ 967 }) 968 rejectedBlockTemplate(ErrSpendTooHigh) 969 970 // Same as before but based on the current tip. 971 // 972 // ... -> b2(1) -> b3(2) 973 // \-> b4bt(3) 974 g.SetTip("b3") 975 g.NextBlock("b4bt", outs[3], ticketOuts[3], func(b *wire.MsgBlock) { 976 changeNonce(b) 977 b.STransactions[0].TxOut[0].Value++ 978 }) 979 rejectedBlockTemplate(ErrSpendTooHigh) 980 981 // --------------------------------------------------------------------- 982 // Generate block templates that build on the tip and its parent after a 983 // forced reorg. 984 // --------------------------------------------------------------------- 985 986 // Create a fork from b2. There should not be a reorg since b3 was seen 987 // first. 988 // 989 // ... -> b2(1) -> b3(2) 990 // \-> b3a(2) 991 g.SetTip("b2") 992 g.NextBlock("b3a", outs[2], ticketOuts[2]) 993 g.AcceptedToSideChainWithExpectedTip("b3") 994 995 // Force tip reorganization to b3a. 996 // 997 // ... -> b2(1) -> b3a(2) 998 // \-> b3(2) 999 g.ForceTipReorg("b3", "b3a") 1000 g.ExpectTip("b3a") 1001 1002 // Create a block template that forks from b2 (the tip's parent) and 1003 // ensure it is still accepted after the forced reorg. 1004 // 1005 // ... -> b2(1) -> b3a(2) 1006 // \-> b3dt(2) 1007 g.SetTip("b2") 1008 g.NextBlock("b3dt", outs[2], ticketOuts[2], changeNonce) 1009 acceptedBlockTemplate() 1010 g.ExpectTip("b3a") // Ensure chain tip didn't change. 1011 1012 // Create a block template that builds on the current tip and ensure it 1013 // it is still accepted after the forced reorg. 1014 // 1015 // ... -> b2(1) -> b3a(2) 1016 // \-> b4ct(3) 1017 g.SetTip("b3a") 1018 g.NextBlock("b4ct", outs[3], ticketOuts[3], changeNonce) 1019 acceptedBlockTemplate() 1020 }