Skip to content

Commit

Permalink
Merge pull request #620 from iotaledger/validator-tests
Browse files Browse the repository at this point in the history
Add Validator tests
  • Loading branch information
PhilippGackstatter authored Dec 11, 2023
2 parents 2ab4636 + 09c8696 commit fce8007
Show file tree
Hide file tree
Showing 8 changed files with 446 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/iotaledger/hive.go/ds/shrinkingmap"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/kvstore"
"github.com/iotaledger/hive.go/log"
"github.com/iotaledger/hive.go/runtime/syncutils"
"github.com/iotaledger/iota-core/pkg/core/account"
"github.com/iotaledger/iota-core/pkg/model"
Expand All @@ -32,6 +33,8 @@ type Tracker struct {

performanceFactorsMutex syncutils.RWMutex
mutex syncutils.RWMutex

log.Logger
}

func NewTracker(
Expand All @@ -43,6 +46,7 @@ func NewTracker(
latestAppliedEpoch iotago.EpochIndex,
apiProvider iotago.APIProvider,
errHandler func(error),
logger log.Logger,
) *Tracker {
return &Tracker{
nextEpochCommitteeCandidates: shrinkingmap.New[iotago.AccountID, iotago.SlotIndex](),
Expand All @@ -54,6 +58,7 @@ func NewTracker(
latestAppliedEpoch: latestAppliedEpoch,
apiProvider: apiProvider,
errHandler: errHandler,
Logger: logger,
}
}

Expand Down Expand Up @@ -212,12 +217,12 @@ func (t *Tracker) ApplyEpoch(epoch iotago.EpochIndex, committee *account.Account
}

committee.ForEach(func(accountID iotago.AccountID, pool *account.Pool) bool {
validatorPerformances := make([]*model.ValidatorPerformance, timeProvider.EpochDurationSlots())
validatorPerformances := make([]*model.ValidatorPerformance, 0, timeProvider.EpochDurationSlots())

for slot := epochStartSlot; slot <= epochEndSlot; slot++ {
validatorSlotPerformances, err := t.validatorPerformancesFunc(slot)
if err != nil {
validatorPerformances = append(validatorPerformances, nil)

continue
}

Expand All @@ -233,8 +238,11 @@ func (t *Tracker) ApplyEpoch(epoch iotago.EpochIndex, committee *account.Account

validatorPerformances = append(validatorPerformances, validatorPerformance)
}
pf := t.aggregatePerformanceFactors(validatorPerformances, epoch)
if pf == 0 {

// Aggregate the performance factor of the epoch which approximates the average of the slot's performance factor.
epochPerformanceFactor := t.aggregatePerformanceFactors(validatorPerformances, epoch)

if epochPerformanceFactor == 0 {
// no rewards for this pool, we do not set pool rewards at all,
// to differientiate between situation when poolReward == fixedCost (no reward for delegators)

Expand All @@ -247,11 +255,14 @@ func (t *Tracker) ApplyEpoch(epoch iotago.EpochIndex, committee *account.Account
committee.TotalStake(),
pool.PoolStake,
pool.ValidatorStake,
pf,
epochPerformanceFactor,
)
if err != nil {
panic(ierrors.Wrapf(err, "failed to calculate pool rewards for account %s", accountID))
}

t.LogDebug("PerformanceFactor", "accountID", accountID, "epochPerformanceFactor", epochPerformanceFactor, "poolReward", poolReward)

if err = rewardsMap.Set(accountID, &model.PoolRewards{
PoolStake: pool.PoolStake,
PoolRewards: poolReward,
Expand Down Expand Up @@ -286,6 +297,7 @@ func (t *Tracker) aggregatePerformanceFactors(slotActivityVector []*model.Valida
if pf == nil {
continue
}

// each one bit represents at least one block issued in that subslot,
// we reward not only total number of blocks issued, but also regularity based on block timestamp
slotPerformanceFactor := bits.OnesCount32(pf.SlotActivityVector)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/iotaledger/hive.go/ads"
"github.com/iotaledger/hive.go/core/safemath"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/iota-core/pkg/model"
iotago "github.com/iotaledger/iota.go/v4"
)
Expand Down Expand Up @@ -34,6 +35,8 @@ func (t *Tracker) ValidatorReward(validatorID iotago.AccountID, stakingFeature *
lastRewardEpoch = t.latestAppliedEpoch
}

decayEndEpoch := t.decayEndEpoch(claimingEpoch, lastRewardEpoch)

for epoch := firstRewardEpoch; epoch <= lastRewardEpoch; epoch++ {
rewardsForAccountInEpoch, exists, err := t.rewardsForAccount(validatorID, epoch)
if err != nil {
Expand Down Expand Up @@ -98,7 +101,7 @@ func (t *Tracker) ValidatorReward(validatorID iotago.AccountID, stakingFeature *
}

decayProvider := t.apiProvider.APIForEpoch(epoch).ManaDecayProvider()
decayedEpochRewards, err := decayProvider.DecayManaByEpochs(iotago.Mana(undecayedEpochRewards), epoch, claimingEpoch)
decayedEpochRewards, err := decayProvider.DecayManaByEpochs(iotago.Mana(undecayedEpochRewards), epoch, decayEndEpoch)
if err != nil {
return 0, 0, 0, ierrors.Wrapf(err, "failed to calculate rewards with decay for epoch %d and validator accountID %s", epoch, validatorID)
}
Expand Down Expand Up @@ -126,6 +129,8 @@ func (t *Tracker) DelegatorReward(validatorID iotago.AccountID, delegatedAmount
lastRewardEpoch = t.latestAppliedEpoch
}

decayEndEpoch := t.decayEndEpoch(claimingEpoch, lastRewardEpoch)

for epoch := firstRewardEpoch; epoch <= lastRewardEpoch; epoch++ {
rewardsForAccountInEpoch, exists, err := t.rewardsForAccount(validatorID, epoch)
if err != nil {
Expand Down Expand Up @@ -178,7 +183,7 @@ func (t *Tracker) DelegatorReward(validatorID iotago.AccountID, delegatedAmount
}

decayProvider := t.apiProvider.APIForEpoch(epoch).ManaDecayProvider()
decayedEpochRewards, err := decayProvider.DecayManaByEpochs(iotago.Mana(undecayedEpochRewards), epoch, claimingEpoch)
decayedEpochRewards, err := decayProvider.DecayManaByEpochs(iotago.Mana(undecayedEpochRewards), epoch, decayEndEpoch)
if err != nil {
return 0, 0, 0, ierrors.Wrapf(err, "failed to calculate rewards with decay for epoch %d and validator accountID %s", epoch, validatorID)
}
Expand All @@ -189,6 +194,23 @@ func (t *Tracker) DelegatorReward(validatorID iotago.AccountID, delegatedAmount
return delegatorsReward, firstRewardEpoch, lastRewardEpoch, nil
}

// Returns the epoch until which rewards are decayed.
//
// When claiming rewards in epoch X for epoch X-1, decay of X-(X-1) = 1 would be applied. Since epoch X is the
// very first epoch in which one can claim those rewards, decaying by 1 is odd, as one could never claim the full reward then.
// Hence, one epoch worth of decay is deducted in general.
//
// The decay end epoch must however be greater or equal than the last epoch for which rewards are claimed, otherwise
// the decay operation would fail since the amount of epochs to decay would be negative.
// Hence, the smallest returned decay end epoch will be the lastRewardEpoch.
func (t *Tracker) decayEndEpoch(claimingEpoch iotago.EpochIndex, lastRewardEpoch iotago.EpochIndex) iotago.EpochIndex {
if claimingEpoch >= 1 {
claimingEpoch = claimingEpoch - 1
}

return lo.Max(claimingEpoch, lastRewardEpoch)
}

func (t *Tracker) rewardsMap(epoch iotago.EpochIndex) (ads.Map[iotago.Identifier, iotago.AccountID, *model.PoolRewards], error) {
kv, err := t.rewardsStorePerEpochFunc(epoch)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/iotaledger/hive.go/kvstore"
"github.com/iotaledger/hive.go/kvstore/mapdb"
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/hive.go/log"
"github.com/iotaledger/iota-core/pkg/core/account"
"github.com/iotaledger/iota-core/pkg/model"
"github.com/iotaledger/iota-core/pkg/protocol/engine/blocks"
Expand Down Expand Up @@ -90,6 +91,7 @@ func (t *TestSuite) InitPerformanceTracker() {
t.latestCommittedEpoch,
iotago.SingleVersionProvider(t.api),
func(err error) {},
log.NewLogger("PerfTestsuite"),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ func NewProvider(opts ...options.Option[SybilProtection]) module.Provider[*engin
e.Constructed.OnTrigger(func() {
o.ledger = e.Ledger
o.errHandler = e.ErrorHandler("SybilProtection")

logger, _ := e.NewChildLogger("PerformanceTracker")
latestCommittedSlot := e.Storage.Settings().LatestCommitment().Slot()
latestCommittedEpoch := o.apiProvider.APIForSlot(latestCommittedSlot).TimeProvider().EpochFromSlot(latestCommittedSlot)
o.performanceTracker = performance.NewTracker(e.Storage.RewardsForEpoch, e.Storage.PoolStats(), e.Storage.Committee(), e.Storage.CommitteeCandidates, e.Storage.ValidatorPerformances, latestCommittedEpoch, e, o.errHandler)
o.performanceTracker = performance.NewTracker(e.Storage.RewardsForEpoch, e.Storage.PoolStats(), e.Storage.Committee(), e.Storage.CommitteeCandidates, e.Storage.ValidatorPerformances, latestCommittedEpoch, e, o.errHandler, logger)
o.lastCommittedSlot = latestCommittedSlot

if o.optsInitialCommittee != nil {
Expand Down
Loading

0 comments on commit fce8007

Please sign in to comment.