From 4c86bb7572e3c76c9e22c2a70e559ce263011870 Mon Sep 17 00:00:00 2001 From: John Letey Date: Sun, 19 Mar 2023 17:45:34 +0100 Subject: [PATCH] feat: backport KYVE changes to `v0.47.x` --- simapp/app.go | 4 +- x/gov/keeper/common_test.go | 2 +- x/gov/keeper/keeper.go | 22 ++++---- x/gov/keeper/tally.go | 47 ++++++++++++++++- x/gov/module.go | 1 + x/gov/types/expected_keepers.go | 8 +++ x/mint/keeper/genesis_test.go | 2 +- x/mint/keeper/grpc_query_test.go | 1 + x/mint/keeper/keeper.go | 38 +++++++++----- x/mint/keeper/keeper_test.go | 5 +- x/mint/module.go | 1 + x/mint/testutil/expected_keepers_mocks.go | 63 ++++++++++++++++++----- x/mint/types/expected_keepers.go | 7 ++- 13 files changed, 159 insertions(+), 42 deletions(-) diff --git a/simapp/app.go b/simapp/app.go index 8f36bc694dc3..f27373d2e388 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -312,7 +312,7 @@ func NewSimApp( app.StakingKeeper = stakingkeeper.NewKeeper( appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, app.BankKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - app.MintKeeper = mintkeeper.NewKeeper(appCodec, keys[minttypes.StoreKey], app.StakingKeeper, app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + app.MintKeeper = mintkeeper.NewKeeper(appCodec, keys[minttypes.StoreKey], app.StakingKeeper, nil, app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String()) app.DistrKeeper = distrkeeper.NewKeeper(appCodec, keys[distrtypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String()) @@ -365,7 +365,7 @@ func NewSimApp( */ govKeeper := govkeeper.NewKeeper( appCodec, keys[govtypes.StoreKey], app.AccountKeeper, app.BankKeeper, - app.StakingKeeper, app.MsgServiceRouter(), govConfig, authtypes.NewModuleAddress(govtypes.ModuleName).String(), + app.StakingKeeper, nil, app.MsgServiceRouter(), govConfig, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) // Set legacy router for backwards compatibility with gov v1beta1 diff --git a/x/gov/keeper/common_test.go b/x/gov/keeper/common_test.go index cd3aa29fccf3..dd3645e1327a 100644 --- a/x/gov/keeper/common_test.go +++ b/x/gov/keeper/common_test.go @@ -81,7 +81,7 @@ func setupGovKeeper(t *testing.T) ( stakingKeeper.EXPECT().TotalBondedTokens(gomock.Any()).Return(math.NewInt(10000000)).AnyTimes() // Gov keeper initializations - govKeeper := keeper.NewKeeper(encCfg.Codec, key, acctKeeper, bankKeeper, stakingKeeper, msr, types.DefaultConfig(), govAcct.String()) + govKeeper := keeper.NewKeeper(encCfg.Codec, key, acctKeeper, bankKeeper, stakingKeeper, nil, msr, types.DefaultConfig(), govAcct.String()) govKeeper.SetProposalID(ctx, 1) govRouter := v1beta1.NewRouter() // Also register legacy gov handlers to test them too. govRouter.AddRoute(types.RouterKey, v1beta1.ProposalHandler) diff --git a/x/gov/keeper/keeper.go b/x/gov/keeper/keeper.go index ecd8c3dea3ab..4331927a6dda 100644 --- a/x/gov/keeper/keeper.go +++ b/x/gov/keeper/keeper.go @@ -24,6 +24,9 @@ type Keeper struct { // The reference to the DelegationSet and ValidatorSet to get information about validators and delegators sk types.StakingKeeper + // The reference to the Protocol DelegationSet and ValidatorSet to get information about KYVE protocol validators and delegators + protocolStakingKeeper types.ProtocolStakingKeeper + // GovHooks hooks types.GovHooks @@ -60,7 +63,7 @@ func (k Keeper) GetAuthority() string { // CONTRACT: the parameter Subspace must have the param key table already initialized func NewKeeper( cdc codec.BinaryCodec, key storetypes.StoreKey, authKeeper types.AccountKeeper, - bankKeeper types.BankKeeper, sk types.StakingKeeper, + bankKeeper types.BankKeeper, sk types.StakingKeeper, psk types.ProtocolStakingKeeper, router *baseapp.MsgServiceRouter, config types.Config, authority string, ) *Keeper { // ensure governance module account is set @@ -78,14 +81,15 @@ func NewKeeper( } return &Keeper{ - storeKey: key, - authKeeper: authKeeper, - bankKeeper: bankKeeper, - sk: sk, - cdc: cdc, - router: router, - config: config, - authority: authority, + storeKey: key, + authKeeper: authKeeper, + bankKeeper: bankKeeper, + sk: sk, + protocolStakingKeeper: psk, + cdc: cdc, + router: router, + config: config, + authority: authority, } } diff --git a/x/gov/keeper/tally.go b/x/gov/keeper/tally.go index 863c64f51443..5038fd3c04b7 100644 --- a/x/gov/keeper/tally.go +++ b/x/gov/keeper/tally.go @@ -9,6 +9,8 @@ import ( // TODO: Break into several smaller functions for clarity +// NOTE: We have to check if the KYVE Protocol staking keeper is defined to ensure minimal changes. + // Tally iterates over the votes and updates the tally of a proposal based on the voting power of the // voters func (keeper Keeper) Tally(ctx sdk.Context, proposal v1.Proposal) (passes bool, burnDeposits bool, tallyResults v1.TallyResult) { @@ -34,6 +36,18 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal v1.Proposal) (passes bool, return false }) + // Fetch and insert all KYVE Protocol validators into list of current validators. + // NOTE: The key used is a normal "kyve1blah" address. + if keeper.protocolStakingKeeper != nil { + for _, rawVal := range keeper.protocolStakingKeeper.GetActiveValidators(ctx) { + // NOTE: We have to typecast to avoid creating import cycles when defining the function interfaces. + if val, ok := rawVal.(v1.ValidatorGovInfo); ok { + address := sdk.AccAddress(val.Address).String() + currValidators[address] = val + } + } + } + keeper.IterateVotes(ctx, proposal.Id, func(vote v1.Vote) bool { // if validator, just record it in the map voter := sdk.MustAccAddressFromBech32(vote.Voter) @@ -43,6 +57,11 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal v1.Proposal) (passes bool, val.Vote = vote.Options currValidators[valAddrStr] = val } + // Check if the voter is a KYVE Protocol validator. + if val, ok := currValidators[voter.String()]; ok { + val.Vote = vote.Options + currValidators[voter.String()] = val + } // iterate over all delegations from voter, deduct from any delegated-to validators keeper.sk.IterateDelegations(ctx, voter, func(index int64, delegation stakingtypes.DelegationI) (stop bool) { @@ -68,6 +87,23 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal v1.Proposal) (passes bool, return false }) + if keeper.protocolStakingKeeper != nil { + validators, amounts := keeper.protocolStakingKeeper.GetDelegations(ctx, voter.String()) + for idx, address := range validators { + if val, ok := currValidators[address]; ok { + val.DelegatorDeductions = val.DelegatorDeductions.Add(amounts[idx]) + currValidators[address] = val + + for _, option := range vote.Options { + weight, _ := math.LegacyNewDecFromStr(option.Weight) + subPower := amounts[idx].Mul(weight) + results[option.Option] = results[option.Option].Add(subPower) + } + totalVotingPower = totalVotingPower.Add(amounts[idx]) + } + } + } + keeper.deleteVote(ctx, vote.ProposalId, voter) return false }) @@ -92,14 +128,21 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal v1.Proposal) (passes bool, params := keeper.GetParams(ctx) tallyResults = v1.NewTallyResultFromMap(results) + totalBondedTokens := keeper.sk.TotalBondedTokens(ctx) + if keeper.protocolStakingKeeper != nil { + totalBondedTokens = totalBondedTokens.Add( + keeper.protocolStakingKeeper.TotalBondedTokens(ctx), + ) + } + // TODO: Upgrade the spec to cover all of these cases & remove pseudocode. // If there is no staked coins, the proposal fails - if keeper.sk.TotalBondedTokens(ctx).IsZero() { + if totalBondedTokens.IsZero() { return false, false, tallyResults } // If there is not enough quorum of votes, the proposal fails - percentVoting := totalVotingPower.Quo(sdk.NewDecFromInt(keeper.sk.TotalBondedTokens(ctx))) + percentVoting := totalVotingPower.Quo(math.LegacyNewDecFromInt(totalBondedTokens)) quorum, _ := sdk.NewDecFromStr(params.Quorum) if percentVoting.LT(quorum) { return false, params.BurnVoteQuorum, tallyResults diff --git a/x/gov/module.go b/x/gov/module.go index e12897743c52..4d17e24d61a3 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -205,6 +205,7 @@ func ProvideModule(in GovInputs) GovOutputs { in.AccountKeeper, in.BankKeeper, in.StakingKeeper, + nil, in.MsgServiceRouter, kConfig, authority.String(), diff --git a/x/gov/types/expected_keepers.go b/x/gov/types/expected_keepers.go index 70f0e5fbe994..55cfe5dc52d1 100644 --- a/x/gov/types/expected_keepers.go +++ b/x/gov/types/expected_keepers.go @@ -27,6 +27,14 @@ type StakingKeeper interface { ) } +// ProtocolStakingKeeper expected KYVE protocol staking keeper (Protocol Validator and Delegator sets) (noalias) +type ProtocolStakingKeeper interface { + GetActiveValidators(sdk.Context) []interface{} + + TotalBondedTokens(sdk.Context) math.Int + GetDelegations(sdk.Context, string) ([]string, []math.LegacyDec) +} + // AccountKeeper defines the expected account keeper (noalias) type AccountKeeper interface { GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI diff --git a/x/mint/keeper/genesis_test.go b/x/mint/keeper/genesis_test.go index 5a88558613c4..b8c20041d6f3 100644 --- a/x/mint/keeper/genesis_test.go +++ b/x/mint/keeper/genesis_test.go @@ -53,7 +53,7 @@ func (s *GenesisTestSuite) SetupTest() { accountKeeper.EXPECT().GetModuleAddress(minterAcc.Name).Return(minterAcc.GetAddress()) accountKeeper.EXPECT().GetModuleAccount(s.sdkCtx, minterAcc.Name).Return(minterAcc) - s.keeper = keeper.NewKeeper(s.cdc, key, stakingKeeper, accountKeeper, bankKeeper, "", "") + s.keeper = keeper.NewKeeper(s.cdc, key, stakingKeeper, nil, accountKeeper, bankKeeper, "", "") } func (s *GenesisTestSuite) TestImportExportGenesis() { diff --git a/x/mint/keeper/grpc_query_test.go b/x/mint/keeper/grpc_query_test.go index 34c7751f3099..7ad3349ff164 100644 --- a/x/mint/keeper/grpc_query_test.go +++ b/x/mint/keeper/grpc_query_test.go @@ -45,6 +45,7 @@ func (suite *MintTestSuite) SetupTest() { encCfg.Codec, key, stakingKeeper, + nil, accountKeeper, bankKeeper, authtypes.FeeCollectorName, diff --git a/x/mint/keeper/keeper.go b/x/mint/keeper/keeper.go index 693dbd1da4b8..ba4632c1691f 100644 --- a/x/mint/keeper/keeper.go +++ b/x/mint/keeper/keeper.go @@ -14,11 +14,12 @@ import ( // Keeper of the mint store type Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - stakingKeeper types.StakingKeeper - bankKeeper types.BankKeeper - feeCollectorName string + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + stakingKeeper types.StakingKeeper + protocolStakingKeeper types.ProtocolStakingKeeper + bankKeeper types.BankKeeper + feeCollectorName string // the address capable of executing a MsgUpdateParams message. Typically, this // should be the x/gov module account. @@ -30,6 +31,7 @@ func NewKeeper( cdc codec.BinaryCodec, key storetypes.StoreKey, sk types.StakingKeeper, + psk types.ProtocolStakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, feeCollectorName string, @@ -41,12 +43,13 @@ func NewKeeper( } return Keeper{ - cdc: cdc, - storeKey: key, - stakingKeeper: sk, - bankKeeper: bk, - feeCollectorName: feeCollectorName, - authority: authority, + cdc: cdc, + storeKey: key, + stakingKeeper: sk, + protocolStakingKeeper: psk, + bankKeeper: bk, + feeCollectorName: feeCollectorName, + authority: authority, } } @@ -113,7 +116,18 @@ func (k Keeper) StakingTokenSupply(ctx sdk.Context) math.Int { // BondedRatio implements an alias call to the underlying staking keeper's // BondedRatio to be used in BeginBlocker. func (k Keeper) BondedRatio(ctx sdk.Context) math.LegacyDec { - return k.stakingKeeper.BondedRatio(ctx) + totalSupply := k.StakingTokenSupply(ctx) + if !totalSupply.IsPositive() { + return sdk.ZeroDec() + } + + bondedTokens := math.LegacyNewDecFromInt(k.stakingKeeper.TotalBondedTokens(ctx)) + protocolBondedTokens := math.LegacyZeroDec() + if k.protocolStakingKeeper != nil { + protocolBondedTokens = math.LegacyNewDecFromInt(k.protocolStakingKeeper.TotalBondedTokens(ctx)) + } + + return bondedTokens.Add(protocolBondedTokens).QuoInt(totalSupply) } // MintCoins implements an alias call to the underlying supply keeper's diff --git a/x/mint/keeper/keeper_test.go b/x/mint/keeper/keeper_test.go index 3f39387eede4..f6a2ce88d82f 100644 --- a/x/mint/keeper/keeper_test.go +++ b/x/mint/keeper/keeper_test.go @@ -6,6 +6,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/suite" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" @@ -49,6 +50,7 @@ func (s *IntegrationTestSuite) SetupTest() { encCfg.Codec, key, stakingKeeper, + nil, accountKeeper, bankKeeper, authtypes.FeeCollectorName, @@ -124,7 +126,8 @@ func (s *IntegrationTestSuite) TestAliasFunctions() { s.Require().Equal(s.mintKeeper.StakingTokenSupply(s.ctx), stakingTokenSupply) bondedRatio := sdk.NewDecWithPrec(15, 2) - s.stakingKeeper.EXPECT().BondedRatio(s.ctx).Return(bondedRatio) + s.stakingKeeper.EXPECT().StakingTokenSupply(s.ctx).Return(stakingTokenSupply) + s.stakingKeeper.EXPECT().TotalBondedTokens(s.ctx).Return(math.NewInt(15000000000)) s.Require().Equal(s.mintKeeper.BondedRatio(s.ctx), bondedRatio) coins := sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000000))) diff --git a/x/mint/module.go b/x/mint/module.go index 0b8fd81d1034..6e7b00fa8398 100644 --- a/x/mint/module.go +++ b/x/mint/module.go @@ -255,6 +255,7 @@ func ProvideModule(in MintInputs) MintOutputs { in.Cdc, in.Key, in.StakingKeeper, + nil, in.AccountKeeper, in.BankKeeper, feeCollectorName, diff --git a/x/mint/testutil/expected_keepers_mocks.go b/x/mint/testutil/expected_keepers_mocks.go index c174f75d97cb..eab888a06fe8 100644 --- a/x/mint/testutil/expected_keepers_mocks.go +++ b/x/mint/testutil/expected_keepers_mocks.go @@ -36,32 +36,69 @@ func (m *MockStakingKeeper) EXPECT() *MockStakingKeeperMockRecorder { return m.recorder } -// BondedRatio mocks base method. -func (m *MockStakingKeeper) BondedRatio(ctx types.Context) types.Dec { +// StakingTokenSupply mocks base method. +func (m *MockStakingKeeper) StakingTokenSupply(ctx types.Context) math.Int { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BondedRatio", ctx) - ret0, _ := ret[0].(types.Dec) + ret := m.ctrl.Call(m, "StakingTokenSupply", ctx) + ret0, _ := ret[0].(math.Int) return ret0 } -// BondedRatio indicates an expected call of BondedRatio. -func (mr *MockStakingKeeperMockRecorder) BondedRatio(ctx interface{}) *gomock.Call { +// 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, "BondedRatio", reflect.TypeOf((*MockStakingKeeper)(nil).BondedRatio), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StakingTokenSupply", reflect.TypeOf((*MockStakingKeeper)(nil).StakingTokenSupply), ctx) } -// StakingTokenSupply mocks base method. -func (m *MockStakingKeeper) StakingTokenSupply(ctx types.Context) math.Int { +// TotalBondedTokens mocks base method. +func (m *MockStakingKeeper) TotalBondedTokens(ctx types.Context) math.Int { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StakingTokenSupply", ctx) + ret := m.ctrl.Call(m, "TotalBondedTokens", ctx) ret0, _ := ret[0].(math.Int) return ret0 } -// StakingTokenSupply indicates an expected call of StakingTokenSupply. -func (mr *MockStakingKeeperMockRecorder) StakingTokenSupply(ctx interface{}) *gomock.Call { +// TotalBondedTokens indicates an expected call of TotalBondedTokens. +func (mr *MockStakingKeeperMockRecorder) TotalBondedTokens(ctx interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StakingTokenSupply", reflect.TypeOf((*MockStakingKeeper)(nil).StakingTokenSupply), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalBondedTokens", reflect.TypeOf((*MockStakingKeeper)(nil).TotalBondedTokens), ctx) +} + +// MockProtocolStakingKeeper is a mock of ProtocolStakingKeeper interface. +type MockProtocolStakingKeeper struct { + ctrl *gomock.Controller + recorder *MockProtocolStakingKeeperMockRecorder +} + +// MockProtocolStakingKeeperMockRecorder is the mock recorder for MockProtocolStakingKeeper. +type MockProtocolStakingKeeperMockRecorder struct { + mock *MockProtocolStakingKeeper +} + +// NewMockProtocolStakingKeeper creates a new mock instance. +func NewMockProtocolStakingKeeper(ctrl *gomock.Controller) *MockProtocolStakingKeeper { + mock := &MockProtocolStakingKeeper{ctrl: ctrl} + mock.recorder = &MockProtocolStakingKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockProtocolStakingKeeper) EXPECT() *MockProtocolStakingKeeperMockRecorder { + return m.recorder +} + +// TotalBondedTokens mocks base method. +func (m *MockProtocolStakingKeeper) TotalBondedTokens(ctx types.Context) math.Int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TotalBondedTokens", ctx) + ret0, _ := ret[0].(math.Int) + return ret0 +} + +// TotalBondedTokens indicates an expected call of TotalBondedTokens. +func (mr *MockProtocolStakingKeeperMockRecorder) TotalBondedTokens(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalBondedTokens", reflect.TypeOf((*MockProtocolStakingKeeper)(nil).TotalBondedTokens), ctx) } // MockAccountKeeper is a mock of AccountKeeper interface. diff --git a/x/mint/types/expected_keepers.go b/x/mint/types/expected_keepers.go index 68fb5765bf6d..4a4ce2196c52 100644 --- a/x/mint/types/expected_keepers.go +++ b/x/mint/types/expected_keepers.go @@ -9,7 +9,12 @@ import ( // StakingKeeper defines the expected staking keeper type StakingKeeper interface { StakingTokenSupply(ctx sdk.Context) math.Int - BondedRatio(ctx sdk.Context) sdk.Dec + TotalBondedTokens(ctx sdk.Context) math.Int +} + +// ProtocolStakingKeeper defines the expected KYVE protocol staking keeper +type ProtocolStakingKeeper interface { + TotalBondedTokens(ctx sdk.Context) math.Int } // AccountKeeper defines the contract required for account APIs.