Merge pull request #32 from StephenButtolph/slush

Slush
This commit is contained in:
Stephen Buttolph 2020-03-27 14:10:49 -04:00 committed by GitHub
commit 1fe5092e9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 426 additions and 206 deletions

View File

@ -0,0 +1,27 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package snowball
import (
"fmt"
)
// binarySlush is the implementation of a binary slush instance
type binarySlush struct {
// preference is the choice that last had a successful poll. Unless there
// hasn't been a successful poll, in which case it is the initially provided
// choice.
preference int
}
// Initialize implements the BinarySlush interface
func (sl *binarySlush) Initialize(choice int) { sl.preference = choice }
// Preference implements the BinarySlush interface
func (sl *binarySlush) Preference() int { return sl.preference }
// RecordSuccessfulPoll implements the BinarySlush interface
func (sl *binarySlush) RecordSuccessfulPoll(choice int) { sl.preference = choice }
func (sl *binarySlush) String() string { return fmt.Sprintf("SL(Preference = %d)", sl.preference) }

View File

@ -9,6 +9,9 @@ import (
// binarySnowball is the implementation of a binary snowball instance
type binarySnowball struct {
// wrap the binary snowflake logic
binarySnowflake
// preference is the choice with the largest number of successful polls.
// Ties are broken by switching choice lazily
preference int
@ -16,15 +19,12 @@ type binarySnowball struct {
// numSuccessfulPolls tracks the total number of successful network polls of
// the 0 and 1 choices
numSuccessfulPolls [2]int
// snowflake wraps the binary snowflake logic
snowflake binarySnowflake
}
// Initialize implements the BinarySnowball interface
func (sb *binarySnowball) Initialize(beta, choice int) {
sb.binarySnowflake.Initialize(beta, choice)
sb.preference = choice
sb.snowflake.Initialize(beta, choice)
}
// Preference implements the BinarySnowball interface
@ -34,7 +34,7 @@ func (sb *binarySnowball) Preference() int {
// this case is handled for completion. Therefore, if snowflake is
// finalized, then our finalized snowflake choice should be preferred.
if sb.Finalized() {
return sb.snowflake.Preference()
return sb.binarySnowflake.Preference()
}
return sb.preference
}
@ -45,20 +45,14 @@ func (sb *binarySnowball) RecordSuccessfulPoll(choice int) {
if sb.numSuccessfulPolls[choice] > sb.numSuccessfulPolls[1-choice] {
sb.preference = choice
}
sb.snowflake.RecordSuccessfulPoll(choice)
sb.binarySnowflake.RecordSuccessfulPoll(choice)
}
// RecordUnsuccessfulPoll implements the BinarySnowball interface
func (sb *binarySnowball) RecordUnsuccessfulPoll() { sb.snowflake.RecordUnsuccessfulPoll() }
// Finalized implements the BinarySnowball interface
func (sb *binarySnowball) Finalized() bool { return sb.snowflake.Finalized() }
func (sb *binarySnowball) String() string {
return fmt.Sprintf(
"SB(Preference = %d, NumSuccessfulPolls[0] = %d, NumSuccessfulPolls[1] = %d, SF = %s)",
"SB(Preference = %d, NumSuccessfulPolls[0] = %d, NumSuccessfulPolls[1] = %d, %s)",
sb.preference,
sb.numSuccessfulPolls[0],
sb.numSuccessfulPolls[1],
&sb.snowflake)
&sb.binarySnowflake)
}

View File

@ -96,7 +96,7 @@ func TestBinarySnowballRecordUnsuccessfulPoll(t *testing.T) {
t.Fatalf("Finalized too late")
}
expected := "SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 3, SF = SF(Preference = 1, Confidence = 2, Finalized = true))"
expected := "SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 3, SF(Confidence = 2, Finalized = true, SL(Preference = 1)))"
if str := sb.String(); str != expected {
t.Fatalf("Wrong state. Expected:\n%s\nGot:\n%s", expected, str)
}
@ -151,7 +151,7 @@ func TestBinarySnowballAcceptWeirdColor(t *testing.T) {
t.Fatalf("Finalized too late")
}
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 2, NumSuccessfulPolls[1] = 2, SF = SF(Preference = 1, Confidence = 2, Finalized = true))"
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 2, NumSuccessfulPolls[1] = 2, SF(Confidence = 2, Finalized = true, SL(Preference = 1)))"
if str := sb.String(); str != expected {
t.Fatalf("Wrong state. Expected:\n%s\nGot:\n%s", expected, str)
}
@ -190,7 +190,7 @@ func TestBinarySnowballLockColor(t *testing.T) {
t.Fatalf("Finalized too late")
}
expected := "SB(Preference = 1, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 2, SF = SF(Preference = 0, Confidence = 1, Finalized = true))"
expected := "SB(Preference = 1, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 2, SF(Confidence = 1, Finalized = true, SL(Preference = 0)))"
if str := sb.String(); str != expected {
t.Fatalf("Wrong state. Expected:\n%s\nGot:\n%s", expected, str)
}

View File

@ -9,10 +9,8 @@ import (
// binarySnowflake is the implementation of a binary snowflake instance
type binarySnowflake struct {
// preference is the choice that last had a successful poll. Unless there
// hasn't been a successful poll, in which case it is the initially provided
// choice.
preference int
// wrap the binary slush logic
binarySlush
// confidence tracks the number of successful polls in a row that have
// returned the preference
@ -29,29 +27,26 @@ type binarySnowflake struct {
// Initialize implements the BinarySnowflake interface
func (sf *binarySnowflake) Initialize(beta, choice int) {
sf.binarySlush.Initialize(choice)
sf.beta = beta
sf.preference = choice
}
// Preference implements the BinarySnowflake interface
func (sf *binarySnowflake) Preference() int { return sf.preference }
// RecordSuccessfulPoll implements the BinarySnowflake interface
func (sf *binarySnowflake) RecordSuccessfulPoll(choice int) {
if sf.Finalized() {
if sf.finalized {
return // This instace is already decided.
}
if sf.preference == choice {
if preference := sf.Preference(); preference == choice {
sf.confidence++
} else {
// confidence is set to 1 because there has already been 1 successful
// poll, namely this poll.
sf.confidence = 1
sf.preference = choice
}
sf.finalized = sf.confidence >= sf.beta
sf.binarySlush.RecordSuccessfulPoll(choice)
}
// RecordUnsuccessfulPoll implements the BinarySnowflake interface
@ -61,8 +56,8 @@ func (sf *binarySnowflake) RecordUnsuccessfulPoll() { sf.confidence = 0 }
func (sf *binarySnowflake) Finalized() bool { return sf.finalized }
func (sf *binarySnowflake) String() string {
return fmt.Sprintf("SF(Preference = %d, Confidence = %d, Finalized = %v)",
sf.Preference(),
return fmt.Sprintf("SF(Confidence = %d, Finalized = %v, %s)",
sf.confidence,
sf.Finalized())
sf.finalized,
&sf.binarySlush)
}

View File

@ -24,6 +24,7 @@ type Byzantine struct {
// Initialize implements the Consensus interface
func (b *Byzantine) Initialize(params Parameters, choice ids.ID) {
b.params = params
b.preference = choice
}

View File

@ -0,0 +1,54 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package snowball
import (
"testing"
"github.com/ava-labs/gecko/ids"
"github.com/prometheus/client_golang/prometheus"
)
func TestByzantine(t *testing.T) {
params := Parameters{
Metrics: prometheus.NewRegistry(),
K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5,
}
byzFactory := ByzantineFactory{}
byz := byzFactory.New()
byz.Initialize(params, Blue)
if ret := byz.Parameters(); ret != params {
t.Fatalf("Should have returned the correct params")
}
byz.Add(Green)
if pref := byz.Preference(); !pref.Equals(Blue) {
t.Fatalf("Wrong preference, expected %s returned %s", Blue, pref)
}
oneGreen := ids.Bag{}
oneGreen.Add(Green)
byz.RecordPoll(oneGreen)
if pref := byz.Preference(); !pref.Equals(Blue) {
t.Fatalf("Wrong preference, expected %s returned %s", Blue, pref)
}
byz.RecordUnsuccessfulPoll()
if pref := byz.Preference(); !pref.Equals(Blue) {
t.Fatalf("Wrong preference, expected %s returned %s", Blue, pref)
}
if final := byz.Finalized(); !final {
t.Fatalf("Should be marked as accepted")
}
if str := byz.String(); str != Blue.String() {
t.Fatalf("Wrong string, expected %s returned %s", Blue, str)
}
}

View File

@ -69,6 +69,23 @@ type NnarySnowflake interface {
Finalized() bool
}
// NnarySlush is a slush instance deciding between an unbounded number of
// values. After performing a network sample of k nodes, if you have alpha
// votes for one of the choices, you should vote for that choice.
type NnarySlush interface {
fmt.Stringer
// Takes in the initial choice
Initialize(initialPreference ids.ID)
// Returns the currently preferred choice to be finalized
Preference() ids.ID
// RecordSuccessfulPoll records a successful poll towards finalizing the
// specified choice. Assumes the choice was previously added.
RecordSuccessfulPoll(choice ids.ID)
}
// BinarySnowball augments BinarySnowflake with a counter that tracks the total
// number of positive responses from a network sample.
type BinarySnowball interface{ BinarySnowflake }
@ -97,6 +114,23 @@ type BinarySnowflake interface {
Finalized() bool
}
// BinarySlush is a slush instance deciding between two values. After performing
// a network sample of k nodes, if you have alpha votes for one of the choices,
// you should vote for that choice.
type BinarySlush interface {
fmt.Stringer
// Takes in the initial choice
Initialize(initialPreference int)
// Returns the currently preferred choice to be finalized
Preference() int
// RecordSuccessfulPoll records a successful poll towards finalizing the
// specified choice
RecordSuccessfulPoll(choice int)
}
// UnarySnowball is a snowball instance deciding on one value. After performing
// a network sample of k nodes, if you have alpha votes for the choice, you
// should vote. Otherwise, you should reset.
@ -122,3 +156,29 @@ type UnarySnowball interface {
// Returns a new unary snowball instance with the same state
Clone() UnarySnowball
}
// UnarySnowflake is a snowflake instance deciding on one value. After
// performing a network sample of k nodes, if you have alpha votes for the
// choice, you should vote. Otherwise, you should reset.
type UnarySnowflake interface {
fmt.Stringer
// Takes in the beta value
Initialize(beta int)
// RecordSuccessfulPoll records a successful poll towards finalizing
RecordSuccessfulPoll()
// RecordUnsuccessfulPoll resets the snowflake counter of this instance
RecordUnsuccessfulPoll()
// Return whether a choice has been finalized
Finalized() bool
// Returns a new binary snowball instance with the agreement parameters
// transferred. Takes in the new beta value and the original choice
Extend(beta, originalPreference int) BinarySnowflake
// Returns a new unary snowflake instance with the same state
Clone() UnarySnowflake
}

View File

@ -15,40 +15,27 @@ func (FlatFactory) New() Consensus { return &Flat{} }
// Flat is a naive implementation of a multi-choice snowball instance
type Flat struct {
// wraps the n-nary snowball logic
nnarySnowball
// params contains all the configurations of a snowball instance
params Parameters
// snowball wraps the n-nary snowball logic
snowball nnarySnowball
}
// Initialize implements the Consensus interface
func (f *Flat) Initialize(params Parameters, choice ids.ID) {
f.nnarySnowball.Initialize(params.BetaVirtuous, params.BetaRogue, choice)
f.params = params
f.snowball.Initialize(params.BetaVirtuous, params.BetaRogue, choice)
}
// Parameters implements the Consensus interface
func (f *Flat) Parameters() Parameters { return f.params }
// Add implements the Consensus interface
func (f *Flat) Add(choice ids.ID) { f.snowball.Add(choice) }
// Preference implements the Consensus interface
func (f *Flat) Preference() ids.ID { return f.snowball.Preference() }
// RecordPoll implements the Consensus interface
func (f *Flat) RecordPoll(votes ids.Bag) {
if pollMode, numVotes := votes.Mode(); numVotes >= f.params.Alpha {
f.snowball.RecordSuccessfulPoll(pollMode)
f.nnarySnowball.RecordSuccessfulPoll(pollMode)
} else {
f.RecordUnsuccessfulPoll()
}
}
// RecordUnsuccessfulPoll implements the Consensus interface
func (f *Flat) RecordUnsuccessfulPoll() { f.snowball.RecordUnsuccessfulPoll() }
// Finalized implements the Consensus interface
func (f *Flat) Finalized() bool { return f.snowball.Finalized() }
func (f *Flat) String() string { return f.snowball.String() }

View File

@ -65,7 +65,7 @@ func TestFlat(t *testing.T) {
t.Fatalf("Finalized too late")
}
expected := "SB(Preference = TtF4d2QWbk5vzQGTEPrN48x6vwgAoAmKQ9cbp79inpQmcRKES, NumSuccessfulPolls = 3, SF = SF(Preference = TtF4d2QWbk5vzQGTEPrN48x6vwgAoAmKQ9cbp79inpQmcRKES, Confidence = 2, Finalized = true))"
expected := "SB(Preference = TtF4d2QWbk5vzQGTEPrN48x6vwgAoAmKQ9cbp79inpQmcRKES, NumSuccessfulPolls = 3, SF(Confidence = 2, Finalized = true, SL(Preference = TtF4d2QWbk5vzQGTEPrN48x6vwgAoAmKQ9cbp79inpQmcRKES)))"
if str := f.String(); str != expected {
t.Fatalf("Wrong state. Expected:\n%s\nGot:\n%s", expected, str)
}

View File

@ -0,0 +1,30 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package snowball
import (
"fmt"
"github.com/ava-labs/gecko/ids"
)
// nnarySlush is the implementation of a slush instance with an unbounded number
// of choices
type nnarySlush struct {
// preference is the choice that last had a successful poll. Unless there
// hasn't been a successful poll, in which case it is the initially provided
// choice.
preference ids.ID
}
// Initialize implements the NnarySlush interface
func (sl *nnarySlush) Initialize(choice ids.ID) { sl.preference = choice }
// Preference implements the NnarySlush interface
func (sl *nnarySlush) Preference() ids.ID { return sl.preference }
// RecordSuccessfulPoll implements the NnarySlush interface
func (sl *nnarySlush) RecordSuccessfulPoll(choice ids.ID) { sl.preference = choice }
func (sl *nnarySlush) String() string { return fmt.Sprintf("SL(Preference = %s)", sl.preference) }

View File

@ -11,6 +11,9 @@ import (
// nnarySnowball is a naive implementation of a multi-color snowball instance
type nnarySnowball struct {
// wrap the n-nary snowflake logic
nnarySnowflake
// preference is the choice with the largest number of successful polls.
// Ties are broken by switching choice lazily
preference ids.ID
@ -22,21 +25,15 @@ type nnarySnowball struct {
// numSuccessfulPolls tracks the total number of successful network polls of
// the choices
numSuccessfulPolls map[[32]byte]int
// snowflake wraps the n-nary snowflake logic
snowflake nnarySnowflake
}
// Initialize implements the NnarySnowball interface
func (sb *nnarySnowball) Initialize(betaVirtuous, betaRogue int, choice ids.ID) {
sb.nnarySnowflake.Initialize(betaVirtuous, betaRogue, choice)
sb.preference = choice
sb.numSuccessfulPolls = make(map[[32]byte]int)
sb.snowflake.Initialize(betaVirtuous, betaRogue, choice)
}
// Add implements the NnarySnowball interface
func (sb *nnarySnowball) Add(choice ids.ID) { sb.snowflake.Add(choice) }
// Preference implements the NnarySnowball interface
func (sb *nnarySnowball) Preference() ids.ID {
// It is possible, with low probability, that the snowflake preference is
@ -44,17 +41,13 @@ func (sb *nnarySnowball) Preference() ids.ID {
// this case is handled for completion. Therefore, if snowflake is
// finalized, then our finalized snowflake choice should be preferred.
if sb.Finalized() {
return sb.snowflake.Preference()
return sb.nnarySnowflake.Preference()
}
return sb.preference
}
// RecordSuccessfulPoll implements the NnarySnowball interface
func (sb *nnarySnowball) RecordSuccessfulPoll(choice ids.ID) {
if sb.Finalized() {
return
}
key := choice.Key()
numSuccessfulPolls := sb.numSuccessfulPolls[key] + 1
sb.numSuccessfulPolls[key] = numSuccessfulPolls
@ -64,16 +57,10 @@ func (sb *nnarySnowball) RecordSuccessfulPoll(choice ids.ID) {
sb.maxSuccessfulPolls = numSuccessfulPolls
}
sb.snowflake.RecordSuccessfulPoll(choice)
sb.nnarySnowflake.RecordSuccessfulPoll(choice)
}
// RecordUnsuccessfulPoll implements the NnarySnowball interface
func (sb *nnarySnowball) RecordUnsuccessfulPoll() { sb.snowflake.RecordUnsuccessfulPoll() }
// Finalized implements the NnarySnowball interface
func (sb *nnarySnowball) Finalized() bool { return sb.snowflake.Finalized() }
func (sb *nnarySnowball) String() string {
return fmt.Sprintf("SB(Preference = %s, NumSuccessfulPolls = %d, SF = %s)",
sb.preference, sb.maxSuccessfulPolls, &sb.snowflake)
return fmt.Sprintf("SB(Preference = %s, NumSuccessfulPolls = %d, %s)",
sb.preference, sb.maxSuccessfulPolls, &sb.nnarySnowflake)
}

View File

@ -143,7 +143,7 @@ func TestNarySnowballRecordUnsuccessfulPoll(t *testing.T) {
t.Fatalf("Finalized too late")
}
expected := "SB(Preference = TtF4d2QWbk5vzQGTEPrN48x6vwgAoAmKQ9cbp79inpQmcRKES, NumSuccessfulPolls = 3, SF = SF(Preference = TtF4d2QWbk5vzQGTEPrN48x6vwgAoAmKQ9cbp79inpQmcRKES, Confidence = 2, Finalized = true))"
expected := "SB(Preference = TtF4d2QWbk5vzQGTEPrN48x6vwgAoAmKQ9cbp79inpQmcRKES, NumSuccessfulPolls = 3, SF(Confidence = 2, Finalized = true, SL(Preference = TtF4d2QWbk5vzQGTEPrN48x6vwgAoAmKQ9cbp79inpQmcRKES)))"
if str := sb.String(); str != expected {
t.Fatalf("Wrong state. Expected:\n%s\nGot:\n%s", expected, str)
}
@ -175,7 +175,7 @@ func TestNarySnowflakeColor(t *testing.T) {
sb.RecordSuccessfulPoll(Blue)
if pref := sb.snowflake.Preference(); !Blue.Equals(pref) {
if pref := sb.nnarySnowflake.Preference(); !Blue.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", Blue, pref)
}
@ -183,7 +183,7 @@ func TestNarySnowflakeColor(t *testing.T) {
if pref := sb.Preference(); !Blue.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", Blue, pref)
} else if pref := sb.snowflake.Preference(); !Red.Equals(pref) {
} else if pref := sb.nnarySnowflake.Preference(); !Red.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", Blue, pref)
}
}

View File

@ -12,6 +12,9 @@ import (
// nnarySnowflake is the implementation of a snowflake instance with an
// unbounded number of choices
type nnarySnowflake struct {
// wrap the n-nary slush logic
nnarySlush
// betaVirtuous is the number of consecutive successful queries required for
// finalization on a virtuous instance.
betaVirtuous int
@ -24,11 +27,6 @@ type nnarySnowflake struct {
// returned the preference
confidence int
// preference is the choice that last had a successful poll. Unless there
// hasn't been a successful poll, in which case it is the initially provided
// choice.
preference ids.ID
// rogue tracks if this instance has multiple choices or only one
rogue bool
@ -39,32 +37,31 @@ type nnarySnowflake struct {
// Initialize implements the NnarySnowflake interface
func (sf *nnarySnowflake) Initialize(betaVirtuous, betaRogue int, choice ids.ID) {
sf.nnarySlush.Initialize(choice)
sf.betaVirtuous = betaVirtuous
sf.betaRogue = betaRogue
sf.preference = choice
}
// Add implements the NnarySnowflake interface
func (sf *nnarySnowflake) Add(choice ids.ID) { sf.rogue = sf.rogue || !choice.Equals(sf.preference) }
// Preference implements the NnarySnowflake interface
func (sf *nnarySnowflake) Preference() ids.ID { return sf.preference }
// RecordSuccessfulPoll implements the NnarySnowflake interface
func (sf *nnarySnowflake) RecordSuccessfulPoll(choice ids.ID) {
if sf.Finalized() {
return
if sf.finalized {
return // This instace is already decided.
}
if sf.preference.Equals(choice) {
if preference := sf.nnarySlush.Preference(); preference.Equals(choice) {
sf.confidence++
} else {
// confidence is set to 1 because there has already been 1 successful
// poll, namely this poll.
sf.confidence = 1
sf.preference = choice
}
sf.finalized = (!sf.rogue && sf.confidence >= sf.betaVirtuous) ||
sf.confidence >= sf.betaRogue
sf.nnarySlush.RecordSuccessfulPoll(choice)
}
// RecordUnsuccessfulPoll implements the NnarySnowflake interface
@ -74,8 +71,8 @@ func (sf *nnarySnowflake) RecordUnsuccessfulPoll() { sf.confidence = 0 }
func (sf *nnarySnowflake) Finalized() bool { return sf.finalized }
func (sf *nnarySnowflake) String() string {
return fmt.Sprintf("SF(Preference = %s, Confidence = %d, Finalized = %v)",
sf.preference,
return fmt.Sprintf("SF(Confidence = %d, Finalized = %v, %s)",
sf.confidence,
sf.Finalized())
sf.finalized,
&sf.nnarySlush)
}

View File

@ -18,6 +18,10 @@ func (TreeFactory) New() Consensus { return &Tree{} }
// Tree implements the snowball interface by using a modified patricia tree.
type Tree struct {
// node is the root that represents the first snowball instance in the tree,
// and contains references to all the other snowball instances in the tree.
node
// params contains all the configurations of a snowball instance
params Parameters
@ -31,10 +35,6 @@ type Tree struct {
// that any later traversal into this sub-tree should call
// RecordUnsuccessfulPoll before performing any other action.
shouldReset bool
// root is the node that represents the first snowball instance in the tree,
// and contains references to all the other snowball instances in the tree.
root node
}
// Initialize implements the Consensus interface
@ -44,7 +44,7 @@ func (t *Tree) Initialize(params Parameters, choice ids.ID) {
snowball := &unarySnowball{}
snowball.Initialize(params.BetaVirtuous)
t.root = &unaryNode{
t.node = &unaryNode{
tree: t,
preference: choice,
commonPrefix: ids.NumBits, // The initial state has no conflicts
@ -57,20 +57,17 @@ func (t *Tree) Parameters() Parameters { return t.params }
// Add implements the Consensus interface
func (t *Tree) Add(choice ids.ID) {
prefix := t.root.DecidedPrefix()
prefix := t.node.DecidedPrefix()
// Make sure that we haven't already decided against this new id
if ids.EqualSubset(0, prefix, t.Preference(), choice) {
t.root = t.root.Add(choice)
t.node = t.node.Add(choice)
}
}
// Preference implements the Consensus interface
func (t *Tree) Preference() ids.ID { return t.root.Preference() }
// RecordPoll implements the Consensus interface
func (t *Tree) RecordPoll(votes ids.Bag) {
// Get the assumed decided prefix of the root node.
decidedPrefix := t.root.DecidedPrefix()
decidedPrefix := t.node.DecidedPrefix()
// If any of the bits differ from the preference in this prefix, the vote is
// for a rejected operation. So, we filter out these invalid votes.
@ -78,7 +75,7 @@ func (t *Tree) RecordPoll(votes ids.Bag) {
// Now that the votes have been restricted to valid votes, pass them into
// the first snowball instance
t.root = t.root.RecordPoll(filteredVotes, t.shouldReset)
t.node = t.node.RecordPoll(filteredVotes, t.shouldReset)
// Because we just passed the reset into the snowball instance, we should no
// longer reset.
@ -88,14 +85,11 @@ func (t *Tree) RecordPoll(votes ids.Bag) {
// RecordUnsuccessfulPoll implements the Consensus interface
func (t *Tree) RecordUnsuccessfulPoll() { t.shouldReset = true }
// Finalized implements the Consensus interface
func (t *Tree) Finalized() bool { return t.root.Finalized() }
func (t *Tree) String() string {
builder := strings.Builder{}
prefixes := []string{""}
nodes := []node{t.root}
nodes := []node{t.node}
for len(prefixes) > 0 {
newSize := len(prefixes) - 1

View File

@ -170,10 +170,10 @@ func TestSnowballLastBinary(t *testing.T) {
tree.Initialize(params, zero)
tree.Add(one)
expected := "SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [0, 255)\n" +
" SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 0, Finalized = false)) Bit = 255"
expected := "SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [0, 255)\n" +
" SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 255"
if str := tree.String(); expected != str {
t.Fatalf("Wrong string. Expected %s got %s", expected, str)
t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str)
} else if pref := tree.Preference(); !zero.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", zero, pref)
} else if tree.Finalized() {
@ -378,9 +378,9 @@ func TestSnowballFineGrained(t *testing.T) {
tree := Tree{}
tree.Initialize(params, c0000)
{
expected := "SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [0, 256)"
expected := "SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [0, 256)"
if str := tree.String(); expected != str {
t.Fatalf("Wrong string. Expected %s got %s", expected, str)
t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str)
} else if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {
@ -390,11 +390,11 @@ func TestSnowballFineGrained(t *testing.T) {
tree.Add(c1100)
{
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 0, Finalized = false)) Bit = 0\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [1, 256)\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [1, 256)"
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 0\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)"
if str := tree.String(); expected != str {
t.Fatalf("Wrong string. Expected %s got %s", expected, str)
t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str)
} else if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {
@ -404,13 +404,13 @@ func TestSnowballFineGrained(t *testing.T) {
tree.Add(c1000)
{
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 0, Finalized = false)) Bit = 0\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [1, 256)\n" +
" SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 1, Confidence = 0, Finalized = false)) Bit = 1\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [2, 256)\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [2, 256)"
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 0\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)\n" +
" SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 1))) Bit = 1\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)"
if str := tree.String(); expected != str {
t.Fatalf("Wrong string. Expected %s got %s", expected, str)
t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str)
} else if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {
@ -420,16 +420,16 @@ func TestSnowballFineGrained(t *testing.T) {
tree.Add(c0010)
{
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 0, Finalized = false)) Bit = 0\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [1, 2)\n" +
" SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 0, Finalized = false)) Bit = 2\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [3, 256)\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [3, 256)\n" +
" SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 1, Confidence = 0, Finalized = false)) Bit = 1\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [2, 256)\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [2, 256)"
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 0\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 2)\n" +
" SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 2\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n" +
" SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 1))) Bit = 1\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)"
if str := tree.String(); expected != str {
t.Fatalf("Wrong string. Expected %s got %s", expected, str)
t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str)
} else if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {
@ -441,15 +441,15 @@ func TestSnowballFineGrained(t *testing.T) {
c0000Bag.Add(c0000)
tree.RecordPoll(c0000Bag)
{
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 1, Finalized = false)) Bit = 0\n" +
" SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 1, Finalized = false)) Bit = 2\n" +
" SB(NumSuccessfulPolls = 1, Confidence = 1, Finalized = true) Bits = [3, 256)\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [3, 256)\n" +
" SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 1, Confidence = 0, Finalized = false)) Bit = 1\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [2, 256)\n" +
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [2, 256)"
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 0\n" +
" SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 2\n" +
" SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n" +
" SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 1))) Bit = 1\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)\n" +
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)"
if str := tree.String(); expected != str {
t.Fatalf("Wrong string. Expected %s got %s", expected, str)
t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str)
} else if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {
@ -461,11 +461,11 @@ func TestSnowballFineGrained(t *testing.T) {
c0010Bag.Add(c0010)
tree.RecordPoll(c0010Bag)
{
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 1, SF = SF(Preference = 1, Confidence = 1, Finalized = false)) Bit = 2\n" +
" SB(NumSuccessfulPolls = 1, Confidence = 1, Finalized = true) Bits = [3, 256)\n" +
" SB(NumSuccessfulPolls = 1, Confidence = 1, Finalized = true) Bits = [3, 256)"
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 1, SF(Confidence = 1, Finalized = false, SL(Preference = 1))) Bit = 2\n" +
" SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)\n" +
" SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)"
if str := tree.String(); expected != str {
t.Fatalf("Wrong string. Expected %s got %s", expected, str)
t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str)
} else if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {
@ -475,9 +475,9 @@ func TestSnowballFineGrained(t *testing.T) {
tree.RecordPoll(c0010Bag)
{
expected := "SB(NumSuccessfulPolls = 2, Confidence = 2, Finalized = true) Bits = [3, 256)"
expected := "SB(NumSuccessfulPolls = 2, SF(Confidence = 2, Finalized = true)) Bits = [3, 256)"
if str := tree.String(); expected != str {
t.Fatalf("Wrong string. Expected %s got %s", expected, str)
t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str)
} else if pref := tree.Preference(); !c0010.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0010, pref)
} else if !tree.Finalized() {
@ -496,9 +496,9 @@ func TestSnowballDoubleAdd(t *testing.T) {
tree.Add(Red)
{
expected := "SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [0, 256)"
expected := "SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [0, 256)"
if str := tree.String(); expected != str {
t.Fatalf("Wrong string. Expected %s got %s", expected, str)
t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str)
} else if pref := tree.Preference(); !Red.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", Red, pref)
} else if tree.Finalized() {
@ -547,7 +547,7 @@ func TestSnowballFilterBinaryChildren(t *testing.T) {
tree := Tree{}
tree.Initialize(params, c0000)
{
expected := "SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [0, 256)"
expected := "SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [0, 256)"
if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {
@ -559,9 +559,9 @@ func TestSnowballFilterBinaryChildren(t *testing.T) {
tree.Add(c1000)
{
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 0, Finalized = false)) Bit = 0\n"+
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [1, 256)\n"+
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [1, 256)"
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 0\n"+
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)\n"+
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)"
if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {
@ -573,12 +573,12 @@ func TestSnowballFilterBinaryChildren(t *testing.T) {
tree.Add(c0010)
{
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 0, Finalized = false)) Bit = 0\n"+
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [1, 2)\n"+
" SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 0, Finalized = false)) Bit = 2\n"+
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [1, 256)"
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 0\n"+
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 2)\n"+
" SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 2\n"+
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)"
if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {
@ -592,11 +592,11 @@ func TestSnowballFilterBinaryChildren(t *testing.T) {
c0000Bag.Add(c0000)
tree.RecordPoll(c0000Bag)
{
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 1, Finalized = false)) Bit = 0\n"+
" SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 1, Finalized = false)) Bit = 2\n"+
" SB(NumSuccessfulPolls = 1, Confidence = 1, Finalized = true) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [1, 256)"
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 0\n"+
" SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 2\n"+
" SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)"
if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {
@ -608,11 +608,11 @@ func TestSnowballFilterBinaryChildren(t *testing.T) {
tree.Add(c0100)
{
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 1, Finalized = false)) Bit = 0\n"+
" SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 1, Finalized = false)) Bit = 2\n"+
" SB(NumSuccessfulPolls = 1, Confidence = 1, Finalized = true) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [1, 256)"
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 0\n"+
" SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 2\n"+
" SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)"
if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {
@ -626,9 +626,9 @@ func TestSnowballFilterBinaryChildren(t *testing.T) {
c0100Bag.Add(c0100)
tree.RecordPoll(c0100Bag)
{
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF = SF(Preference = 0, Confidence = 0, Finalized = false)) Bit = 2\n"+
" SB(NumSuccessfulPolls = 1, Confidence = 1, Finalized = true) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, Confidence = 0, Finalized = false) Bits = [3, 256)"
expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 2\n"+
" SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)\n"+
" SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)"
if pref := tree.Preference(); !c0000.Equals(pref) {
t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref)
} else if tree.Finalized() {

View File

@ -9,64 +9,40 @@ import (
// unarySnowball is the implementation of a unary snowball instance
type unarySnowball struct {
// beta is the number of consecutive successful queries required for
// finalization.
beta int
// confidence tracks the number of successful polls in a row that have
// returned the preference
confidence int
// wrap the unary snowflake logic
unarySnowflake
// numSuccessfulPolls tracks the total number of successful network polls
numSuccessfulPolls int
// finalized prevents the state from changing after the required number of
// consecutive polls has been reached
finalized bool
}
// Initialize implements the UnarySnowball interface
func (sb *unarySnowball) Initialize(beta int) { sb.beta = beta }
// RecordSuccessfulPoll implements the UnarySnowball interface
func (sb *unarySnowball) RecordSuccessfulPoll() {
sb.numSuccessfulPolls++
sb.confidence++
sb.finalized = sb.finalized || sb.confidence >= sb.beta
sb.unarySnowflake.RecordSuccessfulPoll()
}
// RecordUnsuccessfulPoll implements the UnarySnowball interface
func (sb *unarySnowball) RecordUnsuccessfulPoll() { sb.confidence = 0 }
// Finalized implements the UnarySnowball interface
func (sb *unarySnowball) Finalized() bool { return sb.finalized }
// Extend implements the UnarySnowball interface
func (sb *unarySnowball) Extend(beta int, choice int) BinarySnowball {
bs := &binarySnowball{
preference: choice,
snowflake: binarySnowflake{
beta: beta,
preference: choice,
finalized: sb.Finalized(),
binarySnowflake: binarySnowflake{
binarySlush: binarySlush{preference: choice},
beta: beta,
finalized: sb.Finalized(),
},
preference: choice,
}
return bs
}
// Clone implements the UnarySnowball interface
func (sb *unarySnowball) Clone() UnarySnowball {
return &unarySnowball{
beta: sb.beta,
numSuccessfulPolls: sb.numSuccessfulPolls,
confidence: sb.confidence,
finalized: sb.Finalized(),
}
newSnowball := *sb
return &newSnowball
}
func (sb *unarySnowball) String() string {
return fmt.Sprintf("SB(NumSuccessfulPolls = %d, Confidence = %d, Finalized = %v)",
return fmt.Sprintf("SB(NumSuccessfulPolls = %d, %s)",
sb.numSuccessfulPolls,
sb.confidence,
sb.Finalized())
&sb.unarySnowflake)
}

View File

@ -35,7 +35,7 @@ func TestUnarySnowball(t *testing.T) {
sbCloneIntf := sb.Clone()
sbClone, ok := sbCloneIntf.(*unarySnowball)
if !ok {
t.Fatalf("Unexpectedly clone type")
t.Fatalf("Unexpected clone type")
}
UnarySnowballStateTest(t, sbClone, 2, 1, false)

View File

@ -0,0 +1,60 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package snowball
import (
"fmt"
)
// unarySnowflake is the implementation of a unary snowflake instance
type unarySnowflake struct {
// beta is the number of consecutive successful queries required for
// finalization.
beta int
// confidence tracks the number of successful polls in a row that have
// returned the preference
confidence int
// finalized prevents the state from changing after the required number of
// consecutive polls has been reached
finalized bool
}
// Initialize implements the UnarySnowflake interface
func (sf *unarySnowflake) Initialize(beta int) { sf.beta = beta }
// RecordSuccessfulPoll implements the UnarySnowflake interface
func (sf *unarySnowflake) RecordSuccessfulPoll() {
sf.confidence++
sf.finalized = sf.finalized || sf.confidence >= sf.beta
}
// RecordUnsuccessfulPoll implements the UnarySnowflake interface
func (sf *unarySnowflake) RecordUnsuccessfulPoll() { sf.confidence = 0 }
// Finalized implements the UnarySnowflake interface
func (sf *unarySnowflake) Finalized() bool { return sf.finalized }
// Extend implements the UnarySnowflake interface
func (sf *unarySnowflake) Extend(beta int, choice int) BinarySnowflake {
return &binarySnowflake{
binarySlush: binarySlush{preference: choice},
confidence: sf.confidence,
beta: beta,
finalized: sf.finalized,
}
}
// Clone implements the UnarySnowflake interface
func (sf *unarySnowflake) Clone() UnarySnowflake {
newSnowflake := *sf
return &newSnowflake
}
func (sf *unarySnowflake) String() string {
return fmt.Sprintf("SF(Confidence = %d, Finalized = %v)",
sf.confidence,
sf.finalized)
}

View File

@ -0,0 +1,58 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package snowball
import (
"testing"
)
func UnarySnowflakeStateTest(t *testing.T, sf *unarySnowflake, expectedConfidence int, expectedFinalized bool) {
if confidence := sf.confidence; confidence != expectedConfidence {
t.Fatalf("Wrong confidence. Expected %d got %d", expectedConfidence, confidence)
} else if finalized := sf.Finalized(); finalized != expectedFinalized {
t.Fatalf("Wrong finalized status. Expected %v got %v", expectedFinalized, finalized)
}
}
func TestUnarySnowflake(t *testing.T) {
beta := 2
sf := &unarySnowflake{}
sf.Initialize(beta)
sf.RecordSuccessfulPoll()
UnarySnowflakeStateTest(t, sf, 1, false)
sf.RecordUnsuccessfulPoll()
UnarySnowflakeStateTest(t, sf, 0, false)
sf.RecordSuccessfulPoll()
UnarySnowflakeStateTest(t, sf, 1, false)
sfCloneIntf := sf.Clone()
sfClone, ok := sfCloneIntf.(*unarySnowflake)
if !ok {
t.Fatalf("Unexpected clone type")
}
UnarySnowflakeStateTest(t, sfClone, 1, false)
binarySnowflake := sfClone.Extend(beta, 0)
binarySnowflake.RecordUnsuccessfulPoll()
binarySnowflake.RecordSuccessfulPoll(1)
if binarySnowflake.Finalized() {
t.Fatalf("Should not have finalized")
}
binarySnowflake.RecordSuccessfulPoll(1)
if binarySnowflake.Preference() != 1 {
t.Fatalf("Wrong preference")
} else if !binarySnowflake.Finalized() {
t.Fatalf("Should have finalized")
}
}