Skip to content

Commit

Permalink
Add tests, hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
Eric-Warehime committed Jan 16, 2025
1 parent 4648833 commit 7bfb79d
Show file tree
Hide file tree
Showing 9 changed files with 1,638 additions and 32 deletions.
21 changes: 11 additions & 10 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,16 +276,6 @@ func NewAppKeeper(
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)

// register the staking hooks
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
appKeepers.StakingKeeper.SetHooks(
stakingtypes.NewMultiStakingHooks(
appKeepers.DistrKeeper.Hooks(),
appKeepers.SlashingKeeper.Hooks(),
appKeepers.ProviderKeeper.Hooks(),
),
)

appKeepers.LsmKeeper = lsmkeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(appKeepers.keys[lsmtypes.StoreKey]),
Expand All @@ -296,6 +286,17 @@ func NewAppKeeper(
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)

// register the staking hooks
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
appKeepers.StakingKeeper.SetHooks(
stakingtypes.NewMultiStakingHooks(
appKeepers.DistrKeeper.Hooks(),
appKeepers.SlashingKeeper.Hooks(),
appKeepers.ProviderKeeper.Hooks(),
appKeepers.LsmKeeper.Hooks(),
),
)

appKeepers.FeeMarketKeeper = feemarketkeeper.NewKeeper(
appCodec,
appKeepers.keys[feemarkettypes.StoreKey],
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/e2e_lsm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (s *IntegrationTestSuite) testLSM() {

s.Require().Equal(lsmParams.Params.GlobalLiquidStakingCap, math.LegacyNewDecWithPrec(25, 2))
s.Require().Equal(lsmParams.Params.ValidatorLiquidStakingCap, math.LegacyNewDecWithPrec(50, 2))
s.Require().Equal(lsmParams.Params.ValidatorBondFactor, math.LegacyNewDec(250))
s.Require().Equal(lsmParams.Params.ValidatorBondFactor, math.LegacyNewDec(-1))

return true
},
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/e2e_setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ func (s *IntegrationTestSuite) writeLiquidStakingParamsUpdateProposal(c *chain)
}`
propMsgBody := fmt.Sprintf(template,
govAuthority,
math.LegacyNewDec(250), // validator bond factor
math.LegacyNewDec(-1), // validator bond factor
math.LegacyNewDecWithPrec(25, 2), // 25 global_liquid_staking_cap
math.LegacyNewDecWithPrec(50, 2), // 50 validator_liquid_staking_cap
)
Expand Down
1,301 changes: 1,301 additions & 0 deletions tests/integration/lsm_test.go

Large diffs are not rendered by default.

176 changes: 176 additions & 0 deletions tests/integration/test_common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package integration

import (
"testing"

"github.com/stretchr/testify/require"

abci "github.com/cometbft/cometbft/abci/types"
cmtprototypes "github.com/cometbft/cometbft/proto/tendermint/types"

"cosmossdk.io/core/appmodule"
"cosmossdk.io/log"
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"

"github.com/cosmos/cosmos-sdk/codec"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil/integration"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/x/auth"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
"github.com/cosmos/cosmos-sdk/x/bank"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/distribution"
distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

"github.com/cosmos/gaia/v22/x/lsm"
lsmkeeper "github.com/cosmos/gaia/v22/x/lsm/keeper"
lsmtypes "github.com/cosmos/gaia/v22/x/lsm/types"
)

type fixture struct {
app *integration.App

sdkCtx sdk.Context
cdc codec.Codec
keys map[string]*storetypes.KVStoreKey

accountKeeper authkeeper.AccountKeeper
bankKeeper bankkeeper.Keeper
distributionKeeper distributionkeeper.Keeper
stakingKeeper *stakingkeeper.Keeper
lsmKeeper *lsmkeeper.Keeper
}

func initFixture(tb testing.TB) *fixture {
tb.Helper()
keys := storetypes.NewKVStoreKeys(
authtypes.StoreKey, banktypes.StoreKey, distributiontypes.StoreKey, stakingtypes.StoreKey, lsmtypes.StoreKey,
)
cdc := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, staking.AppModuleBasic{}, vesting.AppModuleBasic{}).Codec

logger := log.NewTestLogger(tb)
cms := integration.CreateMultiStore(keys, logger)

newCtx := sdk.NewContext(cms, cmtprototypes.Header{}, true, logger)

authority := authtypes.NewModuleAddress("gov")

maccPerms := map[string][]string{
distributiontypes.ModuleName: {authtypes.Minter},
minttypes.ModuleName: {authtypes.Minter},
stakingtypes.ModuleName: {authtypes.Minter},
stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking},
stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
}

accountKeeper := authkeeper.NewAccountKeeper(
cdc,
runtime.NewKVStoreService(keys[authtypes.StoreKey]),
authtypes.ProtoBaseAccount,
maccPerms,
addresscodec.NewBech32Codec(sdk.Bech32MainPrefix),

Check warning

Code scanning / CodeQL

Directly using the bech32 constants Warning test

Directly using the bech32 constants instead of the configuration values
sdk.Bech32MainPrefix,

Check warning

Code scanning / CodeQL

Directly using the bech32 constants Warning test

Directly using the bech32 constants instead of the configuration values
authority.String(),
)

blockedAddresses := map[string]bool{
accountKeeper.GetAuthority(): false,
}
bankKeeper := bankkeeper.NewBaseKeeper(
cdc,
runtime.NewKVStoreService(keys[banktypes.StoreKey]),
accountKeeper,
blockedAddresses,
authority.String(),
log.NewNopLogger(),
)

stakingKeeper := stakingkeeper.NewKeeper(cdc, runtime.NewKVStoreService(keys[stakingtypes.StoreKey]),
accountKeeper, bankKeeper, authority.String(), addresscodec.NewBech32Codec(sdk.Bech32PrefixValAddr), addresscodec.NewBech32Codec(sdk.Bech32PrefixConsAddr))

Check warning

Code scanning / CodeQL

Directly using the bech32 constants Warning test

Directly using the bech32 constants instead of the configuration values

Check warning

Code scanning / CodeQL

Directly using the bech32 constants Warning test

Directly using the bech32 constants instead of the configuration values
distributionKeeper := distributionkeeper.NewKeeper(cdc, runtime.NewKVStoreService(keys[distributiontypes.
StoreKey]), accountKeeper, bankKeeper, stakingKeeper, distributiontypes.ModuleName, authority.String())
lsmKeeper := lsmkeeper.NewKeeper(cdc, runtime.NewKVStoreService(keys[lsmtypes.StoreKey]), accountKeeper,
bankKeeper, stakingKeeper, distributionKeeper, authority.String())

authModule := auth.NewAppModule(cdc, accountKeeper, authsims.RandomGenesisAccounts, nil)
bankModule := bank.NewAppModule(cdc, bankKeeper, accountKeeper, nil)
stakingModule := staking.NewAppModule(cdc, stakingKeeper, accountKeeper, bankKeeper, nil)
distributionModule := distribution.NewAppModule(cdc, distributionKeeper, accountKeeper, bankKeeper,
stakingKeeper, nil)
lsmModule := lsm.NewAppModule(cdc, lsmKeeper, accountKeeper, bankKeeper, stakingKeeper)

integrationApp := integration.NewIntegrationApp(newCtx, logger, keys, cdc, map[string]appmodule.AppModule{
authtypes.ModuleName: authModule,
banktypes.ModuleName: bankModule,
distributiontypes.ModuleName: distributionModule,
stakingtypes.ModuleName: stakingModule,
lsmtypes.ModuleName: lsmModule,
})

sdkCtx := sdk.UnwrapSDKContext(integrationApp.Context())

stakingKeeper.SetHooks(stakingtypes.NewMultiStakingHooks(
lsmKeeper.Hooks(),
))

// Register staking MsgServer and QueryServer
stakingtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), stakingkeeper.NewMsgServerImpl(stakingKeeper))
stakingtypes.RegisterQueryServer(integrationApp.QueryHelper(), stakingkeeper.NewQuerier(stakingKeeper))

// set default staking params
require.NoError(tb, stakingKeeper.SetParams(sdkCtx, stakingtypes.DefaultParams()))

// Register lsm MsgServer and QueryServer
lsmtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), lsmkeeper.NewMsgServerImpl(lsmKeeper))
lsmtypes.RegisterQueryServer(integrationApp.QueryHelper(), lsmkeeper.NewQuerier(lsmKeeper))

// set default lsm params
require.NoError(tb, lsmKeeper.SetParams(sdkCtx, lsmtypes.DefaultParams()))

f := fixture{
app: integrationApp,
sdkCtx: sdkCtx,
cdc: cdc,
keys: keys,
accountKeeper: accountKeeper,
bankKeeper: bankKeeper,
distributionKeeper: distributionKeeper,
stakingKeeper: stakingKeeper,
lsmKeeper: lsmKeeper,
}

return &f
}

func delegateCoinsFromAccount(ctx sdk.Context, sk stakingkeeper.Keeper, addr sdk.AccAddress, amount math.Int,
val stakingtypes.ValidatorI,
) error {
_, err := sk.Delegate(ctx, addr, amount, stakingtypes.Unbonded, val.(stakingtypes.Validator), true)

return err
}

func applyValidatorSetUpdates(t *testing.T, ctx sdk.Context, k *stakingkeeper.Keeper,
expectedUpdatesLen int,
) []abci.ValidatorUpdate {
t.Helper()
updates, err := k.ApplyAndReturnValidatorSetUpdates(ctx)
require.NoError(t, err)
if expectedUpdatesLen >= 0 {
require.Equal(t, expectedUpdatesLen, len(updates), "%v", updates)
}
return updates
}
119 changes: 119 additions & 0 deletions x/lsm/keeper/hooks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package keeper

import (
"context"
"errors"

sdkmath "cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

"github.com/cosmos/gaia/v22/x/lsm/types"
)

// Wrapper struct
type Hooks struct {
k Keeper
}

var _ stakingtypes.StakingHooks = Hooks{}

// Create new lsm hooks
func (k Keeper) Hooks() Hooks {
return Hooks{k}
}

// initialize liquid validator record
func (h Hooks) AfterValidatorCreated(ctx context.Context, valAddr sdk.ValAddress) error {
val, err := h.k.stakingKeeper.Validator(ctx, valAddr)
if err != nil {
return err
}
lVal := types.NewLiquidValidator(val.GetOperator())
del, err := h.k.stakingKeeper.GetDelegation(ctx, sdk.AccAddress(val.GetOperator()), valAddr)
if err != nil && !errors.Is(err, stakingtypes.ErrNoDelegation) {
return err
} else if err == nil {
lVal.ValidatorBondShares = del.Shares
}
return h.k.SetLiquidValidator(ctx, lVal)
}

func (h Hooks) AfterValidatorRemoved(ctx context.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) error {
return h.k.RemoveLiquidValidator(ctx, valAddr)
}

func (h Hooks) BeforeDelegationCreated(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
return nil
}

func (h Hooks) BeforeDelegationSharesModified(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
return nil
}

func (h Hooks) AfterDelegationModified(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
if delAddr.Equals(sdk.AccAddress(valAddr)) {
del, err := h.k.stakingKeeper.GetDelegation(ctx, sdk.AccAddress(valAddr), valAddr)
if err != nil {
return err
}
lVal, err := h.k.GetLiquidValidator(ctx, valAddr)
if err != nil {
return err
}
lVal.ValidatorBondShares = del.Shares
return h.k.SetLiquidValidator(ctx, lVal)
}
return nil
}

func (h Hooks) BeforeValidatorSlashed(ctx context.Context, valAddr sdk.ValAddress, fraction sdkmath.LegacyDec) error {
validator, err := h.k.stakingKeeper.Validator(ctx, valAddr)
if err != nil {
return err
}
liquidVal, err := h.k.GetLiquidValidator(ctx, valAddr)
if err != nil {
return err
}
initialLiquidTokens := validator.TokensFromShares(liquidVal.LiquidShares).TruncateInt()
slashedLiquidTokens := fraction.Mul(sdkmath.LegacyNewDecFromInt(initialLiquidTokens))

decrease := slashedLiquidTokens.TruncateInt()
if err := h.k.DecreaseTotalLiquidStakedTokens(ctx, decrease); err != nil {
// This only error's if the total liquid staked tokens underflows
// which would indicate there's a corrupted state where the validator has
// liquid tokens that are not accounted for in the global total
panic(err)
}
return nil
}

func (h Hooks) BeforeValidatorModified(_ context.Context, _ sdk.ValAddress) error {
return nil
}

func (h Hooks) AfterValidatorBonded(_ context.Context, _ sdk.ConsAddress, _ sdk.ValAddress) error {
return nil
}

func (h Hooks) AfterValidatorBeginUnbonding(_ context.Context, _ sdk.ConsAddress, _ sdk.ValAddress) error {
return nil
}

func (h Hooks) BeforeDelegationRemoved(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
if delAddr.Equals(sdk.AccAddress(valAddr)) {
lVal, err := h.k.GetLiquidValidator(ctx, valAddr)
if err != nil {
return err
}
lVal.ValidatorBondShares = sdkmath.LegacyZeroDec()
return h.k.SetLiquidValidator(ctx, lVal)
}
return nil
}

func (h Hooks) AfterUnbondingInitiated(_ context.Context, _ uint64) error {
return nil
}
Loading

0 comments on commit 7bfb79d

Please sign in to comment.