Skip to content

Commit

Permalink
Abort query if not enough stake connected
Browse files Browse the repository at this point in the history
This commit makes the snowman engine to abort early and avoid sending a query
if the node considers the connected stake as insufficient to achieve a successful poll.

The rational for this, is to prevent the node to try to agree on blocks when it is in a partition.
The intent here is twofold: (1) To prevent needless resource expenditure,
and (2) to prevent snowball from increasing its preference strength while the protocol has no way to make progress.
The higher preference strength in a partition, the longer it takes to recover and achieve consensus once the partition heals.

Signed-off-by: Yacov Manevich <yacov.manevich@avalabs.org>
  • Loading branch information
yacovm committed Sep 5, 2024
1 parent 718128a commit f20879e
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
24 changes: 23 additions & 1 deletion snow/engine/snowman/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ import (
"github.com/ava-labs/avalanchego/utils/units"
)

const nonVerifiedCacheSize = 64 * units.MiB
const (
nonVerifiedCacheSize = 64 * units.MiB
errInsufficientStake = "insufficient connected stake"
)

var _ common.Engine = (*Engine)(nil)

Expand Down Expand Up @@ -871,6 +874,10 @@ func (e *Engine) sendQuery(
blkBytes []byte,
push bool,
) {
if e.abortDueToInsufficientConnectedStake(blkID) {
return
}

e.Ctx.Log.Verbo("sampling from validators",
zap.Stringer("validators", e.Validators),
)
Expand Down Expand Up @@ -916,6 +923,21 @@ func (e *Engine) sendQuery(
}
}

func (e *Engine) abortDueToInsufficientConnectedStake(blkID ids.ID) bool {
stakeConnectedRatio := e.Config.ConnectedValidators.ConnectedPercent()
minConnectedStakeToQuery := float64(e.Params.AlphaConfidence) / float64(e.Params.K)

if stakeConnectedRatio < minConnectedStakeToQuery {
e.Ctx.Log.Debug("dropped query for block",
zap.String("reason", errInsufficientStake),
zap.Stringer("blkID", blkID),
zap.Float64("ratio", stakeConnectedRatio),
)
return true
}
return false
}

// issue [blk] to consensus
// If [push] is true, a push query will be used. Otherwise, a pull query will be
// used.
Expand Down
39 changes: 39 additions & 0 deletions snow/engine/snowman/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ import (
"github.com/ava-labs/avalanchego/snow/consensus/snowman"
"github.com/ava-labs/avalanchego/snow/consensus/snowman/snowmantest"
"github.com/ava-labs/avalanchego/snow/engine/common"
"github.com/ava-labs/avalanchego/snow/engine/common/tracker"
"github.com/ava-labs/avalanchego/snow/engine/enginetest"
"github.com/ava-labs/avalanchego/snow/engine/snowman/ancestor"
"github.com/ava-labs/avalanchego/snow/engine/snowman/block/blocktest"
"github.com/ava-labs/avalanchego/snow/engine/snowman/getter"
"github.com/ava-labs/avalanchego/snow/snowtest"
"github.com/ava-labs/avalanchego/snow/validators"
"github.com/ava-labs/avalanchego/utils"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/utils/set"
"github.com/ava-labs/avalanchego/version"
)
Expand Down Expand Up @@ -3182,3 +3184,40 @@ func TestShouldIssueBlock(t *testing.T) {
})
}
}

type mockConnVDR struct {
tracker.Peers
percent float64
}

func (m *mockConnVDR) ConnectedPercent() float64 {
return m.percent
}

type logBuffer struct {
bytes.Buffer
}

func (logBuffer) Close() error {
return nil
}

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

// Buffer to record the log entries
buff := logBuffer{}

conf := DefaultConfig(t)
// Overwrite the log to record what it says
conf.Ctx.Log = logging.NewLogger("", logging.NewWrappedCore(logging.Verbo, &buff, logging.Plain.ConsoleEncoder()))
conf.Params = snowball.DefaultParameters
conf.ConnectedValidators = &mockConnVDR{percent: 0.7, Peers: conf.ConnectedValidators}

_, _, _, _, engine := setup(t, conf)

// Gossip will cause a pull query if enough stake is connected
engine.sendQuery(context.Background(), ids.ID{}, nil, false)

require.Contains(buff.String(), errInsufficientStake)
}

0 comments on commit f20879e

Please sign in to comment.