blockchain: Use harness in threshold state tests.

This refactors the threshold state tests in blockchain to use the
recently introduced chaingen harness.

Since these tests also require non-default choices, a new function named
TestThresholdStateChoice is also introduced on the chaingen harness
which allows both the state and the choice to be checked.
This commit is contained in:
Dave Collins 2019-01-27 10:01:09 -06:00
parent 46e2852c2c
commit fb59230010
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
2 changed files with 91 additions and 122 deletions

View File

@ -461,6 +461,40 @@ func (g *chaingenHarness) TestThresholdState(id string, state ThresholdState) {
}
}
// TestThresholdStateChoice queries the threshold state from the current tip
// block associated with the harness generator and expects the returned state
// and choice to match the provided value.
func (g *chaingenHarness) TestThresholdStateChoice(id string, state ThresholdState, choice uint32) {
g.t.Helper()
tipHash := g.Tip().BlockHash()
tipHeight := g.Tip().Header.Height
deploymentVer, err := g.lookupDeploymentVersion(id)
if err != nil {
g.t.Fatalf("block %q (hash %s, height %d) unexpected error when "+
"retrieving threshold state: %v", g.TipName(), tipHash, tipHeight,
err)
}
s, err := g.chain.NextThresholdState(&tipHash, deploymentVer, id)
if err != nil {
g.t.Fatalf("block %q (hash %s, height %d) unexpected error when "+
"retrieving threshold state: %v", g.TipName(), tipHash, tipHeight,
err)
}
if s.State != state {
g.t.Fatalf("block %q (hash %s, height %d) unexpected threshold "+
"state for %s -- got %v, want %v", g.TipName(), tipHash, tipHeight,
id, s.State, state)
}
if s.Choice != choice {
g.t.Fatalf("block %q (hash %s, height %d) unexpected choice for %s -- "+
"got %v, want %v", g.TipName(), tipHash, tipHeight, id, s.Choice,
choice)
}
}
// ForceTipReorg forces the chain instance associated with the generator to
// reorganize the current tip of the main chain from the given block to the
// given block. An error will result if the provided from block is not actually

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017-2018 The Decred developers
// Copyright (c) 2017-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -12,7 +12,6 @@ import (
"github.com/decred/dcrd/blockchain/chaingen"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/dcrutil"
)
const (
@ -139,78 +138,10 @@ func TestThresholdState(t *testing.T) {
ExpireTime: math.MaxUint64,
})
// Create a test generator instance initialized with the genesis block
// as the tip.
g, err := chaingen.MakeGenerator(params)
if err != nil {
t.Fatalf("Failed to create generator: %v", err)
}
// Create a new database and chain instance to run tests against.
chain, teardownFunc, err := chainSetup("thresholdstatetest", params)
if err != nil {
t.Fatalf("Failed to setup chain instance: %v", err)
}
// Create a test harness initialized with the genesis block as the tip.
g, teardownFunc := newChaingenHarness(t, params, "thresholdstatetest")
defer teardownFunc()
// accepted processes the current tip block associated with the
// generator and expects it to be accepted to the main chain.
accepted := func() {
msgBlock := g.Tip()
blockHeight := msgBlock.Header.Height
block := dcrutil.NewBlock(msgBlock)
t.Logf("Testing block %s (hash %s, height %d)",
g.TipName(), block.Hash(), blockHeight)
forkLen, isOrphan, err := chain.ProcessBlock(block, BFNone)
if err != nil {
t.Fatalf("block %q (hash %s, height %d) should "+
"have been accepted: %v", g.TipName(),
block.Hash(), blockHeight, err)
}
// Ensure the main chain and orphan flags match the values
// specified in the test.
isMainChain := !isOrphan && forkLen == 0
if !isMainChain {
t.Fatalf("block %q (hash %s, height %d) unexpected main "+
"chain flag -- got %v, want true", g.TipName(),
block.Hash(), blockHeight, isMainChain)
}
if isOrphan {
t.Fatalf("block %q (hash %s, height %d) unexpected "+
"orphan flag -- got %v, want false", g.TipName(),
block.Hash(), blockHeight, isOrphan)
}
}
// testThresholdState queries the threshold state from the current
// tip block associated with the generator and expects the returned
// state and choice to match the provided values.
testThresholdState := func(id string, state ThresholdState, choice uint32) {
tipHash := g.Tip().BlockHash()
s, err := chain.NextThresholdState(&tipHash, posVersion, id)
if err != nil {
t.Fatalf("block %q (hash %s, height %d) unexpected "+
"error when retrieving threshold state: %v",
g.TipName(), tipHash, g.Tip().Header.Height,
err)
}
if s.State != state {
t.Fatalf("block %q (hash %s, height %d) unexpected "+
"threshold state for %s -- got %v, want %v",
g.TipName(), tipHash, g.Tip().Header.Height,
id, s.State, state)
}
if s.Choice != choice {
t.Fatalf("block %q (hash %s, height %d) unexpected "+
"choice for %s -- got %v, want %v",
g.TipName(), tipHash, g.Tip().Header.Height,
id, s.Choice, choice)
}
}
// Shorter versions of useful params for convenience.
ticketsPerBlock := int64(params.TicketsPerBlock)
coinbaseMaturity := params.CoinbaseMaturity
@ -224,33 +155,37 @@ func TestThresholdState(t *testing.T) {
ruleChangeDiv := int64(params.RuleChangeActivationDivisor)
// ---------------------------------------------------------------------
// Premine.
// Block One.
//
// NOTE: The advance funcs on the harness are intentionally not used in
// these tests since they need to manually test the threshold state at
// all key heights.
// ---------------------------------------------------------------------
// Add the required premine block.
// Add the required initial block.
//
// genesis -> bp
g.CreatePremineBlock("bp", 0)
// genesis -> bfb
g.CreatePremineBlock("bfb", 0)
g.AssertTipHeight(1)
accepted()
testThresholdState(testDummy1ID, ThresholdDefined, invalidChoice)
testThresholdState(testDummy2ID, ThresholdDefined, invalidChoice)
g.Accepted()
g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to have mature coinbase outputs to work with.
//
// genesis -> bp -> bm0 -> bm1 -> ... -> bm#
// genesis -> bfb -> bm0 -> bm1 -> ... -> bm#
// ---------------------------------------------------------------------
for i := uint16(0); i < coinbaseMaturity; i++ {
blockName := fmt.Sprintf("bm%d", i)
g.NextBlock(blockName, nil, nil)
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(coinbaseMaturity) + 1)
testThresholdState(testDummy1ID, ThresholdDefined, invalidChoice)
testThresholdState(testDummy2ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to reach the stake enabled height while
@ -268,11 +203,11 @@ func TestThresholdState(t *testing.T) {
blockName := fmt.Sprintf("bse%d", i)
g.NextBlock(blockName, nil, ticketOuts)
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeEnabledHeight))
testThresholdState(testDummy1ID, ThresholdDefined, invalidChoice)
testThresholdState(testDummy2ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to reach the stake validation height while
@ -303,11 +238,11 @@ func TestThresholdState(t *testing.T) {
g.NextBlock(blockName, nil, ticketOuts,
chaingen.ReplaceBlockVersion(3))
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight))
testThresholdState(testDummy1ID, ThresholdDefined, invalidChoice)
testThresholdState(testDummy2ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to reach one block before the next stake
@ -330,13 +265,13 @@ func TestThresholdState(t *testing.T) {
chaingen.ReplaceStakeVersion(0),
chaingen.ReplaceVoteVersions(3))
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight + stakeVerInterval - 1))
g.AssertBlockVersion(3)
g.AssertStakeVersion(0)
testThresholdState(testDummy1ID, ThresholdDefined, invalidChoice)
testThresholdState(testDummy2ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to reach one block before the next rule change
@ -358,13 +293,13 @@ func TestThresholdState(t *testing.T) {
chaingen.ReplaceStakeVersion(3),
chaingen.ReplaceVoteVersions(3))
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval - 2))
g.AssertBlockVersion(3)
g.AssertStakeVersion(3)
testThresholdState(testDummy1ID, ThresholdDefined, invalidChoice)
testThresholdState(testDummy2ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to reach one block before the next stake
@ -389,13 +324,13 @@ func TestThresholdState(t *testing.T) {
chaingen.ReplaceStakeVersion(3),
chaingen.ReplaceVoteVersions(4))
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight + stakeVerInterval*4 - 1))
g.AssertBlockVersion(3)
g.AssertStakeVersion(3)
testThresholdState(testDummy1ID, ThresholdDefined, invalidChoice)
testThresholdState(testDummy2ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to reach the next rule change interval with
@ -422,13 +357,13 @@ func TestThresholdState(t *testing.T) {
chaingen.ReplaceStakeVersion(4),
chaingen.ReplaceVoteVersions(4))
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*2 - 1))
g.AssertBlockVersion(4)
g.AssertStakeVersion(4)
testThresholdState(testDummy1ID, ThresholdDefined, invalidChoice)
testThresholdState(testDummy2ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to achieve proof-of-work block version lockin
@ -455,14 +390,14 @@ func TestThresholdState(t *testing.T) {
chaingen.ReplaceVotes(vbPrevBlockValid|vbTestDummy1Yes|
vbTestDummy2No, 4))
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*2 -
1 + powNumToCheck))
g.AssertBlockVersion(4)
g.AssertStakeVersion(4)
testThresholdState(testDummy1ID, ThresholdDefined, invalidChoice)
testThresholdState(testDummy2ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdDefined, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdDefined, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to reach the next rule change interval with
@ -488,13 +423,13 @@ func TestThresholdState(t *testing.T) {
chaingen.ReplaceVotes(vbPrevBlockValid|vbTestDummy1Yes|
vbTestDummy2No, 4))
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*3 - 1))
g.AssertBlockVersion(4)
g.AssertStakeVersion(4)
testThresholdState(testDummy1ID, ThresholdStarted, invalidChoice)
testThresholdState(testDummy2ID, ThresholdStarted, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdStarted, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdStarted, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to reach the next rule change interval with
@ -518,13 +453,13 @@ func TestThresholdState(t *testing.T) {
chaingen.ReplaceVotes(vbPrevBlockValid|vbTestDummy1Yes|
vbTestDummy2No, 3))
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*4 - 1))
g.AssertBlockVersion(4)
g.AssertStakeVersion(4)
testThresholdState(testDummy1ID, ThresholdStarted, invalidChoice)
testThresholdState(testDummy2ID, ThresholdStarted, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdStarted, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdStarted, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to reach the next rule change interval with
@ -554,13 +489,13 @@ func TestThresholdState(t *testing.T) {
chaingen.ReplaceVotes(voteBits, 4))
totalVotes += ticketsPerBlock
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*5 - 1))
g.AssertBlockVersion(4)
g.AssertStakeVersion(4)
testThresholdState(testDummy1ID, ThresholdStarted, invalidChoice)
testThresholdState(testDummy2ID, ThresholdStarted, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdStarted, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdStarted, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to reach the next rule change interval with
@ -598,13 +533,13 @@ func TestThresholdState(t *testing.T) {
chaingen.ReplaceVotes(voteBits, 4))
totalVotes += ticketsPerBlock
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*6 - 1))
g.AssertBlockVersion(4)
g.AssertStakeVersion(4)
testThresholdState(testDummy1ID, ThresholdStarted, invalidChoice)
testThresholdState(testDummy2ID, ThresholdStarted, invalidChoice)
g.TestThresholdStateChoice(testDummy1ID, ThresholdStarted, invalidChoice)
g.TestThresholdStateChoice(testDummy2ID, ThresholdStarted, invalidChoice)
// ---------------------------------------------------------------------
// Generate enough blocks to reach the next rule change interval with
@ -629,13 +564,13 @@ func TestThresholdState(t *testing.T) {
chaingen.ReplaceVotes(vbPrevBlockValid|vbTestDummy1Yes|
vbTestDummy2No, 4))
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*7 - 1))
g.AssertBlockVersion(4)
g.AssertStakeVersion(4)
testThresholdState(testDummy1ID, ThresholdLockedIn, testDummy1YesIndex)
testThresholdState(testDummy2ID, ThresholdFailed, testDummy2NoIndex)
g.TestThresholdStateChoice(testDummy1ID, ThresholdLockedIn, testDummy1YesIndex)
g.TestThresholdStateChoice(testDummy2ID, ThresholdFailed, testDummy2NoIndex)
// ---------------------------------------------------------------------
// Generate enough blocks to reach the next rule change interval with
@ -662,11 +597,11 @@ func TestThresholdState(t *testing.T) {
chaingen.ReplaceVotes(vbPrevBlockValid|vbTestDummy1No|
vbTestDummy2Yes, 4))
g.SaveTipCoinbaseOuts()
accepted()
g.Accepted()
}
g.AssertTipHeight(uint32(stakeValidationHeight + ruleChangeInterval*8 - 1))
g.AssertBlockVersion(4)
g.AssertStakeVersion(4)
testThresholdState(testDummy1ID, ThresholdActive, testDummy1YesIndex)
testThresholdState(testDummy2ID, ThresholdFailed, testDummy2NoIndex)
g.TestThresholdStateChoice(testDummy1ID, ThresholdActive, testDummy1YesIndex)
g.TestThresholdStateChoice(testDummy2ID, ThresholdFailed, testDummy2NoIndex)
}