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

     1  // Copyright (c) 2016-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  	"testing"
    10  
    11  	"github.com/decred/dcrd/blockchain/chaingen"
    12  	"github.com/decred/dcrd/chaincfg"
    13  )
    14  
    15  // TestStakeVersion ensures that the stake version field in the block header is
    16  // enforced properly.
    17  func TestStakeVersion(t *testing.T) {
    18  	// Create a test harness initialized with the genesis block as the tip.
    19  	params := &chaincfg.RegNetParams
    20  	g, teardownFunc := newChaingenHarness(t, params, "stakeversiontest")
    21  	defer teardownFunc()
    22  
    23  	// Shorter versions of useful params for convenience.
    24  	ticketsPerBlock := params.TicketsPerBlock
    25  	stakeValidationHeight := params.StakeValidationHeight
    26  	stakeVerInterval := params.StakeVersionInterval
    27  	stakeMajorityMul := int64(params.StakeMajorityMultiplier)
    28  	stakeMajorityDiv := int64(params.StakeMajorityDivisor)
    29  
    30  	// ---------------------------------------------------------------------
    31  	// Generate and accept enough blocks to reach stake validation height.
    32  	// ---------------------------------------------------------------------
    33  
    34  	g.AdvanceToStakeValidationHeight()
    35  
    36  	// ---------------------------------------------------------------------
    37  	// Generate enough blocks to reach one block before the next stake
    38  	// version interval with block version 2, stake version 0, and vote
    39  	// version 3.
    40  	//
    41  	// This will result in a majority of blocks with a version prior to
    42  	// version 3 where stake version enforcement begins and thus it must not
    43  	// be enforced.
    44  	// ---------------------------------------------------------------------
    45  
    46  	for i := int64(0); i < stakeVerInterval-1; i++ {
    47  		outs := g.OldestCoinbaseOuts()
    48  		blockName := fmt.Sprintf("bsvtA%d", i)
    49  		g.NextBlock(blockName, nil, outs[1:],
    50  			chaingen.ReplaceBlockVersion(2),
    51  			chaingen.ReplaceStakeVersion(0),
    52  			chaingen.ReplaceVoteVersions(3))
    53  		g.SaveTipCoinbaseOuts()
    54  		g.AcceptTipBlock()
    55  	}
    56  	g.AssertTipHeight(uint32(stakeValidationHeight + stakeVerInterval - 1))
    57  	g.AssertBlockVersion(2)
    58  	g.AssertStakeVersion(0)
    59  
    60  	// ---------------------------------------------------------------------
    61  	// Generate a single block with block version 3, stake version 42, and
    62  	// vote version 41.
    63  	//
    64  	// This block must be accepted because even though it is a version 3
    65  	// block with an invalid stake version, there have not yet been a
    66  	// majority of version 3 blocks which is required to trigger stake
    67  	// version enforcement.
    68  	// ---------------------------------------------------------------------
    69  
    70  	outs := g.OldestCoinbaseOuts()
    71  	g.NextBlock("bsvtB0", nil, outs[1:],
    72  		chaingen.ReplaceBlockVersion(3),
    73  		chaingen.ReplaceStakeVersion(42),
    74  		chaingen.ReplaceVoteVersions(41))
    75  	g.SaveTipCoinbaseOuts()
    76  	g.AcceptTipBlock()
    77  	g.AssertTipHeight(uint32(stakeValidationHeight + stakeVerInterval))
    78  	g.AssertBlockVersion(3)
    79  	g.AssertStakeVersion(42) // expected bogus
    80  
    81  	// ---------------------------------------------------------------------
    82  	// Generate enough blocks to reach one block before the next stake
    83  	// version interval with block version 3, stake version 0, and vote
    84  	// version 2.
    85  	//
    86  	// This will result in a majority of version 3 blocks which will trigger
    87  	// enforcement of the stake version.  It also results in a majority of
    88  	// version 2 votes, however, since enforcement is not yet active in this
    89  	// interval, they will not actually count toward establishing a
    90  	// majority.
    91  	// ---------------------------------------------------------------------
    92  
    93  	for i := int64(0); i < stakeVerInterval-1; i++ {
    94  		outs := g.OldestCoinbaseOuts()
    95  		blockName := fmt.Sprintf("bsvtB%d", i+1)
    96  		g.NextBlock(blockName, nil, outs[1:],
    97  			chaingen.ReplaceBlockVersion(3),
    98  			chaingen.ReplaceStakeVersion(0),
    99  			chaingen.ReplaceVoteVersions(2))
   100  		g.SaveTipCoinbaseOuts()
   101  		g.AcceptTipBlock()
   102  	}
   103  	g.AssertTipHeight(uint32(stakeValidationHeight + 2*stakeVerInterval - 1))
   104  	g.AssertBlockVersion(3)
   105  	g.AssertStakeVersion(0)
   106  
   107  	// ---------------------------------------------------------------------
   108  	// Generate a single block with block version 3, stake version 2, and
   109  	// vote version 3.
   110  	//
   111  	// This block must be rejected because even though the majority stake
   112  	// version per voters was 2 in the previous period, stake version
   113  	// enforcement had not yet been achieved and thus the required stake
   114  	// version is still 0.
   115  	// ---------------------------------------------------------------------
   116  
   117  	g.NextBlock("bsvtCbad0", nil, nil,
   118  		chaingen.ReplaceBlockVersion(3),
   119  		chaingen.ReplaceStakeVersion(2),
   120  		chaingen.ReplaceVoteVersions(3))
   121  	g.AssertTipHeight(uint32(stakeValidationHeight + 2*stakeVerInterval))
   122  	g.AssertBlockVersion(3)
   123  	g.AssertStakeVersion(2)
   124  	g.RejectTipBlock(ErrBadStakeVersion)
   125  
   126  	// ---------------------------------------------------------------------
   127  	// Generate a single block with block version 3, stake version 1, and
   128  	// vote version 3.
   129  	//
   130  	// This block must be rejected because even though the majority stake
   131  	// version per voters was 2 in the previous period, stake version
   132  	// enforcement had not yet been achieved and thus the required stake
   133  	// version is still 0.
   134  	// ---------------------------------------------------------------------
   135  
   136  	g.SetTip(fmt.Sprintf("bsvtB%d", stakeVerInterval-1))
   137  	g.NextBlock("bsvtCbad1", nil, nil,
   138  		chaingen.ReplaceBlockVersion(3),
   139  		chaingen.ReplaceStakeVersion(1),
   140  		chaingen.ReplaceVoteVersions(3))
   141  	g.AssertTipHeight(uint32(stakeValidationHeight + 2*stakeVerInterval))
   142  	g.AssertBlockVersion(3)
   143  	g.AssertStakeVersion(1)
   144  	g.RejectTipBlock(ErrBadStakeVersion)
   145  
   146  	// ---------------------------------------------------------------------
   147  	// Generate enough blocks to reach one block before the next stake
   148  	// version interval with block version 3, stake version 0, and vote
   149  	// version 3.
   150  	//
   151  	// This will result in a majority of version 3 votes which will trigger
   152  	// enforcement of a bump in the stake version to 3.
   153  	// ---------------------------------------------------------------------
   154  
   155  	g.SetTip(fmt.Sprintf("bsvtB%d", stakeVerInterval-1))
   156  	for i := int64(0); i < stakeVerInterval; i++ {
   157  		outs := g.OldestCoinbaseOuts()
   158  		blockName := fmt.Sprintf("bsvtC%d", i)
   159  		g.NextBlock(blockName, nil, outs[1:],
   160  			chaingen.ReplaceBlockVersion(3),
   161  			chaingen.ReplaceStakeVersion(0),
   162  			chaingen.ReplaceVoteVersions(3))
   163  		g.SaveTipCoinbaseOuts()
   164  		g.AcceptTipBlock()
   165  	}
   166  	g.AssertTipHeight(uint32(stakeValidationHeight + 3*stakeVerInterval - 1))
   167  	g.AssertBlockVersion(3)
   168  	g.AssertStakeVersion(0)
   169  
   170  	// ---------------------------------------------------------------------
   171  	// Generate a single block with block version 3, stake version 2, and
   172  	// vote version 2.
   173  	//
   174  	// This block must be rejected because the majority stake version per
   175  	// voters is now 3 and stake version enforcement has been achieved.
   176  	// ---------------------------------------------------------------------
   177  
   178  	g.NextBlock("bsvtDbad0", nil, nil,
   179  		chaingen.ReplaceBlockVersion(3),
   180  		chaingen.ReplaceStakeVersion(2),
   181  		chaingen.ReplaceVoteVersions(2))
   182  	g.AssertTipHeight(uint32(stakeValidationHeight + 3*stakeVerInterval))
   183  	g.AssertBlockVersion(3)
   184  	g.AssertStakeVersion(2)
   185  	g.RejectTipBlock(ErrBadStakeVersion)
   186  
   187  	// ---------------------------------------------------------------------
   188  	// Generate a single block with block version 3, stake version 4, and
   189  	// vote version 2.
   190  	//
   191  	// This block must be rejected because the majority stake version per
   192  	// voters is now 3 and stake version enforcement has been achieved.
   193  	// ---------------------------------------------------------------------
   194  
   195  	g.SetTip(fmt.Sprintf("bsvtC%d", stakeVerInterval-1))
   196  	g.NextBlock("bsvtDbad1", nil, nil,
   197  		chaingen.ReplaceBlockVersion(3),
   198  		chaingen.ReplaceStakeVersion(4),
   199  		chaingen.ReplaceVoteVersions(2))
   200  	g.AssertTipHeight(uint32(stakeValidationHeight + 3*stakeVerInterval))
   201  	g.AssertBlockVersion(3)
   202  	g.AssertStakeVersion(4)
   203  	g.RejectTipBlock(ErrBadStakeVersion)
   204  
   205  	// ---------------------------------------------------------------------
   206  	// Generate enough blocks to reach one block before the next stake
   207  	// version interval with block version 3, stake version 3, and vote
   208  	// version 2.
   209  	//
   210  	// This will result in a majority of version 2 votes, but since version
   211  	// 3 has already been achieved, the stake version must not regress.
   212  	// ---------------------------------------------------------------------
   213  
   214  	g.SetTip(fmt.Sprintf("bsvtC%d", stakeVerInterval-1))
   215  	for i := int64(0); i < stakeVerInterval; i++ {
   216  		outs := g.OldestCoinbaseOuts()
   217  		blockName := fmt.Sprintf("bsvtD%d", i)
   218  		g.NextBlock(blockName, nil, outs[1:],
   219  			chaingen.ReplaceBlockVersion(3),
   220  			chaingen.ReplaceStakeVersion(3),
   221  			chaingen.ReplaceVoteVersions(2))
   222  		g.SaveTipCoinbaseOuts()
   223  		g.AcceptTipBlock()
   224  	}
   225  	g.AssertTipHeight(uint32(stakeValidationHeight + 4*stakeVerInterval - 1))
   226  	g.AssertBlockVersion(3)
   227  	g.AssertStakeVersion(3)
   228  
   229  	// ---------------------------------------------------------------------
   230  	// Generate a single block with block version 3, stake version 2, and
   231  	// vote version 2.
   232  	//
   233  	// This block must be rejected because even though the majority stake
   234  	// version per voters in the previous interval was 2, the majority stake
   235  	// version is not allowed to regress.
   236  	// ---------------------------------------------------------------------
   237  
   238  	g.NextBlock("bsvtEbad0", nil, nil,
   239  		chaingen.ReplaceBlockVersion(3),
   240  		chaingen.ReplaceStakeVersion(2),
   241  		chaingen.ReplaceVoteVersions(2))
   242  	g.AssertTipHeight(uint32(stakeValidationHeight + 4*stakeVerInterval))
   243  	g.AssertBlockVersion(3)
   244  	g.AssertStakeVersion(2)
   245  	g.RejectTipBlock(ErrBadStakeVersion)
   246  
   247  	// ---------------------------------------------------------------------
   248  	// Generate a single block with block version 3, stake version 4, and
   249  	// vote version 3.
   250  	//
   251  	// This block must be rejected because the majority stake version is
   252  	// still 3.
   253  	// ---------------------------------------------------------------------
   254  
   255  	g.SetTip(fmt.Sprintf("bsvtD%d", stakeVerInterval-1))
   256  	g.NextBlock("bsvtEbad1", nil, nil,
   257  		chaingen.ReplaceBlockVersion(3),
   258  		chaingen.ReplaceStakeVersion(4),
   259  		chaingen.ReplaceVoteVersions(3))
   260  	g.AssertTipHeight(uint32(stakeValidationHeight + 4*stakeVerInterval))
   261  	g.AssertBlockVersion(3)
   262  	g.AssertStakeVersion(4)
   263  	g.RejectTipBlock(ErrBadStakeVersion)
   264  
   265  	// ---------------------------------------------------------------------
   266  	// Generate enough blocks to reach one block before the next stake
   267  	// version interval with block version 3, stake version 3, and a mix of
   268  	// 3 and 4 for the vote version such that a super majority is *NOT*
   269  	// achieved.
   270  	//
   271  	// This will result in an unchanged required stake version.
   272  	// ---------------------------------------------------------------------
   273  
   274  	g.SetTip(fmt.Sprintf("bsvtD%d", stakeVerInterval-1))
   275  	votesPerInterval := stakeVerInterval * int64(ticketsPerBlock)
   276  	targetVotes := (votesPerInterval * stakeMajorityMul) / stakeMajorityDiv
   277  	targetBlocks := targetVotes / int64(ticketsPerBlock)
   278  	for i := int64(0); i < targetBlocks-1; i++ {
   279  		outs := g.OldestCoinbaseOuts()
   280  		blockName := fmt.Sprintf("bsvtE%da", i)
   281  		g.NextBlock(blockName, nil, outs[1:],
   282  			chaingen.ReplaceBlockVersion(3),
   283  			chaingen.ReplaceStakeVersion(3),
   284  			chaingen.ReplaceVoteVersions(4))
   285  		g.SaveTipCoinbaseOuts()
   286  		g.AssertBlockVersion(3)
   287  		g.AssertStakeVersion(3)
   288  		g.AcceptTipBlock()
   289  	}
   290  	for i := int64(0); i < stakeVerInterval-(targetBlocks-1); i++ {
   291  		outs := g.OldestCoinbaseOuts()
   292  		blockName := fmt.Sprintf("bsvtE%db", targetBlocks-1+i)
   293  		g.NextBlock(blockName, nil, outs[1:],
   294  			chaingen.ReplaceBlockVersion(3),
   295  			chaingen.ReplaceStakeVersion(3),
   296  			chaingen.ReplaceVoteVersions(3))
   297  		g.SaveTipCoinbaseOuts()
   298  		g.AssertBlockVersion(3)
   299  		g.AssertStakeVersion(3)
   300  		g.AcceptTipBlock()
   301  	}
   302  	g.AssertTipHeight(uint32(stakeValidationHeight + 5*stakeVerInterval - 1))
   303  
   304  	// ---------------------------------------------------------------------
   305  	// Generate a single block with block version 3, stake version 4, and
   306  	// vote version 3.
   307  	//
   308  	// This block must be rejected because the majority stake version is
   309  	// still 3 due to failing to achieve enough votes in the previous
   310  	// period.
   311  	// ---------------------------------------------------------------------
   312  
   313  	g.NextBlock("bsvtFbad0", nil, nil,
   314  		chaingen.ReplaceBlockVersion(3),
   315  		chaingen.ReplaceStakeVersion(4),
   316  		chaingen.ReplaceVoteVersions(3))
   317  	g.AssertTipHeight(uint32(stakeValidationHeight + 5*stakeVerInterval))
   318  	g.AssertBlockVersion(3)
   319  	g.AssertStakeVersion(4)
   320  	g.RejectTipBlock(ErrBadStakeVersion)
   321  
   322  	// ---------------------------------------------------------------------
   323  	// Generate enough blocks to reach one block before the next stake
   324  	// version interval with block version 3, stake version 3, and a mix of
   325  	// 3 and 4 for the vote version such that a super majority of version 4
   326  	// is achieved.
   327  	//
   328  	// This will result in a majority stake version of 4.
   329  	// ---------------------------------------------------------------------
   330  
   331  	g.SetTip(fmt.Sprintf("bsvtE%db", stakeVerInterval-1))
   332  	for i := int64(0); i < targetBlocks; i++ {
   333  		outs := g.OldestCoinbaseOuts()
   334  		blockName := fmt.Sprintf("bsvtF%da", i)
   335  		g.NextBlock(blockName, nil, outs[1:],
   336  			chaingen.ReplaceBlockVersion(3),
   337  			chaingen.ReplaceStakeVersion(3),
   338  			chaingen.ReplaceVoteVersions(4))
   339  		g.SaveTipCoinbaseOuts()
   340  		g.AssertBlockVersion(3)
   341  		g.AssertStakeVersion(3)
   342  		g.AcceptTipBlock()
   343  	}
   344  	for i := int64(0); i < stakeVerInterval-targetBlocks; i++ {
   345  		outs := g.OldestCoinbaseOuts()
   346  		blockName := fmt.Sprintf("bsvtF%db", targetBlocks+i)
   347  		g.NextBlock(blockName, nil, outs[1:],
   348  			chaingen.ReplaceBlockVersion(3),
   349  			chaingen.ReplaceStakeVersion(3),
   350  			chaingen.ReplaceVoteVersions(3))
   351  		g.SaveTipCoinbaseOuts()
   352  		g.AssertBlockVersion(3)
   353  		g.AssertStakeVersion(3)
   354  		g.AcceptTipBlock()
   355  	}
   356  	g.AssertTipHeight(uint32(stakeValidationHeight + 6*stakeVerInterval - 1))
   357  
   358  	// ---------------------------------------------------------------------
   359  	// Generate a single block with block version 3, stake version 3, and
   360  	// vote version 3.
   361  	//
   362  	// This block must be rejected because the majority stake version is
   363  	// now 4 due to achieving a majority of votes in the previous period.
   364  	// ---------------------------------------------------------------------
   365  
   366  	g.NextBlock("bsvtGbad0", nil, nil,
   367  		chaingen.ReplaceBlockVersion(3),
   368  		chaingen.ReplaceStakeVersion(3),
   369  		chaingen.ReplaceVoteVersions(3))
   370  	g.AssertTipHeight(uint32(stakeValidationHeight + 6*stakeVerInterval))
   371  	g.AssertBlockVersion(3)
   372  	g.AssertStakeVersion(3)
   373  	g.RejectTipBlock(ErrBadStakeVersion)
   374  
   375  	// ---------------------------------------------------------------------
   376  	// Generate a single block with block version 3, stake version 4, and
   377  	// vote version 3.
   378  	//
   379  	// This block must be accepted because the majority stake version is
   380  	// now 4 due to achieving a majority of votes in the previous period.
   381  	// ---------------------------------------------------------------------
   382  
   383  	g.SetTip(fmt.Sprintf("bsvtF%db", stakeVerInterval-1))
   384  	g.NextBlock("bsvtG0", nil, nil,
   385  		chaingen.ReplaceBlockVersion(3),
   386  		chaingen.ReplaceStakeVersion(4),
   387  		chaingen.ReplaceVoteVersions(3))
   388  	g.AssertTipHeight(uint32(stakeValidationHeight + 6*stakeVerInterval))
   389  	g.AssertBlockVersion(3)
   390  	g.AssertStakeVersion(4)
   391  	g.AcceptTipBlock()
   392  }