Skip to content

Commit

Permalink
Fix getValidatorCandidates to get the registered candidates in the gi…
Browse files Browse the repository at this point in the history
…ven epoch for the next epoch, adjust when we rotate the committee and tests accordingly
  • Loading branch information
jonastheis committed Nov 14, 2023
1 parent 7d5ac6b commit 756a696
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,15 @@ func (t *Tracker) TrackCandidateBlock(block *blocks.Block) {

}

// EligibleValidatorCandidates returns the eligible validator candidates registered in the given epoch for the next epoch.
func (t *Tracker) EligibleValidatorCandidates(epoch iotago.EpochIndex) (ds.Set[iotago.AccountID], error) {
t.mutex.RLock()
defer t.mutex.RUnlock()

return t.getValidatorCandidates(epoch)
}

// ValidatorCandidates returns the registered validator candidates for the given epoch.
// ValidatorCandidates returns the eligible validator candidates registered in the given epoch for the next epoch.
func (t *Tracker) ValidatorCandidates(epoch iotago.EpochIndex) (ds.Set[iotago.AccountID], error) {
t.mutex.RLock()
defer t.mutex.RUnlock()
Expand All @@ -152,14 +153,7 @@ func (t *Tracker) ValidatorCandidates(epoch iotago.EpochIndex) (ds.Set[iotago.Ac
func (t *Tracker) getValidatorCandidates(epoch iotago.EpochIndex) (ds.Set[iotago.AccountID], error) {
candidates := ds.NewSet[iotago.AccountID]()

// Epoch 0 has no candidates as it's the genesis committee.
if epoch == 0 {
return candidates, nil
}

// we store candidates in the store for the epoch of their activity, but the passed argument points to the target epoch,
// so it's necessary to subtract one epoch from the passed value
candidateStore, err := t.committeeCandidatesInEpochFunc(epoch - 1)
candidateStore, err := t.committeeCandidatesInEpochFunc(epoch)
if err != nil {
return nil, ierrors.Wrapf(err, "error while retrieving candidates for epoch %d", epoch)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,28 +136,8 @@ func TestManager_Candidates(t *testing.T) {
ts.Instance.TrackCandidateBlock(blocks.NewBlock(lo.PanicOnErr(model.BlockFromBlock(block6))))
}

require.True(t, lo.PanicOnErr(ts.Instance.EligibleValidatorCandidates(1)).HasAll(ds.NewReadableSet(issuer1, issuer2, issuer3)))
require.True(t, lo.PanicOnErr(ts.Instance.ValidatorCandidates(1)).HasAll(ds.NewReadableSet(issuer1, issuer2, issuer3)))
require.True(t, lo.PanicOnErr(ts.Instance.EligibleValidatorCandidates(2)).IsEmpty())
require.True(t, lo.PanicOnErr(ts.Instance.ValidatorCandidates(2)).IsEmpty())

// retrieve epoch candidates for epoch 0, because we candidates prefixed with epoch in which they candidated
candidatesStore, err := ts.Instance.committeeCandidatesInEpochFunc(0)
require.NoError(t, err)

candidacySlotIssuer1, err := candidatesStore.Get(issuer1[:])
require.NoError(t, err)
require.Equal(t, iotago.SlotIndex(1).MustBytes(), candidacySlotIssuer1)

candidacySlotIssuer2, err := candidatesStore.Get(issuer2[:])
require.NoError(t, err)
require.Equal(t, iotago.SlotIndex(2).MustBytes(), candidacySlotIssuer2)

candidacySlotIssuer3, err := candidatesStore.Get(issuer3[:])
require.NoError(t, err)
require.Equal(t, iotago.SlotIndex(3).MustBytes(), candidacySlotIssuer3)

ts.Instance.ClearCandidates()

require.True(t, ts.Instance.nextEpochCommitteeCandidates.IsEmpty())
require.True(t, lo.PanicOnErr(ts.Instance.EligibleValidatorCandidates(0)).HasAll(ds.NewReadableSet(issuer1, issuer2, issuer3)))
require.True(t, lo.PanicOnErr(ts.Instance.ValidatorCandidates(0)).HasAll(ds.NewReadableSet(issuer1, issuer2, issuer3)))
require.True(t, lo.PanicOnErr(ts.Instance.EligibleValidatorCandidates(1)).IsEmpty())
require.True(t, lo.PanicOnErr(ts.Instance.ValidatorCandidates(1)).IsEmpty())
}
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,9 @@ func (o *SybilProtection) selectNewCommittee(slot iotago.SlotIndex) (*account.Ac
timeProvider := o.apiProvider.APIForSlot(slot).TimeProvider()
currentEpoch := timeProvider.EpochFromSlot(slot)
nextEpoch := currentEpoch + 1
candidates, err := o.performanceTracker.EligibleValidatorCandidates(nextEpoch)

// We get the list of candidates for the next epoch. They are registered in the current epoch.
candidates, err := o.performanceTracker.EligibleValidatorCandidates(currentEpoch)
if err != nil {
return nil, ierrors.Wrapf(err, "failed to retrieve candidates for epoch %d", nextEpoch)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/tests/committee_rotation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func Test_TopStakersRotation(t *testing.T) {
ts.IssueCandidacyAnnouncementInSlot("node5-candidacy:2", 11, "node4-candidacy:3", ts.Wallet("node3"))

// Assert that only candidates that issued before slot 11 are considered.
ts.AssertSybilProtectionCandidates(1, []iotago.AccountID{
ts.AssertSybilProtectionCandidates(0, []iotago.AccountID{
ts.Node("node1").Validator.AccountID,
ts.Node("node4").Validator.AccountID,
ts.Node("node5").Validator.AccountID,
Expand All @@ -121,7 +121,7 @@ func Test_TopStakersRotation(t *testing.T) {
{
ts.IssueBlocksAtSlots("wave-5:", []iotago.SlotIndex{18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}, 4, "wave-4:17.3", ts.Nodes(), true, nil)

ts.AssertSybilProtectionCandidates(2, []iotago.AccountID{}, ts.Nodes()...)
ts.AssertSybilProtectionCandidates(1, []iotago.AccountID{}, ts.Nodes()...)
ts.AssertLatestCommitmentSlotIndex(28, ts.Nodes()...)
ts.AssertLatestFinalizedSlot(27, ts.Nodes()...)
ts.AssertSybilProtectionCommittee(2, []iotago.AccountID{
Expand All @@ -144,7 +144,7 @@ func Test_TopStakersRotation(t *testing.T) {

ts.AssertLatestCommitmentSlotIndex(43, ts.Nodes()...)
// Even though we have a candidate, the committee should be reused as we did not finalize at epochNearingThreshold before epoch end - maxCommittableAge was committed
ts.AssertSybilProtectionCandidates(3, []iotago.AccountID{
ts.AssertSybilProtectionCandidates(2, []iotago.AccountID{
ts.Node("node6").Validator.AccountID,
}, ts.Nodes()...)
// Check that the committee is reused.
Expand All @@ -166,7 +166,7 @@ func Test_TopStakersRotation(t *testing.T) {
ts.AssertLatestCommitmentSlotIndex(59, ts.Nodes()...)
ts.AssertLatestFinalizedSlot(58, ts.Nodes()...)
// We finalized at epochEnd-epochNearingThreshold, so the committee should be rotated even if there is just one candidate.
ts.AssertSybilProtectionCandidates(4, []iotago.AccountID{
ts.AssertSybilProtectionCandidates(3, []iotago.AccountID{
ts.Node("node3").Validator.AccountID,
}, ts.Nodes()...)
ts.AssertSybilProtectionCommittee(4, []iotago.AccountID{
Expand Down

0 comments on commit 756a696

Please sign in to comment.