github.com/decred/dcrd/blockchain@v1.2.1/common_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 "fmt" 10 "io/ioutil" 11 "math" 12 mrand "math/rand" 13 "os" 14 "testing" 15 "time" 16 17 "github.com/decred/dcrd/blockchain/chaingen" 18 "github.com/decred/dcrd/blockchain/stake" 19 "github.com/decred/dcrd/chaincfg" 20 "github.com/decred/dcrd/chaincfg/chainhash" 21 "github.com/decred/dcrd/database" 22 _ "github.com/decred/dcrd/database/ffldb" 23 "github.com/decred/dcrd/dcrutil" 24 "github.com/decred/dcrd/txscript" 25 "github.com/decred/dcrd/wire" 26 ) 27 28 const ( 29 // testDbType is the database backend type to use for the tests. 30 testDbType = "ffldb" 31 32 // blockDataNet is the expected network in the test block data. 33 blockDataNet = wire.MainNet 34 ) 35 36 // isSupportedDbType returns whether or not the passed database type is 37 // currently supported. 38 func isSupportedDbType(dbType string) bool { 39 supportedDrivers := database.SupportedDrivers() 40 for _, driver := range supportedDrivers { 41 if dbType == driver { 42 return true 43 } 44 } 45 46 return false 47 } 48 49 // chainSetup is used to create a new db and chain instance with the genesis 50 // block already inserted. In addition to the new chain instance, it returns 51 // a teardown function the caller should invoke when done testing to clean up. 52 func chainSetup(dbName string, params *chaincfg.Params) (*BlockChain, func(), error) { 53 if !isSupportedDbType(testDbType) { 54 return nil, nil, fmt.Errorf("unsupported db type %v", testDbType) 55 } 56 57 // Handle memory database specially since it doesn't need the disk 58 // specific handling. 59 var db database.DB 60 var teardown func() 61 if testDbType == "memdb" { 62 ndb, err := database.Create(testDbType) 63 if err != nil { 64 return nil, nil, fmt.Errorf("error creating db: %v", err) 65 } 66 db = ndb 67 68 // Setup a teardown function for cleaning up. This function is 69 // returned to the caller to be invoked when it is done testing. 70 teardown = func() { 71 db.Close() 72 } 73 } else { 74 // Create the directory for test database. 75 dbPath, err := ioutil.TempDir("", dbName) 76 if err != nil { 77 err := fmt.Errorf("unable to create test db path: %v", 78 err) 79 return nil, nil, err 80 } 81 82 // Create a new database to store the accepted blocks into. 83 ndb, err := database.Create(testDbType, dbPath, blockDataNet) 84 if err != nil { 85 os.RemoveAll(dbPath) 86 return nil, nil, fmt.Errorf("error creating db: %v", err) 87 } 88 db = ndb 89 90 // Setup a teardown function for cleaning up. This function is 91 // returned to the caller to be invoked when it is done testing. 92 teardown = func() { 93 db.Close() 94 os.RemoveAll(dbPath) 95 } 96 } 97 98 // Copy the chain params to ensure any modifications the tests do to 99 // the chain parameters do not affect the global instance. 100 paramsCopy := *params 101 102 // Create the main chain instance. 103 chain, err := New(&Config{ 104 DB: db, 105 ChainParams: ¶msCopy, 106 TimeSource: NewMedianTime(), 107 SigCache: txscript.NewSigCache(1000), 108 }) 109 110 if err != nil { 111 teardown() 112 err := fmt.Errorf("failed to create chain instance: %v", err) 113 return nil, nil, err 114 } 115 116 return chain, teardown, nil 117 } 118 119 // newFakeChain returns a chain that is usable for syntetic tests. It is 120 // important to note that this chain has no database associated with it, so 121 // it is not usable with all functions and the tests must take care when making 122 // use of it. 123 func newFakeChain(params *chaincfg.Params) *BlockChain { 124 // Create a genesis block node and block index populated with it for use 125 // when creating the fake chain below. 126 node := newBlockNode(¶ms.GenesisBlock.Header, nil) 127 node.status = statusDataStored | statusValid 128 index := newBlockIndex(nil) 129 index.AddNode(node) 130 131 // Generate a deployment ID to version map from the provided params. 132 deploymentVers, err := extractDeploymentIDVersions(params) 133 if err != nil { 134 panic(err) 135 } 136 137 return &BlockChain{ 138 deploymentVers: deploymentVers, 139 chainParams: params, 140 deploymentCaches: newThresholdCaches(params), 141 index: index, 142 bestChain: newChainView(node), 143 isVoterMajorityVersionCache: make(map[[stakeMajorityCacheKeySize]byte]bool), 144 isStakeMajorityVersionCache: make(map[[stakeMajorityCacheKeySize]byte]bool), 145 calcPriorStakeVersionCache: make(map[[chainhash.HashSize]byte]uint32), 146 calcVoterVersionIntervalCache: make(map[[chainhash.HashSize]byte]uint32), 147 calcStakeVersionCache: make(map[[chainhash.HashSize]byte]uint32), 148 } 149 } 150 151 // testNoncePrng provides a deterministic prng for the nonce in generated fake 152 // nodes. The ensures that the nodes have unique hashes. 153 var testNoncePrng = mrand.New(mrand.NewSource(0)) 154 155 // newFakeNode creates a block node connected to the passed parent with the 156 // provided fields populated and fake values for the other fields. 157 func newFakeNode(parent *blockNode, blockVersion int32, stakeVersion uint32, bits uint32, timestamp time.Time) *blockNode { 158 // Make up a header and create a block node from it. 159 var prevHash chainhash.Hash 160 var height uint32 161 if parent != nil { 162 prevHash = parent.hash 163 height = uint32(parent.height + 1) 164 } 165 header := &wire.BlockHeader{ 166 Version: blockVersion, 167 PrevBlock: prevHash, 168 VoteBits: 0x01, 169 Bits: bits, 170 Height: height, 171 Timestamp: timestamp, 172 Nonce: testNoncePrng.Uint32(), 173 StakeVersion: stakeVersion, 174 } 175 node := newBlockNode(header, parent) 176 node.status = statusDataStored | statusValid 177 return node 178 } 179 180 // chainedFakeNodes returns the specified number of nodes constructed such that 181 // each subsequent node points to the previous one to create a chain. The first 182 // node will point to the passed parent which can be nil if desired. 183 func chainedFakeNodes(parent *blockNode, numNodes int) []*blockNode { 184 nodes := make([]*blockNode, numNodes) 185 tip := parent 186 blockTime := time.Now() 187 if tip != nil { 188 blockTime = time.Unix(tip.timestamp, 0) 189 } 190 for i := 0; i < numNodes; i++ { 191 blockTime = blockTime.Add(time.Second) 192 node := newFakeNode(tip, 1, 1, 0, blockTime) 193 tip = node 194 195 nodes[i] = node 196 } 197 return nodes 198 } 199 200 // branchTip is a convenience function to grab the tip of a chain of block nodes 201 // created via chainedFakeNodes. 202 func branchTip(nodes []*blockNode) *blockNode { 203 return nodes[len(nodes)-1] 204 } 205 206 // appendFakeVotes appends the passed number of votes to the node with the 207 // provided version and vote bits. 208 func appendFakeVotes(node *blockNode, numVotes uint16, voteVersion uint32, voteBits uint16) { 209 for i := uint16(0); i < numVotes; i++ { 210 node.votes = append(node.votes, stake.VoteVersionTuple{ 211 Version: voteVersion, 212 Bits: voteBits, 213 }) 214 } 215 } 216 217 // findDeployment finds the provided vote ID within the deployments of the 218 // provided parameters and either returns the deployment version it was found in 219 // along with a pointer to the deployment or an error when not found. 220 func findDeployment(params *chaincfg.Params, voteID string) (uint32, *chaincfg.ConsensusDeployment, error) { 221 // Find the correct deployment for the passed vote ID. 222 for version, deployments := range params.Deployments { 223 for i, deployment := range deployments { 224 if deployment.Vote.Id == voteID { 225 return version, &deployments[i], nil 226 } 227 } 228 } 229 230 return 0, nil, fmt.Errorf("unable to find deployement for id %q", voteID) 231 } 232 233 // findDeploymentChoice finds the provided choice ID within the given 234 // deployment params and either returns a pointer to the found choice or an 235 // error when not found. 236 func findDeploymentChoice(deployment *chaincfg.ConsensusDeployment, choiceID string) (*chaincfg.Choice, error) { 237 // Find the correct choice for the passed choice ID. 238 for i, choice := range deployment.Vote.Choices { 239 if choice.Id == choiceID { 240 return &deployment.Vote.Choices[i], nil 241 } 242 } 243 244 return nil, fmt.Errorf("unable to find vote choice for id %q", choiceID) 245 } 246 247 // removeDeploymentTimeConstraints modifies the passed deployment to remove the 248 // voting time constraints by making it always available to vote and to never 249 // expire. 250 // 251 // NOTE: This will mutate the passed deployment, so ensure this function is 252 // only called with parameters that are not globally available. 253 func removeDeploymentTimeConstraints(deployment *chaincfg.ConsensusDeployment) { 254 deployment.StartTime = 0 // Always available for vote. 255 deployment.ExpireTime = math.MaxUint64 // Never expires. 256 } 257 258 // chaingenHarness provides a test harness which encapsulates a test instance, a 259 // chaingen generator instance, and a block chain instance to provide all of the 260 // functionality of the aforementioned types as well as several convenience 261 // functions such as block acceptance and rejection, expected tip checking, and 262 // threshold state checking. 263 // 264 // The chaingen generator is embedded in the struct so callers can directly 265 // access its method the same as if they were directly working with the 266 // underlying generator. 267 // 268 // Since chaingen involves creating fully valid and solved blocks, which is 269 // relatively expensive, only tests which actually require that functionality 270 // should make use of this harness. In many cases, a much faster synthetic 271 // chain instance created by newFakeChain will suffice. 272 type chaingenHarness struct { 273 *chaingen.Generator 274 275 t *testing.T 276 chain *BlockChain 277 deploymentVersions map[string]uint32 278 } 279 280 // newChaingenHarness creates and returns a new instance of a chaingen harness 281 // that encapsulates the provided test instance along with a teardown function 282 // the caller should invoke when done testing to clean up. 283 // 284 // See the documentation for the chaingenHarness type for more details. 285 func newChaingenHarness(t *testing.T, params *chaincfg.Params, dbName string) (*chaingenHarness, func()) { 286 t.Helper() 287 288 // Create a test generator instance initialized with the genesis block as 289 // the tip. 290 g, err := chaingen.MakeGenerator(params) 291 if err != nil { 292 t.Fatalf("Failed to create generator: %v", err) 293 } 294 295 // Create a new database and chain instance to run tests against. 296 chain, teardownFunc, err := chainSetup(dbName, params) 297 if err != nil { 298 t.Fatalf("Failed to setup chain instance: %v", err) 299 } 300 301 harness := chaingenHarness{ 302 Generator: &g, 303 t: t, 304 chain: chain, 305 deploymentVersions: make(map[string]uint32), 306 } 307 return &harness, teardownFunc 308 } 309 310 // AcceptBlock processes the block associated with the given name in the 311 // harness generator and expects it to be accepted to the main chain. 312 func (g *chaingenHarness) AcceptBlock(blockName string) { 313 g.t.Helper() 314 315 msgBlock := g.BlockByName(blockName) 316 blockHeight := msgBlock.Header.Height 317 block := dcrutil.NewBlock(msgBlock) 318 g.t.Logf("Testing block %s (hash %s, height %d)", blockName, block.Hash(), 319 blockHeight) 320 321 forkLen, isOrphan, err := g.chain.ProcessBlock(block, BFNone) 322 if err != nil { 323 g.t.Fatalf("block %q (hash %s, height %d) should have been accepted: %v", 324 blockName, block.Hash(), blockHeight, err) 325 } 326 327 // Ensure the main chain and orphan flags match the values specified in the 328 // test. 329 isMainChain := !isOrphan && forkLen == 0 330 if !isMainChain { 331 g.t.Fatalf("block %q (hash %s, height %d) unexpected main chain flag "+ 332 "-- got %v, want true", blockName, block.Hash(), blockHeight, 333 isMainChain) 334 } 335 if isOrphan { 336 g.t.Fatalf("block %q (hash %s, height %d) unexpected orphan flag -- "+ 337 "got %v, want false", blockName, block.Hash(), blockHeight, 338 isOrphan) 339 } 340 } 341 342 // AcceptTipBlock processes the current tip block associated with the harness 343 // generator and expects it to be accepted to the main chain. 344 func (g *chaingenHarness) AcceptTipBlock() { 345 g.t.Helper() 346 347 g.AcceptBlock(g.TipName()) 348 } 349 350 // RejectBlock expects the block associated with the given name in the harness 351 // generator to be rejected with the provided error code. 352 func (g *chaingenHarness) RejectBlock(blockName string, code ErrorCode) { 353 g.t.Helper() 354 355 msgBlock := g.BlockByName(blockName) 356 blockHeight := msgBlock.Header.Height 357 block := dcrutil.NewBlock(msgBlock) 358 g.t.Logf("Testing block %s (hash %s, height %d)", blockName, block.Hash(), 359 blockHeight) 360 361 _, _, err := g.chain.ProcessBlock(block, BFNone) 362 if err == nil { 363 g.t.Fatalf("block %q (hash %s, height %d) should not have been accepted", 364 blockName, block.Hash(), blockHeight) 365 } 366 367 // Ensure the error code is of the expected type and the reject code matches 368 // the value specified in the test instance. 369 rerr, ok := err.(RuleError) 370 if !ok { 371 g.t.Fatalf("block %q (hash %s, height %d) returned unexpected error "+ 372 "type -- got %T, want blockchain.RuleError", blockName, 373 block.Hash(), blockHeight, err) 374 } 375 if rerr.ErrorCode != code { 376 g.t.Fatalf("block %q (hash %s, height %d) does not have expected reject "+ 377 "code -- got %v, want %v", blockName, block.Hash(), blockHeight, 378 rerr.ErrorCode, code) 379 } 380 } 381 382 // RejectTipBlock expects the current tip block associated with the harness 383 // generator to be rejected with the provided error code. 384 func (g *chaingenHarness) RejectTipBlock(code ErrorCode) { 385 g.t.Helper() 386 387 g.RejectBlock(g.TipName(), code) 388 } 389 390 // ExpectTip expects the provided block to be the current tip of the main chain 391 // associated with the harness generator. 392 func (g *chaingenHarness) ExpectTip(tipName string) { 393 g.t.Helper() 394 395 // Ensure hash and height match. 396 wantTip := g.BlockByName(tipName) 397 best := g.chain.BestSnapshot() 398 if best.Hash != wantTip.BlockHash() || 399 best.Height != int64(wantTip.Header.Height) { 400 g.t.Fatalf("block %q (hash %s, height %d) should be the current tip "+ 401 "-- got (hash %s, height %d)", tipName, wantTip.BlockHash(), 402 wantTip.Header.Height, best.Hash, best.Height) 403 } 404 } 405 406 // AcceptedToSideChainWithExpectedTip expects the tip block associated with the 407 // generator to be accepted to a side chain, but the current best chain tip to 408 // be the provided value. 409 func (g *chaingenHarness) AcceptedToSideChainWithExpectedTip(tipName string) { 410 g.t.Helper() 411 412 msgBlock := g.Tip() 413 blockHeight := msgBlock.Header.Height 414 block := dcrutil.NewBlock(msgBlock) 415 g.t.Logf("Testing block %s (hash %s, height %d)", g.TipName(), block.Hash(), 416 blockHeight) 417 418 forkLen, isOrphan, err := g.chain.ProcessBlock(block, BFNone) 419 if err != nil { 420 g.t.Fatalf("block %q (hash %s, height %d) should have been accepted: %v", 421 g.TipName(), block.Hash(), blockHeight, err) 422 } 423 424 // Ensure the main chain and orphan flags match the values specified in 425 // the test. 426 isMainChain := !isOrphan && forkLen == 0 427 if isMainChain { 428 g.t.Fatalf("block %q (hash %s, height %d) unexpected main chain flag "+ 429 "-- got %v, want false", g.TipName(), block.Hash(), blockHeight, 430 isMainChain) 431 } 432 if isOrphan { 433 g.t.Fatalf("block %q (hash %s, height %d) unexpected orphan flag -- "+ 434 "got %v, want false", g.TipName(), block.Hash(), blockHeight, 435 isOrphan) 436 } 437 438 g.ExpectTip(tipName) 439 } 440 441 // lookupDeploymentVersion returns the version of the deployment with the 442 // provided ID and caches the result for future invocations. An error is 443 // returned if the ID is not found. 444 func (g *chaingenHarness) lookupDeploymentVersion(voteID string) (uint32, error) { 445 if version, ok := g.deploymentVersions[voteID]; ok { 446 return version, nil 447 } 448 449 version, _, err := findDeployment(g.Params(), voteID) 450 if err != nil { 451 return 0, err 452 } 453 454 g.deploymentVersions[voteID] = version 455 return version, nil 456 } 457 458 // TestThresholdState queries the threshold state from the current tip block 459 // associated with the harness generator and expects the returned state to match 460 // the provided value. 461 func (g *chaingenHarness) TestThresholdState(id string, state ThresholdState) { 462 g.t.Helper() 463 464 tipHash := g.Tip().BlockHash() 465 tipHeight := g.Tip().Header.Height 466 deploymentVer, err := g.lookupDeploymentVersion(id) 467 if err != nil { 468 g.t.Fatalf("block %q (hash %s, height %d) unexpected error when "+ 469 "retrieving threshold state: %v", g.TipName(), tipHash, tipHeight, 470 err) 471 } 472 473 s, err := g.chain.NextThresholdState(&tipHash, deploymentVer, id) 474 if err != nil { 475 g.t.Fatalf("block %q (hash %s, height %d) unexpected error when "+ 476 "retrieving threshold state: %v", g.TipName(), tipHash, tipHeight, 477 err) 478 } 479 480 if s.State != state { 481 g.t.Fatalf("block %q (hash %s, height %d) unexpected threshold "+ 482 "state for %s -- got %v, want %v", g.TipName(), tipHash, tipHeight, 483 id, s.State, state) 484 } 485 } 486 487 // TestThresholdStateChoice queries the threshold state from the current tip 488 // block associated with the harness generator and expects the returned state 489 // and choice to match the provided value. 490 func (g *chaingenHarness) TestThresholdStateChoice(id string, state ThresholdState, choice uint32) { 491 g.t.Helper() 492 493 tipHash := g.Tip().BlockHash() 494 tipHeight := g.Tip().Header.Height 495 deploymentVer, err := g.lookupDeploymentVersion(id) 496 if err != nil { 497 g.t.Fatalf("block %q (hash %s, height %d) unexpected error when "+ 498 "retrieving threshold state: %v", g.TipName(), tipHash, tipHeight, 499 err) 500 } 501 502 s, err := g.chain.NextThresholdState(&tipHash, deploymentVer, id) 503 if err != nil { 504 g.t.Fatalf("block %q (hash %s, height %d) unexpected error when "+ 505 "retrieving threshold state: %v", g.TipName(), tipHash, tipHeight, 506 err) 507 } 508 509 if s.State != state { 510 g.t.Fatalf("block %q (hash %s, height %d) unexpected threshold "+ 511 "state for %s -- got %v, want %v", g.TipName(), tipHash, tipHeight, 512 id, s.State, state) 513 } 514 if s.Choice != choice { 515 g.t.Fatalf("block %q (hash %s, height %d) unexpected choice for %s -- "+ 516 "got %v, want %v", g.TipName(), tipHash, tipHeight, id, s.Choice, 517 choice) 518 } 519 } 520 521 // ForceTipReorg forces the chain instance associated with the generator to 522 // reorganize the current tip of the main chain from the given block to the 523 // given block. An error will result if the provided from block is not actually 524 // the current tip. 525 func (g *chaingenHarness) ForceTipReorg(fromTipName, toTipName string) { 526 g.t.Helper() 527 528 from := g.BlockByName(fromTipName) 529 to := g.BlockByName(toTipName) 530 g.t.Logf("Testing forced reorg from %s (hash %s, height %d) to %s (hash "+ 531 "%s, height %d)", fromTipName, from.BlockHash(), from.Header.Height, 532 toTipName, to.BlockHash(), to.Header.Height) 533 534 err := g.chain.ForceHeadReorganization(from.BlockHash(), to.BlockHash()) 535 if err != nil { 536 g.t.Fatalf("failed to force header reorg from block %q (hash %s, "+ 537 "height %d) to block %q (hash %s, height %d): %v", fromTipName, 538 from.BlockHash(), from.Header.Height, toTipName, to.BlockHash(), 539 to.Header.Height, err) 540 } 541 } 542 543 // AdvanceToStakeValidationHeight generates and accepts enough blocks to the 544 // chain instance associated with the harness to reach stake validation height. 545 // 546 // The function will fail with a fatal test error if it is not called with the 547 // harness at the genesis block which is the case when it is first created. 548 func (g *chaingenHarness) AdvanceToStakeValidationHeight() { 549 g.t.Helper() 550 551 // Only allow this to be called on a newly created harness. 552 if g.Tip().Header.Height != 0 { 553 g.t.Fatalf("chaingen harness instance must be at the genesis block " + 554 "to advance to stake validation height") 555 } 556 557 // Shorter versions of useful params for convenience. 558 params := g.Params() 559 ticketsPerBlock := params.TicketsPerBlock 560 coinbaseMaturity := params.CoinbaseMaturity 561 stakeEnabledHeight := params.StakeEnabledHeight 562 stakeValidationHeight := params.StakeValidationHeight 563 564 // --------------------------------------------------------------------- 565 // Block One. 566 // --------------------------------------------------------------------- 567 568 // Add the required first block. 569 // 570 // genesis -> bfb 571 g.CreatePremineBlock("bfb", 0) 572 g.AssertTipHeight(1) 573 g.AcceptTipBlock() 574 575 // --------------------------------------------------------------------- 576 // Generate enough blocks to have mature coinbase outputs to work with. 577 // 578 // genesis -> bfb -> bm0 -> bm1 -> ... -> bm# 579 // --------------------------------------------------------------------- 580 581 for i := uint16(0); i < coinbaseMaturity; i++ { 582 blockName := fmt.Sprintf("bm%d", i) 583 g.NextBlock(blockName, nil, nil) 584 g.SaveTipCoinbaseOuts() 585 g.AcceptTipBlock() 586 } 587 g.AssertTipHeight(uint32(coinbaseMaturity) + 1) 588 589 // --------------------------------------------------------------------- 590 // Generate enough blocks to reach the stake enabled height while 591 // creating ticket purchases that spend from the coinbases matured 592 // above. This will also populate the pool of immature tickets. 593 // 594 // ... -> bm# ... -> bse0 -> bse1 -> ... -> bse# 595 // --------------------------------------------------------------------- 596 597 var ticketsPurchased int 598 for i := int64(0); int64(g.Tip().Header.Height) < stakeEnabledHeight; i++ { 599 outs := g.OldestCoinbaseOuts() 600 ticketOuts := outs[1:] 601 ticketsPurchased += len(ticketOuts) 602 blockName := fmt.Sprintf("bse%d", i) 603 g.NextBlock(blockName, nil, ticketOuts) 604 g.SaveTipCoinbaseOuts() 605 g.AcceptTipBlock() 606 } 607 g.AssertTipHeight(uint32(stakeEnabledHeight)) 608 609 // --------------------------------------------------------------------- 610 // Generate enough blocks to reach the stake validation height while 611 // continuing to purchase tickets using the coinbases matured above and 612 // allowing the immature tickets to mature and thus become live. 613 // 614 // ... -> bse# -> bsv0 -> bsv1 -> ... -> bsv# 615 // --------------------------------------------------------------------- 616 617 targetPoolSize := g.Params().TicketPoolSize * ticketsPerBlock 618 for i := int64(0); int64(g.Tip().Header.Height) < stakeValidationHeight; i++ { 619 // Only purchase tickets until the target ticket pool size is 620 // reached. 621 outs := g.OldestCoinbaseOuts() 622 ticketOuts := outs[1:] 623 if ticketsPurchased+len(ticketOuts) > int(targetPoolSize) { 624 ticketsNeeded := int(targetPoolSize) - ticketsPurchased 625 if ticketsNeeded > 0 { 626 ticketOuts = ticketOuts[1 : ticketsNeeded+1] 627 } else { 628 ticketOuts = nil 629 } 630 } 631 ticketsPurchased += len(ticketOuts) 632 633 blockName := fmt.Sprintf("bsv%d", i) 634 g.NextBlock(blockName, nil, ticketOuts) 635 g.SaveTipCoinbaseOuts() 636 g.AcceptTipBlock() 637 } 638 g.AssertTipHeight(uint32(stakeValidationHeight)) 639 } 640 641 // AdvanceFromSVHToActiveAgenda generates and accepts enough blocks with the 642 // appropriate vote bits set to reach one block prior to the specified agenda 643 // becoming active. 644 // 645 // The function will fail with a fatal test error if it is called when the 646 // harness is not at stake validation height. 647 // 648 // WARNING: This function currently assumes the chain parameters were created 649 // via the quickVoteActivationParams. It should be updated in the future to 650 // work with arbitrary params. 651 func (g *chaingenHarness) AdvanceFromSVHToActiveAgenda(voteID string) { 652 g.t.Helper() 653 654 // Find the correct deployment for the provided ID along with the the yes 655 // vote choice within it. 656 params := g.Params() 657 deploymentVer, deployment, err := findDeployment(params, voteID) 658 if err != nil { 659 g.t.Fatal(err) 660 } 661 yesChoice, err := findDeploymentChoice(deployment, "yes") 662 if err != nil { 663 g.t.Fatal(err) 664 } 665 666 // Shorter versions of useful params for convenience. 667 stakeValidationHeight := params.StakeValidationHeight 668 stakeVerInterval := params.StakeVersionInterval 669 ruleChangeInterval := int64(params.RuleChangeActivationInterval) 670 671 // Only allow this to be called on a harness at SVH. 672 if g.Tip().Header.Height != uint32(stakeValidationHeight) { 673 g.t.Fatalf("chaingen harness instance must be at the genesis block " + 674 "to advance to stake validation height") 675 } 676 677 // --------------------------------------------------------------------- 678 // Generate enough blocks to reach one block before the next two stake 679 // version intervals with block and vote versions for the agenda and 680 // stake version 0. 681 // 682 // This will result in triggering enforcement of the stake version and 683 // that the stake version is the deployment version. The threshold 684 // state for deployment will move to started since the next block also 685 // coincides with the start of a new rule change activation interval for 686 // the chosen parameters. 687 // 688 // ... -> bsv# -> bvu0 -> bvu1 -> ... -> bvu# 689 // --------------------------------------------------------------------- 690 691 blocksNeeded := stakeValidationHeight + stakeVerInterval*2 - 1 - 692 int64(g.Tip().Header.Height) 693 for i := int64(0); i < blocksNeeded; i++ { 694 outs := g.OldestCoinbaseOuts() 695 blockName := fmt.Sprintf("bvu%d", i) 696 g.NextBlock(blockName, nil, outs[1:], 697 chaingen.ReplaceBlockVersion(int32(deploymentVer)), 698 chaingen.ReplaceVoteVersions(deploymentVer)) 699 g.SaveTipCoinbaseOuts() 700 g.AcceptTipBlock() 701 } 702 g.TestThresholdState(voteID, ThresholdStarted) 703 704 // --------------------------------------------------------------------- 705 // Generate enough blocks to reach the next rule change interval with 706 // block, stake, and vote versions for the agenda. Also, set the vote 707 // bits to include yes votes for the agenda. 708 // 709 // This will result in moving the threshold state for the agenda to 710 // locked in. 711 // 712 // ... -> bvu# -> bvli0 -> bvli1 -> ... -> bvli# 713 // --------------------------------------------------------------------- 714 715 blocksNeeded = stakeValidationHeight + ruleChangeInterval*2 - 1 - 716 int64(g.Tip().Header.Height) 717 for i := int64(0); i < blocksNeeded; i++ { 718 outs := g.OldestCoinbaseOuts() 719 blockName := fmt.Sprintf("bvli%d", i) 720 g.NextBlock(blockName, nil, outs[1:], 721 chaingen.ReplaceBlockVersion(int32(deploymentVer)), 722 chaingen.ReplaceStakeVersion(deploymentVer), 723 chaingen.ReplaceVotes(vbPrevBlockValid|yesChoice.Bits, deploymentVer)) 724 g.SaveTipCoinbaseOuts() 725 g.AcceptTipBlock() 726 } 727 g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*2 - 1)) 728 g.AssertBlockVersion(int32(deploymentVer)) 729 g.AssertStakeVersion(deploymentVer) 730 g.TestThresholdState(voteID, ThresholdLockedIn) 731 732 // --------------------------------------------------------------------- 733 // Generate enough blocks to reach the next rule change interval with 734 // block, stake, and vote versions for the agenda. 735 // 736 // This will result in moving the threshold state for the agenda to 737 // active thereby activating it. 738 // 739 // ... -> bvli# -> bva0 -> bva1 -> ... -> bva# 740 // --------------------------------------------------------------------- 741 742 blocksNeeded = stakeValidationHeight + ruleChangeInterval*3 - 1 - 743 int64(g.Tip().Header.Height) 744 for i := int64(0); i < blocksNeeded; i++ { 745 outs := g.OldestCoinbaseOuts() 746 blockName := fmt.Sprintf("bva%d", i) 747 g.NextBlock(blockName, nil, outs[1:], 748 chaingen.ReplaceBlockVersion(int32(deploymentVer)), 749 chaingen.ReplaceStakeVersion(deploymentVer), 750 chaingen.ReplaceVoteVersions(deploymentVer), 751 ) 752 g.SaveTipCoinbaseOuts() 753 g.AcceptTipBlock() 754 } 755 g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*3 - 1)) 756 g.AssertBlockVersion(int32(deploymentVer)) 757 g.AssertStakeVersion(deploymentVer) 758 g.TestThresholdState(voteID, ThresholdActive) 759 }