mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
1668 lines
39 KiB
Go
1668 lines
39 KiB
Go
// 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.
|
|
|
|
package blockchain
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/decred/dcrd/blockchain/stake/v2"
|
|
"github.com/decred/dcrd/chaincfg/v2"
|
|
)
|
|
|
|
var (
|
|
posVersion = uint32(4)
|
|
powVersion = int32(4)
|
|
|
|
pedro = chaincfg.Vote{
|
|
Id: "voteforpedro",
|
|
Description: "You should always vote for Pedro",
|
|
Mask: 0x6, // 0b0110
|
|
Choices: []chaincfg.Choice{
|
|
{
|
|
Id: "Abstain",
|
|
Description: "Abstain voting for Pedro",
|
|
Bits: 0x0, // 0b0000
|
|
IsAbstain: true,
|
|
IsNo: false,
|
|
},
|
|
{
|
|
Id: "Yes",
|
|
Description: "Vote for Pedro",
|
|
Bits: 0x2, // 0b0010
|
|
IsAbstain: false,
|
|
IsNo: false,
|
|
},
|
|
{
|
|
Id: "No",
|
|
Description: "Dont vote for Pedro",
|
|
Bits: 0x4, // 0b0100
|
|
IsAbstain: false,
|
|
IsNo: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
multipleChoice = chaincfg.Vote{
|
|
Id: "multiplechoice",
|
|
Description: "Pick one",
|
|
Mask: 0x70, // 0b0111 0000
|
|
Choices: []chaincfg.Choice{
|
|
{
|
|
Id: "Abstain",
|
|
Description: "Abstain multiple choice",
|
|
Bits: 0x0, // 0b0000 0000
|
|
IsAbstain: true,
|
|
IsNo: false,
|
|
},
|
|
{
|
|
Id: "one",
|
|
Description: "Choice 1",
|
|
Bits: 0x10, // 0b0001 0000
|
|
IsAbstain: false,
|
|
IsNo: false,
|
|
},
|
|
{
|
|
Id: "Vote against",
|
|
Description: "Vote against all multiple",
|
|
Bits: 0x20, // 0b0010 0000
|
|
IsAbstain: false,
|
|
IsNo: true,
|
|
},
|
|
{
|
|
Id: "two",
|
|
Description: "Choice 2",
|
|
Bits: 0x30, // 0b0011 0000
|
|
IsAbstain: false,
|
|
IsNo: false,
|
|
},
|
|
{
|
|
Id: "three",
|
|
Description: "Choice 3",
|
|
Bits: 0x40, // 0b0100 0000
|
|
IsAbstain: false,
|
|
IsNo: false,
|
|
},
|
|
{
|
|
Id: "four",
|
|
Description: "Choice 4",
|
|
Bits: 0x50, // 0b0101 0000
|
|
IsAbstain: false,
|
|
IsNo: false,
|
|
},
|
|
},
|
|
}
|
|
)
|
|
|
|
// defaultParams returns net parameters modified to have a single known
|
|
// deployment that is used throughout the various votebit tests.
|
|
func defaultParams(vote chaincfg.Vote) chaincfg.Params {
|
|
params := chaincfg.RegNetParams()
|
|
params.Deployments = make(map[uint32][]chaincfg.ConsensusDeployment)
|
|
params.Deployments[posVersion] = []chaincfg.ConsensusDeployment{{
|
|
Vote: vote,
|
|
StartTime: uint64(time.Now().Add(time.Duration(
|
|
params.RuleChangeActivationInterval) *
|
|
time.Second).Unix()),
|
|
ExpireTime: uint64(time.Now().Add(24 * time.Hour).Unix()),
|
|
}}
|
|
|
|
return *params
|
|
}
|
|
|
|
// TestNoQuorum ensures that the quorum behavior works as expected with no
|
|
// votes.
|
|
func TestNoQuorum(t *testing.T) {
|
|
params := defaultParams(pedro)
|
|
bc := newFakeChain(¶ms)
|
|
node := bc.bestChain.Tip()
|
|
node.stakeVersion = posVersion
|
|
|
|
// get to svi
|
|
curTimestamp := time.Now()
|
|
for i := uint32(0); i < uint32(params.StakeValidationHeight); i++ {
|
|
node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
ts, err := bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(SVI): %v", err)
|
|
}
|
|
tse := ThresholdStateTuple{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}
|
|
if ts != tse {
|
|
t.Fatalf("expected %+v got %+v", ts, tse)
|
|
}
|
|
|
|
// get to started
|
|
for i := uint32(0); i < params.RuleChangeActivationInterval-1; i++ {
|
|
// Set stake versions and vote bits.
|
|
node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
|
|
appendFakeVotes(node, params.TicketsPerBlock, posVersion, 0x01)
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
|
|
ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(started): %v", err)
|
|
}
|
|
tse = ThresholdStateTuple{
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}
|
|
if ts != tse {
|
|
t.Fatalf("expected %+v got %+v", ts, tse)
|
|
}
|
|
|
|
// get to quorum - 1
|
|
voteCount := uint32(0)
|
|
for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
|
|
// Set stake versions and vote bits.
|
|
node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
|
|
for x := 0; x < int(params.TicketsPerBlock); x++ {
|
|
bits := uint16(0x01) // abstain
|
|
if voteCount < params.RuleChangeActivationQuorum-1 {
|
|
bits = 0x05 // vote no
|
|
}
|
|
appendFakeVotes(node, 1, posVersion, bits)
|
|
voteCount++
|
|
}
|
|
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
|
|
ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(quorum-1): %v", err)
|
|
}
|
|
tse = ThresholdStateTuple{
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}
|
|
if ts != tse {
|
|
t.Fatalf("expected %+v got %+v", ts, tse)
|
|
}
|
|
|
|
// get to exact quorum but with 75%%-1 yes votes
|
|
voteCount = uint32(0)
|
|
for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
|
|
// Set stake versions and vote bits.
|
|
node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
|
|
for x := 0; x < int(params.TicketsPerBlock); x++ {
|
|
// 119 no, 41 yes -> 120 == 75% and 120 reaches quorum
|
|
bits := uint16(0x01) // abstain
|
|
quorum := params.RuleChangeActivationQuorum*
|
|
params.RuleChangeActivationMultiplier/
|
|
params.RuleChangeActivationDivisor - 1
|
|
if voteCount < quorum {
|
|
bits = 0x05 // vote no
|
|
} else if voteCount < params.RuleChangeActivationQuorum {
|
|
bits = 0x03 // vote yes
|
|
}
|
|
appendFakeVotes(node, 1, posVersion, bits)
|
|
voteCount++
|
|
}
|
|
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
|
|
ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(quorum 75%%-1): %v", err)
|
|
}
|
|
tse = ThresholdStateTuple{
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}
|
|
if ts != tse {
|
|
t.Fatalf("expected %+v got %+v", ts, tse)
|
|
}
|
|
|
|
// get to exact quorum with exactly 75% of votes
|
|
voteCount = uint32(0)
|
|
for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
|
|
// Set stake versions and vote bits.
|
|
node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
|
|
for x := 0; x < int(params.TicketsPerBlock); x++ {
|
|
// 120 no, 40 yes -> 120 == 75% and 120 reaches quorum
|
|
bits := uint16(0x01) // abstain
|
|
quorum := params.RuleChangeActivationQuorum *
|
|
params.RuleChangeActivationMultiplier /
|
|
params.RuleChangeActivationDivisor
|
|
if voteCount < quorum {
|
|
bits = 0x05 // vote no
|
|
} else if voteCount < params.RuleChangeActivationQuorum {
|
|
bits = 0x03 // vote yes
|
|
}
|
|
appendFakeVotes(node, 1, posVersion, bits)
|
|
voteCount++
|
|
}
|
|
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
|
|
ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(quorum 75%%): %v", err)
|
|
}
|
|
tse = ThresholdStateTuple{
|
|
State: ThresholdFailed,
|
|
Choice: 0x02,
|
|
}
|
|
if ts != tse {
|
|
t.Fatalf("expected %+v got %+v", ts, tse)
|
|
}
|
|
}
|
|
|
|
// TestNoQuorum ensures that the quorum behavior works as expected with yes
|
|
// votes.
|
|
func TestYesQuorum(t *testing.T) {
|
|
params := defaultParams(pedro)
|
|
bc := newFakeChain(¶ms)
|
|
node := bc.bestChain.Tip()
|
|
node.stakeVersion = posVersion
|
|
|
|
// get to svi
|
|
curTimestamp := time.Now()
|
|
for i := uint32(0); i < uint32(params.StakeValidationHeight); i++ {
|
|
node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
|
|
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
ts, err := bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(SVI): %v", err)
|
|
}
|
|
tse := ThresholdStateTuple{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}
|
|
if ts != tse {
|
|
t.Fatalf("expected %+v got %+v", ts, tse)
|
|
}
|
|
|
|
// get to started
|
|
for i := uint32(0); i < params.RuleChangeActivationInterval-1; i++ {
|
|
// Set stake versions and vote bits.
|
|
node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
|
|
appendFakeVotes(node, params.TicketsPerBlock, posVersion, 0x01)
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
|
|
ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(started): %v", err)
|
|
}
|
|
tse = ThresholdStateTuple{
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}
|
|
if ts != tse {
|
|
t.Fatalf("expected %+v got %+v", ts, tse)
|
|
}
|
|
|
|
// get to quorum - 1
|
|
voteCount := uint32(0)
|
|
for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
|
|
// Set stake versions and vote bits.
|
|
node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
|
|
for x := 0; x < int(params.TicketsPerBlock); x++ {
|
|
bits := uint16(0x01) // abstain
|
|
if voteCount < params.RuleChangeActivationQuorum-1 {
|
|
bits = 0x03 // vote yes
|
|
}
|
|
appendFakeVotes(node, 1, posVersion, bits)
|
|
voteCount++
|
|
}
|
|
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
|
|
ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(quorum-1): %v", err)
|
|
}
|
|
tse = ThresholdStateTuple{
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}
|
|
if ts != tse {
|
|
t.Fatalf("expected %+v got %+v", ts, tse)
|
|
}
|
|
|
|
// get to exact quorum but with 75%-1 yes votes
|
|
voteCount = uint32(0)
|
|
for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
|
|
// Set stake versions and vote bits.
|
|
node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
|
|
for x := 0; x < int(params.TicketsPerBlock); x++ {
|
|
// 119 yes, 41 no -> 120 == 75% and 120 reaches quorum
|
|
bits := uint16(0x01) // abstain
|
|
quorum := params.RuleChangeActivationQuorum*
|
|
params.RuleChangeActivationMultiplier/
|
|
params.RuleChangeActivationDivisor - 1
|
|
if voteCount < quorum {
|
|
bits = 0x03 // vote yes
|
|
} else if voteCount < params.RuleChangeActivationQuorum {
|
|
bits = 0x05 // vote no
|
|
}
|
|
appendFakeVotes(node, 1, posVersion, bits)
|
|
voteCount++
|
|
}
|
|
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
|
|
ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(quorum 75%%-1): %v", err)
|
|
}
|
|
tse = ThresholdStateTuple{
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}
|
|
if ts != tse {
|
|
t.Fatalf("expected %+v got %+v", ts, tse)
|
|
}
|
|
|
|
// get to exact quorum with exactly 75% of votes
|
|
voteCount = uint32(0)
|
|
for i := uint32(0); i < params.RuleChangeActivationInterval; i++ {
|
|
// Set stake versions and vote bits.
|
|
node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp)
|
|
for x := 0; x < int(params.TicketsPerBlock); x++ {
|
|
// 120 yes, 40 no -> 120 == 75% and 120 reaches quorum
|
|
bits := uint16(0x01) // abstain
|
|
quorum := params.RuleChangeActivationQuorum *
|
|
params.RuleChangeActivationMultiplier /
|
|
params.RuleChangeActivationDivisor
|
|
if voteCount < quorum {
|
|
bits = 0x03 // vote yes
|
|
} else if voteCount < params.RuleChangeActivationQuorum {
|
|
bits = 0x05 // vote no
|
|
}
|
|
appendFakeVotes(node, 1, posVersion, bits)
|
|
voteCount++
|
|
}
|
|
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
|
|
ts, err = bc.NextThresholdState(&node.hash, posVersion, pedro.Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(quorum 75%%): %v", err)
|
|
}
|
|
tse = ThresholdStateTuple{
|
|
State: ThresholdLockedIn,
|
|
Choice: 0x01,
|
|
}
|
|
if ts != tse {
|
|
t.Fatalf("expected %+v got %+v", ts, tse)
|
|
}
|
|
}
|
|
|
|
// TestVoting ensure the overall voting of an agenda works as expected including
|
|
// a wide variety of conditions.
|
|
func TestVoting(t *testing.T) {
|
|
params := defaultParams(chaincfg.Vote{})
|
|
rci := params.RuleChangeActivationInterval
|
|
svh := uint32(params.StakeValidationHeight)
|
|
svi := uint32(params.StakeVersionInterval)
|
|
|
|
type voteCount struct {
|
|
vote stake.VoteVersionTuple
|
|
count uint32
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
vote chaincfg.Vote
|
|
blockVersion int32
|
|
startStakeVersion uint32
|
|
end func(time.Time) uint64
|
|
voteBitsCounts []voteCount
|
|
expectedState []ThresholdStateTuple
|
|
}{
|
|
{
|
|
name: "pedro too shallow",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh - 1,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro greater PoS version",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion - 1,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svi - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - svi,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 1,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro greater PoS version calcStakeVersion",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion - 1,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion - 1,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh + 2*svi - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci % svi,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 1,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro smaller PoS version",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion + 1,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 0x01,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 0x01,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro smaller PoW version",
|
|
vote: pedro,
|
|
blockVersion: powVersion - 1,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro greater PoW version",
|
|
vote: pedro,
|
|
blockVersion: powVersion + 1,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 0x01,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 0x01,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro 100% yes, wrong version",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion + 1,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion + 1,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion + 1,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion + 1,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion + 1,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 0x01,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 0x01,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 0x01,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro 100% yes",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 0x01,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 0x01,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 0x01,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro 100% no",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x05,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x05,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdFailed,
|
|
Choice: 0x02,
|
|
}, {
|
|
State: ThresholdFailed,
|
|
Choice: 0x02,
|
|
}, {
|
|
State: ThresholdFailed,
|
|
Choice: 0x02,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro 100% invalid",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x06,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x06,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 0x01,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 0x01,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro 100% abstain",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro expire before started",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion - 1,
|
|
end: func(t time.Time) uint64 {
|
|
return uint64(t.Add(time.Second *
|
|
time.Duration(int64(svh)+int64(rci+rci/2))).Unix())
|
|
},
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion - 1,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion - 1,
|
|
Bits: 0x05,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion - 1,
|
|
Bits: 0x05,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdFailed,
|
|
Choice: invalidChoice,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro expire after started",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
end: func(t time.Time) uint64 {
|
|
return uint64(t.Add(time.Second *
|
|
time.Duration(int64(svh)+int64(rci+rci/2))).Unix())
|
|
},
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x05,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x05,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdFailed,
|
|
Choice: invalidChoice,
|
|
}},
|
|
},
|
|
{
|
|
name: "pedro overlap",
|
|
vote: pedro,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion - 1,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion - 1,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh + 19*svi - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svi - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x03,
|
|
},
|
|
count: 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 1,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 1,
|
|
}},
|
|
},
|
|
{
|
|
name: "multiple choice 100% abstain",
|
|
vote: multipleChoice,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}},
|
|
},
|
|
{
|
|
name: "multiple choice 100% no",
|
|
vote: multipleChoice,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x21,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdFailed,
|
|
Choice: 2,
|
|
}},
|
|
},
|
|
{
|
|
name: "multiple choice 100% choice 1",
|
|
vote: multipleChoice,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x11,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x11,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 1,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 1,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 1,
|
|
}},
|
|
},
|
|
{
|
|
name: "multiple choice 100% choice 2",
|
|
vote: multipleChoice,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x31,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x31,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 3,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 3,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 3,
|
|
}},
|
|
},
|
|
{
|
|
name: "multiple choice 100% choice 3",
|
|
vote: multipleChoice,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x41,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x41,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 4,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 4,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 4,
|
|
}},
|
|
},
|
|
{
|
|
name: "multiple choice 100% choice 4",
|
|
vote: multipleChoice,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x51,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x51,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 5,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 5,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 5,
|
|
}},
|
|
},
|
|
{
|
|
name: "multiple choice 100% choice 5 (invalid)",
|
|
vote: multipleChoice,
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: svh,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x61,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x61,
|
|
},
|
|
count: rci,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: rci,
|
|
}},
|
|
expectedState: []ThresholdStateTuple{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
// Reset params.
|
|
params = defaultParams(test.vote)
|
|
// We have to reset the cache for every test.
|
|
bc := newFakeChain(¶ms)
|
|
node := bc.bestChain.Tip()
|
|
node.stakeVersion = test.startStakeVersion
|
|
|
|
t.Logf("running: %v", test.name)
|
|
|
|
// Override start time.
|
|
curTimestamp := time.Now()
|
|
|
|
// Override expiration time.
|
|
if test.end != nil {
|
|
params.Deployments[posVersion][0].ExpireTime =
|
|
test.end(curTimestamp)
|
|
}
|
|
|
|
t.Logf("curTimestamp %v start %v expiration %v",
|
|
uint64(curTimestamp.Unix()),
|
|
params.Deployments[posVersion][0].StartTime,
|
|
params.Deployments[posVersion][0].ExpireTime)
|
|
|
|
for k := range test.expectedState {
|
|
for i := uint32(0); i < test.voteBitsCounts[k].count; i++ {
|
|
// Set stake versions and vote bits.
|
|
node = newFakeNode(node, test.blockVersion,
|
|
test.startStakeVersion, 0, curTimestamp)
|
|
vote := &test.voteBitsCounts[k].vote
|
|
appendFakeVotes(node, params.TicketsPerBlock,
|
|
vote.Version, vote.Bits)
|
|
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
t.Logf("Height %v, Start time %v, curTime %v, delta %v",
|
|
node.height, params.Deployments[4][0].StartTime,
|
|
node.timestamp, node.timestamp-
|
|
int64(params.Deployments[4][0].StartTime))
|
|
ts, err := bc.NextThresholdState(&node.hash, posVersion,
|
|
test.vote.Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(%v): %v", k, err)
|
|
}
|
|
if ts != test.expectedState[k] {
|
|
t.Fatalf("%v.%v (%v) got state %+v wanted "+
|
|
"state %+v", test.name, test.vote.Id, k,
|
|
ts, test.expectedState[k])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// defaultParallelParams returns net parameters modified to have two known
|
|
// deployments that are used throughout the parallel votebit tests.
|
|
func defaultParallelParams() chaincfg.Params {
|
|
params := chaincfg.RegNetParams()
|
|
params.Deployments = make(map[uint32][]chaincfg.ConsensusDeployment)
|
|
params.Deployments[posVersion] = []chaincfg.ConsensusDeployment{
|
|
{
|
|
Vote: testDummy1,
|
|
StartTime: uint64(time.Now().Add(time.Duration(params.RuleChangeActivationInterval) *
|
|
time.Second).Unix()),
|
|
ExpireTime: uint64(time.Now().Add(24 * time.Hour).Unix()),
|
|
},
|
|
{
|
|
Vote: testDummy2,
|
|
StartTime: uint64(time.Now().Add(time.Duration(params.RuleChangeActivationInterval) *
|
|
time.Second).Unix()),
|
|
ExpireTime: uint64(time.Now().Add(24 * time.Hour).Unix()),
|
|
},
|
|
}
|
|
|
|
return *params
|
|
}
|
|
|
|
// TestParallelVoting ensures that two agendas running at the same time progress
|
|
// through the expected states.
|
|
func TestParallelVoting(t *testing.T) {
|
|
params := defaultParallelParams()
|
|
|
|
type voteCount struct {
|
|
vote stake.VoteVersionTuple
|
|
count uint32
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
vote []chaincfg.Vote
|
|
blockVersion int32
|
|
startStakeVersion uint32
|
|
voteBitsCounts []voteCount
|
|
expectedState [][]ThresholdStateTuple
|
|
}{
|
|
{
|
|
name: "parallel",
|
|
vote: []chaincfg.Vote{testDummy1, testDummy2},
|
|
blockVersion: powVersion,
|
|
startStakeVersion: posVersion,
|
|
voteBitsCounts: []voteCount{{
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: uint32(params.StakeValidationHeight),
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: params.RuleChangeActivationInterval - 1,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion - 1,
|
|
Bits: vbTestDummy1Yes | vbTestDummy2No,
|
|
},
|
|
count: params.RuleChangeActivationInterval,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: vbTestDummy1Yes | vbTestDummy2No,
|
|
},
|
|
count: params.RuleChangeActivationInterval,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: params.RuleChangeActivationInterval,
|
|
}, {
|
|
vote: stake.VoteVersionTuple{
|
|
Version: posVersion,
|
|
Bits: 0x01,
|
|
},
|
|
count: params.RuleChangeActivationInterval,
|
|
}},
|
|
expectedState: [][]ThresholdStateTuple{
|
|
// 0
|
|
{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdLockedIn,
|
|
Choice: 0x02,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 0x02,
|
|
}, {
|
|
State: ThresholdActive,
|
|
Choice: 0x02,
|
|
}},
|
|
// 1
|
|
{{
|
|
State: ThresholdDefined,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdStarted,
|
|
Choice: invalidChoice,
|
|
}, {
|
|
State: ThresholdFailed,
|
|
Choice: 0x01,
|
|
}, {
|
|
State: ThresholdFailed,
|
|
Choice: 0x01,
|
|
}, {
|
|
State: ThresholdFailed,
|
|
Choice: 0x01,
|
|
}},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
// Reset params.
|
|
params = defaultParallelParams()
|
|
// We have to reset the cache for every test.
|
|
bc := newFakeChain(¶ms)
|
|
node := bc.bestChain.Tip()
|
|
node.stakeVersion = test.startStakeVersion
|
|
|
|
curTimestamp := time.Now()
|
|
for k := range test.expectedState[0] {
|
|
for i := uint32(0); i < test.voteBitsCounts[k].count; i++ {
|
|
// Set stake versions and vote bits.
|
|
node = newFakeNode(node, test.blockVersion,
|
|
test.startStakeVersion, 0, curTimestamp)
|
|
vote := &test.voteBitsCounts[k].vote
|
|
appendFakeVotes(node, params.TicketsPerBlock,
|
|
vote.Version, vote.Bits)
|
|
|
|
bc.bestChain.SetTip(node)
|
|
bc.index.AddNode(node)
|
|
curTimestamp = curTimestamp.Add(time.Second)
|
|
}
|
|
for i := range test.vote {
|
|
ts, err := bc.NextThresholdState(&node.hash,
|
|
posVersion, test.vote[i].Id)
|
|
if err != nil {
|
|
t.Fatalf("NextThresholdState(%v): %v", k, err)
|
|
}
|
|
|
|
if ts != test.expectedState[i][k] {
|
|
t.Fatalf("%v.%v (%v) got state %+v "+
|
|
"wanted state %+v",
|
|
test.name, test.vote[i].Id, k,
|
|
ts, test.expectedState[i][k])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|