diff --git a/app/provider/app.go b/app/provider/app.go index 37b26b491c..c96ab8d2e9 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -373,15 +373,6 @@ func New( authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), ) - app.MintKeeper = mintkeeper.NewKeeper( - appCodec, - runtime.NewKVStoreService(keys[minttypes.StoreKey]), - app.StakingKeeper, - app.AccountKeeper, - app.BankKeeper, - authtypes.FeeCollectorName, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), - ) app.DistrKeeper = distrkeeper.NewKeeper( appCodec, runtime.NewKVStoreService(keys[distrtypes.StoreKey]), @@ -456,19 +447,6 @@ func New( runtime.ProvideCometInfoService(), ) - govConfig := govtypes.DefaultConfig() - app.GovKeeper = govkeeper.NewKeeper( - appCodec, - runtime.NewKVStoreService(keys[govtypes.StoreKey]), - app.AccountKeeper, - app.BankKeeper, - app.StakingKeeper, - app.DistrKeeper, - app.MsgServiceRouter(), - govConfig, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), - ) - app.ProviderKeeper = ibcproviderkeeper.NewKeeper( appCodec, keys[providertypes.StoreKey], @@ -483,13 +461,41 @@ func New( app.AccountKeeper, app.DistrKeeper, app.BankKeeper, - *app.GovKeeper, + govkeeper.Keeper{}, // will be set after the GovKeeper is created authtypes.NewModuleAddress(govtypes.ModuleName).String(), authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), authtypes.FeeCollectorName, ) + govConfig := govtypes.DefaultConfig() + app.GovKeeper = govkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[govtypes.StoreKey]), + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, + app.DistrKeeper, + app.MsgServiceRouter(), + govConfig, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // set the GovKeeper in the ProviderKeeper + app.ProviderKeeper.SetGovKeeper(*app.GovKeeper) + + app.MintKeeper = mintkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[minttypes.StoreKey]), + // use the ProviderKeeper as StakingKeeper for mint + // because minting should be based on the consensus-active validators + app.ProviderKeeper, + app.AccountKeeper, + app.BankKeeper, + authtypes.FeeCollectorName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + // gov router must be set after the provider keeper is created // otherwise the provider keeper will not be able to handle proposals (will be nil) govRouter := govv1beta1.NewRouter() diff --git a/testutil/keeper/mocks.go b/testutil/keeper/mocks.go index 7424e8bc63..600f0596d9 100644 --- a/testutil/keeper/mocks.go +++ b/testutil/keeper/mocks.go @@ -9,6 +9,7 @@ import ( reflect "reflect" time "time" + address "cosmossdk.io/core/address" math "cosmossdk.io/math" types "cosmossdk.io/store/types" types0 "github.com/cometbft/cometbft/abci/types" @@ -62,6 +63,21 @@ func (mr *MockStakingKeeperMockRecorder) BondDenom(ctx interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BondDenom", reflect.TypeOf((*MockStakingKeeper)(nil).BondDenom), ctx) } +// BondedRatio mocks base method. +func (m *MockStakingKeeper) BondedRatio(ctx context.Context) (math.LegacyDec, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BondedRatio", ctx) + ret0, _ := ret[0].(math.LegacyDec) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BondedRatio indicates an expected call of BondedRatio. +func (mr *MockStakingKeeperMockRecorder) BondedRatio(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BondedRatio", reflect.TypeOf((*MockStakingKeeper)(nil).BondedRatio), ctx) +} + // Delegation mocks base method. func (m *MockStakingKeeper) Delegation(ctx context.Context, addr types1.AccAddress, valAddr types1.ValAddress) (types3.DelegationI, error) { m.ctrl.T.Helper() @@ -287,6 +303,34 @@ func (mr *MockStakingKeeperMockRecorder) IsValidatorJailed(ctx, addr interface{} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsValidatorJailed", reflect.TypeOf((*MockStakingKeeper)(nil).IsValidatorJailed), ctx, addr) } +// IterateBondedValidatorsByPower mocks base method. +func (m *MockStakingKeeper) IterateBondedValidatorsByPower(arg0 context.Context, arg1 func(int64, types3.ValidatorI) bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IterateBondedValidatorsByPower", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// IterateBondedValidatorsByPower indicates an expected call of IterateBondedValidatorsByPower. +func (mr *MockStakingKeeperMockRecorder) IterateBondedValidatorsByPower(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateBondedValidatorsByPower", reflect.TypeOf((*MockStakingKeeper)(nil).IterateBondedValidatorsByPower), arg0, arg1) +} + +// IterateDelegations mocks base method. +func (m *MockStakingKeeper) IterateDelegations(ctx context.Context, delegator types1.AccAddress, fn func(int64, types3.DelegationI) bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IterateDelegations", ctx, delegator, fn) + ret0, _ := ret[0].(error) + return ret0 +} + +// IterateDelegations indicates an expected call of IterateDelegations. +func (mr *MockStakingKeeperMockRecorder) IterateDelegations(ctx, delegator, fn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateDelegations", reflect.TypeOf((*MockStakingKeeper)(nil).IterateDelegations), ctx, delegator, fn) +} + // IterateLastValidatorPowers mocks base method. func (m *MockStakingKeeper) IterateLastValidatorPowers(ctx context.Context, cb func(types1.ValAddress, int64) bool) error { m.ctrl.T.Helper() @@ -447,6 +491,21 @@ func (mr *MockStakingKeeperMockRecorder) SlashWithInfractionReason(ctx, consAddr return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SlashWithInfractionReason", reflect.TypeOf((*MockStakingKeeper)(nil).SlashWithInfractionReason), ctx, consAddr, infractionHeight, power, slashFactor, infraction) } +// StakingTokenSupply mocks base method. +func (m *MockStakingKeeper) StakingTokenSupply(ctx context.Context) (math.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StakingTokenSupply", ctx) + ret0, _ := ret[0].(math.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StakingTokenSupply indicates an expected call of StakingTokenSupply. +func (mr *MockStakingKeeperMockRecorder) StakingTokenSupply(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StakingTokenSupply", reflect.TypeOf((*MockStakingKeeper)(nil).StakingTokenSupply), ctx) +} + // UnbondingCanComplete mocks base method. func (m *MockStakingKeeper) UnbondingCanComplete(ctx context.Context, id uint64) error { m.ctrl.T.Helper() @@ -505,6 +564,20 @@ func (mr *MockStakingKeeperMockRecorder) Validator(ctx, addr interface{}) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validator", reflect.TypeOf((*MockStakingKeeper)(nil).Validator), ctx, addr) } +// ValidatorAddressCodec mocks base method. +func (m *MockStakingKeeper) ValidatorAddressCodec() address.Codec { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidatorAddressCodec") + ret0, _ := ret[0].(address.Codec) + return ret0 +} + +// ValidatorAddressCodec indicates an expected call of ValidatorAddressCodec. +func (mr *MockStakingKeeperMockRecorder) ValidatorAddressCodec() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorAddressCodec", reflect.TypeOf((*MockStakingKeeper)(nil).ValidatorAddressCodec)) +} + // ValidatorByConsAddr mocks base method. func (m *MockStakingKeeper) ValidatorByConsAddr(ctx context.Context, consAddr types1.ConsAddress) (types3.ValidatorI, error) { m.ctrl.T.Helper() diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 1bbd724154..7a7591e26b 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -143,6 +143,10 @@ func (k Keeper) mustValidateFields() { // ccv.PanicIfZeroOrNil(k.govKeeper, "govKeeper") // 17 } +func (k *Keeper) SetGovKeeper(govKeeper govkeeper.Keeper) { + k.govKeeper = govKeeper +} + // Logger returns a module-specific logger. func (k Keeper) Logger(ctx context.Context) log.Logger { sdkCtx := sdk.UnwrapSDKContext(ctx) diff --git a/x/ccv/provider/keeper/staking_keeper_interface.go b/x/ccv/provider/keeper/staking_keeper_interface.go new file mode 100644 index 0000000000..a491853fea --- /dev/null +++ b/x/ccv/provider/keeper/staking_keeper_interface.go @@ -0,0 +1,81 @@ +package keeper + +import ( + "context" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// Iterates over the consensus-active validators by power. +// The same as IterateBondedValidatorsByPower in the StakingKeeper, +// but only returns the first MaxProviderConsensusValidators validators. +// This is used to implement the interface of the staking keeper to interface with +// modules that need to reference the consensus-active validators. +func (k Keeper) IterateBondedValidatorsByPower(ctx context.Context, fn func(index int64, validator stakingtypes.ValidatorI) (stop bool)) error { + maxProviderConsensusVals := k.GetMaxProviderConsensusValidators(sdk.UnwrapSDKContext(ctx)) + counter := int64(0) + return k.stakingKeeper.IterateBondedValidatorsByPower(ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) { + if counter >= maxProviderConsensusVals { + return true + } + counter++ + return fn(index, validator) + }) +} + +// Gets the amount of bonded tokens, which is equal +// to the amount of tokens of the consensus-active validators. +// The same as TotalBondedTokens, but only counts +// tokens of the first MaxProviderConsensusValidators validators. +// This is used to implement the interface of the staking keeper to interface with +// modules that need to reference the consensus-active validators. +func (k Keeper) TotalBondedTokens(ctx context.Context) (math.Int, error) { + // iterate through the bonded validators + totalBondedTokens := math.ZeroInt() + + k.IterateBondedValidatorsByPower(ctx, func(_ int64, validator stakingtypes.ValidatorI) (stop bool) { + tokens := validator.GetTokens() + totalBondedTokens = totalBondedTokens.Add(tokens) + return false + }) + + return totalBondedTokens, nil +} + +// The same as IterateDelegations in the StakingKeeper. +// Necessary to implement the interface of the staking keeper to interface with +// other modules. +func (k Keeper) IterateDelegations(ctx context.Context, delegator sdk.AccAddress, fn func(index int64, delegation stakingtypes.DelegationI) (stop bool)) error { + return k.stakingKeeper.IterateDelegations(ctx, delegator, fn) +} + +// The same as StakingTotalSupply in the StakingKeeper. +// Necessary to implement the interface of the staking keeper to interface with +// other modules. +func (k Keeper) StakingTokenSupply(ctx context.Context) (math.Int, error) { + return k.stakingKeeper.StakingTokenSupply(ctx) +} + +// Gets the ratio of tokens staked to validators active in the consensus +// to the total supply of tokens. +// Same as BondedRatio in the StakingKeeper, but only counts +// tokens of the first MaxProviderConsensusValidators bonded validators. +func (k Keeper) BondedRatio(ctx context.Context) (math.LegacyDec, error) { + totalSupply, err := k.StakingTokenSupply(ctx) + if err != nil { + return math.LegacyZeroDec(), err + } + + bondedTokens, err := k.TotalBondedTokens(ctx) + if err != nil { + return math.LegacyZeroDec(), err + } + + if !totalSupply.IsPositive() { + return math.LegacyZeroDec(), nil + } + + return math.LegacyNewDecFromInt(bondedTokens).QuoInt(totalSupply), nil +} diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index e57487df5f..280863daa6 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -4,6 +4,7 @@ import ( context "context" "time" + addresscodec "cosmossdk.io/core/address" capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" @@ -57,6 +58,16 @@ type StakingKeeper interface { GetRedelegationByUnbondingID(ctx context.Context, id uint64) (stakingtypes.Redelegation, error) GetValidatorByUnbondingID(ctx context.Context, id uint64) (stakingtypes.Validator, error) GetBondedValidatorsByPower(ctx context.Context) ([]stakingtypes.Validator, error) + ValidatorAddressCodec() addresscodec.Codec + IterateDelegations( + ctx context.Context, delegator sdk.AccAddress, + fn func(index int64, delegation stakingtypes.DelegationI) (stop bool), + ) error + IterateBondedValidatorsByPower( + context.Context, func(index int64, validator stakingtypes.ValidatorI) (stop bool), + ) error + StakingTokenSupply(ctx context.Context) (math.Int, error) + BondedRatio(ctx context.Context) (math.LegacyDec, error) } // SlashingKeeper defines the contract expected to perform ccv slashing