Skip to content

Commit

Permalink
Make snowman use snowflake directly instead of snowball
Browse files Browse the repository at this point in the history
Snowball was introduced in order to improve the stability of snowflake,
by taking into account the history when returning the preference.

However, if deployed with a configuration where the preference is small enough, and a network partition
causes a 50-50 split of the stake, it can actually backfire, as described in 6e1a905.

Additionally, latest research [1] points out that snowflake suffices for snowman.

This commit removes snowball from snowman and adds a test that simulates a mixed network
which runs snowman with and without snowball, and ensures that a mixed network still converges.

[1] https://arxiv.org/abs/2404.14250

Signed-off-by: Yacov Manevich <[email protected]>
  • Loading branch information
yacovm committed Sep 19, 2024
1 parent c18d475 commit e3597d8
Show file tree
Hide file tree
Showing 17 changed files with 400 additions and 501 deletions.
5 changes: 3 additions & 2 deletions chains/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/ava-labs/avalanchego/network"
"github.com/ava-labs/avalanchego/network/p2p"
"github.com/ava-labs/avalanchego/snow"
"github.com/ava-labs/avalanchego/snow/consensus/snowball"
"github.com/ava-labs/avalanchego/snow/engine/avalanche/bootstrap/queue"
"github.com/ava-labs/avalanchego/snow/engine/avalanche/state"
"github.com/ava-labs/avalanchego/snow/engine/avalanche/vertex"
Expand Down Expand Up @@ -922,7 +923,7 @@ func (m *manager) createAvalancheChain(
return nil, fmt.Errorf("couldn't initialize snow base message handler: %w", err)
}

var snowmanConsensus smcon.Consensus = &smcon.Topological{}
var snowmanConsensus smcon.Consensus = &smcon.Topological{Factory: snowball.SnowflakeFactory}
if m.TracingEnabled {
snowmanConsensus = smcon.Trace(snowmanConsensus, m.Tracer)
}
Expand Down Expand Up @@ -1321,7 +1322,7 @@ func (m *manager) createSnowmanChain(
return nil, fmt.Errorf("couldn't initialize snow base message handler: %w", err)
}

var consensus smcon.Consensus = &smcon.Topological{}
var consensus smcon.Consensus = &smcon.Topological{Factory: snowball.SnowflakeFactory}
if m.TracingEnabled {
consensus = smcon.Trace(consensus, m.Tracer)
}
Expand Down
139 changes: 70 additions & 69 deletions snow/consensus/snowball/binary_snowball_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,26 @@ func TestBinarySnowball(t *testing.T) {
beta := 2
terminationConditions := newSingleTerminationCondition(alphaConfidence, beta)

sb := newBinarySnowball(alphaPreference, terminationConditions, red)
require.Equal(red, sb.Preference())
require.False(sb.Finalized())
sf := newBinarySnowflake(alphaPreference, terminationConditions, red)

sb.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sb.Preference())
require.False(sb.Finalized())
require.Equal(red, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, red)
require.Equal(blue, sb.Preference())
require.False(sb.Finalized())
sf.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sb.Preference())
require.False(sb.Finalized())
sf.RecordPoll(alphaConfidence, red)
require.Equal(red, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sb.Preference())
require.True(sb.Finalized())
sf.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sf.Preference())
require.False(sf.Finalized())

sf.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sf.Preference())
require.True(sf.Finalized())
}

func TestBinarySnowballRecordPollPreference(t *testing.T) {
Expand All @@ -50,32 +51,32 @@ func TestBinarySnowballRecordPollPreference(t *testing.T) {
beta := 2
terminationConditions := newSingleTerminationCondition(alphaConfidence, beta)

sb := newBinarySnowball(alphaPreference, terminationConditions, red)
require.Equal(red, sb.Preference())
require.False(sb.Finalized())
sf := newBinarySnowflake(alphaPreference, terminationConditions, red)
require.Equal(red, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sb.Preference())
require.False(sb.Finalized())
sf.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, red)
require.Equal(blue, sb.Preference())
require.False(sb.Finalized())
sf.RecordPoll(alphaConfidence, red)
require.Equal(red, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaPreference, red)
require.Equal(red, sb.Preference())
require.False(sb.Finalized())
sf.RecordPoll(alphaPreference, red)
require.Equal(red, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, red)
require.Equal(red, sb.Preference())
require.False(sb.Finalized())
sf.RecordPoll(alphaConfidence, red)
require.Equal(red, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, red)
require.Equal(red, sb.Preference())
require.True(sb.Finalized())
sf.RecordPoll(alphaConfidence, red)
require.Equal(red, sf.Preference())
require.True(sf.Finalized())

expected := "SB(Preference = 0, PreferenceStrength[0] = 4, PreferenceStrength[1] = 1, SF(Confidence = [2], Finalized = true, SL(Preference = 0)))"
require.Equal(expected, sb.String())
expected := "SF(Confidence = [2], Finalized = true, SL(Preference = 0))"
require.Equal(expected, sf.String())
}

func TestBinarySnowballRecordUnsuccessfulPoll(t *testing.T) {
Expand All @@ -88,26 +89,26 @@ func TestBinarySnowballRecordUnsuccessfulPoll(t *testing.T) {
beta := 2
terminationConditions := newSingleTerminationCondition(alphaConfidence, beta)

sb := newBinarySnowball(alphaPreference, terminationConditions, red)
require.Equal(red, sb.Preference())
require.False(sb.Finalized())
sf := newBinarySnowflake(alphaPreference, terminationConditions, red)
require.Equal(red, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sb.Preference())
require.False(sb.Finalized())
sf.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sf.Preference())
require.False(sf.Finalized())

sb.RecordUnsuccessfulPoll()
sf.RecordUnsuccessfulPoll()

sb.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sb.Preference())
require.False(sb.Finalized())
sf.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sb.Preference())
require.True(sb.Finalized())
sf.RecordPoll(alphaConfidence, blue)
require.Equal(blue, sf.Preference())
require.True(sf.Finalized())

expected := "SB(Preference = 1, PreferenceStrength[0] = 0, PreferenceStrength[1] = 3, SF(Confidence = [2], Finalized = true, SL(Preference = 1)))"
require.Equal(expected, sb.String())
expected := "SF(Confidence = [2], Finalized = true, SL(Preference = 1))"
require.Equal(expected, sf.String())
}

func TestBinarySnowballAcceptWeirdColor(t *testing.T) {
Expand All @@ -120,36 +121,36 @@ func TestBinarySnowballAcceptWeirdColor(t *testing.T) {
beta := 2
terminationConditions := newSingleTerminationCondition(alphaConfidence, beta)

sb := newBinarySnowball(alphaPreference, terminationConditions, red)
sf := newBinarySnowflake(alphaPreference, terminationConditions, red)

require.Equal(red, sb.Preference())
require.False(sb.Finalized())
require.Equal(red, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, red)
sb.RecordUnsuccessfulPoll()
sf.RecordPoll(alphaConfidence, red)
sf.RecordUnsuccessfulPoll()

require.Equal(red, sb.Preference())
require.False(sb.Finalized())
require.Equal(red, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, red)
sf.RecordPoll(alphaConfidence, red)

sb.RecordUnsuccessfulPoll()
sf.RecordUnsuccessfulPoll()

require.Equal(red, sb.Preference())
require.False(sb.Finalized())
require.Equal(red, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, blue)
sf.RecordPoll(alphaConfidence, blue)

require.Equal(red, sb.Preference())
require.False(sb.Finalized())
require.Equal(blue, sf.Preference())
require.False(sf.Finalized())

sb.RecordPoll(alphaConfidence, blue)
sf.RecordPoll(alphaConfidence, blue)

require.Equal(blue, sb.Preference())
require.True(sb.Finalized())
require.Equal(blue, sf.Preference())
require.True(sf.Finalized())

expected := "SB(Preference = 1, PreferenceStrength[0] = 2, PreferenceStrength[1] = 2, SF(Confidence = [2], Finalized = true, SL(Preference = 0)))"
require.Equal(expected, sb.String())
expected := "SF(Confidence = [2], Finalized = true, SL(Preference = 0))"
require.Equal(expected, sf.String())
}

func TestBinarySnowballLockColor(t *testing.T) {
Expand Down
8 changes: 4 additions & 4 deletions snow/consensus/snowball/consensus_performance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ func TestDualAlphaOptimization(t *testing.T) {
source = prng.NewMT19937()
)

singleAlphaNetwork := NewNetwork(SnowballFactory, params, numColors, source)
singleAlphaNetwork := NewNetwork(SnowflakeFactory, params, numColors, source)

params.AlphaPreference = params.K/2 + 1
dualAlphaNetwork := NewNetwork(SnowballFactory, params, numColors, source)
dualAlphaNetwork := NewNetwork(SnowflakeFactory, params, numColors, source)

source.Seed(seed)
for i := 0; i < numNodes; i++ {
Expand Down Expand Up @@ -61,8 +61,8 @@ func TestTreeConvergenceOptimization(t *testing.T) {
source = prng.NewMT19937()
)

treeNetwork := NewNetwork(SnowballFactory, params, numColors, source)
flatNetwork := NewNetwork(SnowballFactory, params, numColors, source)
treeNetwork := NewNetwork(SnowflakeFactory, params, numColors, source)
flatNetwork := NewNetwork(SnowflakeFactory, params, numColors, source)

source.Seed(seed)
for i := 0; i < numNodes; i++ {
Expand Down
2 changes: 1 addition & 1 deletion snow/consensus/snowball/consensus_reversibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestSnowballGovernance(t *testing.T) {
source = prng.NewMT19937()
)

nBitwise := NewNetwork(SnowballFactory, params, numColors, source)
nBitwise := NewNetwork(SnowflakeFactory, params, numColors, source)

source.Seed(seed)
for i := 0; i < numRed; i++ {
Expand Down
6 changes: 3 additions & 3 deletions snow/consensus/snowball/flat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestFlat(t *testing.T) {
AlphaConfidence: 3,
Beta: 2,
}
f := NewFlat(SnowballFactory, params, Red)
f := NewFlat(SnowflakeFactory, params, Red)
f.Add(Green)
f.Add(Blue)

Expand All @@ -34,7 +34,7 @@ func TestFlat(t *testing.T) {

twoGreen := bag.Of(Green, Green)
require.True(f.RecordPoll(twoGreen))
require.Equal(Blue, f.Preference())
require.Equal(Green, f.Preference())
require.False(f.Finalized())

threeGreen := bag.Of(Green, Green, Green)
Expand All @@ -56,6 +56,6 @@ func TestFlat(t *testing.T) {
require.Equal(Green, f.Preference())
require.True(f.Finalized())

expected := "SB(Preference = 2mcwQKiD8VEspmMJpL1dc7okQQ5dDVAWeCBZ7FWBFAbxpv3t7w, PreferenceStrength = 4, SF(Confidence = [2], Finalized = true, SL(Preference = 2mcwQKiD8VEspmMJpL1dc7okQQ5dDVAWeCBZ7FWBFAbxpv3t7w)))"
expected := "SF(Confidence = [2], Finalized = true, SL(Preference = 2mcwQKiD8VEspmMJpL1dc7okQQ5dDVAWeCBZ7FWBFAbxpv3t7w))"
require.Equal(expected, f.String())
}
Loading

0 comments on commit e3597d8

Please sign in to comment.