blockchain: Add AssumeValid tests.
This adds tests that validate that it is correctly determined whether or not a node is an ancestor of an assumed valid node under a variety of conditions.
This commit is contained in:
parent
8962ce7f18
commit
526d33c873
@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/decred/dcrd/blockchain/v4/chaingen"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
@ -1877,3 +1878,197 @@ func TestInvalidateReconsider(t *testing.T) {
|
||||
g.ExpectBestInvalidHeader("b9c")
|
||||
g.ExpectIsCurrent(true)
|
||||
}
|
||||
|
||||
// TestAssumeValid validates that it is correctly determined whether or not a
|
||||
// node is an ancestor of an assumed valid node under a variety of conditions.
|
||||
func TestAssumeValid(t *testing.T) {
|
||||
// Clone the parameters so that they can be mutated.
|
||||
params := cloneParams(quickVoteActivationParams())
|
||||
stakeValidationHeight := params.StakeValidationHeight
|
||||
|
||||
// Set the target time per block to 1 day so that less blocks are needed to
|
||||
// test against a 2 week threshold.
|
||||
params.TargetTimePerBlock = time.Hour * 24
|
||||
|
||||
// Create a test harness initialized with the genesis block as the tip.
|
||||
g, teardownFunc := newChaingenHarness(t, params, "testassumevalid")
|
||||
defer teardownFunc()
|
||||
|
||||
// Calculate the expected number of blocks in 2 weeks.
|
||||
const timeInTwoWeeks = time.Hour * 24 * 14
|
||||
targetTimePerBlock := params.TargetTimePerBlock
|
||||
expectedBlocksInTwoWeeks := int64(timeInTwoWeeks / targetTimePerBlock)
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Generate and accept enough blocks to reach stake validation height.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
g.AdvanceToStakeValidationHeight()
|
||||
g.AssertTipHeight(uint32(stakeValidationHeight))
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Generate additional blocks until there is 1 less than the number of
|
||||
// blocks expected in 2 weeks.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
numBlocksToGen := expectedBlocksInTwoWeeks - stakeValidationHeight - 1
|
||||
i := int64(0)
|
||||
for ; i < numBlocksToGen; i++ {
|
||||
outs := g.OldestCoinbaseOuts()
|
||||
blockName := fmt.Sprintf("bbav%d", i)
|
||||
g.NextBlock(blockName, nil, outs[1:])
|
||||
g.SaveTipCoinbaseOuts()
|
||||
g.AcceptTipBlock()
|
||||
}
|
||||
g.AssertTipHeight(uint32(expectedBlocksInTwoWeeks - 1))
|
||||
|
||||
// Set the assumed valid node to the tip.
|
||||
tipHash := &g.chain.BestSnapshot().Hash
|
||||
assumeValidNode := g.chain.index.LookupNode(tipHash)
|
||||
g.chain.assumeValidNode = assumeValidNode
|
||||
|
||||
// Validate that isAssumeValidAncestor returns false for this scenario:
|
||||
// - The tip height is less than the number of blocks expected in 2 weeks
|
||||
node := assumeValidNode.parent
|
||||
if g.chain.isAssumeValidAncestor(node) {
|
||||
t.Fatal("expected isAssumeValidAncestor to return false since the " +
|
||||
"tip height is less than the number of blocks expected in 2 weeks")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Generate an additional block so that there is exactly the number of
|
||||
// blocks expected in 2 weeks.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
outs := g.OldestCoinbaseOuts()
|
||||
blockName := fmt.Sprintf("bbav%d", i)
|
||||
g.NextBlock(blockName, nil, outs[1:])
|
||||
g.SaveTipCoinbaseOuts()
|
||||
g.AcceptTipBlock()
|
||||
i++
|
||||
g.AssertTipHeight(uint32(expectedBlocksInTwoWeeks))
|
||||
|
||||
// Set the assumed valid node to the tip.
|
||||
tipHash = &g.chain.BestSnapshot().Hash
|
||||
assumeValidNode = g.chain.index.LookupNode(tipHash)
|
||||
g.chain.assumeValidNode = assumeValidNode
|
||||
|
||||
// Validate that isAssumeValidAncestor returns false for this scenario:
|
||||
// - The assumed valid node is clamped back to be 2 weeks behind the tip
|
||||
// - The node that is only 1 block behind the assumed valid node is not an
|
||||
// ancestor of the clamped back assumed valid node
|
||||
node = assumeValidNode.parent
|
||||
if g.chain.isAssumeValidAncestor(node) {
|
||||
t.Fatal("expected isAssumeValidAncestor to return false since the " +
|
||||
"node is not an ancestor of the clamped back assumed valid node")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Generate an additional 2 weeks worth of blocks + 2. In addition to
|
||||
// the main chain, generate a side chain of the same length.
|
||||
// ---------------------------------------------------------------------
|
||||
bestSideBlockName := g.TipName()
|
||||
numBlocksToGen = i + expectedBlocksInTwoWeeks + 2
|
||||
for ; i < numBlocksToGen; i++ {
|
||||
outs := g.OldestCoinbaseOuts()
|
||||
blockName := fmt.Sprintf("bbav%d", i)
|
||||
g.NextBlock(blockName, nil, outs[1:])
|
||||
g.SaveTipCoinbaseOuts()
|
||||
g.AcceptTipBlock()
|
||||
tipName := g.TipName()
|
||||
|
||||
g.SetTip(bestSideBlockName)
|
||||
bestSideBlockName = fmt.Sprintf("bbava%d", i)
|
||||
g.NextBlock(bestSideBlockName, nil, outs[1:])
|
||||
g.AcceptedToSideChainWithExpectedTip(tipName)
|
||||
g.SetTip(tipName)
|
||||
}
|
||||
g.AssertTipHeight(2*uint32(expectedBlocksInTwoWeeks) + 2)
|
||||
|
||||
// Set the assumed valid node to 2 weeks - 1 block behind the best side
|
||||
// chain block.
|
||||
sideBlockHash := g.BlockByName(bestSideBlockName).BlockHash()
|
||||
sideNode := g.chain.index.LookupNode(&sideBlockHash)
|
||||
assumeValidNode = sideNode.RelativeAncestor(expectedBlocksInTwoWeeks - 1)
|
||||
g.chain.assumeValidNode = assumeValidNode
|
||||
|
||||
// Validate that isAssumeValidAncestor returns false for this scenario:
|
||||
// - The node is not an ancestor of the best header
|
||||
if g.chain.isAssumeValidAncestor(assumeValidNode) {
|
||||
t.Fatal("expected isAssumeValidAncestor to return false when the " +
|
||||
"node is not an ancestor of the best header")
|
||||
}
|
||||
|
||||
// Validate that isAssumeValidAncestor returns false for this scenario:
|
||||
// - The node is an ancestor of the best header, and is at a height below
|
||||
// the clamped back height, but is not an ancestor of the assumed valid
|
||||
// block
|
||||
tipHash = &g.chain.BestSnapshot().Hash
|
||||
tip := g.chain.index.LookupNode(tipHash)
|
||||
node = tip.RelativeAncestor(expectedBlocksInTwoWeeks + 1)
|
||||
if g.chain.isAssumeValidAncestor(node) {
|
||||
t.Fatal("expected isAssumeValidAncestor to return false when the " +
|
||||
"node is not an ancestor of the assumed valid block")
|
||||
}
|
||||
|
||||
// Set the assumed valid node to 2 weeks - 1 block behind the tip
|
||||
assumeValidNode = tip.RelativeAncestor(expectedBlocksInTwoWeeks - 1)
|
||||
g.chain.assumeValidNode = assumeValidNode
|
||||
|
||||
// Validate that isAssumeValidAncestor returns false for this scenario:
|
||||
// - The assumed valid node is 2 weeks - 1 block behind the tip
|
||||
// - The assumed valid node is not an ancestor of the clamped back assumed
|
||||
// valid node
|
||||
if g.chain.isAssumeValidAncestor(assumeValidNode) {
|
||||
t.Fatal("expected isAssumeValidAncestor to return false when the " +
|
||||
"assumed valid node is 2 weeks - 1 block behind the tip")
|
||||
}
|
||||
|
||||
// Validate that isAssumeValidAncestor returns true for this scenario:
|
||||
// - The assumed valid node is 2 weeks - 1 block behind the tip
|
||||
// - The node that is 1 block behind the assumed valid node is an ancestor
|
||||
// of the clamped back assumed valid node
|
||||
node = assumeValidNode.parent
|
||||
if !g.chain.isAssumeValidAncestor(node) {
|
||||
t.Fatal("expected isAssumeValidAncestor to return true when the " +
|
||||
"assumed valid node is 2 weeks - 1 block behind the tip and the " +
|
||||
"node is an ancestor of the clamped back assumed valid node")
|
||||
}
|
||||
|
||||
// Set the assumed valid node to its parent so that it is exactly 2 weeks
|
||||
// behind the tip.
|
||||
assumeValidNode = assumeValidNode.parent
|
||||
g.chain.assumeValidNode = assumeValidNode
|
||||
|
||||
// Validate that isAssumeValidAncestor returns true for this scenario:
|
||||
// - The assumed valid node is exactly 2 weeks behind the tip
|
||||
node = assumeValidNode.parent
|
||||
if !g.chain.isAssumeValidAncestor(node) {
|
||||
t.Fatal("expected isAssumeValidAncestor to return true when the " +
|
||||
"assumed valid node is exactly 2 weeks behind the tip")
|
||||
}
|
||||
|
||||
// Set the assumed valid node to its parent so that it is 2 weeks + 1 block
|
||||
// behind the tip.
|
||||
assumeValidNode = assumeValidNode.parent
|
||||
g.chain.assumeValidNode = assumeValidNode
|
||||
|
||||
// Validate that isAssumeValidAncestor returns true for this scenario:
|
||||
// - The assumed valid node is 2 weeks + 1 block behind the tip
|
||||
node = assumeValidNode.parent
|
||||
if !g.chain.isAssumeValidAncestor(node) {
|
||||
t.Fatal("expected isAssumeValidAncestor to return true when the " +
|
||||
"assumed valid node is 2 weeks + 1 block behind the tip")
|
||||
}
|
||||
|
||||
// Set the assumed valid node to nil.
|
||||
assumeValidNode = nil
|
||||
g.chain.assumeValidNode = assumeValidNode
|
||||
|
||||
// Validate that isAssumeValidAncestor returns false for this scenario:
|
||||
// - The assumed valid node is nil
|
||||
if g.chain.isAssumeValidAncestor(node) {
|
||||
t.Fatal("expected isAssumeValidAncestor to return false when the " +
|
||||
"assumed valid node is nil")
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user