chore(gov): decouple gov staking (#25615)
This commit is contained in:
parent
346c59f6e4
commit
ca3241e227
@ -46,6 +46,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* `x/circuit`
|
||||
* `x/crisis`
|
||||
* (crypto) [#24414](https://github.com/cosmos/cosmos-sdk/pull/24414) Remove sr25519 support, since it was removed in CometBFT v1.x (see: CometBFT [#3646](https://github.com/cometbft/cometbft/pull/3646)).
|
||||
* (x/gov) [#25615](https://github.com/cosmos/cosmos-sdk/pull/25615) Decouple `x/gov` from `x/staking` by making `CalculateVoteResultsAndVotingPowerFn` a required parameter to `keeper.NewKeeper` instead of `StakingKeeper`.
|
||||
`BondedTokens` has been renamed to `ValidatorPower` and `TotalBondedTokens` has been renamed to `TotalValidatorPower` to allow for multiple validator power representations.
|
||||
|
||||
### Features
|
||||
|
||||
|
||||
40
UPGRADING.md
40
UPGRADING.md
@ -6,4 +6,42 @@ Note, always read the **App Wiring Changes** section for more information on app
|
||||
|
||||
### TLDR
|
||||
|
||||
For a full list of changes, see the [Changelog](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/CHANGELOG.md).
|
||||
For a full list of changes, see the [Changelog](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/CHANGELOG.md).
|
||||
|
||||
## x/gov
|
||||
|
||||
### Keeper Initialization
|
||||
|
||||
The `x/gov` module has been decoupled from `x/staking`. The `keeper.NewKeeper` constructor now requires a `CalculateVoteResultsAndVotingPowerFn` parameter instead of a `StakingKeeper`.
|
||||
|
||||
**Before:**
|
||||
```go
|
||||
govKeeper := keeper.NewKeeper(
|
||||
cdc,
|
||||
storeService,
|
||||
authKeeper,
|
||||
bankKeeper,
|
||||
stakingKeeper, // StakingKeeper parameter
|
||||
distrKeeper,
|
||||
router,
|
||||
config,
|
||||
authority,
|
||||
)
|
||||
```
|
||||
|
||||
**After:**
|
||||
```go
|
||||
govKeeper := keeper.NewKeeper(
|
||||
cdc,
|
||||
storeService,
|
||||
authKeeper,
|
||||
bankKeeper,
|
||||
keeper.NewDefaultCalculateVoteResultsAndVotingPower(stakingKeeper), // Function parameter
|
||||
distrKeeper,
|
||||
router,
|
||||
config,
|
||||
authority,
|
||||
)
|
||||
```
|
||||
|
||||
For applications using depinject, the governance module now accepts an optional `CalculateVoteResultsAndVotingPowerFn`. If not provided, it will use the `StakingKeeper` (also optional) to create the default function.
|
||||
@ -420,12 +420,11 @@ func NewSimApp(
|
||||
runtime.NewKVStoreService(keys[govtypes.StoreKey]),
|
||||
app.AccountKeeper,
|
||||
app.BankKeeper,
|
||||
app.StakingKeeper,
|
||||
app.DistrKeeper,
|
||||
app.MsgServiceRouter(),
|
||||
govConfig,
|
||||
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
|
||||
// govkeeper.WithCustomCalculateVoteResultsAndVotingPowerFn(...), // Add if you want to use a custom vote calculation function.
|
||||
govkeeper.NewDefaultCalculateVoteResultsAndVotingPower(app.StakingKeeper),
|
||||
)
|
||||
|
||||
// Set legacy router for backwards compatibility with gov v1beta1
|
||||
@ -433,7 +432,7 @@ func NewSimApp(
|
||||
|
||||
app.GovKeeper = *govKeeper.SetHooks(
|
||||
govtypes.NewMultiGovHooks(
|
||||
// register the governance hooks
|
||||
// register the governance hooks
|
||||
),
|
||||
)
|
||||
|
||||
@ -458,7 +457,7 @@ func NewSimApp(
|
||||
|
||||
app.EpochsKeeper.SetHooks(
|
||||
epochstypes.NewMultiEpochHooks(
|
||||
// insert epoch hooks receivers here
|
||||
// insert epoch hooks receivers here
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@ -189,7 +189,7 @@ func TestHandleDoubleSign(t *testing.T) {
|
||||
)
|
||||
val, err := f.stakingKeeper.Validator(ctx, operatorAddr)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, selfDelegation, val.GetBondedTokens())
|
||||
assert.DeepEqual(t, selfDelegation, val.GetValidatorPower())
|
||||
|
||||
assert.NilError(t, f.slashingKeeper.AddPubkey(f.sdkCtx, valpubkey))
|
||||
|
||||
@ -282,7 +282,7 @@ func TestHandleDoubleSign_TooOld(t *testing.T) {
|
||||
)
|
||||
val, err := f.stakingKeeper.Validator(ctx, operatorAddr)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, amt, val.GetBondedTokens())
|
||||
assert.DeepEqual(t, amt, val.GetValidatorPower())
|
||||
|
||||
nci := NewCometInfo(abci.RequestFinalizeBlock{
|
||||
Misbehavior: []abci.Misbehavior{{
|
||||
|
||||
@ -106,16 +106,18 @@ func initFixture(tb testing.TB) *fixture {
|
||||
router := baseapp.NewMsgServiceRouter()
|
||||
router.SetInterfaceRegistry(cdc.InterfaceRegistry())
|
||||
|
||||
tallyFn := keeper.NewDefaultCalculateVoteResultsAndVotingPower(stakingKeeper)
|
||||
|
||||
govKeeper := keeper.NewKeeper(
|
||||
cdc,
|
||||
runtime.NewKVStoreService(keys[types.StoreKey]),
|
||||
accountKeeper,
|
||||
bankKeeper,
|
||||
stakingKeeper,
|
||||
distrKeeper,
|
||||
router,
|
||||
types.DefaultConfig(),
|
||||
authority.String(),
|
||||
tallyFn,
|
||||
)
|
||||
err := govKeeper.ProposalID.Set(newCtx, 1)
|
||||
assert.NilError(tb, err)
|
||||
|
||||
@ -253,7 +253,7 @@ func TestHandleNewValidator(t *testing.T) {
|
||||
|
||||
val, err := f.stakingKeeper.Validator(f.ctx, addr)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, amt, val.GetBondedTokens())
|
||||
assert.DeepEqual(t, amt, val.GetValidatorPower())
|
||||
|
||||
// Now a validator, for two blocks
|
||||
assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, valpubkey.Address(), 100, comet.BlockIDFlagCommit))
|
||||
|
||||
@ -38,14 +38,13 @@ type ModuleInputs struct {
|
||||
ModuleKey depinject.OwnModuleKey
|
||||
MsgServiceRouter baseapp.MessageRouter
|
||||
|
||||
AccountKeeper govtypes.AccountKeeper
|
||||
BankKeeper govtypes.BankKeeper
|
||||
StakingKeeper govtypes.StakingKeeper
|
||||
DistributionKeeper govtypes.DistributionKeeper
|
||||
AccountKeeper govtypes.AccountKeeper
|
||||
BankKeeper govtypes.BankKeeper
|
||||
DistributionKeeper govtypes.DistributionKeeper
|
||||
CalculateVoteResultsAndVotingPowerFn keeper.CalculateVoteResultsAndVotingPowerFn `optional:"true"`
|
||||
|
||||
// CustomCalculateVoteResultsAndVotingPowerFn is an optional input to set a custom CalculateVoteResultsAndVotingPowerFn.
|
||||
// If this function is not provided, the default function is used.
|
||||
CustomCalculateVoteResultsAndVotingPowerFn keeper.CalculateVoteResultsAndVotingPowerFn `optional:"true"`
|
||||
// StakingKeeper is required if CalculateVoteResultsAndVotingPowerFn is not provided
|
||||
StakingKeeper govtypes.StakingKeeper `optional:"true"`
|
||||
|
||||
// LegacySubspace is used solely for migration of x/params managed parameters
|
||||
LegacySubspace govtypes.ParamSubspace `optional:"true"`
|
||||
@ -71,9 +70,13 @@ func ProvideModule(in ModuleInputs) ModuleOutputs {
|
||||
authority = authtypes.NewModuleAddressOrBech32Address(in.Config.Authority)
|
||||
}
|
||||
|
||||
var opts []keeper.InitOption
|
||||
if in.CustomCalculateVoteResultsAndVotingPowerFn != nil {
|
||||
opts = append(opts, keeper.WithCustomCalculateVoteResultsAndVotingPowerFn(in.CustomCalculateVoteResultsAndVotingPowerFn))
|
||||
// If no custom tally function is provided, use the default with staking keeper
|
||||
tallyFn := in.CalculateVoteResultsAndVotingPowerFn
|
||||
if tallyFn == nil {
|
||||
if in.StakingKeeper == nil {
|
||||
panic("either CalculateVoteResultsAndVotingPowerFn or StakingKeeper must be provided")
|
||||
}
|
||||
tallyFn = keeper.NewDefaultCalculateVoteResultsAndVotingPower(in.StakingKeeper)
|
||||
}
|
||||
|
||||
k := keeper.NewKeeper(
|
||||
@ -81,12 +84,11 @@ func ProvideModule(in ModuleInputs) ModuleOutputs {
|
||||
in.StoreService,
|
||||
in.AccountKeeper,
|
||||
in.BankKeeper,
|
||||
in.StakingKeeper,
|
||||
in.DistributionKeeper,
|
||||
in.MsgServiceRouter,
|
||||
defaultConfig,
|
||||
authority.String(),
|
||||
opts...,
|
||||
tallyFn,
|
||||
)
|
||||
m := NewAppModule(in.Cdc, k, in.AccountKeeper, in.BankKeeper, in.LegacySubspace)
|
||||
hr := v1beta1.HandlerRoute{Handler: v1beta1.ProposalHandler, RouteKey: govtypes.RouterKey}
|
||||
|
||||
@ -95,12 +95,12 @@ func setupGovKeeper(t *testing.T) (
|
||||
stakingKeeper.EXPECT().BondDenom(ctx).Return("stake", nil).AnyTimes()
|
||||
stakingKeeper.EXPECT().IterateBondedValidatorsByPower(gomock.Any(), gomock.Any()).AnyTimes()
|
||||
stakingKeeper.EXPECT().IterateDelegations(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
stakingKeeper.EXPECT().TotalBondedTokens(gomock.Any()).Return(math.NewInt(10000000), nil).AnyTimes()
|
||||
stakingKeeper.EXPECT().TotalValidatorPower(gomock.Any()).Return(math.NewInt(10000000), nil).AnyTimes()
|
||||
distributionKeeper.EXPECT().FundCommunityPool(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
||||
|
||||
// Gov keeper initializations
|
||||
|
||||
govKeeper := keeper.NewKeeper(encCfg.Codec, storeService, acctKeeper, bankKeeper, stakingKeeper, distributionKeeper, msr, types.DefaultConfig(), govAcct.String())
|
||||
govKeeper := keeper.NewKeeper(encCfg.Codec, storeService, acctKeeper, bankKeeper, distributionKeeper, msr, types.DefaultConfig(), govAcct.String(), keeper.NewDefaultCalculateVoteResultsAndVotingPower(stakingKeeper))
|
||||
require.NoError(t, govKeeper.ProposalID.Set(ctx, 1))
|
||||
govRouter := v1beta1.NewRouter() // Also register legacy gov handlers to test them too.
|
||||
govRouter.AddRoute(types.RouterKey, v1beta1.ProposalHandler)
|
||||
|
||||
@ -23,9 +23,6 @@ type Keeper struct {
|
||||
bankKeeper types.BankKeeper
|
||||
distrKeeper types.DistributionKeeper
|
||||
|
||||
// The reference to the DelegationSet and ValidatorSet to get information about validators and delegators
|
||||
sk types.StakingKeeper
|
||||
|
||||
// GovHooks
|
||||
hooks types.GovHooks
|
||||
|
||||
@ -61,20 +58,6 @@ type Keeper struct {
|
||||
VotingPeriodProposals collections.Map[uint64, []byte] // TODO(tip): this could be a keyset or index.
|
||||
}
|
||||
|
||||
type InitOption func(*Keeper)
|
||||
|
||||
// WithCustomCalculateVoteResultsAndVotingPowerFn is an optional input to set a custom CalculateVoteResultsAndVotingPowerFn.
|
||||
// If this function is not provided, the default function is used.
|
||||
func WithCustomCalculateVoteResultsAndVotingPowerFn(calculateVoteResultsAndVotingPowerFn CalculateVoteResultsAndVotingPowerFn) InitOption {
|
||||
return func(k *Keeper) {
|
||||
if calculateVoteResultsAndVotingPowerFn == nil {
|
||||
panic("calculateVoteResultsAndVotingPowerFn cannot be nil")
|
||||
}
|
||||
|
||||
k.calculateVoteResultsAndVotingPowerFn = calculateVoteResultsAndVotingPowerFn
|
||||
}
|
||||
}
|
||||
|
||||
// GetAuthority returns the x/gov module's authority.
|
||||
func (k Keeper) GetAuthority() string {
|
||||
return k.authority
|
||||
@ -88,9 +71,15 @@ func (k Keeper) GetAuthority() string {
|
||||
//
|
||||
// CONTRACT: the parameter Subspace must have the param key table already initialized
|
||||
func NewKeeper(
|
||||
cdc codec.Codec, storeService corestoretypes.KVStoreService, authKeeper types.AccountKeeper,
|
||||
bankKeeper types.BankKeeper, sk types.StakingKeeper, distrKeeper types.DistributionKeeper,
|
||||
router baseapp.MessageRouter, config types.Config, authority string, initOptions ...InitOption,
|
||||
cdc codec.Codec,
|
||||
storeService corestoretypes.KVStoreService,
|
||||
authKeeper types.AccountKeeper,
|
||||
bankKeeper types.BankKeeper,
|
||||
distrKeeper types.DistributionKeeper,
|
||||
router baseapp.MessageRouter,
|
||||
config types.Config,
|
||||
authority string,
|
||||
calculateVoteResultsAndVotingPowerFn CalculateVoteResultsAndVotingPowerFn,
|
||||
) *Keeper {
|
||||
// ensure governance module account is set
|
||||
if addr := authKeeper.GetModuleAddress(types.ModuleName); addr == nil {
|
||||
@ -112,11 +101,10 @@ func NewKeeper(
|
||||
authKeeper: authKeeper,
|
||||
bankKeeper: bankKeeper,
|
||||
distrKeeper: distrKeeper,
|
||||
sk: sk,
|
||||
cdc: cdc,
|
||||
router: router,
|
||||
config: config,
|
||||
calculateVoteResultsAndVotingPowerFn: defaultCalculateVoteResultsAndVotingPower,
|
||||
calculateVoteResultsAndVotingPowerFn: calculateVoteResultsAndVotingPowerFn,
|
||||
authority: authority,
|
||||
Constitution: collections.NewItem(sb, types.ConstitutionKey, "constitution", collections.StringValue),
|
||||
Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[v1.Params](cdc)),
|
||||
@ -129,10 +117,6 @@ func NewKeeper(
|
||||
VotingPeriodProposals: collections.NewMap(sb, types.VotingPeriodProposalKeyPrefix, "voting_period_proposals", collections.Uint64Key, collections.BytesValue),
|
||||
}
|
||||
|
||||
for _, opt := range initOptions {
|
||||
opt(k)
|
||||
}
|
||||
|
||||
schema, err := sb.Build()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@ -8,148 +8,143 @@ import (
|
||||
"cosmossdk.io/math"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
// CalculateVoteResultsAndVotingPowerFn is a function signature for calculating vote results and voting power
|
||||
// It can be overridden to customize the voting power calculation for proposals
|
||||
// It gets the proposal tallied and the validators governance infos (bonded tokens, voting power, etc.)
|
||||
// It must return the total voting power and the results of the vote
|
||||
// It must fetch validators, calculate total validator power, and return vote results
|
||||
// totalVoterPower is the sum of voting power that actually voted
|
||||
// totalValPower is the sum of all active validator power (for quorum calculation)
|
||||
type CalculateVoteResultsAndVotingPowerFn func(
|
||||
ctx context.Context,
|
||||
k Keeper,
|
||||
proposal v1.Proposal,
|
||||
validators map[string]v1.ValidatorGovInfo,
|
||||
) (totalVoterPower math.LegacyDec, results map[v1.VoteOption]math.LegacyDec, err error)
|
||||
) (totalVoterPower math.LegacyDec, totalValPower math.Int, results map[v1.VoteOption]math.LegacyDec, err error)
|
||||
|
||||
func defaultCalculateVoteResultsAndVotingPower(
|
||||
ctx context.Context,
|
||||
k Keeper,
|
||||
proposal v1.Proposal,
|
||||
validators map[string]v1.ValidatorGovInfo,
|
||||
) (totalVoterPower math.LegacyDec, results map[v1.VoteOption]math.LegacyDec, err error) {
|
||||
totalVotingPower := math.LegacyZeroDec()
|
||||
// NewDefaultCalculateVoteResultsAndVotingPower returns a CalculateVoteResultsAndVotingPowerFn
|
||||
// that uses the provided StakingKeeper to calculate voting power
|
||||
func NewDefaultCalculateVoteResultsAndVotingPower(sk types.StakingKeeper) CalculateVoteResultsAndVotingPowerFn {
|
||||
return func(ctx context.Context, k Keeper, proposal v1.Proposal) (totalVoterPower math.LegacyDec, totalValPower math.Int, results map[v1.VoteOption]math.LegacyDec, err error) {
|
||||
// Fetch all bonded validators and calculate total validator power
|
||||
validators := make(map[string]v1.ValidatorGovInfo)
|
||||
totalValPower = math.ZeroInt()
|
||||
|
||||
results = make(map[v1.VoteOption]math.LegacyDec)
|
||||
results[v1.OptionYes] = math.LegacyZeroDec()
|
||||
results[v1.OptionAbstain] = math.LegacyZeroDec()
|
||||
results[v1.OptionNo] = math.LegacyZeroDec()
|
||||
results[v1.OptionNoWithVeto] = math.LegacyZeroDec()
|
||||
|
||||
rng := collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposal.Id)
|
||||
votesToRemove := []collections.Pair[uint64, sdk.AccAddress]{}
|
||||
err = k.Votes.Walk(ctx, rng, func(key collections.Pair[uint64, sdk.AccAddress], vote v1.Vote) (bool, error) {
|
||||
// if validator, just record it in the map
|
||||
voter, err := k.authKeeper.AddressCodec().StringToBytes(vote.Voter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
if err := sk.IterateBondedValidatorsByPower(ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) {
|
||||
valBz, err := sk.ValidatorAddressCodec().StringToBytes(validator.GetOperator())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
validatorPower := validator.GetValidatorPower()
|
||||
validators[validator.GetOperator()] = v1.NewValidatorGovInfo(
|
||||
valBz,
|
||||
validatorPower,
|
||||
validator.GetDelegatorShares(),
|
||||
math.LegacyZeroDec(),
|
||||
v1.WeightedVoteOptions{},
|
||||
)
|
||||
// Sum up total validator power from active (bonded) validators only
|
||||
totalValPower = totalValPower.Add(validatorPower)
|
||||
return false
|
||||
}); err != nil {
|
||||
return math.LegacyZeroDec(), math.ZeroInt(), nil, err
|
||||
}
|
||||
|
||||
valAddrStr, err := k.sk.ValidatorAddressCodec().BytesToString(voter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if val, ok := validators[valAddrStr]; ok {
|
||||
val.Vote = vote.Options
|
||||
validators[valAddrStr] = val
|
||||
}
|
||||
totalVotingPower := math.LegacyZeroDec()
|
||||
|
||||
// iterate over all delegations from voter, deduct from any delegated-to validators
|
||||
err = k.sk.IterateDelegations(ctx, voter, func(index int64, delegation stakingtypes.DelegationI) (stop bool) {
|
||||
valAddrStr := delegation.GetValidatorAddr()
|
||||
results = make(map[v1.VoteOption]math.LegacyDec)
|
||||
results[v1.OptionYes] = math.LegacyZeroDec()
|
||||
results[v1.OptionAbstain] = math.LegacyZeroDec()
|
||||
results[v1.OptionNo] = math.LegacyZeroDec()
|
||||
results[v1.OptionNoWithVeto] = math.LegacyZeroDec()
|
||||
|
||||
if val, ok := validators[valAddrStr]; ok {
|
||||
// There is no need to handle the special case that validator address equal to voter address.
|
||||
// Because voter's voting power will tally again even if there will be deduction of voter's voting power from validator.
|
||||
val.DelegatorDeductions = val.DelegatorDeductions.Add(delegation.GetShares())
|
||||
validators[valAddrStr] = val
|
||||
|
||||
// delegation shares * bonded / total shares
|
||||
votingPower := delegation.GetShares().MulInt(val.BondedTokens).Quo(val.DelegatorShares)
|
||||
|
||||
for _, option := range vote.Options {
|
||||
weight, _ := math.LegacyNewDecFromStr(option.Weight)
|
||||
subPower := votingPower.Mul(weight)
|
||||
results[option.Option] = results[option.Option].Add(subPower)
|
||||
}
|
||||
totalVotingPower = totalVotingPower.Add(votingPower)
|
||||
rng := collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposal.Id)
|
||||
votesToRemove := []collections.Pair[uint64, sdk.AccAddress]{}
|
||||
err = k.Votes.Walk(ctx, rng, func(key collections.Pair[uint64, sdk.AccAddress], vote v1.Vote) (bool, error) {
|
||||
// if validator, just record it in the map
|
||||
voter, err := k.authKeeper.AddressCodec().StringToBytes(vote.Voter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return false
|
||||
valAddrStr, err := sk.ValidatorAddressCodec().BytesToString(voter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if val, ok := validators[valAddrStr]; ok {
|
||||
val.Vote = vote.Options
|
||||
validators[valAddrStr] = val
|
||||
}
|
||||
|
||||
// iterate over all delegations from voter, deduct from any delegated-to validators
|
||||
err = sk.IterateDelegations(ctx, voter, func(index int64, delegation stakingtypes.DelegationI) (stop bool) {
|
||||
valAddrStr := delegation.GetValidatorAddr()
|
||||
|
||||
if val, ok := validators[valAddrStr]; ok {
|
||||
// There is no need to handle the special case that validator address equal to voter address.
|
||||
// Because voter's voting power will tally again even if there will be deduction of voter's voting power from validator.
|
||||
val.DelegatorDeductions = val.DelegatorDeductions.Add(delegation.GetShares())
|
||||
validators[valAddrStr] = val
|
||||
|
||||
// delegation shares * bonded / total shares
|
||||
votingPower := delegation.GetShares().MulInt(val.BondedTokens).Quo(val.DelegatorShares)
|
||||
|
||||
for _, option := range vote.Options {
|
||||
weight, _ := math.LegacyNewDecFromStr(option.Weight)
|
||||
subPower := votingPower.Mul(weight)
|
||||
results[option.Option] = results[option.Option].Add(subPower)
|
||||
}
|
||||
totalVotingPower = totalVotingPower.Add(votingPower)
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
votesToRemove = append(votesToRemove, key)
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
return math.LegacyZeroDec(), math.ZeroInt(), nil, fmt.Errorf("error while iterating delegations: %w", err)
|
||||
}
|
||||
|
||||
votesToRemove = append(votesToRemove, key)
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return math.LegacyZeroDec(), nil, fmt.Errorf("error while iterating delegations: %w", err)
|
||||
// remove all votes from store
|
||||
for _, key := range votesToRemove {
|
||||
if err := k.Votes.Remove(ctx, key); err != nil {
|
||||
return math.LegacyDec{}, math.ZeroInt(), nil, fmt.Errorf("error while removing vote (%d/%s): %w", key.K1(), key.K2(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over the validators again to tally their voting power
|
||||
for _, val := range validators {
|
||||
if len(val.Vote) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
sharesAfterDeductions := val.DelegatorShares.Sub(val.DelegatorDeductions)
|
||||
votingPower := sharesAfterDeductions.MulInt(val.BondedTokens).Quo(val.DelegatorShares)
|
||||
|
||||
for _, option := range val.Vote {
|
||||
weight, _ := math.LegacyNewDecFromStr(option.Weight)
|
||||
subPower := votingPower.Mul(weight)
|
||||
results[option.Option] = results[option.Option].Add(subPower)
|
||||
}
|
||||
totalVotingPower = totalVotingPower.Add(votingPower)
|
||||
}
|
||||
|
||||
return totalVotingPower, totalValPower, results, nil
|
||||
}
|
||||
|
||||
// remove all votes from store
|
||||
for _, key := range votesToRemove {
|
||||
if err := k.Votes.Remove(ctx, key); err != nil {
|
||||
return math.LegacyDec{}, nil, fmt.Errorf("error while removing vote (%d/%s): %w", key.K1(), key.K2(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over the validators again to tally their voting power
|
||||
for _, val := range validators {
|
||||
if len(val.Vote) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
sharesAfterDeductions := val.DelegatorShares.Sub(val.DelegatorDeductions)
|
||||
votingPower := sharesAfterDeductions.MulInt(val.BondedTokens).Quo(val.DelegatorShares)
|
||||
|
||||
for _, option := range val.Vote {
|
||||
weight, _ := math.LegacyNewDecFromStr(option.Weight)
|
||||
subPower := votingPower.Mul(weight)
|
||||
results[option.Option] = results[option.Option].Add(subPower)
|
||||
}
|
||||
totalVotingPower = totalVotingPower.Add(votingPower)
|
||||
}
|
||||
|
||||
return totalVotingPower, results, nil
|
||||
}
|
||||
|
||||
// getCurrentValidators fetches all the bonded validators, insert them into currValidators
|
||||
func (k Keeper) getCurrentValidators(ctx context.Context) (map[string]v1.ValidatorGovInfo, error) {
|
||||
currValidators := make(map[string]v1.ValidatorGovInfo)
|
||||
if err := k.sk.IterateBondedValidatorsByPower(ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) {
|
||||
valBz, err := k.sk.ValidatorAddressCodec().StringToBytes(validator.GetOperator())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
currValidators[validator.GetOperator()] = v1.NewValidatorGovInfo(
|
||||
valBz,
|
||||
validator.GetBondedTokens(),
|
||||
validator.GetDelegatorShares(),
|
||||
math.LegacyZeroDec(),
|
||||
v1.WeightedVoteOptions{},
|
||||
)
|
||||
|
||||
return false
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return currValidators, nil
|
||||
}
|
||||
|
||||
// Tally iterates over the votes and updates the tally of a proposal based on the voting power of the
|
||||
// voters
|
||||
func (k Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, burnDeposits bool, tallyResults v1.TallyResult, err error) {
|
||||
currValidators, err := k.getCurrentValidators(ctx)
|
||||
if err != nil {
|
||||
return false, false, tallyResults, fmt.Errorf("error while getting current validators: %w", err)
|
||||
}
|
||||
|
||||
tallyFn := k.calculateVoteResultsAndVotingPowerFn
|
||||
totalVotingPower, results, err := tallyFn(ctx, k, proposal, currValidators)
|
||||
totalVotingPower, totalValPower, results, err := tallyFn(ctx, k, proposal)
|
||||
if err != nil {
|
||||
return false, false, tallyResults, fmt.Errorf("error while calculating tally results: %w", err)
|
||||
}
|
||||
@ -157,13 +152,8 @@ func (k Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, burnDe
|
||||
tallyResults = v1.NewTallyResultFromMap(results)
|
||||
|
||||
// TODO: Upgrade the spec to cover all of these cases & remove pseudocode.
|
||||
// If there is no staked coins, the proposal fails
|
||||
totalBonded, err := k.sk.TotalBondedTokens(ctx)
|
||||
if err != nil {
|
||||
return false, false, tallyResults, err
|
||||
}
|
||||
|
||||
if totalBonded.IsZero() {
|
||||
// If there is no validator power, the proposal fails
|
||||
if totalValPower.IsZero() {
|
||||
return false, false, tallyResults, nil
|
||||
}
|
||||
|
||||
@ -173,7 +163,7 @@ func (k Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, burnDe
|
||||
}
|
||||
|
||||
// If there is not enough quorum of votes, the proposal fails
|
||||
percentVoting := totalVotingPower.Quo(math.LegacyNewDecFromInt(totalBonded))
|
||||
percentVoting := totalVotingPower.Quo(math.LegacyNewDecFromInt(totalValPower))
|
||||
quorum, _ := math.LegacyNewDecFromStr(params.Quorum)
|
||||
if percentVoting.LT(quorum) {
|
||||
return false, params.BurnVoteQuorum, tallyResults, nil
|
||||
|
||||
@ -1200,19 +1200,19 @@ func (mr *MockStakingKeeperMockRecorder) TokensFromConsensusPower(ctx, power any
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TokensFromConsensusPower", reflect.TypeOf((*MockStakingKeeper)(nil).TokensFromConsensusPower), ctx, power)
|
||||
}
|
||||
|
||||
// TotalBondedTokens mocks base method.
|
||||
func (m *MockStakingKeeper) TotalBondedTokens(arg0 context.Context) (math.Int, error) {
|
||||
// TotalValidatorPower mocks base method.
|
||||
func (m *MockStakingKeeper) TotalValidatorPower(arg0 context.Context) (math.Int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "TotalBondedTokens", arg0)
|
||||
ret := m.ctrl.Call(m, "TotalValidatorPower", arg0)
|
||||
ret0, _ := ret[0].(math.Int)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// TotalBondedTokens indicates an expected call of TotalBondedTokens.
|
||||
func (mr *MockStakingKeeperMockRecorder) TotalBondedTokens(arg0 any) *gomock.Call {
|
||||
// TotalValidatorPower indicates an expected call of TotalValidatorPower.
|
||||
func (mr *MockStakingKeeperMockRecorder) TotalValidatorPower(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalBondedTokens", reflect.TypeOf((*MockStakingKeeper)(nil).TotalBondedTokens), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalValidatorPower", reflect.TypeOf((*MockStakingKeeper)(nil).TotalValidatorPower), arg0)
|
||||
}
|
||||
|
||||
// ValidatorAddressCodec mocks base method.
|
||||
|
||||
@ -24,7 +24,7 @@ type StakingKeeper interface {
|
||||
context.Context, func(index int64, validator stakingtypes.ValidatorI) (stop bool),
|
||||
) error
|
||||
|
||||
TotalBondedTokens(context.Context) (math.Int, error) // total bonded tokens within the validator set
|
||||
TotalValidatorPower(context.Context) (math.Int, error) // total validator power within the validator set
|
||||
IterateDelegations(
|
||||
ctx context.Context, delegator sdk.AccAddress,
|
||||
fn func(index int64, delegation stakingtypes.DelegationI) (stop bool),
|
||||
|
||||
@ -63,7 +63,7 @@ func TestBeginBlocker(t *testing.T) {
|
||||
)
|
||||
val, err := stakingKeeper.Validator(ctx, addr)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, amt, val.GetBondedTokens())
|
||||
require.Equal(t, amt, val.GetValidatorPower())
|
||||
|
||||
abciVal := abci.Validator{
|
||||
Address: pk.Address(),
|
||||
|
||||
@ -75,8 +75,8 @@ func (k Keeper) burnNotBondedTokens(ctx context.Context, amt math.Int) error {
|
||||
return k.bankKeeper.BurnCoins(ctx, types.NotBondedPoolName, coins)
|
||||
}
|
||||
|
||||
// TotalBondedTokens total staking tokens supply which is bonded
|
||||
func (k Keeper) TotalBondedTokens(ctx context.Context) (math.Int, error) {
|
||||
// TotalValidatorPower total validator power based on the bonded supply for the staking implementation
|
||||
func (k Keeper) TotalValidatorPower(ctx context.Context) (math.Int, error) {
|
||||
bondedPool := k.GetBondedPool(ctx)
|
||||
bondDenom, err := k.BondDenom(ctx)
|
||||
if err != nil {
|
||||
@ -102,7 +102,7 @@ func (k Keeper) BondedRatio(ctx context.Context) (math.LegacyDec, error) {
|
||||
}
|
||||
|
||||
if stakeSupply.IsPositive() {
|
||||
totalBonded, err := k.TotalBondedTokens(ctx)
|
||||
totalBonded, err := k.TotalValidatorPower(ctx)
|
||||
if err != nil {
|
||||
return math.LegacyZeroDec(), err
|
||||
}
|
||||
|
||||
@ -445,19 +445,19 @@ func (mr *MockValidatorSetMockRecorder) StakingTokenSupply(arg0 any) *gomock.Cal
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StakingTokenSupply", reflect.TypeOf((*MockValidatorSet)(nil).StakingTokenSupply), arg0)
|
||||
}
|
||||
|
||||
// TotalBondedTokens mocks base method.
|
||||
func (m *MockValidatorSet) TotalBondedTokens(arg0 context.Context) (math.Int, error) {
|
||||
// TotalValidatorPower mocks base method.
|
||||
func (m *MockValidatorSet) TotalValidatorPower(arg0 context.Context) (math.Int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "TotalBondedTokens", arg0)
|
||||
ret := m.ctrl.Call(m, "TotalValidatorPower", arg0)
|
||||
ret0, _ := ret[0].(math.Int)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// TotalBondedTokens indicates an expected call of TotalBondedTokens.
|
||||
func (mr *MockValidatorSetMockRecorder) TotalBondedTokens(arg0 any) *gomock.Call {
|
||||
// TotalValidatorPower indicates an expected call of TotalValidatorPower.
|
||||
func (mr *MockValidatorSetMockRecorder) TotalValidatorPower(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalBondedTokens", reflect.TypeOf((*MockValidatorSet)(nil).TotalBondedTokens), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalValidatorPower", reflect.TypeOf((*MockValidatorSet)(nil).TotalValidatorPower), arg0)
|
||||
}
|
||||
|
||||
// Unjail mocks base method.
|
||||
|
||||
@ -57,7 +57,7 @@ type ValidatorSet interface {
|
||||
|
||||
Validator(context.Context, sdk.ValAddress) (ValidatorI, error) // get a particular validator by operator address
|
||||
ValidatorByConsAddr(context.Context, sdk.ConsAddress) (ValidatorI, error) // get a particular validator by consensus address
|
||||
TotalBondedTokens(context.Context) (math.Int, error) // total bonded tokens within the validator set
|
||||
TotalValidatorPower(context.Context) (math.Int, error) // total validator power within the validator set
|
||||
StakingTokenSupply(context.Context) (math.Int, error) // total staking token supply
|
||||
|
||||
// slash the validator and delegators of the validator, specifying offense height, offense power, and slash fraction
|
||||
|
||||
@ -28,7 +28,7 @@ type ValidatorI interface {
|
||||
TmConsPublicKey() (cmtprotocrypto.PublicKey, error) // validation consensus pubkey (CometBFT)
|
||||
GetConsAddr() ([]byte, error) // validation consensus address
|
||||
GetTokens() math.Int // validation tokens
|
||||
GetBondedTokens() math.Int // validator bonded tokens
|
||||
GetValidatorPower() math.Int // validator bonded tokens
|
||||
GetConsensusPower(math.Int) int64 // validation power in CometBFT
|
||||
GetCommission() math.LegacyDec // validator commission rate
|
||||
GetMinSelfDelegation() math.Int // validator minimum self delegation
|
||||
|
||||
@ -504,8 +504,8 @@ func (v Validator) GetConsAddr() ([]byte, error) {
|
||||
return pk.Address().Bytes(), nil
|
||||
}
|
||||
|
||||
func (v Validator) GetTokens() math.Int { return v.Tokens }
|
||||
func (v Validator) GetBondedTokens() math.Int { return v.BondedTokens() }
|
||||
func (v Validator) GetTokens() math.Int { return v.Tokens }
|
||||
func (v Validator) GetValidatorPower() math.Int { return v.BondedTokens() }
|
||||
func (v Validator) GetConsensusPower(r math.Int) int64 {
|
||||
return v.ConsensusPower(r)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user