github.com/decred/dcrd/blockchain@v1.2.1/votebits_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  	"testing"
     9  	"time"
    10  
    11  	"github.com/decred/dcrd/blockchain/stake"
    12  	"github.com/decred/dcrd/chaincfg"
    13  )
    14  
    15  var (
    16  	posVersion = uint32(4)
    17  	powVersion = int32(4)
    18  
    19  	pedro = chaincfg.Vote{
    20  		Id:          "voteforpedro",
    21  		Description: "You should always vote for Pedro",
    22  		Mask:        0x6, // 0b0110
    23  		Choices: []chaincfg.Choice{
    24  			{
    25  				Id:          "Abstain",
    26  				Description: "Abstain voting for Pedro",
    27  				Bits:        0x0, // 0b0000
    28  				IsAbstain:   true,
    29  				IsNo:        false,
    30  			},
    31  			{
    32  				Id:          "Yes",
    33  				Description: "Vote for Pedro",
    34  				Bits:        0x2, // 0b0010
    35  				IsAbstain:   false,
    36  				IsNo:        false,
    37  			},
    38  			{
    39  				Id:          "No",
    40  				Description: "Dont vote for Pedro",
    41  				Bits:        0x4, // 0b0100
    42  				IsAbstain:   false,
    43  				IsNo:        true,
    44  			},
    45  		},
    46  	}
    47  
    48  	multipleChoice = chaincfg.Vote{
    49  		Id:          "multiplechoice",
    50  		Description: "Pick one",
    51  		Mask:        0x70, // 0b0111 0000
    52  		Choices: []chaincfg.Choice{
    53  			{
    54  				Id:          "Abstain",
    55  				Description: "Abstain multiple choice",
    56  				Bits:        0x0, // 0b0000 0000
    57  				IsAbstain:   true,
    58  				IsNo:        false,
    59  			},
    60  			{
    61  				Id:          "one",
    62  				Description: "Choice 1",
    63  				Bits:        0x10, // 0b0001 0000
    64  				IsAbstain:   false,
    65  				IsNo:        false,
    66  			},
    67  			{
    68  				Id:          "Vote against",
    69  				Description: "Vote against all multiple ",
    70  				Bits:        0x20, // 0b0010 0000
    71  				IsAbstain:   false,
    72  				IsNo:        true,
    73  			},
    74  			{
    75  				Id:          "two",
    76  				Description: "Choice 2",
    77  				Bits:        0x30, // 0b0011 0000
    78  				IsAbstain:   false,
    79  				IsNo:        false,
    80  			},
    81  			{
    82  				Id:          "three",
    83  				Description: "Choice 3",
    84  				Bits:        0x40, // 0b0100 0000
    85  				IsAbstain:   false,
    86  				IsNo:        false,
    87  			},
    88  			{
    89  				Id:          "four",
    90  				Description: "Choice 4",
    91  				Bits:        0x50, // 0b0101 0000
    92  				IsAbstain:   false,
    93  				IsNo:        false,
    94  			},
    95  		},
    96  	}
    97  )
    98  
    99  // defaultParams returns net parameters modified to have a single known
   100  // deployment that is used throughout the various votebit tests.
   101  func defaultParams(vote chaincfg.Vote) chaincfg.Params {
   102  	params := chaincfg.RegNetParams
   103  	params.Deployments = make(map[uint32][]chaincfg.ConsensusDeployment)
   104  	params.Deployments[posVersion] = []chaincfg.ConsensusDeployment{{
   105  		Vote: vote,
   106  		StartTime: uint64(time.Now().Add(time.Duration(
   107  			params.RuleChangeActivationInterval) *
   108  			time.Second).Unix()),
   109  		ExpireTime: uint64(time.Now().Add(24 * time.Hour).Unix()),
   110  	}}
   111  
   112  	return params
   113  }
   114  
   115  // TestNoQuorum ensures that the quorum behavior works as expected with no
   116  // votes.
   117  func TestNoQuorum(t *testing.T) {
   118  	params := defaultParams(pedro)
   119  	bc := newFakeChain(&params)
   120  	node := bc.bestChain.Tip()
   121  	node.stakeVersion = posVersion
   122  
   123  	// get to svi
   124  	curTimestamp := time.Now()
   125  	for i := uint32(0); i < uint32(params.StakeValidationHeight); i++ {
   126  		node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
   127  		bc.bestChain.SetTip(node)
   128  		bc.index.AddNode(node)
   129  		curTimestamp = curTimestamp.Add(time.Second)
   130  	}
   131  	ts, err := bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
   132  	if err != nil {
   133  		t.Fatalf("NextThresholdState(SVI): %v", err)
   134  	}
   135  	tse := ThresholdStateTuple{
   136  		State:  ThresholdDefined,
   137  		Choice: invalidChoice,
   138  	}
   139  	if ts != tse {
   140  		t.Fatalf("expected %+v got %+v", ts, tse)
   141  	}
   142  
   143  	// get to started
   144  	for i := uint32(0); i < params.RuleChangeActivationInterval-1; i++ {
   145  		// Set stake versions and vote bits.
   146  		node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
   147  		appendFakeVotes(node, params.TicketsPerBlock, posVersion, 0x01)
   148  		bc.bestChain.SetTip(node)
   149  		bc.index.AddNode(node)
   150  		curTimestamp = curTimestamp.Add(time.Second)
   151  	}
   152  
   153  	ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
   154  	if err != nil {
   155  		t.Fatalf("NextThresholdState(started): %v", err)
   156  	}
   157  	tse = ThresholdStateTuple{
   158  		State:  ThresholdStarted,
   159  		Choice: invalidChoice,
   160  	}
   161  	if ts != tse {
   162  		t.Fatalf("expected %+v got %+v", ts, tse)
   163  	}
   164  
   165  	// get to quorum - 1
   166  	voteCount := uint32(0)
   167  	for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
   168  		// Set stake versions and vote bits.
   169  		node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
   170  		for x := 0; x < int(params.TicketsPerBlock); x++ {
   171  			bits := uint16(0x01) // abstain
   172  			if voteCount < params.RuleChangeActivationQuorum-1 {
   173  				bits = 0x05 // vote no
   174  			}
   175  			appendFakeVotes(node, 1, posVersion, bits)
   176  			voteCount++
   177  		}
   178  
   179  		bc.bestChain.SetTip(node)
   180  		bc.index.AddNode(node)
   181  		curTimestamp = curTimestamp.Add(time.Second)
   182  	}
   183  
   184  	ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
   185  	if err != nil {
   186  		t.Fatalf("NextThresholdState(quorum-1): %v", err)
   187  	}
   188  	tse = ThresholdStateTuple{
   189  		State:  ThresholdStarted,
   190  		Choice: invalidChoice,
   191  	}
   192  	if ts != tse {
   193  		t.Fatalf("expected %+v got %+v", ts, tse)
   194  	}
   195  
   196  	// get to exact quorum but with 75%%-1 yes votes
   197  	voteCount = uint32(0)
   198  	for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
   199  		// Set stake versions and vote bits.
   200  		node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
   201  		for x := 0; x < int(params.TicketsPerBlock); x++ {
   202  			// 119 no, 41 yes -> 120 == 75% and 120 reaches quorum
   203  			bits := uint16(0x01) // abstain
   204  			quorum := params.RuleChangeActivationQuorum*
   205  				params.RuleChangeActivationMultiplier/
   206  				params.RuleChangeActivationDivisor - 1
   207  			if voteCount < quorum {
   208  				bits = 0x05 // vote no
   209  			} else if voteCount < params.RuleChangeActivationQuorum {
   210  				bits = 0x03 // vote yes
   211  			}
   212  			appendFakeVotes(node, 1, posVersion, bits)
   213  			voteCount++
   214  		}
   215  
   216  		bc.bestChain.SetTip(node)
   217  		bc.index.AddNode(node)
   218  		curTimestamp = curTimestamp.Add(time.Second)
   219  	}
   220  
   221  	ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
   222  	if err != nil {
   223  		t.Fatalf("NextThresholdState(quorum 75%%-1): %v", err)
   224  	}
   225  	tse = ThresholdStateTuple{
   226  		State:  ThresholdStarted,
   227  		Choice: invalidChoice,
   228  	}
   229  	if ts != tse {
   230  		t.Fatalf("expected %+v got %+v", ts, tse)
   231  	}
   232  
   233  	// get to exact quorum with exactly 75% of votes
   234  	voteCount = uint32(0)
   235  	for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
   236  		// Set stake versions and vote bits.
   237  		node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
   238  		for x := 0; x < int(params.TicketsPerBlock); x++ {
   239  			// 120 no, 40 yes -> 120 == 75% and 120 reaches quorum
   240  			bits := uint16(0x01) // abstain
   241  			quorum := params.RuleChangeActivationQuorum *
   242  				params.RuleChangeActivationMultiplier /
   243  				params.RuleChangeActivationDivisor
   244  			if voteCount < quorum {
   245  				bits = 0x05 // vote no
   246  			} else if voteCount < params.RuleChangeActivationQuorum {
   247  				bits = 0x03 // vote yes
   248  			}
   249  			appendFakeVotes(node, 1, posVersion, bits)
   250  			voteCount++
   251  		}
   252  
   253  		bc.bestChain.SetTip(node)
   254  		bc.index.AddNode(node)
   255  		curTimestamp = curTimestamp.Add(time.Second)
   256  	}
   257  
   258  	ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
   259  	if err != nil {
   260  		t.Fatalf("NextThresholdState(quorum 75%%): %v", err)
   261  	}
   262  	tse = ThresholdStateTuple{
   263  		State:  ThresholdFailed,
   264  		Choice: 0x02,
   265  	}
   266  	if ts != tse {
   267  		t.Fatalf("expected %+v got %+v", ts, tse)
   268  	}
   269  }
   270  
   271  // TestNoQuorum ensures that the quorum behavior works as expected with yes
   272  // votes.
   273  func TestYesQuorum(t *testing.T) {
   274  	params := defaultParams(pedro)
   275  	bc := newFakeChain(&params)
   276  	node := bc.bestChain.Tip()
   277  	node.stakeVersion = posVersion
   278  
   279  	// get to svi
   280  	curTimestamp := time.Now()
   281  	for i := uint32(0); i < uint32(params.StakeValidationHeight); i++ {
   282  		node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
   283  
   284  		bc.bestChain.SetTip(node)
   285  		bc.index.AddNode(node)
   286  		curTimestamp = curTimestamp.Add(time.Second)
   287  	}
   288  	ts, err := bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
   289  	if err != nil {
   290  		t.Fatalf("NextThresholdState(SVI): %v", err)
   291  	}
   292  	tse := ThresholdStateTuple{
   293  		State:  ThresholdDefined,
   294  		Choice: invalidChoice,
   295  	}
   296  	if ts != tse {
   297  		t.Fatalf("expected %+v got %+v", ts, tse)
   298  	}
   299  
   300  	// get to started
   301  	for i := uint32(0); i < params.RuleChangeActivationInterval-1; i++ {
   302  		// Set stake versions and vote bits.
   303  		node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
   304  		appendFakeVotes(node, params.TicketsPerBlock, posVersion, 0x01)
   305  		bc.bestChain.SetTip(node)
   306  		bc.index.AddNode(node)
   307  		curTimestamp = curTimestamp.Add(time.Second)
   308  	}
   309  
   310  	ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
   311  	if err != nil {
   312  		t.Fatalf("NextThresholdState(started): %v", err)
   313  	}
   314  	tse = ThresholdStateTuple{
   315  		State:  ThresholdStarted,
   316  		Choice: invalidChoice,
   317  	}
   318  	if ts != tse {
   319  		t.Fatalf("expected %+v got %+v", ts, tse)
   320  	}
   321  
   322  	// get to quorum - 1
   323  	voteCount := uint32(0)
   324  	for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
   325  		// Set stake versions and vote bits.
   326  		node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
   327  		for x := 0; x < int(params.TicketsPerBlock); x++ {
   328  			bits := uint16(0x01) // abstain
   329  			if voteCount < params.RuleChangeActivationQuorum-1 {
   330  				bits = 0x03 // vote yes
   331  			}
   332  			appendFakeVotes(node, 1, posVersion, bits)
   333  			voteCount++
   334  		}
   335  
   336  		bc.bestChain.SetTip(node)
   337  		bc.index.AddNode(node)
   338  		curTimestamp = curTimestamp.Add(time.Second)
   339  	}
   340  
   341  	ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
   342  	if err != nil {
   343  		t.Fatalf("NextThresholdState(quorum-1): %v", err)
   344  	}
   345  	tse = ThresholdStateTuple{
   346  		State:  ThresholdStarted,
   347  		Choice: invalidChoice,
   348  	}
   349  	if ts != tse {
   350  		t.Fatalf("expected %+v got %+v", ts, tse)
   351  	}
   352  
   353  	// get to exact quorum but with 75%-1 yes votes
   354  	voteCount = uint32(0)
   355  	for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
   356  		// Set stake versions and vote bits.
   357  		node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
   358  		for x := 0; x < int(params.TicketsPerBlock); x++ {
   359  			// 119 yes, 41 no -> 120 == 75% and 120 reaches quorum
   360  			bits := uint16(0x01) // abstain
   361  			quorum := params.RuleChangeActivationQuorum*
   362  				params.RuleChangeActivationMultiplier/
   363  				params.RuleChangeActivationDivisor - 1
   364  			if voteCount < quorum {
   365  				bits = 0x03 // vote yes
   366  			} else if voteCount < params.RuleChangeActivationQuorum {
   367  				bits = 0x05 // vote no
   368  			}
   369  			appendFakeVotes(node, 1, posVersion, bits)
   370  			voteCount++
   371  		}
   372  
   373  		bc.bestChain.SetTip(node)
   374  		bc.index.AddNode(node)
   375  		curTimestamp = curTimestamp.Add(time.Second)
   376  	}
   377  
   378  	ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
   379  	if err != nil {
   380  		t.Fatalf("NextThresholdState(quorum 75%%-1): %v", err)
   381  	}
   382  	tse = ThresholdStateTuple{
   383  		State:  ThresholdStarted,
   384  		Choice: invalidChoice,
   385  	}
   386  	if ts != tse {
   387  		t.Fatalf("expected %+v got %+v", ts, tse)
   388  	}
   389  
   390  	// get to exact quorum with exactly 75% of votes
   391  	voteCount = uint32(0)
   392  	for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
   393  		// Set stake versions and vote bits.
   394  		node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
   395  		for x := 0; x < int(params.TicketsPerBlock); x++ {
   396  			// 120 yes, 40 no -> 120 == 75% and 120 reaches quorum
   397  			bits := uint16(0x01) // abstain
   398  			quorum := params.RuleChangeActivationQuorum *
   399  				params.RuleChangeActivationMultiplier /
   400  				params.RuleChangeActivationDivisor
   401  			if voteCount < quorum {
   402  				bits = 0x03 // vote yes
   403  			} else if voteCount < params.RuleChangeActivationQuorum {
   404  				bits = 0x05 // vote no
   405  			}
   406  			appendFakeVotes(node, 1, posVersion, bits)
   407  			voteCount++
   408  		}
   409  
   410  		bc.bestChain.SetTip(node)
   411  		bc.index.AddNode(node)
   412  		curTimestamp = curTimestamp.Add(time.Second)
   413  	}
   414  
   415  	ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
   416  	if err != nil {
   417  		t.Fatalf("NextThresholdState(quorum 75%%): %v", err)
   418  	}
   419  	tse = ThresholdStateTuple{
   420  		State:  ThresholdLockedIn,
   421  		Choice: 0x01,
   422  	}
   423  	if ts != tse {
   424  		t.Fatalf("expected %+v got %+v", ts, tse)
   425  	}
   426  }
   427  
   428  // TestVoting ensure the overall voting of an agenda works as expected including
   429  // a wide variety of conditions.
   430  func TestVoting(t *testing.T) {
   431  	params := defaultParams(chaincfg.Vote{})
   432  	rci := params.RuleChangeActivationInterval
   433  	svh := uint32(params.StakeValidationHeight)
   434  	svi := uint32(params.StakeVersionInterval)
   435  
   436  	type voteCount struct {
   437  		vote  stake.VoteVersionTuple
   438  		count uint32
   439  	}
   440  
   441  	tests := []struct {
   442  		name              string
   443  		vote              chaincfg.Vote
   444  		blockVersion      int32
   445  		startStakeVersion uint32
   446  		end               func(time.Time) uint64
   447  		voteBitsCounts    []voteCount
   448  		expectedState     []ThresholdStateTuple
   449  	}{
   450  		{
   451  			name:              "pedro too shallow",
   452  			vote:              pedro,
   453  			blockVersion:      powVersion,
   454  			startStakeVersion: posVersion,
   455  			voteBitsCounts: []voteCount{{
   456  				vote: stake.VoteVersionTuple{
   457  					Version: posVersion,
   458  					Bits:    0x01,
   459  				},
   460  				count: svh - 1,
   461  			}},
   462  			expectedState: []ThresholdStateTuple{{
   463  				State:  ThresholdDefined,
   464  				Choice: invalidChoice,
   465  			}},
   466  		},
   467  		{
   468  			name:              "pedro greater PoS version",
   469  			vote:              pedro,
   470  			blockVersion:      powVersion,
   471  			startStakeVersion: posVersion - 1,
   472  			voteBitsCounts: []voteCount{{
   473  				vote: stake.VoteVersionTuple{
   474  					Version: posVersion,
   475  					Bits:    0x01,
   476  				},
   477  				count: svh,
   478  			}, {
   479  				vote: stake.VoteVersionTuple{
   480  					Version: posVersion,
   481  					Bits:    0x01,
   482  				},
   483  				count: svi - 1,
   484  			}, {
   485  				vote: stake.VoteVersionTuple{
   486  					Version: posVersion,
   487  					Bits:    0x01,
   488  				},
   489  				count: rci - svi,
   490  			}, {
   491  				vote: stake.VoteVersionTuple{
   492  					Version: posVersion,
   493  					Bits:    0x03,
   494  				},
   495  				count: rci,
   496  			}},
   497  			expectedState: []ThresholdStateTuple{{
   498  				State:  ThresholdDefined,
   499  				Choice: invalidChoice,
   500  			}, {
   501  				State:  ThresholdDefined,
   502  				Choice: invalidChoice,
   503  			}, {
   504  				State:  ThresholdStarted,
   505  				Choice: invalidChoice,
   506  			}, {
   507  				State:  ThresholdLockedIn,
   508  				Choice: 1,
   509  			}},
   510  		},
   511  		{
   512  			name:              "pedro greater PoS version calcStakeVersion",
   513  			vote:              pedro,
   514  			blockVersion:      powVersion,
   515  			startStakeVersion: posVersion - 1,
   516  			voteBitsCounts: []voteCount{{
   517  				vote: stake.VoteVersionTuple{
   518  					Version: posVersion - 1,
   519  					Bits:    0x01,
   520  				},
   521  				count: svh + 2*svi - 1,
   522  			}, {
   523  				vote: stake.VoteVersionTuple{
   524  					Version: posVersion,
   525  					Bits:    0x01,
   526  				},
   527  				count: rci % svi,
   528  			}, {
   529  				vote: stake.VoteVersionTuple{
   530  					Version: posVersion,
   531  					Bits:    0x01,
   532  				},
   533  				count: rci,
   534  			}, {
   535  				vote: stake.VoteVersionTuple{
   536  					Version: posVersion,
   537  					Bits:    0x03},
   538  				count: rci,
   539  			}},
   540  			expectedState: []ThresholdStateTuple{{
   541  				State:  ThresholdDefined,
   542  				Choice: invalidChoice,
   543  			}, {
   544  				State:  ThresholdDefined,
   545  				Choice: invalidChoice,
   546  			}, {
   547  				State:  ThresholdStarted,
   548  				Choice: invalidChoice,
   549  			}, {
   550  				State:  ThresholdLockedIn,
   551  				Choice: 1,
   552  			}},
   553  		},
   554  		{
   555  			name:              "pedro smaller PoS version",
   556  			vote:              pedro,
   557  			blockVersion:      powVersion,
   558  			startStakeVersion: posVersion + 1,
   559  			voteBitsCounts: []voteCount{{
   560  				vote: stake.VoteVersionTuple{
   561  					Version: posVersion,
   562  					Bits:    0x01,
   563  				},
   564  				count: svh,
   565  			}, {
   566  				vote: stake.VoteVersionTuple{
   567  					Version: posVersion,
   568  					Bits:    0x01,
   569  				},
   570  				count: rci - 1,
   571  			}, {
   572  				vote: stake.VoteVersionTuple{
   573  					Version: posVersion,
   574  					Bits:    0x03,
   575  				},
   576  				count: rci,
   577  			}, {
   578  				vote: stake.VoteVersionTuple{
   579  					Version: posVersion,
   580  					Bits:    0x03,
   581  				},
   582  				count: rci,
   583  			}},
   584  			expectedState: []ThresholdStateTuple{{
   585  				State:  ThresholdDefined,
   586  				Choice: invalidChoice,
   587  			}, {
   588  				State:  ThresholdStarted,
   589  				Choice: invalidChoice,
   590  			}, {
   591  				State:  ThresholdLockedIn,
   592  				Choice: 0x01,
   593  			}, {
   594  				State:  ThresholdActive,
   595  				Choice: 0x01,
   596  			}},
   597  		},
   598  		{
   599  			name:              "pedro smaller PoW version",
   600  			vote:              pedro,
   601  			blockVersion:      powVersion - 1,
   602  			startStakeVersion: posVersion,
   603  			voteBitsCounts: []voteCount{{
   604  				vote: stake.VoteVersionTuple{
   605  					Version: posVersion,
   606  					Bits:    0x01,
   607  				},
   608  				count: svh,
   609  			}, {
   610  				vote: stake.VoteVersionTuple{
   611  					Version: posVersion,
   612  					Bits:    0x01,
   613  				},
   614  				count: rci - 1,
   615  			}, {
   616  				vote: stake.VoteVersionTuple{
   617  					Version: posVersion,
   618  					Bits:    0x03,
   619  				},
   620  				count: rci,
   621  			}},
   622  			expectedState: []ThresholdStateTuple{{
   623  				State:  ThresholdDefined,
   624  				Choice: invalidChoice,
   625  			}, {
   626  				State:  ThresholdDefined,
   627  				Choice: invalidChoice,
   628  			}, {
   629  				State:  ThresholdDefined,
   630  				Choice: invalidChoice,
   631  			}},
   632  		},
   633  		{
   634  			name:              "pedro greater PoW version",
   635  			vote:              pedro,
   636  			blockVersion:      powVersion + 1,
   637  			startStakeVersion: posVersion,
   638  			voteBitsCounts: []voteCount{{
   639  				vote: stake.VoteVersionTuple{
   640  					Version: posVersion,
   641  					Bits:    0x01,
   642  				},
   643  				count: svh,
   644  			}, {
   645  				vote: stake.VoteVersionTuple{
   646  					Version: posVersion,
   647  					Bits:    0x01,
   648  				},
   649  				count: rci - 1,
   650  			}, {
   651  				vote: stake.VoteVersionTuple{
   652  					Version: posVersion,
   653  					Bits:    0x03,
   654  				},
   655  				count: rci,
   656  			}, {
   657  				vote: stake.VoteVersionTuple{
   658  					Version: posVersion,
   659  					Bits:    0x03,
   660  				},
   661  				count: rci,
   662  			}},
   663  			expectedState: []ThresholdStateTuple{{
   664  				State:  ThresholdDefined,
   665  				Choice: invalidChoice,
   666  			}, {
   667  				State:  ThresholdStarted,
   668  				Choice: invalidChoice,
   669  			}, {
   670  				State:  ThresholdLockedIn,
   671  				Choice: 0x01,
   672  			}, {
   673  				State:  ThresholdActive,
   674  				Choice: 0x01,
   675  			}},
   676  		},
   677  		{
   678  			name:              "pedro 100% yes, wrong version",
   679  			vote:              pedro,
   680  			blockVersion:      powVersion,
   681  			startStakeVersion: posVersion,
   682  			voteBitsCounts: []voteCount{{
   683  				vote: stake.VoteVersionTuple{
   684  					Version: posVersion + 1,
   685  					Bits:    0x01,
   686  				},
   687  				count: svh,
   688  			}, {
   689  				vote: stake.VoteVersionTuple{
   690  					Version: posVersion + 1,
   691  					Bits:    0x01,
   692  				},
   693  				count: rci - 1,
   694  			}, {
   695  				vote: stake.VoteVersionTuple{
   696  					Version: posVersion + 1,
   697  					Bits:    0x03,
   698  				},
   699  				count: rci,
   700  			}, {
   701  				vote: stake.VoteVersionTuple{
   702  					Version: posVersion + 1,
   703  					Bits:    0x03,
   704  				},
   705  				count: rci,
   706  			}, {
   707  				vote: stake.VoteVersionTuple{
   708  					Version: posVersion,
   709  					Bits:    0x03,
   710  				},
   711  				count: rci,
   712  			}, {
   713  				vote: stake.VoteVersionTuple{
   714  					Version: posVersion,
   715  					Bits:    0x03,
   716  				},
   717  				count: rci,
   718  			}, {
   719  				vote: stake.VoteVersionTuple{
   720  					Version: posVersion + 1,
   721  					Bits:    0x01,
   722  				},
   723  				count: rci,
   724  			}},
   725  			expectedState: []ThresholdStateTuple{{
   726  				State:  ThresholdDefined,
   727  				Choice: invalidChoice,
   728  			}, {
   729  				State:  ThresholdStarted,
   730  				Choice: invalidChoice,
   731  			}, {
   732  				State:  ThresholdStarted,
   733  				Choice: invalidChoice,
   734  			}, {
   735  				State:  ThresholdStarted,
   736  				Choice: invalidChoice,
   737  			}, {
   738  				State:  ThresholdLockedIn,
   739  				Choice: 0x01,
   740  			}, {
   741  				State:  ThresholdActive,
   742  				Choice: 0x01,
   743  			}, {
   744  				State:  ThresholdActive,
   745  				Choice: 0x01,
   746  			}},
   747  		},
   748  		{
   749  			name:              "pedro 100% yes",
   750  			vote:              pedro,
   751  			blockVersion:      powVersion,
   752  			startStakeVersion: posVersion,
   753  			voteBitsCounts: []voteCount{{
   754  				vote: stake.VoteVersionTuple{
   755  					Version: posVersion,
   756  					Bits:    0x01,
   757  				},
   758  				count: svh,
   759  			}, {
   760  				vote: stake.VoteVersionTuple{
   761  					Version: posVersion,
   762  					Bits:    0x01,
   763  				},
   764  				count: rci - 1,
   765  			}, {
   766  				vote: stake.VoteVersionTuple{
   767  					Version: posVersion,
   768  					Bits:    0x03,
   769  				},
   770  				count: rci,
   771  			}, {
   772  				vote: stake.VoteVersionTuple{
   773  					Version: posVersion,
   774  					Bits:    0x03,
   775  				},
   776  				count: rci,
   777  			}, {
   778  				vote: stake.VoteVersionTuple{
   779  					Version: posVersion,
   780  					Bits:    0x01,
   781  				},
   782  				count: rci,
   783  			}},
   784  			expectedState: []ThresholdStateTuple{{
   785  				State:  ThresholdDefined,
   786  				Choice: invalidChoice,
   787  			}, {
   788  				State:  ThresholdStarted,
   789  				Choice: invalidChoice,
   790  			}, {
   791  				State:  ThresholdLockedIn,
   792  				Choice: 0x01,
   793  			}, {
   794  				State:  ThresholdActive,
   795  				Choice: 0x01,
   796  			}, {
   797  				State:  ThresholdActive,
   798  				Choice: 0x01,
   799  			}},
   800  		},
   801  		{
   802  			name:              "pedro 100% no",
   803  			vote:              pedro,
   804  			blockVersion:      powVersion,
   805  			startStakeVersion: posVersion,
   806  			voteBitsCounts: []voteCount{{
   807  				vote: stake.VoteVersionTuple{
   808  					Version: posVersion,
   809  					Bits:    0x01,
   810  				},
   811  				count: svh,
   812  			}, {
   813  				vote: stake.VoteVersionTuple{
   814  					Version: posVersion,
   815  					Bits:    0x01,
   816  				},
   817  				count: rci - 1,
   818  			}, {
   819  				vote: stake.VoteVersionTuple{
   820  					Version: posVersion,
   821  					Bits:    0x05,
   822  				},
   823  				count: rci,
   824  			}, {
   825  				vote: stake.VoteVersionTuple{
   826  					Version: posVersion,
   827  					Bits:    0x05,
   828  				},
   829  				count: rci,
   830  			}, {
   831  				vote: stake.VoteVersionTuple{
   832  					Version: posVersion,
   833  					Bits:    0x01,
   834  				},
   835  				count: rci,
   836  			}},
   837  			expectedState: []ThresholdStateTuple{{
   838  				State:  ThresholdDefined,
   839  				Choice: invalidChoice,
   840  			}, {
   841  				State:  ThresholdStarted,
   842  				Choice: invalidChoice,
   843  			}, {
   844  				State:  ThresholdFailed,
   845  				Choice: 0x02,
   846  			}, {
   847  				State:  ThresholdFailed,
   848  				Choice: 0x02,
   849  			}, {
   850  				State:  ThresholdFailed,
   851  				Choice: 0x02,
   852  			}},
   853  		},
   854  		{
   855  			name:              "pedro 100% invalid",
   856  			vote:              pedro,
   857  			blockVersion:      powVersion,
   858  			startStakeVersion: posVersion,
   859  			voteBitsCounts: []voteCount{{
   860  				vote: stake.VoteVersionTuple{
   861  					Version: posVersion,
   862  					Bits:    0x01,
   863  				},
   864  				count: svh,
   865  			}, {
   866  				vote: stake.VoteVersionTuple{
   867  					Version: posVersion,
   868  					Bits:    0x01,
   869  				},
   870  				count: rci - 1,
   871  			}, {
   872  				vote: stake.VoteVersionTuple{
   873  					Version: posVersion,
   874  					Bits:    0x06,
   875  				},
   876  				count: rci,
   877  			}, {
   878  				vote: stake.VoteVersionTuple{
   879  					Version: posVersion,
   880  					Bits:    0x06,
   881  				},
   882  				count: rci,
   883  			}, {
   884  				vote: stake.VoteVersionTuple{
   885  					Version: posVersion,
   886  					Bits:    0x03,
   887  				},
   888  				count: rci,
   889  			}, {
   890  				vote: stake.VoteVersionTuple{
   891  					Version: posVersion,
   892  					Bits:    0x01,
   893  				},
   894  				count: rci,
   895  			}},
   896  			expectedState: []ThresholdStateTuple{{
   897  				State:  ThresholdDefined,
   898  				Choice: invalidChoice,
   899  			}, {
   900  				State:  ThresholdStarted,
   901  				Choice: invalidChoice,
   902  			}, {
   903  				State:  ThresholdStarted,
   904  				Choice: invalidChoice,
   905  			}, {
   906  				State:  ThresholdStarted,
   907  				Choice: invalidChoice,
   908  			}, {
   909  				State:  ThresholdLockedIn,
   910  				Choice: 0x01,
   911  			}, {
   912  				State:  ThresholdActive,
   913  				Choice: 0x01,
   914  			}},
   915  		},
   916  		{
   917  			name:              "pedro 100% abstain",
   918  			vote:              pedro,
   919  			blockVersion:      powVersion,
   920  			startStakeVersion: posVersion,
   921  			voteBitsCounts: []voteCount{{
   922  				vote: stake.VoteVersionTuple{
   923  					Version: posVersion,
   924  					Bits:    0x01,
   925  				},
   926  				count: svh,
   927  			}, {
   928  				vote: stake.VoteVersionTuple{
   929  					Version: posVersion,
   930  					Bits:    0x01,
   931  				},
   932  				count: rci - 1,
   933  			}, {
   934  				vote: stake.VoteVersionTuple{
   935  					Version: posVersion,
   936  					Bits:    0x01,
   937  				},
   938  				count: rci,
   939  			}, {
   940  				vote: stake.VoteVersionTuple{
   941  					Version: posVersion,
   942  					Bits:    0x01,
   943  				},
   944  				count: rci,
   945  			}, {
   946  				vote: stake.VoteVersionTuple{
   947  					Version: posVersion,
   948  					Bits:    0x01,
   949  				},
   950  				count: rci,
   951  			}},
   952  			expectedState: []ThresholdStateTuple{{
   953  				State:  ThresholdDefined,
   954  				Choice: invalidChoice,
   955  			}, {
   956  				State:  ThresholdStarted,
   957  				Choice: invalidChoice,
   958  			}, {
   959  				State:  ThresholdStarted,
   960  				Choice: invalidChoice,
   961  			}, {
   962  				State:  ThresholdStarted,
   963  				Choice: invalidChoice,
   964  			}, {
   965  				State:  ThresholdStarted,
   966  				Choice: invalidChoice,
   967  			}},
   968  		},
   969  		{
   970  			name:              "pedro expire before started",
   971  			vote:              pedro,
   972  			blockVersion:      powVersion,
   973  			startStakeVersion: posVersion - 1,
   974  			end: func(t time.Time) uint64 {
   975  				return uint64(t.Add(time.Second *
   976  					time.Duration(int64(svh)+int64(rci+rci/2))).Unix())
   977  			},
   978  			voteBitsCounts: []voteCount{{
   979  				vote: stake.VoteVersionTuple{
   980  					Version: posVersion - 1,
   981  					Bits:    0x01,
   982  				},
   983  				count: svh,
   984  			}, {
   985  				vote: stake.VoteVersionTuple{
   986  					Version: posVersion - 1,
   987  					Bits:    0x05,
   988  				},
   989  				count: rci - 1,
   990  			}, {
   991  				vote: stake.VoteVersionTuple{
   992  					Version: posVersion - 1,
   993  					Bits:    0x05,
   994  				},
   995  				count: rci,
   996  			}},
   997  			expectedState: []ThresholdStateTuple{{
   998  				State:  ThresholdDefined,
   999  				Choice: invalidChoice,
  1000  			}, {
  1001  				State:  ThresholdDefined,
  1002  				Choice: invalidChoice,
  1003  			}, {
  1004  				State:  ThresholdFailed,
  1005  				Choice: invalidChoice,
  1006  			}},
  1007  		},
  1008  		{
  1009  			name:              "pedro expire after started",
  1010  			vote:              pedro,
  1011  			blockVersion:      powVersion,
  1012  			startStakeVersion: posVersion,
  1013  			end: func(t time.Time) uint64 {
  1014  				return uint64(t.Add(time.Second *
  1015  					time.Duration(int64(svh)+int64(rci+rci/2))).Unix())
  1016  			},
  1017  			voteBitsCounts: []voteCount{{
  1018  				vote: stake.VoteVersionTuple{
  1019  					Version: posVersion,
  1020  					Bits:    0x01,
  1021  				},
  1022  				count: svh,
  1023  			}, {
  1024  				vote: stake.VoteVersionTuple{
  1025  					Version: posVersion,
  1026  					Bits:    0x05,
  1027  				},
  1028  				count: rci - 1,
  1029  			}, {
  1030  				vote: stake.VoteVersionTuple{
  1031  					Version: posVersion,
  1032  					Bits:    0x05,
  1033  				},
  1034  				count: rci,
  1035  			}},
  1036  			expectedState: []ThresholdStateTuple{{
  1037  				State:  ThresholdDefined,
  1038  				Choice: invalidChoice,
  1039  			}, {
  1040  				State:  ThresholdStarted,
  1041  				Choice: invalidChoice,
  1042  			}, {
  1043  				State:  ThresholdFailed,
  1044  				Choice: invalidChoice,
  1045  			}},
  1046  		},
  1047  		{
  1048  			name:              "pedro overlap",
  1049  			vote:              pedro,
  1050  			blockVersion:      powVersion,
  1051  			startStakeVersion: posVersion - 1,
  1052  			voteBitsCounts: []voteCount{{
  1053  				vote: stake.VoteVersionTuple{
  1054  					Version: posVersion - 1,
  1055  					Bits:    0x01,
  1056  				},
  1057  				count: svh + 19*svi - 1,
  1058  			}, {
  1059  				vote: stake.VoteVersionTuple{
  1060  					Version: posVersion,
  1061  					Bits:    0x01,
  1062  				},
  1063  				count: svi - 1,
  1064  			}, {
  1065  				vote: stake.VoteVersionTuple{
  1066  					Version: posVersion,
  1067  					Bits:    0x01,
  1068  				},
  1069  				count: 1,
  1070  			}, {
  1071  				vote: stake.VoteVersionTuple{
  1072  					Version: posVersion,
  1073  					Bits:    0x03,
  1074  				},
  1075  				count: rci - 1,
  1076  			}, {
  1077  				vote: stake.VoteVersionTuple{
  1078  					Version: posVersion,
  1079  					Bits:    0x03,
  1080  				},
  1081  				count: 1,
  1082  			}, {
  1083  				vote: stake.VoteVersionTuple{
  1084  					Version: posVersion,
  1085  					Bits:    0x01,
  1086  				},
  1087  				count: rci,
  1088  			}},
  1089  			expectedState: []ThresholdStateTuple{{
  1090  				State:  ThresholdDefined,
  1091  				Choice: invalidChoice,
  1092  			}, {
  1093  				State:  ThresholdDefined,
  1094  				Choice: invalidChoice,
  1095  			}, {
  1096  				State:  ThresholdStarted,
  1097  				Choice: invalidChoice,
  1098  			}, {
  1099  				State:  ThresholdStarted,
  1100  				Choice: invalidChoice,
  1101  			}, {
  1102  				State:  ThresholdLockedIn,
  1103  				Choice: 1,
  1104  			}, {
  1105  				State:  ThresholdActive,
  1106  				Choice: 1,
  1107  			}},
  1108  		},
  1109  		{
  1110  			name:              "multiple choice 100% abstain",
  1111  			vote:              multipleChoice,
  1112  			blockVersion:      powVersion,
  1113  			startStakeVersion: posVersion,
  1114  			voteBitsCounts: []voteCount{{
  1115  				vote: stake.VoteVersionTuple{
  1116  					Version: posVersion,
  1117  					Bits:    0x01,
  1118  				},
  1119  				count: svh,
  1120  			}, {
  1121  				vote: stake.VoteVersionTuple{
  1122  					Version: posVersion,
  1123  					Bits:    0x01,
  1124  				},
  1125  				count: rci - 1,
  1126  			}, {
  1127  				vote: stake.VoteVersionTuple{
  1128  					Version: posVersion,
  1129  					Bits:    0x01,
  1130  				},
  1131  				count: rci,
  1132  			}},
  1133  			expectedState: []ThresholdStateTuple{{
  1134  				State:  ThresholdDefined,
  1135  				Choice: invalidChoice,
  1136  			}, {
  1137  				State:  ThresholdStarted,
  1138  				Choice: invalidChoice,
  1139  			}, {
  1140  				State:  ThresholdStarted,
  1141  				Choice: invalidChoice,
  1142  			}},
  1143  		},
  1144  		{
  1145  			name:              "multiple choice 100% no",
  1146  			vote:              multipleChoice,
  1147  			blockVersion:      powVersion,
  1148  			startStakeVersion: posVersion,
  1149  			voteBitsCounts: []voteCount{{
  1150  				vote: stake.VoteVersionTuple{
  1151  					Version: posVersion,
  1152  					Bits:    0x01,
  1153  				},
  1154  				count: svh,
  1155  			}, {
  1156  				vote: stake.VoteVersionTuple{
  1157  					Version: posVersion,
  1158  					Bits:    0x01,
  1159  				},
  1160  				count: rci - 1,
  1161  			}, {
  1162  				vote: stake.VoteVersionTuple{
  1163  					Version: posVersion,
  1164  					Bits:    0x21,
  1165  				},
  1166  				count: rci,
  1167  			}},
  1168  			expectedState: []ThresholdStateTuple{{
  1169  				State:  ThresholdDefined,
  1170  				Choice: invalidChoice,
  1171  			}, {
  1172  				State:  ThresholdStarted,
  1173  				Choice: invalidChoice,
  1174  			}, {
  1175  				State:  ThresholdFailed,
  1176  				Choice: 2,
  1177  			}},
  1178  		},
  1179  		{
  1180  			name:              "multiple choice 100% choice 1",
  1181  			vote:              multipleChoice,
  1182  			blockVersion:      powVersion,
  1183  			startStakeVersion: posVersion,
  1184  			voteBitsCounts: []voteCount{{
  1185  				vote: stake.VoteVersionTuple{
  1186  					Version: posVersion,
  1187  					Bits:    0x01,
  1188  				},
  1189  				count: svh,
  1190  			}, {
  1191  				vote: stake.VoteVersionTuple{
  1192  					Version: posVersion,
  1193  					Bits:    0x01,
  1194  				},
  1195  				count: rci - 1,
  1196  			}, {
  1197  				vote: stake.VoteVersionTuple{
  1198  					Version: posVersion,
  1199  					Bits:    0x11,
  1200  				},
  1201  				count: rci,
  1202  			}, {
  1203  				vote: stake.VoteVersionTuple{
  1204  					Version: posVersion,
  1205  					Bits:    0x11,
  1206  				},
  1207  				count: rci,
  1208  			}, {
  1209  				vote: stake.VoteVersionTuple{
  1210  					Version: posVersion,
  1211  					Bits:    0x01,
  1212  				},
  1213  				count: rci,
  1214  			}},
  1215  			expectedState: []ThresholdStateTuple{{
  1216  				State:  ThresholdDefined,
  1217  				Choice: invalidChoice,
  1218  			}, {
  1219  				State:  ThresholdStarted,
  1220  				Choice: invalidChoice,
  1221  			}, {
  1222  				State:  ThresholdLockedIn,
  1223  				Choice: 1,
  1224  			}, {
  1225  				State:  ThresholdActive,
  1226  				Choice: 1,
  1227  			}, {
  1228  				State:  ThresholdActive,
  1229  				Choice: 1,
  1230  			}},
  1231  		},
  1232  		{
  1233  			name:              "multiple choice 100% choice 2",
  1234  			vote:              multipleChoice,
  1235  			blockVersion:      powVersion,
  1236  			startStakeVersion: posVersion,
  1237  			voteBitsCounts: []voteCount{{
  1238  				vote: stake.VoteVersionTuple{
  1239  					Version: posVersion,
  1240  					Bits:    0x01,
  1241  				},
  1242  				count: svh,
  1243  			}, {
  1244  				vote: stake.VoteVersionTuple{
  1245  					Version: posVersion,
  1246  					Bits:    0x01,
  1247  				},
  1248  				count: rci - 1,
  1249  			}, {
  1250  				vote: stake.VoteVersionTuple{
  1251  					Version: posVersion,
  1252  					Bits:    0x31,
  1253  				},
  1254  				count: rci,
  1255  			}, {
  1256  				vote: stake.VoteVersionTuple{
  1257  					Version: posVersion,
  1258  					Bits:    0x31,
  1259  				},
  1260  				count: rci,
  1261  			}, {
  1262  				vote: stake.VoteVersionTuple{
  1263  					Version: posVersion,
  1264  					Bits:    0x01,
  1265  				},
  1266  				count: rci,
  1267  			}},
  1268  			expectedState: []ThresholdStateTuple{{
  1269  				State:  ThresholdDefined,
  1270  				Choice: invalidChoice,
  1271  			}, {
  1272  				State:  ThresholdStarted,
  1273  				Choice: invalidChoice,
  1274  			}, {
  1275  				State:  ThresholdLockedIn,
  1276  				Choice: 3,
  1277  			}, {
  1278  				State:  ThresholdActive,
  1279  				Choice: 3,
  1280  			}, {
  1281  				State:  ThresholdActive,
  1282  				Choice: 3,
  1283  			}},
  1284  		},
  1285  		{
  1286  			name:              "multiple choice 100% choice 3",
  1287  			vote:              multipleChoice,
  1288  			blockVersion:      powVersion,
  1289  			startStakeVersion: posVersion,
  1290  			voteBitsCounts: []voteCount{{
  1291  				vote: stake.VoteVersionTuple{
  1292  					Version: posVersion,
  1293  					Bits:    0x01,
  1294  				},
  1295  				count: svh,
  1296  			}, {
  1297  				vote: stake.VoteVersionTuple{
  1298  					Version: posVersion,
  1299  					Bits:    0x01,
  1300  				},
  1301  				count: rci - 1,
  1302  			}, {
  1303  				vote: stake.VoteVersionTuple{
  1304  					Version: posVersion,
  1305  					Bits:    0x41,
  1306  				},
  1307  				count: rci,
  1308  			}, {
  1309  				vote: stake.VoteVersionTuple{
  1310  					Version: posVersion,
  1311  					Bits:    0x41,
  1312  				},
  1313  				count: rci,
  1314  			}, {
  1315  				vote: stake.VoteVersionTuple{
  1316  					Version: posVersion,
  1317  					Bits:    0x01,
  1318  				},
  1319  				count: rci,
  1320  			}},
  1321  			expectedState: []ThresholdStateTuple{{
  1322  				State:  ThresholdDefined,
  1323  				Choice: invalidChoice,
  1324  			}, {
  1325  				State:  ThresholdStarted,
  1326  				Choice: invalidChoice,
  1327  			}, {
  1328  				State:  ThresholdLockedIn,
  1329  				Choice: 4,
  1330  			}, {
  1331  				State:  ThresholdActive,
  1332  				Choice: 4,
  1333  			}, {
  1334  				State:  ThresholdActive,
  1335  				Choice: 4,
  1336  			}},
  1337  		},
  1338  		{
  1339  			name:              "multiple choice 100% choice 4",
  1340  			vote:              multipleChoice,
  1341  			blockVersion:      powVersion,
  1342  			startStakeVersion: posVersion,
  1343  			voteBitsCounts: []voteCount{{
  1344  				vote: stake.VoteVersionTuple{
  1345  					Version: posVersion,
  1346  					Bits:    0x01,
  1347  				},
  1348  				count: svh,
  1349  			}, {
  1350  				vote: stake.VoteVersionTuple{
  1351  					Version: posVersion,
  1352  					Bits:    0x01,
  1353  				},
  1354  				count: rci - 1,
  1355  			}, {
  1356  				vote: stake.VoteVersionTuple{
  1357  					Version: posVersion,
  1358  					Bits:    0x51,
  1359  				},
  1360  				count: rci,
  1361  			}, {
  1362  				vote: stake.VoteVersionTuple{
  1363  					Version: posVersion,
  1364  					Bits:    0x51,
  1365  				},
  1366  				count: rci,
  1367  			}, {
  1368  				vote: stake.VoteVersionTuple{
  1369  					Version: posVersion,
  1370  					Bits:    0x01,
  1371  				},
  1372  				count: rci,
  1373  			}},
  1374  			expectedState: []ThresholdStateTuple{{
  1375  				State:  ThresholdDefined,
  1376  				Choice: invalidChoice,
  1377  			}, {
  1378  				State:  ThresholdStarted,
  1379  				Choice: invalidChoice,
  1380  			}, {
  1381  				State:  ThresholdLockedIn,
  1382  				Choice: 5,
  1383  			}, {
  1384  				State:  ThresholdActive,
  1385  				Choice: 5,
  1386  			}, {
  1387  				State:  ThresholdActive,
  1388  				Choice: 5,
  1389  			}},
  1390  		},
  1391  		{
  1392  			name:              "multiple choice 100% choice 5 (invalid)",
  1393  			vote:              multipleChoice,
  1394  			blockVersion:      powVersion,
  1395  			startStakeVersion: posVersion,
  1396  			voteBitsCounts: []voteCount{{
  1397  				vote: stake.VoteVersionTuple{
  1398  					Version: posVersion,
  1399  					Bits:    0x01,
  1400  				},
  1401  				count: svh,
  1402  			}, {
  1403  				vote: stake.VoteVersionTuple{
  1404  					Version: posVersion,
  1405  					Bits:    0x01,
  1406  				},
  1407  				count: rci - 1,
  1408  			}, {
  1409  				vote: stake.VoteVersionTuple{
  1410  					Version: posVersion,
  1411  					Bits:    0x61,
  1412  				},
  1413  				count: rci,
  1414  			}, {
  1415  				vote: stake.VoteVersionTuple{
  1416  					Version: posVersion,
  1417  					Bits:    0x61,
  1418  				},
  1419  				count: rci,
  1420  			}, {
  1421  				vote: stake.VoteVersionTuple{
  1422  					Version: posVersion,
  1423  					Bits:    0x01,
  1424  				},
  1425  				count: rci,
  1426  			}},
  1427  			expectedState: []ThresholdStateTuple{{
  1428  				State:  ThresholdDefined,
  1429  				Choice: invalidChoice,
  1430  			}, {
  1431  				State:  ThresholdStarted,
  1432  				Choice: invalidChoice,
  1433  			}, {
  1434  				State:  ThresholdStarted,
  1435  				Choice: invalidChoice,
  1436  			}, {
  1437  				State:  ThresholdStarted,
  1438  				Choice: invalidChoice,
  1439  			}, {
  1440  				State:  ThresholdStarted,
  1441  				Choice: invalidChoice,
  1442  			}},
  1443  		},
  1444  	}
  1445  
  1446  	for _, test := range tests {
  1447  		// Reset params.
  1448  		params = defaultParams(test.vote)
  1449  		// We have to reset the cache for every test.
  1450  		bc := newFakeChain(&params)
  1451  		node := bc.bestChain.Tip()
  1452  		node.stakeVersion = test.startStakeVersion
  1453  
  1454  		t.Logf("running: %v", test.name)
  1455  
  1456  		// Override start time.
  1457  		curTimestamp := time.Now()
  1458  
  1459  		// Override expiration time.
  1460  		if test.end != nil {
  1461  			params.Deployments[posVersion][0].ExpireTime =
  1462  				test.end(curTimestamp)
  1463  		}
  1464  
  1465  		t.Logf("curTimestamp %v start %v expiration %v",
  1466  			uint64(curTimestamp.Unix()),
  1467  			params.Deployments[posVersion][0].StartTime,
  1468  			params.Deployments[posVersion][0].ExpireTime)
  1469  
  1470  		for k := range test.expectedState {
  1471  			for i := uint32(0); i < test.voteBitsCounts[k].count; i++ {
  1472  				// Set stake versions and vote bits.
  1473  				node = newFakeNode(node, test.blockVersion,
  1474  					test.startStakeVersion, 0, curTimestamp)
  1475  				vote := &test.voteBitsCounts[k].vote
  1476  				appendFakeVotes(node, params.TicketsPerBlock,
  1477  					vote.Version, vote.Bits)
  1478  
  1479  				bc.bestChain.SetTip(node)
  1480  				bc.index.AddNode(node)
  1481  				curTimestamp = curTimestamp.Add(time.Second)
  1482  			}
  1483  			t.Logf("Height %v, Start time %v, curTime %v, delta %v",
  1484  				node.height, params.Deployments[4][0].StartTime,
  1485  				node.timestamp, node.timestamp-
  1486  					int64(params.Deployments[4][0].StartTime))
  1487  			ts, err := bc.NextThresholdState(&node.hash, posVersion,
  1488  				test.vote.Id)
  1489  			if err != nil {
  1490  				t.Fatalf("NextThresholdState(%v): %v", k, err)
  1491  			}
  1492  			if ts != test.expectedState[k] {
  1493  				t.Fatalf("%v.%v (%v) got state %+v wanted "+
  1494  					"state %+v", test.name, test.vote.Id, k,
  1495  					ts, test.expectedState[k])
  1496  			}
  1497  		}
  1498  	}
  1499  }
  1500  
  1501  // defaultParallelParams returns net parameters modified to have two known
  1502  // deployments that are used throughout the parallel votebit tests.
  1503  func defaultParallelParams() chaincfg.Params {
  1504  	params := chaincfg.RegNetParams
  1505  	params.Deployments = make(map[uint32][]chaincfg.ConsensusDeployment)
  1506  	params.Deployments[posVersion] = []chaincfg.ConsensusDeployment{
  1507  		{
  1508  			Vote: testDummy1,
  1509  			StartTime: uint64(time.Now().Add(time.Duration(params.RuleChangeActivationInterval) *
  1510  				time.Second).Unix()),
  1511  			ExpireTime: uint64(time.Now().Add(24 * time.Hour).Unix()),
  1512  		},
  1513  		{
  1514  			Vote: testDummy2,
  1515  			StartTime: uint64(time.Now().Add(time.Duration(params.RuleChangeActivationInterval) *
  1516  				time.Second).Unix()),
  1517  			ExpireTime: uint64(time.Now().Add(24 * time.Hour).Unix()),
  1518  		},
  1519  	}
  1520  
  1521  	return params
  1522  }
  1523  
  1524  // TestParallelVoting ensures that two agendas running at the same time progress
  1525  // through the expected states.
  1526  func TestParallelVoting(t *testing.T) {
  1527  	params := defaultParallelParams()
  1528  
  1529  	type voteCount struct {
  1530  		vote  stake.VoteVersionTuple
  1531  		count uint32
  1532  	}
  1533  
  1534  	tests := []struct {
  1535  		name              string
  1536  		vote              []chaincfg.Vote
  1537  		blockVersion      int32
  1538  		startStakeVersion uint32
  1539  		voteBitsCounts    []voteCount
  1540  		expectedState     [][]ThresholdStateTuple
  1541  	}{
  1542  		{
  1543  			name:              "parallel",
  1544  			vote:              []chaincfg.Vote{testDummy1, testDummy2},
  1545  			blockVersion:      powVersion,
  1546  			startStakeVersion: posVersion,
  1547  			voteBitsCounts: []voteCount{{
  1548  				vote: stake.VoteVersionTuple{
  1549  					Version: posVersion,
  1550  					Bits:    0x01,
  1551  				},
  1552  				count: uint32(params.StakeValidationHeight),
  1553  			}, {
  1554  				vote: stake.VoteVersionTuple{
  1555  					Version: posVersion,
  1556  					Bits:    0x01,
  1557  				},
  1558  				count: params.RuleChangeActivationInterval - 1,
  1559  			}, {
  1560  				vote: stake.VoteVersionTuple{
  1561  					Version: posVersion - 1,
  1562  					Bits:    vbTestDummy1Yes | vbTestDummy2No,
  1563  				},
  1564  				count: params.RuleChangeActivationInterval,
  1565  			}, {
  1566  				vote: stake.VoteVersionTuple{
  1567  					Version: posVersion,
  1568  					Bits:    vbTestDummy1Yes | vbTestDummy2No,
  1569  				},
  1570  				count: params.RuleChangeActivationInterval,
  1571  			}, {
  1572  				vote: stake.VoteVersionTuple{
  1573  					Version: posVersion,
  1574  					Bits:    0x01,
  1575  				},
  1576  				count: params.RuleChangeActivationInterval,
  1577  			}, {
  1578  				vote: stake.VoteVersionTuple{
  1579  					Version: posVersion,
  1580  					Bits:    0x01,
  1581  				},
  1582  				count: params.RuleChangeActivationInterval,
  1583  			}},
  1584  			expectedState: [][]ThresholdStateTuple{
  1585  				// 0
  1586  				{{
  1587  					State:  ThresholdDefined,
  1588  					Choice: invalidChoice,
  1589  				}, {
  1590  					State:  ThresholdStarted,
  1591  					Choice: invalidChoice,
  1592  				}, {
  1593  					State:  ThresholdStarted,
  1594  					Choice: invalidChoice,
  1595  				}, {
  1596  					State:  ThresholdLockedIn,
  1597  					Choice: 0x02,
  1598  				}, {
  1599  					State:  ThresholdActive,
  1600  					Choice: 0x02,
  1601  				}, {
  1602  					State:  ThresholdActive,
  1603  					Choice: 0x02,
  1604  				}},
  1605  				// 1
  1606  				{{
  1607  					State:  ThresholdDefined,
  1608  					Choice: invalidChoice,
  1609  				}, {
  1610  					State:  ThresholdStarted,
  1611  					Choice: invalidChoice,
  1612  				}, {
  1613  					State:  ThresholdStarted,
  1614  					Choice: invalidChoice,
  1615  				}, {
  1616  					State:  ThresholdFailed,
  1617  					Choice: 0x01,
  1618  				}, {
  1619  					State:  ThresholdFailed,
  1620  					Choice: 0x01,
  1621  				}, {
  1622  					State:  ThresholdFailed,
  1623  					Choice: 0x01,
  1624  				}},
  1625  			},
  1626  		},
  1627  	}
  1628  
  1629  	for _, test := range tests {
  1630  		// Reset params.
  1631  		params = defaultParallelParams()
  1632  		// We have to reset the cache for every test.
  1633  		bc := newFakeChain(&params)
  1634  		node := bc.bestChain.Tip()
  1635  		node.stakeVersion = test.startStakeVersion
  1636  
  1637  		curTimestamp := time.Now()
  1638  		for k := range test.expectedState[0] {
  1639  			for i := uint32(0); i < test.voteBitsCounts[k].count; i++ {
  1640  				// Set stake versions and vote bits.
  1641  				node = newFakeNode(node, test.blockVersion,
  1642  					test.startStakeVersion, 0, curTimestamp)
  1643  				vote := &test.voteBitsCounts[k].vote
  1644  				appendFakeVotes(node, params.TicketsPerBlock,
  1645  					vote.Version, vote.Bits)
  1646  
  1647  				bc.bestChain.SetTip(node)
  1648  				bc.index.AddNode(node)
  1649  				curTimestamp = curTimestamp.Add(time.Second)
  1650  			}
  1651  			for i := range test.vote {
  1652  				ts, err := bc.NextThresholdState(&node.hash,
  1653  					posVersion, test.vote[i].Id)
  1654  				if err != nil {
  1655  					t.Fatalf("NextThresholdState(%v): %v", k, err)
  1656  				}
  1657  
  1658  				if ts != test.expectedState[i][k] {
  1659  					t.Fatalf("%v.%v (%v) got state %+v "+
  1660  						"wanted state %+v",
  1661  						test.name, test.vote[i].Id, k,
  1662  						ts, test.expectedState[i][k])
  1663  				}
  1664  			}
  1665  		}
  1666  	}
  1667  }