Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(tests): add rewards simulation tests #217

Merged
merged 7 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func appModules(
pools.NewAppModule(appCodec, app.PoolsKeeper),
restaking.NewAppModule(appCodec, app.RestakingKeeper, app.AccountKeeper, app.BankKeeper, app.PoolsKeeper, app.OperatorsKeeper, app.ServicesKeeper),
assets.NewAppModule(appCodec, app.AssetsKeeper),
rewards.NewAppModule(appCodec, app.RewardsKeeper),
rewards.NewAppModule(appCodec, app.RewardsKeeper, app.AccountKeeper, app.BankKeeper, app.PoolsKeeper, app.OperatorsKeeper, app.ServicesKeeper),
liquidvesting.NewAppModule(appCodec, app.LiquidVestingKeeper),
}
}
Expand Down Expand Up @@ -206,6 +206,7 @@ func simulationModules(
services.NewAppModule(appCodec, app.ServicesKeeper, app.AccountKeeper, app.BankKeeper),
operators.NewAppModule(appCodec, app.OperatorsKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper),
restaking.NewAppModule(appCodec, app.RestakingKeeper, app.AccountKeeper, app.BankKeeper, app.PoolsKeeper, app.OperatorsKeeper, app.ServicesKeeper),
rewards.NewAppModule(appCodec, app.RewardsKeeper, app.AccountKeeper, app.BankKeeper, app.PoolsKeeper, app.OperatorsKeeper, app.ServicesKeeper),
}
}

Expand Down
34 changes: 34 additions & 0 deletions testutils/simtesting/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ func RandomCoin(r *rand.Rand, denom string, maxAmount int) sdk.Coin {
)
}

// RandomDecCoins returns a random list of DecCoins by randomly selecting
// a sub set of the provided denoms.
func RandomDecCoins(r *rand.Rand, denoms []string, maxAmount sdkmath.LegacyDec) sdk.DecCoins {
coins := sdk.NewDecCoins()
for _, denom := range RandomSubSlice(r, denoms) {
coins = coins.Add(sdk.NewDecCoinFromDec(denom, simtypes.RandomDecAmount(r, maxAmount)))
}

return coins
}

// RandomSubSlice returns a random subset of the given slice
func RandomSubSlice[T any](r *rand.Rand, items []T) []T {
// Empty slice, we can't pick random elements
Expand Down Expand Up @@ -117,3 +128,26 @@ func RandomSubSlice[T any](r *rand.Rand, items []T) []T {

return elements
}

// RandomPositiveUint32 returns a random positive uint32
func RandomPositiveUint32(r *rand.Rand) uint32 {
value := r.Uint32()
for value == 0 {
value = r.Uint32()
}
return value
}

// RandomPositiveUint64 returns a random positive uint64
func RandomPositiveUint64(r *rand.Rand) uint64 {
value := r.Uint64()
for value == 0 {
value = r.Uint64()
}
return value
}

// RandomSliceElement returns a random element from the given slice
func RandomSliceElement[T any](r *rand.Rand, slice []T) T {
return slice[r.Intn(len(slice))]
}
15 changes: 15 additions & 0 deletions utils/slices.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ func RemoveDuplicates[T comparable](slice []T) []T {
return result
}

// RemoveDuplicatesFunc removes all duplicate elements from the slice based
// on the value returned by the provided function.
func RemoveDuplicatesFunc[T any, C comparable](slice []T, compareBy func(T) C) []T {
seen := make(map[C]bool)
result := make([]T, 0, len(slice))
for _, v := range slice {
compareValue := compareBy(v)
if _, ok := seen[compareValue]; !ok {
seen[compareValue] = true
result = append(result, v)
}
}
return result
}

// Remove removes the first instance of value from the provided slice.
func Remove[T comparable](slice []T, value T) (newSlice []T, removed bool) {
index := -1
Expand Down
75 changes: 67 additions & 8 deletions x/rewards/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,35 @@ import (
"encoding/json"
"fmt"

"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/spf13/cobra"

"cosmossdk.io/core/appmodule"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/spf13/cobra"

operatorskeeper "github.com/milkyway-labs/milkyway/v3/x/operators/keeper"
poolskeeper "github.com/milkyway-labs/milkyway/v3/x/pools/keeper"
"github.com/milkyway-labs/milkyway/v3/x/rewards/client/cli"
"github.com/milkyway-labs/milkyway/v3/x/rewards/keeper"
"github.com/milkyway-labs/milkyway/v3/x/rewards/simulation"
"github.com/milkyway-labs/milkyway/v3/x/rewards/types"
serviceskeeper "github.com/milkyway-labs/milkyway/v3/x/services/keeper"
)

const (
consensusVersion = 1
)

var (
_ appmodule.AppModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
_ appmodule.AppModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
_ module.AppModuleSimulation = AppModule{}
)

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -93,12 +100,32 @@ type AppModule struct {

// To ensure setting hooks properly, keeper must be a reference
keeper *keeper.Keeper

accountKeeper authkeeper.AccountKeeper
bankKeeper bankkeeper.Keeper

poolsKeeper *poolskeeper.Keeper
operatorsKeeper *operatorskeeper.Keeper
servicesKeeper *serviceskeeper.Keeper
}

func NewAppModule(cdc codec.Codec, keeper *keeper.Keeper) AppModule {
func NewAppModule(
cdc codec.Codec,
keeper *keeper.Keeper,
accountKeeper authkeeper.AccountKeeper,
bankKeeper bankkeeper.Keeper,
poolsKeeper *poolskeeper.Keeper,
operatorsKeeper *operatorskeeper.Keeper,
serviceKeeper *serviceskeeper.Keeper,
) AppModule {
return AppModule{
AppModuleBasic: NewAppModuleBasic(cdc),
keeper: keeper,
AppModuleBasic: NewAppModuleBasic(cdc),
keeper: keeper,
accountKeeper: accountKeeper,
bankKeeper: bankKeeper,
poolsKeeper: poolsKeeper,
operatorsKeeper: operatorsKeeper,
servicesKeeper: serviceKeeper,
}
}

Expand Down Expand Up @@ -149,3 +176,35 @@ func (am AppModule) BeginBlock(ctx context.Context) error {
func (am AppModule) IsOnePerModuleType() {}

func (am AppModule) IsAppModule() {}

// ----------------------------------------------------------------------------
// AppModuleSimulation
// ----------------------------------------------------------------------------

// GenerateGenesisState creates a randomized GenState of the rewards module.
func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
simulation.RandomizedGenState(simState)
}

// ProposalMsgs returns msgs used for governance proposals for simulations.
func (am AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.WeightedProposalMsg {
return simulation.ProposalMsgs(am.bankKeeper)
}

// RegisterStoreDecoder registers a decoder for rewards module's types.
func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
sdr[types.StoreKey] = simtypes.NewStoreDecoderFuncFromCollectionsSchema(am.keeper.Schema)
}

// WeightedOperations returns the all the rewards module operations with their respective weights.
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return simulation.WeightedOperations(
simState.AppParams,
am.accountKeeper,
am.bankKeeper,
am.poolsKeeper,
am.operatorsKeeper,
am.servicesKeeper,
am.keeper,
)
}
47 changes: 47 additions & 0 deletions x/rewards/simulation/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package simulation

import (
"github.com/cosmos/cosmos-sdk/types/module"

operatorssimulation "github.com/milkyway-labs/milkyway/v3/x/operators/simulation"
poolssimulation "github.com/milkyway-labs/milkyway/v3/x/pools/simulation"
"github.com/milkyway-labs/milkyway/v3/x/rewards/types"
servicessimulation "github.com/milkyway-labs/milkyway/v3/x/services/simulation"
)

// RandomizedGenState generates a random GenesisState for the operators module
func RandomizedGenState(simState *module.SimulationState) {
servicesGenesis := servicessimulation.GetGenesisState(simState)
poolsGenesis := poolssimulation.GetGenesisState(simState)
operatorsGenesis := operatorssimulation.GetGenesisState(simState)

rewardsPlans := RandomRewardsPlans(
simState.Rand,
poolsGenesis.Pools,
operatorsGenesis.Operators,
servicesGenesis.Services,
[]string{simState.BondDenom},
)
nextRewardsPlan := uint64(1)
for _, plan := range rewardsPlans {
if plan.ID >= nextRewardsPlan {
nextRewardsPlan = plan.ID + 1
}
}

genesis := types.NewGenesisState(
RandomParams(simState.Rand, []string{simState.BondDenom}),
nextRewardsPlan,
rewardsPlans,
nil,
RandomDelegatorWithdrawInfos(simState.Rand, simState.Accounts),
// Empty delegation type records since we need to perform side effects on
// other modules to have valid delegations
types.NewDelegationTypeRecords(nil, nil, nil, nil),
types.NewDelegationTypeRecords(nil, nil, nil, nil),
types.NewDelegationTypeRecords(nil, nil, nil, nil),
RandomOperatorAccumulatedCommissionRecords(simState.Rand, operatorsGenesis.Operators, []string{simState.BondDenom}),
RandomPoolServiceTotalDelegatorShares(simState.Rand, poolsGenesis, servicesGenesis, []string{simState.BondDenom}),
)
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesis)
}
Loading
Loading