Skip to content

Commit 6d53e51

Browse files
Split Alpha into AlphaPreference and AlphaConfidence (#2125)
1 parent 1fc8973 commit 6d53e51

34 files changed

+1099
-565
lines changed

config/config.go

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,10 @@ var (
8989
)
9090

9191
func getConsensusConfig(v *viper.Viper) snowball.Parameters {
92-
return snowball.Parameters{
93-
K: v.GetInt(SnowSampleSizeKey),
94-
Alpha: v.GetInt(SnowQuorumSizeKey),
92+
p := snowball.Parameters{
93+
K: v.GetInt(SnowSampleSizeKey),
94+
AlphaPreference: v.GetInt(SnowPreferenceQuorumSizeKey),
95+
AlphaConfidence: v.GetInt(SnowConfidenceQuorumSizeKey),
9596
// During the X-chain linearization we require BetaVirtuous and
9697
// BetaRogue to be equal. Therefore we use the more conservative
9798
// BetaRogue value for both BetaVirtuous and BetaRogue.
@@ -105,6 +106,11 @@ func getConsensusConfig(v *viper.Viper) snowball.Parameters {
105106
MaxOutstandingItems: v.GetInt(SnowMaxProcessingKey),
106107
MaxItemProcessingTime: v.GetDuration(SnowMaxTimeProcessingKey),
107108
}
109+
if v.IsSet(SnowQuorumSizeKey) {
110+
p.AlphaPreference = v.GetInt(SnowQuorumSizeKey)
111+
p.AlphaConfidence = p.AlphaPreference
112+
}
113+
return p
108114
}
109115

110116
func getLoggingConfig(v *viper.Viper) (logging.Config, error) {
@@ -447,7 +453,10 @@ func getNetworkConfig(
447453
}
448454

449455
func getBenchlistConfig(v *viper.Viper, consensusParameters snowball.Parameters) (benchlist.Config, error) {
450-
alpha := consensusParameters.Alpha
456+
// AlphaConfidence is used here to ensure that benching can't cause a
457+
// liveness failure. If AlphaPreference were used, the benchlist may grow to
458+
// a point that committing would be extremely unlikely to happen.
459+
alpha := consensusParameters.AlphaConfidence
451460
k := consensusParameters.K
452461
config := benchlist.Config{
453462
Threshold: v.GetInt(BenchlistFailThresholdKey),
@@ -1102,6 +1111,11 @@ func getSubnetConfigsFromFlags(v *viper.Viper, subnetIDs []ids.ID) (map[ids.ID]s
11021111
return nil, err
11031112
}
11041113

1114+
if config.ConsensusParameters.Alpha != nil {
1115+
config.ConsensusParameters.AlphaPreference = *config.ConsensusParameters.Alpha
1116+
config.ConsensusParameters.AlphaConfidence = config.ConsensusParameters.AlphaPreference
1117+
}
1118+
11051119
if err := config.Valid(); err != nil {
11061120
return nil, err
11071121
}
@@ -1150,6 +1164,11 @@ func getSubnetConfigsFromDir(v *viper.Viper, subnetIDs []ids.ID) (map[ids.ID]sub
11501164
return nil, fmt.Errorf("%w: %w", errUnmarshalling, err)
11511165
}
11521166

1167+
if config.ConsensusParameters.Alpha != nil {
1168+
config.ConsensusParameters.AlphaPreference = *config.ConsensusParameters.Alpha
1169+
config.ConsensusParameters.AlphaConfidence = config.ConsensusParameters.AlphaPreference
1170+
}
1171+
11531172
if err := config.Valid(); err != nil {
11541173
return nil, err
11551174
}

config/config_test.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -399,22 +399,22 @@ func TestGetSubnetConfigsFromFile(t *testing.T) {
399399
},
400400
"invalid consensus parameters": {
401401
fileName: "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.json",
402-
givenJSON: `{"consensusParameters":{"k": 111, "alpha":1234} }`,
402+
givenJSON: `{"consensusParameters":{"k": 111, "alphaPreference":1234} }`,
403403
testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
404404
require.Nil(given)
405405
},
406406
expectedErr: snowball.ErrParametersInvalid,
407407
},
408408
"correct config": {
409409
fileName: "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.json",
410-
givenJSON: `{"validatorOnly": true, "consensusParameters":{"alpha":16} }`,
410+
givenJSON: `{"validatorOnly": true, "consensusParameters":{"alphaConfidence":16} }`,
411411
testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
412412
id, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
413413
config, ok := given[id]
414414
require.True(ok)
415415

416416
require.Equal(true, config.ValidatorOnly)
417-
require.Equal(16, config.ConsensusParameters.Alpha)
417+
require.Equal(16, config.ConsensusParameters.AlphaConfidence)
418418
// must still respect defaults
419419
require.Equal(20, config.ConsensusParameters.K)
420420
},
@@ -499,7 +499,7 @@ func TestGetSubnetConfigsFromFlags(t *testing.T) {
499499
"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": {
500500
"consensusParameters": {
501501
"k": 111,
502-
"alpha": 1234
502+
"alphaPreference": 1234
503503
}
504504
}
505505
}`,
@@ -513,7 +513,8 @@ func TestGetSubnetConfigsFromFlags(t *testing.T) {
513513
"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": {
514514
"consensusParameters": {
515515
"k": 30,
516-
"alpha": 20
516+
"alphaPreference": 16,
517+
"alphaConfidence": 20
517518
},
518519
"validatorOnly": true
519520
}
@@ -523,7 +524,8 @@ func TestGetSubnetConfigsFromFlags(t *testing.T) {
523524
config, ok := given[id]
524525
require.True(ok)
525526
require.Equal(true, config.ValidatorOnly)
526-
require.Equal(20, config.ConsensusParameters.Alpha)
527+
require.Equal(16, config.ConsensusParameters.AlphaPreference)
528+
require.Equal(20, config.ConsensusParameters.AlphaConfidence)
527529
require.Equal(30, config.ConsensusParameters.K)
528530
// must still respect defaults
529531
require.Equal(uint(10), config.GossipConfig.AppGossipValidatorSize)

config/flags.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,9 @@ func addNodeFlags(fs *pflag.FlagSet) {
299299

300300
// Consensus
301301
fs.Int(SnowSampleSizeKey, snowball.DefaultParameters.K, "Number of nodes to query for each network poll")
302-
fs.Int(SnowQuorumSizeKey, snowball.DefaultParameters.Alpha, "Alpha value to use for required number positive results")
302+
fs.Int(SnowQuorumSizeKey, snowball.DefaultParameters.AlphaConfidence, "Threshold of nodes required to update this node's preference and increase its confidence in a network poll")
303+
fs.Int(SnowPreferenceQuorumSizeKey, snowball.DefaultParameters.AlphaPreference, fmt.Sprintf("Threshold of nodes required to update this node's preference in a network poll. Ignored if %s is provided", SnowQuorumSizeKey))
304+
fs.Int(SnowConfidenceQuorumSizeKey, snowball.DefaultParameters.AlphaConfidence, fmt.Sprintf("Threshold of nodes required to increase this node's confidence in a network poll. Ignored if %s is provided", SnowQuorumSizeKey))
303305
// TODO: Replace this temporary flag description after the X-chain
304306
// linearization with "Beta value to use for virtuous transactions"
305307
fs.Int(SnowVirtuousCommitThresholdKey, snowball.DefaultParameters.BetaVirtuous, "This flag is temporarily ignored due to the X-chain linearization")

config/keys.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ const (
123123
LogDisableDisplayPluginLogsKey = "log-disable-display-plugin-logs"
124124
SnowSampleSizeKey = "snow-sample-size"
125125
SnowQuorumSizeKey = "snow-quorum-size"
126+
SnowPreferenceQuorumSizeKey = "snow-preference-quorum-size"
127+
SnowConfidenceQuorumSizeKey = "snow-confidence-quorum-size"
126128
SnowVirtuousCommitThresholdKey = "snow-virtuous-commit-threshold"
127129
SnowRogueCommitThresholdKey = "snow-rogue-commit-threshold"
128130
SnowConcurrentRepollsKey = "snow-concurrent-repolls"

snow/consensus/snowball/binary_snowball.go

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ type binarySnowball struct {
1919
// wrap the binary snowflake logic
2020
binarySnowflake
2121

22-
// preference is the choice with the largest number of successful polls.
23-
// Ties are broken by switching choice lazily
22+
// preference is the choice with the largest number of polls which preferred
23+
// the color. Ties are broken by switching choice lazily
2424
preference int
2525

26-
// numSuccessfulPolls tracks the total number of successful network polls of
27-
// the 0 and 1 choices
28-
numSuccessfulPolls [2]int
26+
// preferenceStrength tracks the total number of network polls which
27+
// preferred each choice
28+
preferenceStrength [2]int
2929
}
3030

3131
func (sb *binarySnowball) Preference() int {
@@ -40,18 +40,27 @@ func (sb *binarySnowball) Preference() int {
4040
}
4141

4242
func (sb *binarySnowball) RecordSuccessfulPoll(choice int) {
43-
sb.numSuccessfulPolls[choice]++
44-
if sb.numSuccessfulPolls[choice] > sb.numSuccessfulPolls[1-choice] {
45-
sb.preference = choice
46-
}
43+
sb.increasePreferenceStrength(choice)
4744
sb.binarySnowflake.RecordSuccessfulPoll(choice)
4845
}
4946

47+
func (sb *binarySnowball) RecordPollPreference(choice int) {
48+
sb.increasePreferenceStrength(choice)
49+
sb.binarySnowflake.RecordPollPreference(choice)
50+
}
51+
5052
func (sb *binarySnowball) String() string {
5153
return fmt.Sprintf(
52-
"SB(Preference = %d, NumSuccessfulPolls[0] = %d, NumSuccessfulPolls[1] = %d, %s)",
54+
"SB(Preference = %d, PreferenceStrength[0] = %d, PreferenceStrength[1] = %d, %s)",
5355
sb.preference,
54-
sb.numSuccessfulPolls[0],
55-
sb.numSuccessfulPolls[1],
56+
sb.preferenceStrength[0],
57+
sb.preferenceStrength[1],
5658
&sb.binarySnowflake)
5759
}
60+
61+
func (sb *binarySnowball) increasePreferenceStrength(choice int) {
62+
sb.preferenceStrength[choice]++
63+
if sb.preferenceStrength[choice] > sb.preferenceStrength[1-choice] {
64+
sb.preference = choice
65+
}
66+
}

snow/consensus/snowball/binary_snowball_test.go

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,42 @@ func TestBinarySnowball(t *testing.T) {
3838
require.True(sb.Finalized())
3939
}
4040

41+
func TestBinarySnowballRecordPollPreference(t *testing.T) {
42+
require := require.New(t)
43+
44+
red := 0
45+
blue := 1
46+
47+
beta := 2
48+
49+
sb := newBinarySnowball(beta, red)
50+
require.Equal(red, sb.Preference())
51+
require.False(sb.Finalized())
52+
53+
sb.RecordSuccessfulPoll(blue)
54+
require.Equal(blue, sb.Preference())
55+
require.False(sb.Finalized())
56+
57+
sb.RecordSuccessfulPoll(red)
58+
require.Equal(blue, sb.Preference())
59+
require.False(sb.Finalized())
60+
61+
sb.RecordPollPreference(red)
62+
require.Equal(red, sb.Preference())
63+
require.False(sb.Finalized())
64+
65+
sb.RecordSuccessfulPoll(red)
66+
require.Equal(red, sb.Preference())
67+
require.False(sb.Finalized())
68+
69+
sb.RecordSuccessfulPoll(red)
70+
require.Equal(red, sb.Preference())
71+
require.True(sb.Finalized())
72+
73+
expected := "SB(Preference = 0, PreferenceStrength[0] = 4, PreferenceStrength[1] = 1, SF(Confidence = 2, Finalized = true, SL(Preference = 0)))"
74+
require.Equal(expected, sb.String())
75+
}
76+
4177
func TestBinarySnowballRecordUnsuccessfulPoll(t *testing.T) {
4278
require := require.New(t)
4379

@@ -64,7 +100,7 @@ func TestBinarySnowballRecordUnsuccessfulPoll(t *testing.T) {
64100
require.Equal(blue, sb.Preference())
65101
require.True(sb.Finalized())
66102

67-
expected := "SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 3, SF(Confidence = 2, Finalized = true, SL(Preference = 1)))"
103+
expected := "SB(Preference = 1, PreferenceStrength[0] = 0, PreferenceStrength[1] = 3, SF(Confidence = 2, Finalized = true, SL(Preference = 1)))"
68104
require.Equal(expected, sb.String())
69105
}
70106

@@ -104,7 +140,7 @@ func TestBinarySnowballAcceptWeirdColor(t *testing.T) {
104140
require.Equal(blue, sb.Preference())
105141
require.True(sb.Finalized())
106142

107-
expected := "SB(Preference = 1, NumSuccessfulPolls[0] = 2, NumSuccessfulPolls[1] = 2, SF(Confidence = 2, Finalized = true, SL(Preference = 0)))"
143+
expected := "SB(Preference = 1, PreferenceStrength[0] = 2, PreferenceStrength[1] = 2, SF(Confidence = 2, Finalized = true, SL(Preference = 0)))"
108144
require.Equal(expected, sb.String())
109145
}
110146

@@ -128,11 +164,12 @@ func TestBinarySnowballLockColor(t *testing.T) {
128164
require.Equal(red, sb.Preference())
129165
require.True(sb.Finalized())
130166

167+
sb.RecordPollPreference(blue)
131168
sb.RecordSuccessfulPoll(blue)
132169

133170
require.Equal(red, sb.Preference())
134171
require.True(sb.Finalized())
135172

136-
expected := "SB(Preference = 1, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 2, SF(Confidence = 1, Finalized = true, SL(Preference = 0)))"
173+
expected := "SB(Preference = 1, PreferenceStrength[0] = 1, PreferenceStrength[1] = 3, SF(Confidence = 1, Finalized = true, SL(Preference = 0)))"
137174
require.Equal(expected, sb.String())
138175
}

snow/consensus/snowball/binary_snowflake.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ func (sf *binarySnowflake) RecordSuccessfulPoll(choice int) {
4949
sf.binarySlush.RecordSuccessfulPoll(choice)
5050
}
5151

52+
func (sf *binarySnowflake) RecordPollPreference(choice int) {
53+
if sf.finalized {
54+
return // This instance is already decided.
55+
}
56+
57+
sf.confidence = 0
58+
sf.binarySlush.RecordSuccessfulPoll(choice)
59+
}
60+
5261
func (sf *binarySnowflake) RecordUnsuccessfulPoll() {
5362
sf.confidence = 0
5463
}

snow/consensus/snowball/binary_snowflake_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,15 @@ func TestBinarySnowflake(t *testing.T) {
3737
require.Equal(blue, sf.Preference())
3838
require.False(sf.Finalized())
3939

40+
sf.RecordPollPreference(red)
41+
require.Equal(red, sf.Preference())
42+
require.False(sf.Finalized())
43+
4044
sf.RecordSuccessfulPoll(blue)
45+
require.Equal(blue, sf.Preference())
46+
require.False(sf.Finalized())
4147

48+
sf.RecordSuccessfulPoll(blue)
4249
require.Equal(blue, sf.Preference())
4350
require.True(sf.Finalized())
4451
}

snow/consensus/snowball/consensus.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ type NnarySnowflake interface {
6161
// specified choice. Assumes the choice was previously added.
6262
RecordSuccessfulPoll(choice ids.ID)
6363

64+
// RecordPollPreference records a poll that preferred the specified choice
65+
// but did not contribute towards finalizing the specified choice. Assumes
66+
// the choice was previously added.
67+
RecordPollPreference(choice ids.ID)
68+
6469
// RecordUnsuccessfulPoll resets the snowflake counter of this instance
6570
RecordUnsuccessfulPoll()
6671

@@ -100,6 +105,10 @@ type BinarySnowflake interface {
100105
// specified choice
101106
RecordSuccessfulPoll(choice int)
102107

108+
// RecordPollPreference records a poll that preferred the specified choice
109+
// but did not contribute towards finalizing the specified choice
110+
RecordPollPreference(choice int)
111+
103112
// RecordUnsuccessfulPoll resets the snowflake counter of this instance
104113
RecordUnsuccessfulPoll()
105114

@@ -130,6 +139,10 @@ type UnarySnowball interface {
130139
// RecordSuccessfulPoll records a successful poll towards finalizing
131140
RecordSuccessfulPoll()
132141

142+
// RecordPollPreference records a poll that strengthens the preference but
143+
// did not contribute towards finalizing
144+
RecordPollPreference()
145+
133146
// RecordUnsuccessfulPoll resets the snowflake counter of this instance
134147
RecordUnsuccessfulPoll()
135148

0 commit comments

Comments
 (0)