Skip to content

Commit

Permalink
Merge pull request #506 from iotaledger/feat/enable-dpos
Browse files Browse the repository at this point in the history
Enable dPoS
  • Loading branch information
jonastheis authored Nov 15, 2023
2 parents ea175ab + 65fce74 commit 3ab0b0a
Show file tree
Hide file tree
Showing 24 changed files with 520 additions and 366 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (g *Gadget) trackConfirmationRatifierWeight(votingBlock *blocks.Block) {

func (g *Gadget) shouldConfirm(block *blocks.Block) bool {
blockSeats := len(block.ConfirmationRatifiers())
totalCommitteeSeats := g.seatManager.SeatCount()
totalCommitteeSeats := g.seatManager.SeatCountInSlot(block.ID().Slot())

return votes.IsThresholdReached(blockSeats, totalCommitteeSeats, g.optsConfirmationThreshold)
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (g *Gadget) TrackWitnessWeight(votingBlock *blocks.Block) {
}

func (g *Gadget) shouldPreAcceptAndPreConfirm(block *blocks.Block) (preAccept bool, preConfirm bool) {
committeeTotalSeats := g.seatManager.SeatCount()
committeeTotalSeats := g.seatManager.SeatCountInSlot(block.ID().Slot())
blockSeats := len(block.Witnesses())

onlineCommitteeTotalSeats := g.seatManager.OnlineCommittee().Size()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,9 @@ func (g *Gadget) trackVotes(block *blocks.Block) {
}

func (g *Gadget) refreshSlotFinalization(tracker *slottracker.SlotTracker, previousLatestSlotIndex iotago.SlotIndex, newLatestSlotIndex iotago.SlotIndex) (finalizedSlots []iotago.SlotIndex) {
committeeTotalSeats := g.seatManager.SeatCount()

for i := lo.Max(g.lastFinalizedSlot, previousLatestSlotIndex) + 1; i <= newLatestSlotIndex; i++ {
committeeTotalSeats := g.seatManager.SeatCountInSlot(i)
attestorsTotalSeats := len(tracker.Voters(i))

if !votes.IsThresholdReached(attestorsTotalSeats, committeeTotalSeats, g.optsSlotFinalizationThreshold) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ func (o *Orchestrator) tryUpgrade(currentEpoch iotago.EpochIndex, lastSlotInEpoc
}

// Check whether the threshold for version was reached.
totalSeatCount := o.seatManager.SeatCount()
totalSeatCount := o.seatManager.SeatCountInEpoch(currentEpoch)
if !votes.IsThresholdReached(mostSupporters, totalSeatCount, votes.SuperMajority) {
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,10 @@ func (m *ManualPOA) OnlineCommittee() ds.Set[account.SeatIndex] {
return m.online
}

func (m *ManualPOA) SeatCount() int {
func (m *ManualPOA) SeatCountInSlot(_ iotago.SlotIndex) int {
return m.committee.SeatCount()
}
func (m *ManualPOA) SeatCountInEpoch(_ iotago.EpochIndex) int {
return m.committee.SeatCount()
}

Expand Down
15 changes: 12 additions & 3 deletions pkg/protocol/sybilprotection/seatmanager/poa/poa.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,14 @@ func (s *SeatManager) OnlineCommittee() ds.Set[account.SeatIndex] {
return s.activityTracker.OnlineCommittee()
}

func (s *SeatManager) SeatCount() int {
func (s *SeatManager) SeatCountInSlot(_ iotago.SlotIndex) int {
s.committeeMutex.RLock()
defer s.committeeMutex.RUnlock()

return s.committee.SeatCount()
}

func (s *SeatManager) SeatCountInEpoch(_ iotago.EpochIndex) int {
s.committeeMutex.RLock()
defer s.committeeMutex.RUnlock()

Expand All @@ -166,9 +173,11 @@ func (s *SeatManager) InitializeCommittee(epoch iotago.EpochIndex, activityTime
return ierrors.Wrapf(err, "failed to load PoA committee for epoch %d", epoch)
}

s.committee = committeeAccounts.SelectCommittee(committeeAccounts.IDs()...)
committeeAccountsIDs := committeeAccounts.IDs()
s.committee = committeeAccounts.SelectCommittee(committeeAccountsIDs...)

onlineValidators := committeeAccounts.IDs()
// Set validators that are part of the committee as active.
onlineValidators := committeeAccountsIDs
if len(s.optsOnlineCommitteeStartup) > 0 {
onlineValidators = s.optsOnlineCommitteeStartup
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/protocol/sybilprotection/seatmanager/seatmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ type SeatManager interface {
OnlineCommittee() ds.Set[account.SeatIndex]

// SeatCount returns the number of seats in the SeatManager.
SeatCount() int
SeatCountInSlot(slot iotago.SlotIndex) int

SeatCountInEpoch(epoch iotago.EpochIndex) int

// Interface embeds the required methods of the module.Interface.
module.Interface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,3 @@ func WithOnlineCommitteeStartup(optsOnlineCommittee ...iotago.AccountID) options
p.optsOnlineCommitteeStartup = optsOnlineCommittee
}
}

func WithSeatCount(optsSeatCount uint32) options.Option[SeatManager] {
return func(p *SeatManager) {
p.optsSeatCount = optsSeatCount
}
}
65 changes: 31 additions & 34 deletions pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/iotaledger/hive.go/ds"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/hive.go/runtime/module"
"github.com/iotaledger/hive.go/runtime/options"
"github.com/iotaledger/hive.go/runtime/syncutils"
Expand All @@ -30,7 +31,6 @@ type SeatManager struct {
committeeMutex syncutils.RWMutex
activityTracker activitytracker.ActivityTracker

optsSeatCount uint32
optsActivityWindow time.Duration
optsOnlineCommitteeStartup []iotago.AccountID

Expand Down Expand Up @@ -85,42 +85,21 @@ func (s *SeatManager) RotateCommittee(epoch iotago.EpochIndex, candidates accoun
s.committeeMutex.Lock()
defer s.committeeMutex.Unlock()

// If there are fewer candidates than required for epoch 0, then the previous committee cannot be copied.
if len(candidates) < s.SeatCount() && epoch == 0 {
return nil, ierrors.Errorf("at least %d candidates are required for committee in epoch 0, got %d", s.SeatCount(), len(candidates))
if len(candidates) == 0 {
return nil, ierrors.New("candidates must not be empty")
}

// If there are fewer candidates than required, then re-use the previous committee.
if len(candidates) < s.SeatCount() {
// TODO: what if staking period of a committee member ends in the next epoch?
committee, exists := s.committeeInEpoch(epoch - 1)
if !exists {
return nil, ierrors.Errorf("cannot re-use previous committee from epoch %d as it does not exist", epoch-1)
}

accounts, err := committee.Accounts()
if err != nil {
return nil, ierrors.Wrapf(err, "error while getting accounts from committee for epoch %d", epoch-1)
}

if err := s.committeeStore.Store(epoch, accounts); err != nil {
return nil, ierrors.Wrapf(err, "error while storing committee for epoch %d", epoch)
}

return committee, nil
}

committee, err := s.selectNewCommittee(candidates)
committee, err := s.selectNewCommittee(epoch, candidates)
if err != nil {
return nil, ierrors.Wrap(err, "error while selecting new committee")
}

accounts, err := committee.Accounts()
committeeAccounts, err := committee.Accounts()
if err != nil {
return nil, ierrors.Wrapf(err, "error while getting accounts for newly selected committee for epoch %d", epoch)
return nil, ierrors.Wrapf(err, "error while getting committeeAccounts for newly selected committee for epoch %d", epoch)
}

if err := s.committeeStore.Store(epoch, accounts); err != nil {
if err := s.committeeStore.Store(epoch, committeeAccounts); err != nil {
return nil, ierrors.Wrapf(err, "error while storing committee for epoch %d", epoch)
}

Expand Down Expand Up @@ -161,8 +140,22 @@ func (s *SeatManager) OnlineCommittee() ds.Set[account.SeatIndex] {
return s.activityTracker.OnlineCommittee()
}

func (s *SeatManager) SeatCount() int {
return int(s.optsSeatCount)
func (s *SeatManager) SeatCountInSlot(slot iotago.SlotIndex) int {
epoch := s.apiProvider.APIForSlot(slot).TimeProvider().EpochFromSlot(slot)

return s.SeatCountInEpoch(epoch)
}

func (s *SeatManager) SeatCountInEpoch(epoch iotago.EpochIndex) int {
s.committeeMutex.RLock()
defer s.committeeMutex.RUnlock()

// TODO: this function is a hot path as it is called for every single block. Maybe accessing the storage is too slow.
if committee, exists := s.committeeInEpoch(epoch); exists {
return committee.SeatCount()
}

return int(s.apiProvider.APIForEpoch(epoch).ProtocolParameters().TargetCommitteeSize())
}

func (s *SeatManager) Shutdown() {
Expand Down Expand Up @@ -202,8 +195,8 @@ func (s *SeatManager) SetCommittee(epoch iotago.EpochIndex, validators *account.
s.committeeMutex.Lock()
defer s.committeeMutex.Unlock()

if validators.Size() != int(s.optsSeatCount) {
return ierrors.Errorf("invalid number of validators: %d, expected: %d", validators.Size(), s.optsSeatCount)
if validators.Size() == 0 {
return ierrors.New("committee must not be empty")
}

err := s.committeeStore.Store(epoch, validators)
Expand All @@ -214,7 +207,7 @@ func (s *SeatManager) SetCommittee(epoch iotago.EpochIndex, validators *account.
return nil
}

func (s *SeatManager) selectNewCommittee(candidates accounts.AccountsData) (*account.SeatedAccounts, error) {
func (s *SeatManager) selectNewCommittee(epoch iotago.EpochIndex, candidates accounts.AccountsData) (*account.SeatedAccounts, error) {
sort.Slice(candidates, func(i int, j int) bool {
// Prioritize the candidate that has a larger pool stake.
if candidates[i].ValidatorStake+candidates[i].DelegationStake != candidates[j].ValidatorStake+candidates[j].DelegationStake {
Expand All @@ -240,10 +233,14 @@ func (s *SeatManager) selectNewCommittee(candidates accounts.AccountsData) (*acc
return bytes.Compare(candidates[i].ID[:], candidates[j].ID[:]) > 0
})

// We try to select up to targetCommitteeSize candidates to be part of the committee. If there are fewer candidates
// than required, then we select all of them and the committee size will be smaller than targetCommitteeSize.
committeeSize := lo.Min(len(candidates), int(s.apiProvider.APIForEpoch(epoch).ProtocolParameters().TargetCommitteeSize()))

// Create new Accounts instance that only included validators selected to be part of the committee.
newCommitteeAccounts := account.NewAccounts()

for _, candidateData := range candidates[:s.optsSeatCount] {
for _, candidateData := range candidates[:committeeSize] {
if err := newCommitteeAccounts.Set(candidateData.ID, &account.Pool{
PoolStake: candidateData.ValidatorStake + candidateData.DelegationStake,
ValidatorStake: candidateData.ValidatorStake,
Expand Down
Loading

0 comments on commit 3ab0b0a

Please sign in to comment.