github.com/decred/dcrd/blockchain@v1.2.1/thresholdstate_test.go (about)

     1  // Copyright (c) 2017-2019 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/decred/dcrd/blockchain/chaingen"
    14  	"github.com/decred/dcrd/chaincfg"
    15  )
    16  
    17  const (
    18  	// vbPrevBlockValid defines the vote bit necessary to vote yes to the
    19  	// previous block being valid.
    20  	vbPrevBlockValid = 0x01
    21  
    22  	// testDummy1ID is the human-readable ID for the first test dummy voting
    23  	// agenda.
    24  	testDummy1ID = "testdummy1"
    25  
    26  	// testDummy1YesIndex is the offset in the choices slice of the first
    27  	// test dummy agenda for the yes choice.
    28  	testDummy1YesIndex = 2
    29  
    30  	// testDummy2NoIndex is the offset in the choices slice of the second
    31  	// test dummy agenda for the no choice.
    32  	testDummy2NoIndex = 1
    33  
    34  	// vbTestDummy1No defines the vote bits necessary to vote no on the first
    35  	// test dummy agenda as well as yes to the previous block being valid.
    36  	vbTestDummy1No = 0x02
    37  
    38  	// vbTestDummy1Yes defines the vote bits necessary to vote yes on the
    39  	// first test dummy agenda as well as yes to the previous block being
    40  	// valid.
    41  	vbTestDummy1Yes = 0x04
    42  
    43  	// testDummy2ID is the human-readable ID for the second test dummy
    44  	// voting agenda.
    45  	testDummy2ID = "testdummy2"
    46  
    47  	// vbTestDummy2No defines the vote bits necessary to vote no on the
    48  	// second test dummy agenda as well as yes to the previous block being
    49  	// valid.
    50  	vbTestDummy2No = 0x08
    51  
    52  	// vbTestDummy2Yes defines the vote bits necessary to vote yes on the
    53  	// second test dummy agenda as well as yes to the previous block being
    54  	// valid.
    55  	vbTestDummy2Yes = 0x10
    56  )
    57  
    58  var (
    59  	// testDummy1 is a voting agenda used throughout these tests.
    60  	testDummy1 = chaincfg.Vote{
    61  		Id:          testDummy1ID,
    62  		Description: "",
    63  		Mask:        0x6, // 0b0110
    64  		Choices: []chaincfg.Choice{{
    65  			Id:          "abstain",
    66  			Description: "abstain voting for change",
    67  			Bits:        0x0000,
    68  			IsAbstain:   true,
    69  			IsNo:        false,
    70  		}, {
    71  			Id:          "no",
    72  			Description: "vote no",
    73  			Bits:        0x0002, // Bit 1
    74  			IsAbstain:   false,
    75  			IsNo:        true,
    76  		}, {
    77  			Id:          "yes",
    78  			Description: "vote yes",
    79  			Bits:        0x0004, // Bit 2
    80  			IsAbstain:   false,
    81  			IsNo:        false,
    82  		}},
    83  	}
    84  
    85  	// testDummy2 is a voting agenda used throughout these tests.
    86  	testDummy2 = chaincfg.Vote{
    87  		Id:          testDummy2ID,
    88  		Description: "",
    89  		Mask:        0x18, // 0b11000
    90  		Choices: []chaincfg.Choice{{
    91  			Id:          "abstain",
    92  			Description: "abstain voting for change",
    93  			Bits:        0x0000,
    94  			IsAbstain:   true,
    95  			IsNo:        false,
    96  		}, {
    97  			Id:          "no",
    98  			Description: "vote no",
    99  			Bits:        0x0008, // Bit 3
   100  			IsAbstain:   false,
   101  			IsNo:        true,
   102  		}, {
   103  			Id:          "yes",
   104  			Description: "vote yes",
   105  			Bits:        0x0010, // Bit 4
   106  			IsAbstain:   false,
   107  			IsNo:        false,
   108  		}},
   109  	}
   110  )
   111  
   112  // TestThresholdState ensures that the threshold state function progresses
   113  // through the states correctly.
   114  func TestThresholdState(t *testing.T) {
   115  	// Create chain params based on regnet params, but add a specific test
   116  	// dummy deployment and set the proof-of-work difficulty readjustment
   117  	// size to a really large number so that the test chain can be generated
   118  	// more quickly.
   119  	posVersion := uint32(4)
   120  	params := cloneParams(&chaincfg.RegNetParams)
   121  	params.WorkDiffWindowSize = 200000
   122  	params.WorkDiffWindows = 1
   123  	params.TargetTimespan = params.TargetTimePerBlock *
   124  		time.Duration(params.WorkDiffWindowSize)
   125  	if params.Deployments == nil {
   126  		params.Deployments = make(map[uint32][]chaincfg.ConsensusDeployment)
   127  	}
   128  	params.Deployments[posVersion] = append(params.Deployments[posVersion],
   129  		chaincfg.ConsensusDeployment{
   130  			Vote:       testDummy1,
   131  			StartTime:  0,
   132  			ExpireTime: math.MaxUint64,
   133  		})
   134  	params.Deployments[posVersion] = append(params.Deployments[posVersion],
   135  		chaincfg.ConsensusDeployment{
   136  			Vote:       testDummy2,
   137  			StartTime:  0,
   138  			ExpireTime: math.MaxUint64,
   139  		})
   140  
   141  	// Create a test harness initialized with the genesis block as the tip.
   142  	g, teardownFunc := newChaingenHarness(t, params, "thresholdstatetest")
   143  	defer teardownFunc()
   144  
   145  	// Shorter versions of useful params for convenience.
   146  	ticketsPerBlock := int64(params.TicketsPerBlock)
   147  	coinbaseMaturity := params.CoinbaseMaturity
   148  	stakeEnabledHeight := params.StakeEnabledHeight
   149  	stakeValidationHeight := params.StakeValidationHeight
   150  	stakeVerInterval := params.StakeVersionInterval
   151  	ruleChangeInterval := int64(params.RuleChangeActivationInterval)
   152  	powNumToCheck := int64(params.BlockUpgradeNumToCheck)
   153  	ruleChangeQuorum := int64(params.RuleChangeActivationQuorum)
   154  	ruleChangeMult := int64(params.RuleChangeActivationMultiplier)
   155  	ruleChangeDiv := int64(params.RuleChangeActivationDivisor)
   156  
   157  	// ---------------------------------------------------------------------
   158  	// Block One.
   159  	//
   160  	// NOTE: The advance funcs on the harness are intentionally not used in
   161  	// these tests since they need to manually test the threshold state at
   162  	// all key heights.
   163  	// ---------------------------------------------------------------------
   164  
   165  	// Add the required initial block.
   166  	//
   167  	//   genesis -> bfb
   168  	g.CreatePremineBlock("bfb", 0)
   169  	g.AssertTipHeight(1)
   170  	g.AcceptTipBlock()
   171  	g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
   172  	g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
   173  
   174  	// ---------------------------------------------------------------------
   175  	// Generate enough blocks to have mature coinbase outputs to work with.
   176  	//
   177  	//   genesis -> bfb -> bm0 -> bm1 -> ... -> bm#
   178  	// ---------------------------------------------------------------------
   179  
   180  	for i := uint16(0); i < coinbaseMaturity; i++ {
   181  		blockName := fmt.Sprintf("bm%d", i)
   182  		g.NextBlock(blockName, nil, nil)
   183  		g.SaveTipCoinbaseOuts()
   184  		g.AcceptTipBlock()
   185  	}
   186  	g.AssertTipHeight(uint32(coinbaseMaturity) + 1)
   187  	g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
   188  	g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
   189  
   190  	// ---------------------------------------------------------------------
   191  	// Generate enough blocks to reach the stake enabled height while
   192  	// creating ticket purchases that spend from the coinbases matured
   193  	// above.  This will also populate the pool of immature tickets.
   194  	//
   195  	//   ... -> bm# ... -> bse0 -> bse1 -> ... -> bse#
   196  	// ---------------------------------------------------------------------
   197  
   198  	var ticketsPurchased int
   199  	for i := int64(0); int64(g.Tip().Header.Height) < stakeEnabledHeight; i++ {
   200  		outs := g.OldestCoinbaseOuts()
   201  		ticketOuts := outs[1:]
   202  		ticketsPurchased += len(ticketOuts)
   203  		blockName := fmt.Sprintf("bse%d", i)
   204  		g.NextBlock(blockName, nil, ticketOuts)
   205  		g.SaveTipCoinbaseOuts()
   206  		g.AcceptTipBlock()
   207  	}
   208  	g.AssertTipHeight(uint32(stakeEnabledHeight))
   209  	g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
   210  	g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
   211  
   212  	// ---------------------------------------------------------------------
   213  	// Generate enough blocks to reach the stake validation height while
   214  	// continuing to purchase tickets using the coinbases matured above and
   215  	// allowing the immature tickets to mature and thus become live.
   216  	//
   217  	// The blocks are also generated with version 3 to ensure stake version
   218  	// enforcement is reached.
   219  	// ---------------------------------------------------------------------
   220  
   221  	targetPoolSize := int64(g.Params().TicketPoolSize) * ticketsPerBlock
   222  	for i := int64(0); int64(g.Tip().Header.Height) < stakeValidationHeight; i++ {
   223  		// Only purchase tickets until the target ticket pool size is
   224  		// reached.
   225  		outs := g.OldestCoinbaseOuts()
   226  		ticketOuts := outs[1:]
   227  		if ticketsPurchased+len(ticketOuts) > int(targetPoolSize) {
   228  			ticketsNeeded := int(targetPoolSize) - ticketsPurchased
   229  			if ticketsNeeded > 0 {
   230  				ticketOuts = ticketOuts[1 : ticketsNeeded+1]
   231  			} else {
   232  				ticketOuts = nil
   233  			}
   234  		}
   235  		ticketsPurchased += len(ticketOuts)
   236  
   237  		blockName := fmt.Sprintf("bsv%d", i)
   238  		g.NextBlock(blockName, nil, ticketOuts,
   239  			chaingen.ReplaceBlockVersion(3))
   240  		g.SaveTipCoinbaseOuts()
   241  		g.AcceptTipBlock()
   242  	}
   243  	g.AssertTipHeight(uint32(stakeValidationHeight))
   244  	g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
   245  	g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
   246  
   247  	// ---------------------------------------------------------------------
   248  	// Generate enough blocks to reach one block before the next stake
   249  	// version interval with block version 3, stake version 0, and vote
   250  	// version 3.
   251  	//
   252  	// This will result in triggering enforcement of the stake version and
   253  	// that the stake version is 3.  The treshold state for the test dummy
   254  	// deployments must still be defined since a v4 majority proof-of-work
   255  	// and proof-of-stake upgrade are required before moving to started.
   256  	// ---------------------------------------------------------------------
   257  
   258  	blocksNeeded := stakeValidationHeight + stakeVerInterval - 1 -
   259  		int64(g.Tip().Header.Height)
   260  	for i := int64(0); i < blocksNeeded; i++ {
   261  		outs := g.OldestCoinbaseOuts()
   262  		blockName := fmt.Sprintf("bsvtA%d", i)
   263  		g.NextBlock(blockName, nil, outs[1:],
   264  			chaingen.ReplaceBlockVersion(3),
   265  			chaingen.ReplaceStakeVersion(0),
   266  			chaingen.ReplaceVoteVersions(3))
   267  		g.SaveTipCoinbaseOuts()
   268  		g.AcceptTipBlock()
   269  	}
   270  	g.AssertTipHeight(uint32(stakeValidationHeight + stakeVerInterval - 1))
   271  	g.AssertBlockVersion(3)
   272  	g.AssertStakeVersion(0)
   273  	g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
   274  	g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
   275  
   276  	// ---------------------------------------------------------------------
   277  	// Generate enough blocks to reach one block before the next rule change
   278  	// interval with block version 3, stake version 3, and vote version 3.
   279  	//
   280  	// The threshold state for the dummy deployments must still be defined
   281  	// since it can only change on a rule change boundary and it requires a
   282  	// v4 majority proof-of-work and proof-of-stake upgrade before moving to
   283  	// started.
   284  	// ---------------------------------------------------------------------
   285  
   286  	blocksNeeded = stakeValidationHeight + ruleChangeInterval - 2 -
   287  		int64(g.Tip().Header.Height)
   288  	for i := int64(0); i < blocksNeeded; i++ {
   289  		outs := g.OldestCoinbaseOuts()
   290  		blockName := fmt.Sprintf("bsvtB%d", i)
   291  		g.NextBlock(blockName, nil, outs[1:],
   292  			chaingen.ReplaceBlockVersion(3),
   293  			chaingen.ReplaceStakeVersion(3),
   294  			chaingen.ReplaceVoteVersions(3))
   295  		g.SaveTipCoinbaseOuts()
   296  		g.AcceptTipBlock()
   297  	}
   298  	g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval - 2))
   299  	g.AssertBlockVersion(3)
   300  	g.AssertStakeVersion(3)
   301  	g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
   302  	g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
   303  
   304  	// ---------------------------------------------------------------------
   305  	// Generate enough blocks to reach one block before the next stake
   306  	// version interval with block version 3, stake version 3, and vote
   307  	// version 4.
   308  	//
   309  	// This will result in achieving stake version 4 enforcement.
   310  	//
   311  	// The treshold state for the dummy deployments must still be defined
   312  	// since it can only change on a rule change boundary and it still
   313  	// requires a v4 majority proof-of-work upgrade before moving to
   314  	// started.
   315  	// ---------------------------------------------------------------------
   316  
   317  	blocksNeeded = stakeValidationHeight + stakeVerInterval*4 - 1 -
   318  		int64(g.Tip().Header.Height)
   319  	for i := int64(0); i < blocksNeeded; i++ {
   320  		outs := g.OldestCoinbaseOuts()
   321  		blockName := fmt.Sprintf("bsvtC%d", i)
   322  		g.NextBlock(blockName, nil, outs[1:],
   323  			chaingen.ReplaceBlockVersion(3),
   324  			chaingen.ReplaceStakeVersion(3),
   325  			chaingen.ReplaceVoteVersions(4))
   326  		g.SaveTipCoinbaseOuts()
   327  		g.AcceptTipBlock()
   328  	}
   329  	g.AssertTipHeight(uint32(stakeValidationHeight + stakeVerInterval*4 - 1))
   330  	g.AssertBlockVersion(3)
   331  	g.AssertStakeVersion(3)
   332  	g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
   333  	g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
   334  
   335  	// ---------------------------------------------------------------------
   336  	// Generate enough blocks to reach the next rule change interval with
   337  	// block version 3 majority, stake version 4, and vote version 4.  Set
   338  	// the final two blocks to block version 4 so that majority version 4
   339  	// is not achieved, but the final block in the interval is version 4.
   340  	//
   341  	// The treshold state for the dummy deployments must still be defined
   342  	// since it still requires a v4 majority proof-of-work upgrade before
   343  	// moving to started.
   344  	// ---------------------------------------------------------------------
   345  
   346  	blocksNeeded = stakeValidationHeight + ruleChangeInterval*2 - 1 -
   347  		int64(g.Tip().Header.Height)
   348  	for i := int64(0); i < blocksNeeded; i++ {
   349  		outs := g.OldestCoinbaseOuts()
   350  		blockName := fmt.Sprintf("bsvtD%d", i)
   351  		blockVersion := int32(3)
   352  		if i >= blocksNeeded-2 {
   353  			blockVersion = 4
   354  		}
   355  		g.NextBlock(blockName, nil, outs[1:],
   356  			chaingen.ReplaceBlockVersion(blockVersion),
   357  			chaingen.ReplaceStakeVersion(4),
   358  			chaingen.ReplaceVoteVersions(4))
   359  		g.SaveTipCoinbaseOuts()
   360  		g.AcceptTipBlock()
   361  	}
   362  	g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*2 - 1))
   363  	g.AssertBlockVersion(4)
   364  	g.AssertStakeVersion(4)
   365  	g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
   366  	g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
   367  
   368  	// ---------------------------------------------------------------------
   369  	// Generate enough blocks to achieve proof-of-work block version lockin
   370  	// with block version 4, stake version 4, and vote version 4.  Also, set
   371  	// the vote bits to include yes votes for the first test dummy agenda
   372  	// and no for the second test dummy agenda for an upcoming test.
   373  	//
   374  	// Since v4 majority proof-of-stake upgrade has been already been
   375  	// achieved and this will achieve v4 majority proof-of-work upgrade,
   376  	// voting can begin at the next rule change interval.
   377  	//
   378  	// The treshold state for the dummy deployments must still be defined
   379  	// since even though all required upgrade conditions are met, the state
   380  	// change must not happen until the start of the next rule change
   381  	// interval.
   382  	// ---------------------------------------------------------------------
   383  
   384  	for i := int64(0); i < powNumToCheck; i++ {
   385  		outs := g.OldestCoinbaseOuts()
   386  		blockName := fmt.Sprintf("bsvtE%d", i)
   387  		g.NextBlock(blockName, nil, outs[1:],
   388  			chaingen.ReplaceBlockVersion(4),
   389  			chaingen.ReplaceStakeVersion(4),
   390  			chaingen.ReplaceVotes(vbPrevBlockValid|vbTestDummy1Yes|
   391  				vbTestDummy2No, 4))
   392  		g.SaveTipCoinbaseOuts()
   393  		g.AcceptTipBlock()
   394  	}
   395  	g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*2 -
   396  		1 + powNumToCheck))
   397  	g.AssertBlockVersion(4)
   398  	g.AssertStakeVersion(4)
   399  	g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
   400  	g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
   401  
   402  	// ---------------------------------------------------------------------
   403  	// Generate enough blocks to reach the next rule change interval with
   404  	// block version 4, stake version 4, and vote version 4.  Also, set the
   405  	// vote bits to include yes votes for the first test dummy agenda and
   406  	// no for the second test dummy agenda to ensure they aren't counted.
   407  	//
   408  	// The treshold state for the dummy deployments must move to started.
   409  	// Even though the majority of the votes have already been voting yes
   410  	// for the first test dummy agenda, and no for the second one, they must
   411  	// not count, otherwise it would move straight to lockedin or failed,
   412  	// respectively.
   413  	// ---------------------------------------------------------------------
   414  
   415  	blocksNeeded = stakeValidationHeight + ruleChangeInterval*3 - 1 -
   416  		int64(g.Tip().Header.Height)
   417  	for i := int64(0); i < blocksNeeded; i++ {
   418  		outs := g.OldestCoinbaseOuts()
   419  		blockName := fmt.Sprintf("bsvtF%d", i)
   420  		g.NextBlock(blockName, nil, outs[1:],
   421  			chaingen.ReplaceBlockVersion(4),
   422  			chaingen.ReplaceStakeVersion(4),
   423  			chaingen.ReplaceVotes(vbPrevBlockValid|vbTestDummy1Yes|
   424  				vbTestDummy2No, 4))
   425  		g.SaveTipCoinbaseOuts()
   426  		g.AcceptTipBlock()
   427  	}
   428  	g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*3 - 1))
   429  	g.AssertBlockVersion(4)
   430  	g.AssertStakeVersion(4)
   431  	g.TestThresholdStateChoice(testDummy1ID, ThresholdStarted, invalidChoice)
   432  	g.TestThresholdStateChoice(testDummy2ID, ThresholdStarted, invalidChoice)
   433  
   434  	// ---------------------------------------------------------------------
   435  	// Generate enough blocks to reach the next rule change interval with
   436  	// block version 4, stake version 4, and vote version 3.  Also, set the
   437  	// vote bits to include yes votes for the first test dummy agenda and
   438  	// no for the second test dummy agenda to ensure they aren't counted.
   439  	//
   440  	// The treshold state for the dummy deployments must remain in started
   441  	// because the votes are an old version and thus have a different
   442  	// definition and don't apply to version 4.
   443  	// ---------------------------------------------------------------------
   444  
   445  	blocksNeeded = stakeValidationHeight + ruleChangeInterval*4 - 1 -
   446  		int64(g.Tip().Header.Height)
   447  	for i := int64(0); i < blocksNeeded; i++ {
   448  		outs := g.OldestCoinbaseOuts()
   449  		blockName := fmt.Sprintf("bsvtG%d", i)
   450  		g.NextBlock(blockName, nil, outs[1:],
   451  			chaingen.ReplaceBlockVersion(4),
   452  			chaingen.ReplaceStakeVersion(4),
   453  			chaingen.ReplaceVotes(vbPrevBlockValid|vbTestDummy1Yes|
   454  				vbTestDummy2No, 3))
   455  		g.SaveTipCoinbaseOuts()
   456  		g.AcceptTipBlock()
   457  	}
   458  	g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*4 - 1))
   459  	g.AssertBlockVersion(4)
   460  	g.AssertStakeVersion(4)
   461  	g.TestThresholdStateChoice(testDummy1ID, ThresholdStarted, invalidChoice)
   462  	g.TestThresholdStateChoice(testDummy2ID, ThresholdStarted, invalidChoice)
   463  
   464  	// ---------------------------------------------------------------------
   465  	// Generate enough blocks to reach the next rule change interval with
   466  	// block version 4, stake version 4, and vote version 4.  Set the vote
   467  	// bits such that quorum is not reached, but there is a majority yes
   468  	// votes for the first test dummy agenda and a majority no for the
   469  	// second test dummy agenda.
   470  	//
   471  	// The treshold state for the dummy deployments must remain in started
   472  	// because quorum was not reached.
   473  	// ---------------------------------------------------------------------
   474  
   475  	var totalVotes int64
   476  	blocksNeeded = stakeValidationHeight + ruleChangeInterval*5 - 1 -
   477  		int64(g.Tip().Header.Height)
   478  	for i := int64(0); i < blocksNeeded; i++ {
   479  		outs := g.OldestCoinbaseOuts()
   480  		blockName := fmt.Sprintf("bsvtH%d", i)
   481  		voteBits := uint16(vbPrevBlockValid) // Abstain both test dummy
   482  		if totalVotes+ticketsPerBlock < ruleChangeQuorum {
   483  			voteBits = vbPrevBlockValid | vbTestDummy1Yes |
   484  				vbTestDummy2No
   485  		}
   486  		g.NextBlock(blockName, nil, outs[1:],
   487  			chaingen.ReplaceBlockVersion(4),
   488  			chaingen.ReplaceStakeVersion(4),
   489  			chaingen.ReplaceVotes(voteBits, 4))
   490  		totalVotes += ticketsPerBlock
   491  		g.SaveTipCoinbaseOuts()
   492  		g.AcceptTipBlock()
   493  	}
   494  	g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*5 - 1))
   495  	g.AssertBlockVersion(4)
   496  	g.AssertStakeVersion(4)
   497  	g.TestThresholdStateChoice(testDummy1ID, ThresholdStarted, invalidChoice)
   498  	g.TestThresholdStateChoice(testDummy2ID, ThresholdStarted, invalidChoice)
   499  
   500  	// ---------------------------------------------------------------------
   501  	// Generate enough blocks to reach the next rule change interval with
   502  	// block version 4, stake version 4, and vote version 4.  Set the vote
   503  	// bits such that quorum is reached, but there are a few votes shy of a
   504  	// majority yes for the first test dummy agenda and a few votes shy of a
   505  	// majority no for the second test dummy agenda.
   506  	//
   507  	// The treshold state for the dummy deployments must remain in started
   508  	// because even though quorum was reached, a required majority was not.
   509  	// ---------------------------------------------------------------------
   510  
   511  	blocksNeeded = stakeValidationHeight + ruleChangeInterval*6 - 1 -
   512  		int64(g.Tip().Header.Height)
   513  	totalVotes = 0
   514  	numActiveNeeded := ruleChangeQuorum * 2
   515  	numMinorityNeeded := numActiveNeeded*ruleChangeMult/ruleChangeDiv - 1
   516  	if numActiveNeeded > ticketsPerBlock*blocksNeeded {
   517  		numActiveNeeded = ticketsPerBlock * blocksNeeded
   518  	}
   519  	for i := int64(0); i < blocksNeeded; i++ {
   520  		outs := g.OldestCoinbaseOuts()
   521  		blockName := fmt.Sprintf("bsvtI%d", i)
   522  		voteBits := uint16(vbPrevBlockValid) // Abstain both test dummy
   523  		if totalVotes+ticketsPerBlock < numMinorityNeeded {
   524  			voteBits = vbPrevBlockValid | vbTestDummy1Yes |
   525  				vbTestDummy2No
   526  		} else if totalVotes+ticketsPerBlock <= numActiveNeeded {
   527  			voteBits = vbPrevBlockValid | vbTestDummy1No |
   528  				vbTestDummy2Yes
   529  		}
   530  		g.NextBlock(blockName, nil, outs[1:],
   531  			chaingen.ReplaceBlockVersion(4),
   532  			chaingen.ReplaceStakeVersion(4),
   533  			chaingen.ReplaceVotes(voteBits, 4))
   534  		totalVotes += ticketsPerBlock
   535  		g.SaveTipCoinbaseOuts()
   536  		g.AcceptTipBlock()
   537  	}
   538  	g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*6 - 1))
   539  	g.AssertBlockVersion(4)
   540  	g.AssertStakeVersion(4)
   541  	g.TestThresholdStateChoice(testDummy1ID, ThresholdStarted, invalidChoice)
   542  	g.TestThresholdStateChoice(testDummy2ID, ThresholdStarted, invalidChoice)
   543  
   544  	// ---------------------------------------------------------------------
   545  	// Generate enough blocks to reach the next rule change interval with
   546  	// block version 4, stake version 4, and vote version 4.  Also, set the
   547  	// vote bits to yes for the first test dummy agenda and no to the second
   548  	// one.
   549  	//
   550  	// The treshold state for the first dummy deployment must move to
   551  	// lockedin since a majority yes vote was achieved while the second
   552  	// dummy deployment must move to failed since a majority no vote was
   553  	// achieved.
   554  	// ---------------------------------------------------------------------
   555  
   556  	blocksNeeded = stakeValidationHeight + ruleChangeInterval*7 - 1 -
   557  		int64(g.Tip().Header.Height)
   558  	for i := int64(0); i < blocksNeeded; i++ {
   559  		outs := g.OldestCoinbaseOuts()
   560  		blockName := fmt.Sprintf("bsvtJ%d", i)
   561  		g.NextBlock(blockName, nil, outs[1:],
   562  			chaingen.ReplaceBlockVersion(4),
   563  			chaingen.ReplaceStakeVersion(4),
   564  			chaingen.ReplaceVotes(vbPrevBlockValid|vbTestDummy1Yes|
   565  				vbTestDummy2No, 4))
   566  		g.SaveTipCoinbaseOuts()
   567  		g.AcceptTipBlock()
   568  	}
   569  	g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*7 - 1))
   570  	g.AssertBlockVersion(4)
   571  	g.AssertStakeVersion(4)
   572  	g.TestThresholdStateChoice(testDummy1ID, ThresholdLockedIn, testDummy1YesIndex)
   573  	g.TestThresholdStateChoice(testDummy2ID, ThresholdFailed, testDummy2NoIndex)
   574  
   575  	// ---------------------------------------------------------------------
   576  	// Generate enough blocks to reach the next rule change interval with
   577  	// block version 4, stake version 4, and vote version 4.  Also, set the
   578  	// vote bits to include no votes for the first test dummy agenda and
   579  	// yes votes for the second one.
   580  	//
   581  	// The treshold state for the first dummy deployment must move to active
   582  	// since even though the interval had a majority no votes, lockedin
   583  	// status has already been achieved and can't be undone without a new
   584  	// agenda.  Similarly, the second one must remain in failed even though
   585  	// the interval had a majority yes votes since a failed state can't be
   586  	// undone.
   587  	// ---------------------------------------------------------------------
   588  
   589  	blocksNeeded = stakeValidationHeight + ruleChangeInterval*8 - 1 -
   590  		int64(g.Tip().Header.Height)
   591  	for i := int64(0); i < blocksNeeded; i++ {
   592  		outs := g.OldestCoinbaseOuts()
   593  		blockName := fmt.Sprintf("bsvtK%d", i)
   594  		g.NextBlock(blockName, nil, outs[1:],
   595  			chaingen.ReplaceBlockVersion(4),
   596  			chaingen.ReplaceStakeVersion(4),
   597  			chaingen.ReplaceVotes(vbPrevBlockValid|vbTestDummy1No|
   598  				vbTestDummy2Yes, 4))
   599  		g.SaveTipCoinbaseOuts()
   600  		g.AcceptTipBlock()
   601  	}
   602  	g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*8 - 1))
   603  	g.AssertBlockVersion(4)
   604  	g.AssertStakeVersion(4)
   605  	g.TestThresholdStateChoice(testDummy1ID, ThresholdActive, testDummy1YesIndex)
   606  	g.TestThresholdStateChoice(testDummy2ID, ThresholdFailed, testDummy2NoIndex)
   607  }