From 567186965231edda2266f9bae74d8411c5c4708f Mon Sep 17 00:00:00 2001 From: keruch Date: Mon, 30 Sep 2024 19:09:11 +0200 Subject: [PATCH] feat(sponsorship): only allow sponsoring rollapps with bonded sequencers --- x/sponsorship/keeper/keeper.go | 15 ++++--- x/sponsorship/keeper/votes.go | 56 ++++++++++++++++++------- x/sponsorship/types/expected_keepers.go | 5 +++ 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/x/sponsorship/keeper/keeper.go b/x/sponsorship/keeper/keeper.go index 200518dfc..a71f0d5d5 100644 --- a/x/sponsorship/keeper/keeper.go +++ b/x/sponsorship/keeper/keeper.go @@ -23,6 +23,7 @@ type Keeper struct { stakingKeeper types.StakingKeeper incentivesKeeper types.IncentivesKeeper + sequencerKeeper types.SequencerKeeper } // NewKeeper returns a new instance of the x/sponsorship keeper. @@ -32,6 +33,7 @@ func NewKeeper( ak types.AccountKeeper, sk types.StakingKeeper, ik types.IncentivesKeeper, + sqk types.SequencerKeeper, authority string, ) Keeper { // ensure the module account is set @@ -53,12 +55,6 @@ func NewKeeper( "params", collcompat.ProtoValue[types.Params](cdc), ), - distribution: collections.NewItem( - sb, - types.DistributionPrefix(), - "distribution", - collcompat.ProtoValue[types.Distribution](cdc), - ), delegatorValidatorPower: collections.NewMap( sb, types.DelegatorValidatorPrefix(), @@ -69,6 +65,12 @@ func NewKeeper( ), collcompat.IntValue, ), + distribution: collections.NewItem( + sb, + types.DistributionPrefix(), + "distribution", + collcompat.ProtoValue[types.Distribution](cdc), + ), votes: collections.NewMap( sb, types.VotePrefix(), @@ -78,5 +80,6 @@ func NewKeeper( ), stakingKeeper: sk, incentivesKeeper: ik, + sequencerKeeper: sqk, } } diff --git a/x/sponsorship/keeper/votes.go b/x/sponsorship/keeper/votes.go index 80aa6a009..2d67f9968 100644 --- a/x/sponsorship/keeper/votes.go +++ b/x/sponsorship/keeper/votes.go @@ -7,15 +7,29 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + incentivestypes "github.com/dymensionxyz/dymension/v3/x/incentives/types" + sequencertypes "github.com/dymensionxyz/dymension/v3/x/sequencer/types" "github.com/dymensionxyz/dymension/v3/x/sponsorship/types" ) func (k Keeper) Vote(ctx sdk.Context, voter sdk.AccAddress, weights []types.GaugeWeight) (types.Vote, types.Distribution, error) { + // Get module params + params, err := k.GetParams(ctx) + if err != nil { + return types.Vote{}, types.Distribution{}, fmt.Errorf("cannot get module params: %w", err) + } + + // Validate specified weights + err = k.validateWeights(ctx, weights, params.MinAllocationWeight) + if err != nil { + return types.Vote{}, types.Distribution{}, fmt.Errorf("error validating weights: %w", err) + } + + // Check if the user's voted. If they have, revoke the previous vote to place a new one. voted, err := k.Voted(ctx, voter) if err != nil { return types.Vote{}, types.Distribution{}, fmt.Errorf("cannot verify if the voter has already voted: %w", err) } - if voted { _, err := k.RevokeVote(ctx, voter) if err != nil { @@ -23,16 +37,6 @@ func (k Keeper) Vote(ctx sdk.Context, voter sdk.AccAddress, weights []types.Gaug } } - params, err := k.GetParams(ctx) - if err != nil { - return types.Vote{}, types.Distribution{}, fmt.Errorf("cannot get module params: %w", err) - } - - err = k.validateWeights(ctx, weights, params.MinAllocationWeight) - if err != nil { - return types.Vote{}, types.Distribution{}, fmt.Errorf("error validating weights: %w", err) - } - // Get the user’s total voting power from the x/staking vpBreakdown, err := k.GetValidatorBreakdown(ctx, voter) if err != nil { @@ -107,21 +111,43 @@ func (k Keeper) revokeVote(ctx sdk.Context, voter sdk.AccAddress, vote types.Vot return d, nil } -// validateWeights validates that no gauge got less than MinAllocationWeight and all of them are perpetual +// validateWeights validates that +// - No gauge get less than MinAllocationWeight +// - All gauges exist +// - All gauges are perpetual +// - Rollapp gauges have at least one bonded sequencer func (k Keeper) validateWeights(ctx sdk.Context, weights []types.GaugeWeight, minAllocationWeight math.Int) error { for _, weight := range weights { + // No gauge get less than MinAllocationWeight if weight.Weight.LT(minAllocationWeight) { - return fmt.Errorf("gauge weight '%s' is less than min allocation weight '%s'", weight.Weight, minAllocationWeight) + return fmt.Errorf("gauge weight is less than min allocation weight: gauge weight %s, min allocation %s", weight.Weight, minAllocationWeight) } + // All gauges exist gauge, err := k.incentivesKeeper.GetGaugeByID(ctx, weight.GaugeId) if err != nil { - return fmt.Errorf("failed to get gauge by id '%d': %w", weight.GaugeId, err) + return fmt.Errorf("failed to get gauge by id: %d: %w", weight.GaugeId, err) } + // All gauges are perpetual if !gauge.IsPerpetual { - return fmt.Errorf("gauge '%d' is not perpetual", weight.GaugeId) + return fmt.Errorf("gauge is not perpetual: %d", weight.GaugeId) } + + // Rollapp gauges have at least one bonded sequencer + switch distrTo := gauge.DistributeTo.(type) { + case *incentivestypes.Gauge_Asset: + // no additional restrictions for asset gauges + case *incentivestypes.Gauge_Rollapp: + // we allow sponsoring only rollapps with bonded sequencers + bondedSequencers := k.sequencerKeeper.GetSequencersByRollappByStatus(ctx, distrTo.Rollapp.RollappId, sequencertypes.Bonded) + if len(bondedSequencers) == 0 { + return fmt.Errorf("rollapp has no bonded sequencers: %s'", distrTo.Rollapp.RollappId) + } + default: + return fmt.Errorf("gauge has an unsupported distribution type: gauge id %d, type %T", gauge.Id, distrTo) + } + } return nil } diff --git a/x/sponsorship/types/expected_keepers.go b/x/sponsorship/types/expected_keepers.go index 073279f5b..c00e42346 100644 --- a/x/sponsorship/types/expected_keepers.go +++ b/x/sponsorship/types/expected_keepers.go @@ -5,6 +5,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" incentivestypes "github.com/dymensionxyz/dymension/v3/x/incentives/types" + sequencertypes "github.com/dymensionxyz/dymension/v3/x/sequencer/types" ) // AccountKeeper defines the contract required for account APIs. @@ -21,3 +22,7 @@ type StakingKeeper interface { type IncentivesKeeper interface { GetGaugeByID(ctx sdk.Context, gaugeID uint64) (*incentivestypes.Gauge, error) } + +type SequencerKeeper interface { + GetSequencersByRollappByStatus(ctx sdk.Context, rollappId string, status sequencertypes.OperatingStatus) []sequencertypes.Sequencer +}