Skip to content

Commit

Permalink
Split Alpha into AlphaPreference and AlphaConfidence (#2125)
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenButtolph authored Oct 6, 2023
1 parent 1fc8973 commit 6d53e51
Show file tree
Hide file tree
Showing 34 changed files with 1,099 additions and 565 deletions.
27 changes: 23 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@ var (
)

func getConsensusConfig(v *viper.Viper) snowball.Parameters {
return snowball.Parameters{
K: v.GetInt(SnowSampleSizeKey),
Alpha: v.GetInt(SnowQuorumSizeKey),
p := snowball.Parameters{
K: v.GetInt(SnowSampleSizeKey),
AlphaPreference: v.GetInt(SnowPreferenceQuorumSizeKey),
AlphaConfidence: v.GetInt(SnowConfidenceQuorumSizeKey),
// During the X-chain linearization we require BetaVirtuous and
// BetaRogue to be equal. Therefore we use the more conservative
// BetaRogue value for both BetaVirtuous and BetaRogue.
Expand All @@ -105,6 +106,11 @@ func getConsensusConfig(v *viper.Viper) snowball.Parameters {
MaxOutstandingItems: v.GetInt(SnowMaxProcessingKey),
MaxItemProcessingTime: v.GetDuration(SnowMaxTimeProcessingKey),
}
if v.IsSet(SnowQuorumSizeKey) {
p.AlphaPreference = v.GetInt(SnowQuorumSizeKey)
p.AlphaConfidence = p.AlphaPreference
}
return p
}

func getLoggingConfig(v *viper.Viper) (logging.Config, error) {
Expand Down Expand Up @@ -447,7 +453,10 @@ func getNetworkConfig(
}

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

if config.ConsensusParameters.Alpha != nil {
config.ConsensusParameters.AlphaPreference = *config.ConsensusParameters.Alpha
config.ConsensusParameters.AlphaConfidence = config.ConsensusParameters.AlphaPreference
}

if err := config.Valid(); err != nil {
return nil, err
}
Expand Down Expand Up @@ -1150,6 +1164,11 @@ func getSubnetConfigsFromDir(v *viper.Viper, subnetIDs []ids.ID) (map[ids.ID]sub
return nil, fmt.Errorf("%w: %w", errUnmarshalling, err)
}

if config.ConsensusParameters.Alpha != nil {
config.ConsensusParameters.AlphaPreference = *config.ConsensusParameters.Alpha
config.ConsensusParameters.AlphaConfidence = config.ConsensusParameters.AlphaPreference
}

if err := config.Valid(); err != nil {
return nil, err
}
Expand Down
14 changes: 8 additions & 6 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,22 +399,22 @@ func TestGetSubnetConfigsFromFile(t *testing.T) {
},
"invalid consensus parameters": {
fileName: "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.json",
givenJSON: `{"consensusParameters":{"k": 111, "alpha":1234} }`,
givenJSON: `{"consensusParameters":{"k": 111, "alphaPreference":1234} }`,
testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
require.Nil(given)
},
expectedErr: snowball.ErrParametersInvalid,
},
"correct config": {
fileName: "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.json",
givenJSON: `{"validatorOnly": true, "consensusParameters":{"alpha":16} }`,
givenJSON: `{"validatorOnly": true, "consensusParameters":{"alphaConfidence":16} }`,
testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
id, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
config, ok := given[id]
require.True(ok)

require.Equal(true, config.ValidatorOnly)
require.Equal(16, config.ConsensusParameters.Alpha)
require.Equal(16, config.ConsensusParameters.AlphaConfidence)
// must still respect defaults
require.Equal(20, config.ConsensusParameters.K)
},
Expand Down Expand Up @@ -499,7 +499,7 @@ func TestGetSubnetConfigsFromFlags(t *testing.T) {
"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": {
"consensusParameters": {
"k": 111,
"alpha": 1234
"alphaPreference": 1234
}
}
}`,
Expand All @@ -513,7 +513,8 @@ func TestGetSubnetConfigsFromFlags(t *testing.T) {
"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": {
"consensusParameters": {
"k": 30,
"alpha": 20
"alphaPreference": 16,
"alphaConfidence": 20
},
"validatorOnly": true
}
Expand All @@ -523,7 +524,8 @@ func TestGetSubnetConfigsFromFlags(t *testing.T) {
config, ok := given[id]
require.True(ok)
require.Equal(true, config.ValidatorOnly)
require.Equal(20, config.ConsensusParameters.Alpha)
require.Equal(16, config.ConsensusParameters.AlphaPreference)
require.Equal(20, config.ConsensusParameters.AlphaConfidence)
require.Equal(30, config.ConsensusParameters.K)
// must still respect defaults
require.Equal(uint(10), config.GossipConfig.AppGossipValidatorSize)
Expand Down
4 changes: 3 additions & 1 deletion config/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,9 @@ func addNodeFlags(fs *pflag.FlagSet) {

// Consensus
fs.Int(SnowSampleSizeKey, snowball.DefaultParameters.K, "Number of nodes to query for each network poll")
fs.Int(SnowQuorumSizeKey, snowball.DefaultParameters.Alpha, "Alpha value to use for required number positive results")
fs.Int(SnowQuorumSizeKey, snowball.DefaultParameters.AlphaConfidence, "Threshold of nodes required to update this node's preference and increase its confidence in a network poll")
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))
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))
// TODO: Replace this temporary flag description after the X-chain
// linearization with "Beta value to use for virtuous transactions"
fs.Int(SnowVirtuousCommitThresholdKey, snowball.DefaultParameters.BetaVirtuous, "This flag is temporarily ignored due to the X-chain linearization")
Expand Down
2 changes: 2 additions & 0 deletions config/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ const (
LogDisableDisplayPluginLogsKey = "log-disable-display-plugin-logs"
SnowSampleSizeKey = "snow-sample-size"
SnowQuorumSizeKey = "snow-quorum-size"
SnowPreferenceQuorumSizeKey = "snow-preference-quorum-size"
SnowConfidenceQuorumSizeKey = "snow-confidence-quorum-size"
SnowVirtuousCommitThresholdKey = "snow-virtuous-commit-threshold"
SnowRogueCommitThresholdKey = "snow-rogue-commit-threshold"
SnowConcurrentRepollsKey = "snow-concurrent-repolls"
Expand Down
33 changes: 21 additions & 12 deletions snow/consensus/snowball/binary_snowball.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ 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 is the choice with the largest number of polls which preferred
// the color. Ties are broken by switching choice lazily
preference int

// numSuccessfulPolls tracks the total number of successful network polls of
// the 0 and 1 choices
numSuccessfulPolls [2]int
// preferenceStrength tracks the total number of network polls which
// preferred each choice
preferenceStrength [2]int
}

func (sb *binarySnowball) Preference() int {
Expand All @@ -40,18 +40,27 @@ func (sb *binarySnowball) Preference() int {
}

func (sb *binarySnowball) RecordSuccessfulPoll(choice int) {
sb.numSuccessfulPolls[choice]++
if sb.numSuccessfulPolls[choice] > sb.numSuccessfulPolls[1-choice] {
sb.preference = choice
}
sb.increasePreferenceStrength(choice)
sb.binarySnowflake.RecordSuccessfulPoll(choice)
}

func (sb *binarySnowball) RecordPollPreference(choice int) {
sb.increasePreferenceStrength(choice)
sb.binarySnowflake.RecordPollPreference(choice)
}

func (sb *binarySnowball) String() string {
return fmt.Sprintf(
"SB(Preference = %d, NumSuccessfulPolls[0] = %d, NumSuccessfulPolls[1] = %d, %s)",
"SB(Preference = %d, PreferenceStrength[0] = %d, PreferenceStrength[1] = %d, %s)",
sb.preference,
sb.numSuccessfulPolls[0],
sb.numSuccessfulPolls[1],
sb.preferenceStrength[0],
sb.preferenceStrength[1],
&sb.binarySnowflake)
}

func (sb *binarySnowball) increasePreferenceStrength(choice int) {
sb.preferenceStrength[choice]++
if sb.preferenceStrength[choice] > sb.preferenceStrength[1-choice] {
sb.preference = choice
}
}
43 changes: 40 additions & 3 deletions snow/consensus/snowball/binary_snowball_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,42 @@ func TestBinarySnowball(t *testing.T) {
require.True(sb.Finalized())
}

func TestBinarySnowballRecordPollPreference(t *testing.T) {
require := require.New(t)

red := 0
blue := 1

beta := 2

sb := newBinarySnowball(beta, red)
require.Equal(red, sb.Preference())
require.False(sb.Finalized())

sb.RecordSuccessfulPoll(blue)
require.Equal(blue, sb.Preference())
require.False(sb.Finalized())

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

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

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

sb.RecordSuccessfulPoll(red)
require.Equal(red, sb.Preference())
require.True(sb.Finalized())

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

func TestBinarySnowballRecordUnsuccessfulPoll(t *testing.T) {
require := require.New(t)

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

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

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

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

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

sb.RecordPollPreference(blue)
sb.RecordSuccessfulPoll(blue)

require.Equal(red, sb.Preference())
require.True(sb.Finalized())

expected := "SB(Preference = 1, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 2, SF(Confidence = 1, Finalized = true, SL(Preference = 0)))"
expected := "SB(Preference = 1, PreferenceStrength[0] = 1, PreferenceStrength[1] = 3, SF(Confidence = 1, Finalized = true, SL(Preference = 0)))"
require.Equal(expected, sb.String())
}
9 changes: 9 additions & 0 deletions snow/consensus/snowball/binary_snowflake.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ func (sf *binarySnowflake) RecordSuccessfulPoll(choice int) {
sf.binarySlush.RecordSuccessfulPoll(choice)
}

func (sf *binarySnowflake) RecordPollPreference(choice int) {
if sf.finalized {
return // This instance is already decided.
}

sf.confidence = 0
sf.binarySlush.RecordSuccessfulPoll(choice)
}

func (sf *binarySnowflake) RecordUnsuccessfulPoll() {
sf.confidence = 0
}
Expand Down
7 changes: 7 additions & 0 deletions snow/consensus/snowball/binary_snowflake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,15 @@ func TestBinarySnowflake(t *testing.T) {
require.Equal(blue, sf.Preference())
require.False(sf.Finalized())

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

sf.RecordSuccessfulPoll(blue)
require.Equal(blue, sf.Preference())
require.False(sf.Finalized())

sf.RecordSuccessfulPoll(blue)
require.Equal(blue, sf.Preference())
require.True(sf.Finalized())
}
13 changes: 13 additions & 0 deletions snow/consensus/snowball/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ type NnarySnowflake interface {
// specified choice. Assumes the choice was previously added.
RecordSuccessfulPoll(choice ids.ID)

// RecordPollPreference records a poll that preferred the specified choice
// but did not contribute towards finalizing the specified choice. Assumes
// the choice was previously added.
RecordPollPreference(choice ids.ID)

// RecordUnsuccessfulPoll resets the snowflake counter of this instance
RecordUnsuccessfulPoll()

Expand Down Expand Up @@ -100,6 +105,10 @@ type BinarySnowflake interface {
// specified choice
RecordSuccessfulPoll(choice int)

// RecordPollPreference records a poll that preferred the specified choice
// but did not contribute towards finalizing the specified choice
RecordPollPreference(choice int)

// RecordUnsuccessfulPoll resets the snowflake counter of this instance
RecordUnsuccessfulPoll()

Expand Down Expand Up @@ -130,6 +139,10 @@ type UnarySnowball interface {
// RecordSuccessfulPoll records a successful poll towards finalizing
RecordSuccessfulPoll()

// RecordPollPreference records a poll that strengthens the preference but
// did not contribute towards finalizing
RecordPollPreference()

// RecordUnsuccessfulPoll resets the snowflake counter of this instance
RecordUnsuccessfulPoll()

Expand Down
Loading

0 comments on commit 6d53e51

Please sign in to comment.