Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NumSubnets to the validator manager interface #3504

Merged
merged 1 commit into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ func (n *network) samplePeers(
// As an optimization, if there are fewer validators than
// [numValidatorsToSample], only attempt to sample [numValidatorsToSample]
// validators to potentially avoid iterating over the entire peer set.
numValidatorsToSample := min(config.Validators, n.config.Validators.Count(subnetID))
numValidatorsToSample := min(config.Validators, n.config.Validators.NumValidators(subnetID))

n.peersLock.RLock()
defer n.peersLock.RUnlock()
Expand Down
2 changes: 1 addition & 1 deletion node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ func (n *Node) initNetworking(reg prometheus.Registerer) error {
}

n.onSufficientlyConnected = make(chan struct{})
numBootstrappers := n.bootstrappers.Count(constants.PrimaryNetworkID)
numBootstrappers := n.bootstrappers.NumValidators(constants.PrimaryNetworkID)
requiredConns := (3*numBootstrappers + 3) / 4

if requiredConns > 0 {
Expand Down
11 changes: 9 additions & 2 deletions node/overridden_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,15 @@ func (o *overriddenManager) RemoveWeight(_ ids.ID, nodeID ids.NodeID, weight uin
return o.manager.RemoveWeight(o.subnetID, nodeID, weight)
}

func (o *overriddenManager) Count(ids.ID) int {
return o.manager.Count(o.subnetID)
func (o *overriddenManager) NumSubnets() int {
if o.manager.NumValidators(o.subnetID) == 0 {
return 0
}
return 1
}

func (o *overriddenManager) NumValidators(ids.ID) int {
return o.manager.NumValidators(o.subnetID)
}

func (o *overriddenManager) TotalWeight(ids.ID) (uint64, error) {
Expand Down
2 changes: 1 addition & 1 deletion snow/engine/snowman/bootstrap/bootstrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ func (b *Bootstrapper) sendBootstrappingMessagesOrFinish(ctx context.Context) er
if numAccepted == 0 {
b.Ctx.Log.Debug("restarting bootstrap",
zap.String("reason", "no blocks accepted"),
zap.Int("numBeacons", b.Beacons.Count(b.Ctx.SubnetID)),
zap.Int("numBeacons", b.Beacons.NumValidators(b.Ctx.SubnetID)),
)
// Invariant: These functions are mutualy recursive. However, when
// [startBootstrapping] calls [sendMessagesOrFinish], it is guaranteed
Expand Down
4 changes: 2 additions & 2 deletions snow/engine/snowman/bootstrap/bootstrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func newConfig(t *testing.T) (Config, ids.NodeID, *enginetest.Sender, *blocktest
AllGetsServer: snowGetHandler,
Ctx: ctx,
Beacons: vdrs,
SampleK: vdrs.Count(ctx.SubnetID),
SampleK: vdrs.NumValidators(ctx.SubnetID),
StartupTracker: startupTracker,
PeerTracker: peerTracker,
Sender: sender,
Expand Down Expand Up @@ -693,7 +693,7 @@ func TestBootstrapNoParseOnNew(t *testing.T) {
AllGetsServer: snowGetHandler,
Ctx: ctx,
Beacons: peers,
SampleK: peers.Count(ctx.SubnetID),
SampleK: peers.NumValidators(ctx.SubnetID),
StartupTracker: startupTracker,
PeerTracker: peerTracker,
Sender: sender,
Expand Down
2 changes: 1 addition & 1 deletion snow/engine/snowman/syncer/state_syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ func (ss *stateSyncer) AcceptedStateSummary(ctx context.Context, nodeID ids.Node
if votingStakes < ss.Alpha {
ss.Ctx.Log.Debug("restarting state sync",
zap.String("reason", "not enough votes received"),
zap.Int("numBeacons", ss.StateSyncBeacons.Count(ss.Ctx.SubnetID)),
zap.Int("numBeacons", ss.StateSyncBeacons.NumValidators(ss.Ctx.SubnetID)),
zap.Int("numFailedSyncers", ss.failedVoters.Len()),
)
return ss.startup(ctx)
Expand Down
12 changes: 6 additions & 6 deletions snow/engine/snowman/syncer/state_syncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ func TestBeaconsAreReachedForFrontiersUponStartup(t *testing.T) {
}

// check that vdrs are reached out for frontiers
require.Len(contactedFrontiersProviders, min(beacons.Count(ctx.SubnetID), maxOutstandingBroadcastRequests))
require.Len(contactedFrontiersProviders, min(beacons.NumValidators(ctx.SubnetID), maxOutstandingBroadcastRequests))
for beaconID := range contactedFrontiersProviders {
// check that beacon is duly marked as reached out
require.Contains(syncer.pendingSeeders, beaconID)
Expand Down Expand Up @@ -344,7 +344,7 @@ func TestUnRequestedStateSummaryFrontiersAreDropped(t *testing.T) {
// other listed vdrs are reached for data
require.True(
len(contactedFrontiersProviders) > initiallyReachedOutBeaconsSize ||
len(contactedFrontiersProviders) == beacons.Count(ctx.SubnetID))
len(contactedFrontiersProviders) == beacons.NumValidators(ctx.SubnetID))
}

func TestMalformedStateSummaryFrontiersAreDropped(t *testing.T) {
Expand Down Expand Up @@ -413,7 +413,7 @@ func TestMalformedStateSummaryFrontiersAreDropped(t *testing.T) {
// are reached for data
require.True(
len(contactedFrontiersProviders) > initiallyReachedOutBeaconsSize ||
len(contactedFrontiersProviders) == beacons.Count(ctx.SubnetID))
len(contactedFrontiersProviders) == beacons.NumValidators(ctx.SubnetID))
}

func TestLateResponsesFromUnresponsiveFrontiersAreNotRecorded(t *testing.T) {
Expand Down Expand Up @@ -475,7 +475,7 @@ func TestLateResponsesFromUnresponsiveFrontiersAreNotRecorded(t *testing.T) {
// are reached for data
require.True(
len(contactedFrontiersProviders) > initiallyReachedOutBeaconsSize ||
len(contactedFrontiersProviders) == beacons.Count(ctx.SubnetID))
len(contactedFrontiersProviders) == beacons.NumValidators(ctx.SubnetID))

// mock VM to simulate a valid but late summary is returned
fullVM.CantParseStateSummary = true
Expand Down Expand Up @@ -773,7 +773,7 @@ func TestUnRequestedVotesAreDropped(t *testing.T) {
// other listed voters are reached out
require.True(
len(contactedVoters) > initiallyContactedVotersSize ||
len(contactedVoters) == beacons.Count(ctx.SubnetID))
len(contactedVoters) == beacons.NumValidators(ctx.SubnetID))
}

func TestVotesForUnknownSummariesAreDropped(t *testing.T) {
Expand Down Expand Up @@ -876,7 +876,7 @@ func TestVotesForUnknownSummariesAreDropped(t *testing.T) {
// on unknown summary
require.True(
len(contactedVoters) > initiallyContactedVotersSize ||
len(contactedVoters) == beacons.Count(ctx.SubnetID))
len(contactedVoters) == beacons.NumValidators(ctx.SubnetID))
}

func TestStateSummaryIsPassedToVMAsMajorityOfVotesIsCastedForIt(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion snow/engine/snowman/syncer/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func buildTestsObjects(
startupTracker,
sender,
beacons,
beacons.Count(ctx.SubnetID),
beacons.NumValidators(ctx.SubnetID),
alpha,
nil,
fullVM,
Expand Down
16 changes: 13 additions & 3 deletions snow/validators/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,11 @@ type Manager interface {
// If an error is returned, the set will be unmodified.
RemoveWeight(subnetID ids.ID, nodeID ids.NodeID, weight uint64) error

// Count returns the number of validators currently in the subnet.
Count(subnetID ids.ID) int
// NumSubnets returns the number of subnets with non-zero weight.
NumSubnets() int

// NumValidators returns the number of validators currently in the subnet.
NumValidators(subnetID ids.ID) int

// TotalWeight returns the cumulative weight of all validators in the subnet.
// Returns err if total weight overflows uint64.
Expand Down Expand Up @@ -227,7 +230,14 @@ func (m *manager) RemoveWeight(subnetID ids.ID, nodeID ids.NodeID, weight uint64
return nil
}

func (m *manager) Count(subnetID ids.ID) int {
func (m *manager) NumSubnets() int {
m.lock.RLock()
defer m.lock.RUnlock()

return len(m.subnetToVdrs)
}

func (m *manager) NumValidators(subnetID ids.ID) int {
m.lock.RLock()
set, exists := m.subnetToVdrs[subnetID]
m.lock.RUnlock()
Expand Down
61 changes: 41 additions & 20 deletions snow/validators/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,36 +242,57 @@ func TestGet(t *testing.T) {
require.False(ok)
}

func TestLen(t *testing.T) {
require := require.New(t)
func TestNum(t *testing.T) {
var (
require = require.New(t)

m := NewManager()
subnetID := ids.GenerateTestID()
m = NewManager()

count := m.Count(subnetID)
require.Zero(count)
subnetID0 = ids.GenerateTestID()
subnetID1 = ids.GenerateTestID()
nodeID0 = ids.GenerateTestNodeID()
nodeID1 = ids.GenerateTestNodeID()
)

nodeID0 := ids.GenerateTestNodeID()
require.NoError(m.AddStaker(subnetID, nodeID0, nil, ids.Empty, 1))
require.Zero(m.NumSubnets())
require.Zero(m.NumValidators(subnetID0))
require.Zero(m.NumValidators(subnetID1))

count = m.Count(subnetID)
require.Equal(1, count)
require.NoError(m.AddStaker(subnetID0, nodeID0, nil, ids.Empty, 1))

nodeID1 := ids.GenerateTestNodeID()
require.NoError(m.AddStaker(subnetID, nodeID1, nil, ids.Empty, 1))
require.Equal(1, m.NumSubnets())
require.Equal(1, m.NumValidators(subnetID0))
require.Zero(m.NumValidators(subnetID1))

count = m.Count(subnetID)
require.Equal(2, count)
require.NoError(m.AddStaker(subnetID0, nodeID1, nil, ids.Empty, 1))

require.NoError(m.RemoveWeight(subnetID, nodeID1, 1))
require.Equal(1, m.NumSubnets())
require.Equal(2, m.NumValidators(subnetID0))
require.Zero(m.NumValidators(subnetID1))

count = m.Count(subnetID)
require.Equal(1, count)
require.NoError(m.AddStaker(subnetID1, nodeID1, nil, ids.Empty, 2))

require.NoError(m.RemoveWeight(subnetID, nodeID0, 1))
require.Equal(2, m.NumSubnets())
require.Equal(2, m.NumValidators(subnetID0))
require.Equal(1, m.NumValidators(subnetID1))

require.NoError(m.RemoveWeight(subnetID0, nodeID1, 1))

require.Equal(2, m.NumSubnets())
require.Equal(1, m.NumValidators(subnetID0))
require.Equal(1, m.NumValidators(subnetID1))

require.NoError(m.RemoveWeight(subnetID0, nodeID0, 1))

require.Equal(1, m.NumSubnets())
require.Zero(m.NumValidators(subnetID0))
require.Equal(1, m.NumValidators(subnetID1))

require.NoError(m.RemoveWeight(subnetID1, nodeID1, 2))

count = m.Count(subnetID)
require.Zero(count)
require.Zero(m.NumSubnets())
require.Zero(m.NumValidators(subnetID0))
require.Zero(m.NumValidators(subnetID1))
}

func TestGetMap(t *testing.T) {
Expand Down
10 changes: 5 additions & 5 deletions vms/platformvm/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -1750,13 +1750,13 @@ func (s *state) loadPendingValidators() error {
// Invariant: initValidatorSets requires loadCurrentValidators to have already
// been called.
func (s *state) initValidatorSets() error {
if s.validators.NumSubnets() != 0 {
// Enforce the invariant that the validator set is empty here.
return errValidatorSetAlreadyPopulated
}

primaryNetworkValidators := s.currentStakers.validators[constants.PrimaryNetworkID]
for subnetID, subnetValidators := range s.currentStakers.validators {
if s.validators.Count(subnetID) != 0 {
// Enforce the invariant that the validator set is empty here.
return fmt.Errorf("%w: %s", errValidatorSetAlreadyPopulated, subnetID)
}

for nodeID, subnetValidator := range subnetValidators {
// The subnet validator's Public Key is inherited from the
// corresponding primary network validator.
Expand Down
4 changes: 2 additions & 2 deletions vms/platformvm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ func TestGenesis(t *testing.T) {
}

// Ensure current validator set of primary network is correct
require.Len(genesisState.Validators, vm.Validators.Count(constants.PrimaryNetworkID))
require.Len(genesisState.Validators, vm.Validators.NumValidators(constants.PrimaryNetworkID))

for _, nodeID := range genesistest.DefaultNodeIDs {
_, ok := vm.Validators.GetValidator(constants.PrimaryNetworkID, nodeID)
Expand Down Expand Up @@ -1326,7 +1326,7 @@ func TestBootstrapPartiallyAccepted(t *testing.T) {
AllGetsServer: snowGetHandler,
Ctx: consensusCtx,
Beacons: beacons,
SampleK: beacons.Count(ctx.SubnetID),
SampleK: beacons.NumValidators(ctx.SubnetID),
StartupTracker: startup,
PeerTracker: peerTracker,
Sender: sender,
Expand Down
Loading