From 270042d915b2610cf3f417300662aa954d946a4f Mon Sep 17 00:00:00 2001 From: Kenny Date: Sat, 31 Aug 2024 02:57:14 -0400 Subject: [PATCH] add online, weak merit based sortition without new scores yet --- x/emissions/keeper/ema_scores.go | 132 +++++++++++++++++++++++++++ x/emissions/keeper/keeper.go | 36 ++++++++ x/emissions/module/rewards/scores.go | 78 +++------------- 3 files changed, 180 insertions(+), 66 deletions(-) create mode 100644 x/emissions/keeper/ema_scores.go diff --git a/x/emissions/keeper/ema_scores.go b/x/emissions/keeper/ema_scores.go new file mode 100644 index 000000000..1965d0789 --- /dev/null +++ b/x/emissions/keeper/ema_scores.go @@ -0,0 +1,132 @@ +package keeper + +import ( + "context" + + "cosmossdk.io/errors" + alloraMath "github.com/allora-network/allora-chain/math" + "github.com/allora-network/allora-chain/x/emissions/types" +) + +// Calculates and saves the EMA scores for a given worker and topic +// Does nothing if the last update of the score was topic.WorkerSubmissionWindow blocks ago or less +// This is useful to ensure workers cannot game the system by spamming submissions to unfairly up their score +func (k *Keeper) CalcAndSaveInfererScoreEmaIfNewUpdate( + ctx context.Context, + topic types.Topic, + block types.BlockHeight, + worker ActorId, + newScore types.Score, +) error { + previousScore, err := k.GetInfererScoreEma(ctx, topic.Id, worker) + if err != nil { + return errors.Wrapf(err, "Error getting inferer score ema") + } + // Only calc and save if there's a new update + if newScore.BlockHeight-previousScore.BlockHeight <= topic.WorkerSubmissionWindow { + return nil + } + firstTime := previousScore.BlockHeight == 0 && previousScore.Score.IsZero() + emaScoreDec, err := alloraMath.CalcEma( + topic.MeritSortitionAlpha, + newScore.Score, + previousScore.Score, + firstTime, + ) + if err != nil { + return errors.Wrapf(err, "Error calculating ema") + } + emaScore := types.Score{ + TopicId: topic.Id, + BlockHeight: block, + Address: worker, + Score: emaScoreDec, + } + err = k.SetInfererScoreEma(ctx, topic.Id, worker, emaScore) + if err != nil { + return errors.Wrapf(err, "error setting latest inferer score") + } + return nil +} + +// Calculates and saves the EMA scores for a given worker and topic +// Does nothing if the last update of the score was topic.WorkerSubmissionWindow blocks ago or less +// This is useful to ensure workers cannot game the system by spamming submissions to unfairly up their score +func (k *Keeper) CalcAndSaveForecasterScoreEmaIfNewUpdate( + ctx context.Context, + topic types.Topic, + block types.BlockHeight, + worker ActorId, + newScore types.Score, +) error { + previousScore, err := k.GetForecasterScoreEma(ctx, topic.Id, worker) + if err != nil { + return errors.Wrapf(err, "Error getting forecaster score ema") + } + // Only calc and save if there's a new update + if newScore.BlockHeight-previousScore.BlockHeight <= topic.WorkerSubmissionWindow { + return nil + } + firstTime := previousScore.BlockHeight == 0 && previousScore.Score.IsZero() + emaScoreDec, err := alloraMath.CalcEma( + topic.MeritSortitionAlpha, + newScore.Score, + previousScore.Score, + firstTime, + ) + if err != nil { + return errors.Wrapf(err, "Error calculating ema") + } + emaScore := types.Score{ + TopicId: topic.Id, + BlockHeight: block, + Address: worker, + Score: emaScoreDec, + } + err = k.SetForecasterScoreEma(ctx, topic.Id, worker, emaScore) + if err != nil { + return errors.Wrapf(err, "error setting latest forecaster score") + } + return nil +} + +// Calculates and saves the EMA scores for a given reputer and topic +// Does nothing if the last update of the score was topic.EpochLength blocks ago or less +// This is useful to ensure reputers cannot game the system by spamming submissions to unfairly up their score +func (k *Keeper) CalcAndSaveReputerScoreEmaIfNewUpdate( + ctx context.Context, + topic types.Topic, + block types.BlockHeight, + worker ActorId, + newScore types.Score, +) error { + previousScore, err := k.GetReputerScoreEma(ctx, topic.Id, worker) + if err != nil { + return errors.Wrapf(err, "Error getting reputer score ema") + } + // Only calc and save if there's a new update + if newScore.BlockHeight-previousScore.BlockHeight <= topic.EpochLength { + return nil + } + firstTime := previousScore.BlockHeight == 0 && previousScore.Score.IsZero() + emaScoreDec, err := alloraMath.CalcEma( + topic.MeritSortitionAlpha, + newScore.Score, + previousScore.Score, + firstTime, + ) + if err != nil { + return errors.Wrapf(err, "Error calculating ema") + } + emaScore := types.Score{ + TopicId: topic.Id, + BlockHeight: block, + Address: worker, + Score: emaScoreDec, + } + err = k.SetReputerScoreEma(ctx, topic.Id, worker, emaScore) + if err != nil { + return errors.Wrapf(err, "error setting latest reputer score") + } + return nil +} diff --git a/x/emissions/keeper/keeper.go b/x/emissions/keeper/keeper.go index c416f175a..17e45ba39 100644 --- a/x/emissions/keeper/keeper.go +++ b/x/emissions/keeper/keeper.go @@ -826,6 +826,18 @@ func (k *Keeper) AppendInference(ctx context.Context, topicId TopicId, nonce typ newInferences.Inferences = append(newInferences.Inferences[:lowScoreIndex], newInferences.Inferences[lowScoreIndex+1:]...) newInferences.Inferences = append(newInferences.Inferences, inference) return k.allInferences.Set(ctx, key, newInferences) + } else { + topic, err := k.GetTopic(ctx, topicId) + if err != nil { + return err + } + /* + * TODO: Get + use previous active set score! + */ + err = k.CalcAndSaveInfererScoreEmaIfNewUpdate(ctx, topic, block, inference.Inferer, score) + if err != nil { + return err + } } return nil } @@ -888,6 +900,18 @@ func (k *Keeper) AppendForecast(ctx context.Context, topicId TopicId, nonce type newForecasts.Forecasts = append(newForecasts.Forecasts[:lowScoreIndex], newForecasts.Forecasts[lowScoreIndex+1:]...) newForecasts.Forecasts = append(newForecasts.Forecasts, forecast) return k.allForecasts.Set(ctx, key, newForecasts) + } else { + topic, err := k.GetTopic(ctx, topicId) + if err != nil { + return err + } + /* + * TODO: Get + use previous active set score! + */ + err = k.CalcAndSaveForecasterScoreEmaIfNewUpdate(ctx, topic, block, forecast.Forecaster, score) + if err != nil { + return err + } } return nil } @@ -989,6 +1013,18 @@ func (k *Keeper) AppendReputerLoss(ctx context.Context, topicId TopicId, block B newReputerLossBundles.ReputerValueBundles[lowScoreIndex+1:]...) newReputerLossBundles.ReputerValueBundles = append(newReputerLossBundles.ReputerValueBundles, reputerLoss) return k.allLossBundles.Set(ctx, key, newReputerLossBundles) + } else { + topic, err := k.GetTopic(ctx, topicId) + if err != nil { + return err + } + /* + * TODO: Get + use previous active set score! + */ + err = k.CalcAndSaveReputerScoreEmaIfNewUpdate(ctx, topic, block, reputerLoss.ValueBundle.Reputer, score) + if err != nil { + return err + } } return nil } diff --git a/x/emissions/module/rewards/scores.go b/x/emissions/module/rewards/scores.go index c28748058..fea68bf69 100644 --- a/x/emissions/module/rewards/scores.go +++ b/x/emissions/module/rewards/scores.go @@ -108,30 +108,12 @@ func GenerateReputerScores( if err != nil { return []types.Score{}, errors.Wrapf(err, "Error inserting reputer score") } - previousScore, err := keeper.GetReputerScoreEma(ctx, topicId, reputer) - if err != nil { - return []types.Score{}, errors.Wrapf(err, "Error getting reputer score ema") - } - firstTime := previousScore.BlockHeight == 0 && previousScore.Score.IsZero() - emaScoreDec, err := alloraMath.CalcEma( - topic.MeritSortitionAlpha, - newScore.Score, - previousScore.Score, - firstTime, - ) - if err != nil { - return []types.Score{}, errors.Wrapf(err, "Error calculating ema") - } - emaScore := types.Score{ - TopicId: topicId, - BlockHeight: block, - Address: reputer, - Score: emaScoreDec, - } - err = keeper.SetReputerScoreEma(ctx, topicId, reputer, emaScore) + + err = keeper.CalcAndSaveReputerScoreEmaIfNewUpdate(ctx, topic, block, reputer, newScore) if err != nil { - return []types.Score{}, errors.Wrapf(err, "Error setting latest reputer score") + return []types.Score{}, errors.Wrapf(err, "Error calculating and saving reputer score ema") } + newScores = append(newScores, newScore) } @@ -186,30 +168,12 @@ func GenerateInferenceScores( if err != nil { return []types.Score{}, errors.Wrapf(err, "Error inserting worker inference score") } - previousScore, err := keeper.GetInfererScoreEma(ctx, topicId, oneOutLoss.Worker) - if err != nil { - return []types.Score{}, errors.Wrapf(err, "Error getting inferer score ema") - } - firstTime := previousScore.BlockHeight == 0 && previousScore.Score.IsZero() - emaScoreDec, err := alloraMath.CalcEma( - topic.MeritSortitionAlpha, - newScore.Score, - previousScore.Score, - firstTime, - ) - if err != nil { - return []types.Score{}, errors.Wrapf(err, "Error calculating ema") - } - emaScore := types.Score{ - TopicId: topicId, - BlockHeight: block, - Address: oneOutLoss.Worker, - Score: emaScoreDec, - } - err = keeper.SetInfererScoreEma(ctx, topicId, oneOutLoss.Worker, emaScore) + + err = keeper.CalcAndSaveInfererScoreEmaIfNewUpdate(ctx, topic, block, oneOutLoss.Worker, newScore) if err != nil { - return []types.Score{}, errors.Wrapf(err, "error setting latest inferer score") + return []types.Score{}, errors.Wrapf(err, "Error calculating and saving inferer score ema") } + newScores = append(newScores, newScore) } @@ -288,30 +252,12 @@ func GenerateForecastScores( if err != nil { return []types.Score{}, errors.Wrapf(err, "Error inserting worker forecast score") } - previousScore, err := keeper.GetForecasterScoreEma(ctx, topicId, oneInNaiveLoss.Worker) - if err != nil { - return []types.Score{}, errors.Wrapf(err, "Error getting inferer score ema") - } - firstTime := previousScore.BlockHeight == 0 && previousScore.Score.IsZero() - emaScoreDec, err := alloraMath.CalcEma( - topic.MeritSortitionAlpha, - newScore.Score, - previousScore.Score, - firstTime, - ) - if err != nil { - return []types.Score{}, errors.Wrapf(err, "Error calculating ema") - } - emaScore := types.Score{ - TopicId: topicId, - BlockHeight: block, - Address: oneInNaiveLoss.Worker, - Score: emaScoreDec, - } - err = keeper.SetForecasterScoreEma(ctx, topicId, oneInNaiveLoss.Worker, emaScore) + + err = keeper.CalcAndSaveForecasterScoreEmaIfNewUpdate(ctx, topic, block, oneInNaiveLoss.Worker, newScore) if err != nil { - return []types.Score{}, errors.Wrapf(err, "Error setting latest forecaster score") + return []types.Score{}, errors.Wrapf(err, "Error calculating and saving forecaster score ema") } + newScores = append(newScores, newScore) }