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: &paramsCopy,
   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(&params.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  }