mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 19:06:51 +00:00
This udpates the blockchain module to use the lastest module major versions. While here, it also corrects a few typos and updates some test function names to more accurately reflect their purpose. The updated direct dependencies are as follows: - github.com/decred/dcrd/blockchain/stake/v2@v2.0.1 - github.com/decred/dcrd/chaincfg/v2@v2.2.0 - github.com/decred/dcrd/database/v2@v2.0.0 - github.com/decred/dcrd/dcrutil/v2@v2.0.0 - github.com/decred/dcrd/txscript/v2@v2.0.0
805 lines
21 KiB
Go
805 lines
21 KiB
Go
// Copyright (c) 2016-2019 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package blockchain
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/decred/dcrd/chaincfg/chainhash"
|
|
"github.com/decred/dcrd/chaincfg/v2"
|
|
)
|
|
|
|
// isVoterMajorityVersion determines if minVer requirement is met based on
|
|
// prevNode. The function always uses the voter versions of the prior window.
|
|
// For example, if StakeVersionInterval = 11 and StakeValidationHeight = 13 the
|
|
// windows start at 13 + 11 -1 = 24 and are as follows: 24-34, 35-45, 46-56 ...
|
|
// If height comes in at 35 we use the 24-34 window, up to height 45.
|
|
// If height comes in at 46 we use the 35-45 window, up to height 56 etc.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for writes).
|
|
func (b *BlockChain) isVoterMajorityVersion(minVer uint32, prevNode *blockNode) bool {
|
|
// Walk blockchain backwards to calculate version.
|
|
node := b.findStakeVersionPriorNode(prevNode)
|
|
if node == nil {
|
|
return 0 >= minVer
|
|
}
|
|
|
|
// Generate map key and look up cached result.
|
|
key := stakeMajorityCacheVersionKey(minVer, &node.hash)
|
|
if result, ok := b.isVoterMajorityVersionCache[key]; ok {
|
|
return result
|
|
}
|
|
|
|
// Tally both the total number of votes in the previous stake version validation
|
|
// interval and how many of those votes are at least the requested minimum
|
|
// version.
|
|
totalVotesFound := int32(0)
|
|
versionCount := int32(0)
|
|
iterNode := node
|
|
for i := int64(0); i < b.chainParams.StakeVersionInterval && iterNode != nil; i++ {
|
|
totalVotesFound += int32(len(iterNode.votes))
|
|
for _, v := range iterNode.votes {
|
|
if v.Version >= minVer {
|
|
versionCount++
|
|
}
|
|
}
|
|
|
|
iterNode = iterNode.parent
|
|
}
|
|
|
|
// Determine the required amount of votes to reach supermajority.
|
|
numRequired := totalVotesFound * b.chainParams.StakeMajorityMultiplier /
|
|
b.chainParams.StakeMajorityDivisor
|
|
|
|
// Cache value.
|
|
result := versionCount >= numRequired
|
|
b.isVoterMajorityVersionCache[key] = result
|
|
|
|
return result
|
|
}
|
|
|
|
func TestCalcWantHeight(t *testing.T) {
|
|
// For example, if StakeVersionInterval = 11 and StakeValidationHeight = 13 the
|
|
// windows start at 13 + (11 * 2) 25 and are as follows: 24-34, 35-45, 46-56 ...
|
|
// If height comes in at 35 we use the 24-34 window, up to height 45.
|
|
// If height comes in at 46 we use the 35-45 window, up to height 56 etc.
|
|
mainNetParams := chaincfg.MainNetParams()
|
|
testNet3Params := chaincfg.TestNet3Params()
|
|
simNetParams := chaincfg.SimNetParams()
|
|
regNetParams := chaincfg.RegNetParams()
|
|
tests := []struct {
|
|
name string
|
|
skip int64
|
|
interval int64
|
|
multiplier int64
|
|
negative int64
|
|
}{
|
|
{
|
|
name: "13 11 10000",
|
|
skip: 13,
|
|
interval: 11,
|
|
multiplier: 10000,
|
|
},
|
|
{
|
|
name: "27 33 10000",
|
|
skip: 27,
|
|
interval: 33,
|
|
multiplier: 10000,
|
|
},
|
|
{
|
|
name: "mainnet params",
|
|
skip: mainNetParams.StakeValidationHeight,
|
|
interval: mainNetParams.StakeVersionInterval,
|
|
multiplier: 5000,
|
|
},
|
|
{
|
|
name: "testnet3 params",
|
|
skip: testNet3Params.StakeValidationHeight,
|
|
interval: testNet3Params.StakeVersionInterval,
|
|
multiplier: 1000,
|
|
},
|
|
{
|
|
name: "simnet params",
|
|
skip: simNetParams.StakeValidationHeight,
|
|
interval: simNetParams.StakeVersionInterval,
|
|
multiplier: 10000,
|
|
},
|
|
{
|
|
name: "regnet params",
|
|
skip: regNetParams.StakeValidationHeight,
|
|
interval: regNetParams.StakeVersionInterval,
|
|
multiplier: 10000,
|
|
},
|
|
{
|
|
name: "negative mainnet params",
|
|
skip: mainNetParams.StakeValidationHeight,
|
|
interval: mainNetParams.StakeVersionInterval,
|
|
multiplier: 1000,
|
|
negative: 1,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Logf("running: %v skip: %v interval: %v",
|
|
test.name, test.skip, test.interval)
|
|
|
|
start := test.skip + test.interval*2
|
|
expectedHeight := start - 1 // zero based
|
|
x := int64(0) + test.negative
|
|
for i := start; i < test.multiplier*test.interval; i++ {
|
|
if x%test.interval == 0 && i != start {
|
|
expectedHeight += test.interval
|
|
}
|
|
wantHeight := calcWantHeight(test.skip, test.interval, i)
|
|
|
|
if wantHeight != expectedHeight {
|
|
if test.negative == 0 {
|
|
t.Fatalf("%v: i %v x %v -> wantHeight %v expectedHeight %v\n",
|
|
test.name, i, x, wantHeight, expectedHeight)
|
|
}
|
|
}
|
|
|
|
x++
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestCalcStakeVersionCorners ensures that stake version calculation works as
|
|
// intended under various corner cases such as attempting to go back backwards.
|
|
func TestCalcStakeVersionCorners(t *testing.T) {
|
|
params := chaincfg.SimNetParams()
|
|
svh := params.StakeValidationHeight
|
|
svi := params.StakeVersionInterval
|
|
|
|
// Generate enough nodes to reach stake validation height with stake
|
|
// versions set to 0.
|
|
bc := newFakeChain(params)
|
|
node := bc.bestChain.Tip()
|
|
for i := int64(1); i <= svh; i++ {
|
|
node = newFakeNode(node, 0, 0, 0, time.Now())
|
|
bc.bestChain.SetTip(node)
|
|
}
|
|
if node.height != svh {
|
|
t.Fatalf("invalid height got %v expected %v", node.height, svh)
|
|
}
|
|
|
|
// Generate 3 intervals with v2 votes and calculated stake version.
|
|
for i := int64(0); i < svi*3; i++ {
|
|
sv := bc.calcStakeVersion(node)
|
|
|
|
// Set vote and stake versions.
|
|
node = newFakeNode(node, 3, sv, 0, time.Now())
|
|
appendFakeVotes(node, params.TicketsPerBlock, 2, 0)
|
|
bc.bestChain.SetTip(node)
|
|
}
|
|
|
|
// Versions 0 and 2 should now be considered the majority version, but
|
|
// v4 should not yet be considered majority.
|
|
if !bc.isStakeMajorityVersion(0, node) {
|
|
t.Fatalf("invalid StakeVersion expected 0 -> true")
|
|
}
|
|
if !bc.isStakeMajorityVersion(2, node) {
|
|
t.Fatalf("invalid StakeVersion expected 2 -> true")
|
|
}
|
|
if bc.isStakeMajorityVersion(4, node) {
|
|
t.Fatalf("invalid StakeVersion expected 4 -> false")
|
|
}
|
|
|
|
// Generate 3 intervals with v4 votes and calculated stake version.
|
|
for i := int64(0); i < svi*3; i++ {
|
|
sv := bc.calcStakeVersion(node)
|
|
|
|
// Set vote and stake versions.
|
|
node = newFakeNode(node, 3, sv, 0, time.Now())
|
|
appendFakeVotes(node, params.TicketsPerBlock, 4, 0)
|
|
bc.bestChain.SetTip(node)
|
|
}
|
|
|
|
// Versions up to and including v4 should now be considered the majority
|
|
// version, but v5 should not yet be considered majority.
|
|
for _, version := range []uint32{0, 2, 4} {
|
|
if !bc.isStakeMajorityVersion(version, node) {
|
|
t.Fatalf("invalid StakeVersion expected %d -> true",
|
|
version)
|
|
}
|
|
|
|
}
|
|
if bc.isStakeMajorityVersion(5, node) {
|
|
t.Fatalf("invalid StakeVersion expected 5 -> false")
|
|
}
|
|
|
|
// Generate 3 intervals with v2 votes and calculated stake version.
|
|
for i := int64(0); i < svi*3; i++ {
|
|
sv := bc.calcStakeVersion(node)
|
|
|
|
// Set vote and stake versions.
|
|
node = newFakeNode(node, 3, sv, 0, time.Now())
|
|
appendFakeVotes(node, params.TicketsPerBlock, 2, 0)
|
|
bc.bestChain.SetTip(node)
|
|
}
|
|
|
|
// Versions up to and including v4 should still be considered the
|
|
// majority version since even though there were multiple intervals with
|
|
// a majority v2 votes, the stake version is not allowed to go
|
|
// backwards. Version 5 should still not be consider majority.
|
|
for _, version := range []uint32{0, 2, 4} {
|
|
if !bc.isStakeMajorityVersion(version, node) {
|
|
t.Fatalf("invalid StakeVersion expected %d -> true",
|
|
version)
|
|
}
|
|
|
|
}
|
|
if bc.isStakeMajorityVersion(5, node) {
|
|
t.Fatalf("invalid StakeVersion expected 5 -> false")
|
|
}
|
|
|
|
// Generate 2 intervals with v5 votes and calculated stake version.
|
|
for i := int64(0); i < svi*2; i++ {
|
|
sv := bc.calcStakeVersion(node)
|
|
|
|
// Set vote and stake versions.
|
|
node = newFakeNode(node, 3, sv, 0, time.Now())
|
|
appendFakeVotes(node, params.TicketsPerBlock, 5, 0)
|
|
bc.bestChain.SetTip(node)
|
|
}
|
|
|
|
// Versions up to and including v5 should now be considered the majority
|
|
// version, but v6 should not yet be.
|
|
for _, version := range []uint32{0, 2, 4, 5} {
|
|
if !bc.isStakeMajorityVersion(version, node) {
|
|
t.Fatalf("invalid StakeVersion expected %d -> true",
|
|
version)
|
|
}
|
|
|
|
}
|
|
if bc.isStakeMajorityVersion(6, node) {
|
|
t.Fatalf("invalid StakeVersion expected 6 -> false")
|
|
}
|
|
|
|
// Generate 1 interval with v4 votes to test the edge condition.
|
|
for i := int64(0); i < svi; i++ {
|
|
sv := bc.calcStakeVersion(node)
|
|
|
|
// Set vote and stake versions.
|
|
node = newFakeNode(node, 3, sv, 0, time.Now())
|
|
appendFakeVotes(node, params.TicketsPerBlock, 4, 0)
|
|
bc.bestChain.SetTip(node)
|
|
|
|
}
|
|
|
|
// Versions up to and including v5 should still be considered the
|
|
// majority version since even though there was an interval with a
|
|
// majority v4 votes, the stake version is not allowed to go backwards.
|
|
// Version 6 should still not be consider majority.
|
|
for _, version := range []uint32{0, 2, 4, 5} {
|
|
if !bc.isStakeMajorityVersion(version, node) {
|
|
t.Fatalf("invalid StakeVersion expected %d -> true",
|
|
version)
|
|
}
|
|
|
|
}
|
|
if bc.isStakeMajorityVersion(6, node) {
|
|
t.Fatalf("invalid StakeVersion expected 6 -> false")
|
|
}
|
|
|
|
// Generate another interval with v4 votes.
|
|
for i := int64(0); i < svi; i++ {
|
|
sv := bc.calcStakeVersion(node)
|
|
|
|
// Set stake versions.
|
|
node = newFakeNode(node, 3, sv, 0, time.Now())
|
|
appendFakeVotes(node, params.TicketsPerBlock, 4, 0)
|
|
bc.bestChain.SetTip(node)
|
|
|
|
}
|
|
|
|
// Versions up to and including v5 should still be considered the
|
|
// majority version since even though there was another interval with a
|
|
// majority v4 votes, the stake version is not allowed to go backwards.
|
|
// Version 6 should still not be consider majority.
|
|
for _, version := range []uint32{0, 2, 4, 5} {
|
|
if !bc.isStakeMajorityVersion(version, node) {
|
|
t.Fatalf("invalid StakeVersion expected %d -> true",
|
|
version)
|
|
}
|
|
|
|
}
|
|
if bc.isStakeMajorityVersion(6, node) {
|
|
t.Fatalf("invalid StakeVersion expected 6 -> false")
|
|
}
|
|
}
|
|
|
|
// TestCalcStakeVersion ensures that stake version calculation works as
|
|
// intended when
|
|
func TestCalcStakeVersion(t *testing.T) {
|
|
params := chaincfg.SimNetParams()
|
|
svh := params.StakeValidationHeight
|
|
svi := params.StakeVersionInterval
|
|
tpb := params.TicketsPerBlock
|
|
|
|
tests := []struct {
|
|
name string
|
|
numNodes int64
|
|
expectVersion uint32
|
|
set func(*blockNode)
|
|
}{
|
|
{
|
|
name: "headerStake 2 votes 3",
|
|
numNodes: svh + svi*3,
|
|
expectVersion: 3,
|
|
set: func(node *blockNode) {
|
|
if node.height > svh {
|
|
appendFakeVotes(node, tpb, 3, 0)
|
|
node.stakeVersion = 2
|
|
node.blockVersion = 3
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "headerStake 3 votes 2",
|
|
numNodes: svh + svi*3,
|
|
expectVersion: 3,
|
|
set: func(node *blockNode) {
|
|
if node.height > svh {
|
|
appendFakeVotes(node, tpb, 2, 0)
|
|
node.stakeVersion = 3
|
|
node.blockVersion = 3
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
bc := newFakeChain(params)
|
|
node := bc.bestChain.Tip()
|
|
|
|
for i := int64(1); i <= test.numNodes; i++ {
|
|
node = newFakeNode(node, 1, 0, 0, time.Now())
|
|
test.set(node)
|
|
bc.bestChain.SetTip(node)
|
|
}
|
|
|
|
version := bc.calcStakeVersion(bc.bestChain.Tip())
|
|
if version != test.expectVersion {
|
|
t.Fatalf("version mismatch: got %v expected %v",
|
|
version, test.expectVersion)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestIsStakeMajorityVersion ensures that determining the current majority
|
|
// stake version works as intended under a wide variety of scenarios.
|
|
func TestIsStakeMajorityVersion(t *testing.T) {
|
|
params := chaincfg.RegNetParams()
|
|
svh := params.StakeValidationHeight
|
|
svi := params.StakeVersionInterval
|
|
tpb := params.TicketsPerBlock
|
|
|
|
// Calculate super majority for 5 and 3 ticket maxes.
|
|
maxTickets5 := int32(svi) * int32(tpb)
|
|
sm5 := maxTickets5 * params.StakeMajorityMultiplier / params.StakeMajorityDivisor
|
|
maxTickets3 := int32(svi) * int32(tpb-2)
|
|
sm3 := maxTickets3 * params.StakeMajorityMultiplier / params.StakeMajorityDivisor
|
|
|
|
// Keep track of ticketcount in set. Must be reset every test.
|
|
ticketCount := int32(0)
|
|
|
|
tests := []struct {
|
|
name string
|
|
numNodes int64
|
|
set func(*blockNode)
|
|
blockVersion int32
|
|
startStakeVersion uint32
|
|
expectedStakeVersion uint32
|
|
expectedCalcVersion uint32
|
|
result bool
|
|
}{
|
|
{
|
|
name: "too shallow",
|
|
numNodes: svh + svi - 1,
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 1,
|
|
expectedCalcVersion: 0,
|
|
result: true,
|
|
},
|
|
{
|
|
name: "just enough",
|
|
numNodes: svh + svi,
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 1,
|
|
expectedCalcVersion: 0,
|
|
result: true,
|
|
},
|
|
{
|
|
name: "odd",
|
|
numNodes: svh + svi + 1,
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 1,
|
|
expectedCalcVersion: 0,
|
|
result: true,
|
|
},
|
|
{
|
|
name: "100%",
|
|
numNodes: svh + svi,
|
|
set: func(node *blockNode) {
|
|
if node.height > svh {
|
|
appendFakeVotes(node, tpb, 2, 0)
|
|
}
|
|
},
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 2,
|
|
expectedCalcVersion: 0,
|
|
result: true,
|
|
},
|
|
{
|
|
name: "50%",
|
|
numNodes: svh + (svi * 2),
|
|
set: func(node *blockNode) {
|
|
if node.height <= svh {
|
|
return
|
|
}
|
|
|
|
if node.height < svh+svi {
|
|
appendFakeVotes(node, tpb, 1, 0)
|
|
return
|
|
}
|
|
|
|
threshold := maxTickets5 / 2
|
|
|
|
v := uint32(1)
|
|
for i := 0; i < int(tpb); i++ {
|
|
if ticketCount >= threshold {
|
|
v = 2
|
|
}
|
|
appendFakeVotes(node, 1, v, 0)
|
|
ticketCount++
|
|
}
|
|
},
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 2,
|
|
expectedCalcVersion: 0,
|
|
result: false,
|
|
},
|
|
{
|
|
name: "75%-1",
|
|
numNodes: svh + (svi * 2),
|
|
set: func(node *blockNode) {
|
|
if node.height < svh {
|
|
return
|
|
}
|
|
|
|
if node.height < svh+svi {
|
|
appendFakeVotes(node, tpb, 1, 0)
|
|
return
|
|
}
|
|
|
|
threshold := maxTickets5 - sm5 + 1
|
|
|
|
v := uint32(1)
|
|
for i := 0; i < int(tpb); i++ {
|
|
if ticketCount >= threshold {
|
|
v = 2
|
|
}
|
|
appendFakeVotes(node, 1, v, 0)
|
|
ticketCount++
|
|
}
|
|
},
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 2,
|
|
expectedCalcVersion: 0,
|
|
result: false,
|
|
},
|
|
{
|
|
name: "75%",
|
|
numNodes: svh + (svi * 2),
|
|
set: func(node *blockNode) {
|
|
if node.height <= svh {
|
|
return
|
|
}
|
|
|
|
if node.height < svh+svi {
|
|
appendFakeVotes(node, tpb, 1, 0)
|
|
return
|
|
}
|
|
|
|
threshold := maxTickets5 - sm5
|
|
|
|
v := uint32(1)
|
|
for i := 0; i < int(tpb); i++ {
|
|
if ticketCount >= threshold {
|
|
v = 2
|
|
}
|
|
appendFakeVotes(node, 1, v, 0)
|
|
ticketCount++
|
|
}
|
|
},
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 2,
|
|
expectedCalcVersion: 0,
|
|
result: true,
|
|
},
|
|
{
|
|
name: "100% after several non majority intervals",
|
|
numNodes: svh + (params.StakeVersionInterval * 222),
|
|
set: func(node *blockNode) {
|
|
if node.height <= svh {
|
|
return
|
|
}
|
|
|
|
if node.height < svh+svi {
|
|
appendFakeVotes(node, tpb, 1, 0)
|
|
return
|
|
}
|
|
|
|
for i := uint32(0); i < uint32(tpb); i++ {
|
|
appendFakeVotes(node, 1, i%5, 0)
|
|
}
|
|
},
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 1,
|
|
expectedCalcVersion: 0,
|
|
result: true,
|
|
},
|
|
{
|
|
name: "no majority ever",
|
|
numNodes: svh + (svi * 8),
|
|
set: func(node *blockNode) {
|
|
if node.height <= svh {
|
|
return
|
|
}
|
|
|
|
for i := uint32(0); i < uint32(tpb); i++ {
|
|
appendFakeVotes(node, 1, i%5, 0)
|
|
}
|
|
},
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 1,
|
|
expectedCalcVersion: 0,
|
|
result: true,
|
|
},
|
|
{
|
|
name: "75%-1 with 3 votes",
|
|
numNodes: svh + (svi * 2),
|
|
set: func(node *blockNode) {
|
|
if node.height < svh {
|
|
return
|
|
}
|
|
|
|
if node.height < svh+svi {
|
|
appendFakeVotes(node, tpb-2, 1, 0)
|
|
return
|
|
}
|
|
|
|
threshold := maxTickets3 - sm3 + 1
|
|
|
|
v := uint32(1)
|
|
for i := 0; i < int(tpb-2); i++ {
|
|
if ticketCount >= threshold {
|
|
v = 2
|
|
}
|
|
appendFakeVotes(node, 1, v, 0)
|
|
ticketCount++
|
|
}
|
|
},
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 2,
|
|
expectedCalcVersion: 0,
|
|
result: false,
|
|
},
|
|
{
|
|
name: "75% with 3 votes",
|
|
numNodes: svh + (svi * 2),
|
|
set: func(node *blockNode) {
|
|
if node.height <= svh {
|
|
return
|
|
}
|
|
|
|
if node.height < svh+svi {
|
|
appendFakeVotes(node, tpb-2, 1, 0)
|
|
return
|
|
}
|
|
|
|
threshold := maxTickets3 - sm3
|
|
|
|
v := uint32(1)
|
|
for i := 0; i < int(tpb-2); i++ {
|
|
if ticketCount >= threshold {
|
|
v = 2
|
|
}
|
|
appendFakeVotes(node, 1, v, 0)
|
|
ticketCount++
|
|
}
|
|
},
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 2,
|
|
expectedCalcVersion: 0,
|
|
result: true,
|
|
},
|
|
{
|
|
name: "75% with 3 votes blockversion 3",
|
|
numNodes: svh + (svi * 2),
|
|
set: func(node *blockNode) {
|
|
if node.height <= svh {
|
|
return
|
|
}
|
|
|
|
if node.height < svh+svi {
|
|
appendFakeVotes(node, tpb-2, 1, 0)
|
|
return
|
|
}
|
|
|
|
threshold := maxTickets3 - sm3
|
|
|
|
v := uint32(1)
|
|
for i := 0; i < int(tpb-2); i++ {
|
|
if ticketCount >= threshold {
|
|
v = 2
|
|
}
|
|
appendFakeVotes(node, 1, v, 0)
|
|
ticketCount++
|
|
}
|
|
},
|
|
blockVersion: 3,
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 2,
|
|
expectedCalcVersion: 2,
|
|
result: true,
|
|
},
|
|
{
|
|
name: "75%-1 with 3 votes blockversion 3",
|
|
numNodes: svh + (svi * 2),
|
|
set: func(node *blockNode) {
|
|
if node.height < svh {
|
|
return
|
|
}
|
|
|
|
if node.height < svh+svi {
|
|
appendFakeVotes(node, tpb-2, 1, 0)
|
|
return
|
|
}
|
|
|
|
threshold := maxTickets3 - sm3 + 1
|
|
|
|
v := uint32(1)
|
|
for i := 0; i < int(tpb-2); i++ {
|
|
if ticketCount >= threshold {
|
|
v = 2
|
|
}
|
|
appendFakeVotes(node, 1, v, 0)
|
|
ticketCount++
|
|
}
|
|
},
|
|
blockVersion: 3,
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 2,
|
|
expectedCalcVersion: 1,
|
|
result: false,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
// Create new BlockChain in order to blow away cache.
|
|
bc := newFakeChain(params)
|
|
node := bc.bestChain.Tip()
|
|
node.stakeVersion = test.startStakeVersion
|
|
|
|
ticketCount = 0
|
|
|
|
for i := int64(1); i <= test.numNodes; i++ {
|
|
node = newFakeNode(node, test.blockVersion,
|
|
test.startStakeVersion, 0, time.Now())
|
|
|
|
// Override version.
|
|
if test.set != nil {
|
|
test.set(node)
|
|
} else {
|
|
appendFakeVotes(node, tpb,
|
|
test.startStakeVersion, 0)
|
|
}
|
|
|
|
bc.bestChain.SetTip(node)
|
|
}
|
|
|
|
res := bc.isVoterMajorityVersion(test.expectedStakeVersion, node)
|
|
if res != test.result {
|
|
t.Fatalf("%v isVoterMajorityVersion", test.name)
|
|
}
|
|
|
|
// validate calcStakeVersion
|
|
version := bc.calcStakeVersion(node)
|
|
if version != test.expectedCalcVersion {
|
|
t.Fatalf("%v calcStakeVersionByNode got %v expected %v",
|
|
test.name, version, test.expectedCalcVersion)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLarge(t *testing.T) {
|
|
params := chaincfg.MainNetParams()
|
|
|
|
numRuns := 5
|
|
numBlocks := params.StakeVersionInterval * 100
|
|
numBlocksShallow := params.StakeVersionInterval * 10
|
|
tests := []struct {
|
|
name string
|
|
numNodes int64
|
|
blockVersion int32
|
|
startStakeVersion uint32
|
|
expectedStakeVersion uint32
|
|
expectedCalcVersion uint32
|
|
result bool
|
|
}{
|
|
{
|
|
name: "shallow cache",
|
|
numNodes: numBlocksShallow,
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 1,
|
|
expectedCalcVersion: 0,
|
|
result: true,
|
|
},
|
|
{
|
|
name: "deep cache",
|
|
numNodes: numBlocks,
|
|
startStakeVersion: 1,
|
|
expectedStakeVersion: 1,
|
|
expectedCalcVersion: 0,
|
|
result: true,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
// Create new BlockChain in order to blow away cache.
|
|
bc := newFakeChain(params)
|
|
node := bc.bestChain.Tip()
|
|
node.stakeVersion = test.startStakeVersion
|
|
|
|
for i := int64(1); i <= test.numNodes; i++ {
|
|
node = newFakeNode(node, test.blockVersion,
|
|
test.startStakeVersion, 0, time.Now())
|
|
|
|
// Override version.
|
|
appendFakeVotes(node, params.TicketsPerBlock,
|
|
test.startStakeVersion, 0)
|
|
bc.bestChain.SetTip(node)
|
|
}
|
|
|
|
for x := 0; x < numRuns; x++ {
|
|
start := time.Now()
|
|
res := bc.isVoterMajorityVersion(test.expectedStakeVersion, node)
|
|
if res != test.result {
|
|
t.Fatalf("%v isVoterMajorityVersion got %v expected %v", test.name, res, test.result)
|
|
}
|
|
|
|
// validate calcStakeVersion
|
|
version := bc.calcStakeVersion(node)
|
|
if version != test.expectedCalcVersion {
|
|
t.Fatalf("%v calcStakeVersion got %v expected %v",
|
|
test.name, version, test.expectedCalcVersion)
|
|
}
|
|
end := time.Now()
|
|
|
|
setup := "setup 0"
|
|
if x != 0 {
|
|
setup = fmt.Sprintf("run %v", x)
|
|
}
|
|
|
|
vkey := stakeMajorityCacheKeySize + 8 // bool on x86_64
|
|
key := chainhash.HashSize + 4 // size of uint32
|
|
|
|
cost := len(bc.isVoterMajorityVersionCache) * vkey
|
|
cost += len(bc.isStakeMajorityVersionCache) * vkey
|
|
cost += len(bc.calcPriorStakeVersionCache) * key
|
|
cost += len(bc.calcVoterVersionIntervalCache) * key
|
|
cost += len(bc.calcStakeVersionCache) * key
|
|
memoryCost := fmt.Sprintf("memory cost: %v", cost)
|
|
|
|
t.Logf("run time (%v) %v %v", setup, end.Sub(start),
|
|
memoryCost)
|
|
}
|
|
}
|
|
}
|