feat: custom gov tally fn (#24355)
Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com>
This commit is contained in:
parent
640e70736e
commit
ed70bf336f
@ -59,6 +59,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* (x/protocolpool) [#23933](https://github.com/cosmos/cosmos-sdk/pull/23933) Add x/protocolpool module.
|
||||
* x/distribution can now utilize an externally managed community pool. NOTE: this will make the message handlers for FundCommunityPool and CommunityPoolSpend error, as well as the query handler for CommunityPool.
|
||||
* (client) [#18101](https://github.com/cosmos/cosmos-sdk/pull/18101) Add a `keyring-default-keyname` in `client.toml` for specifying a default key name, and skip the need to use the `--from` flag when signing transactions.
|
||||
* (x/gov) [#24355](https://github.com/cosmos/cosmos-sdk/pull/24355) Allow users to set a custom CalculateVoteResultsAndVotingPower function to be used in govkeeper.Tally.
|
||||
|
||||
### Improvements
|
||||
|
||||
|
||||
@ -461,6 +461,7 @@ func NewSimApp(
|
||||
app.MsgServiceRouter(),
|
||||
govConfig,
|
||||
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
|
||||
// govkeeper.WithCustomCalculateVoteResultsAndVotingPowerFn(...), // Add if you want to use a custom vote calculation function.
|
||||
)
|
||||
|
||||
// Set legacy router for backwards compatibility with gov v1beta1
|
||||
|
||||
@ -211,7 +211,7 @@ func (s *E2ETestSuite) TestNewCmdSubmitLegacyProposal() {
|
||||
{
|
||||
"invalid proposal (file)",
|
||||
[]string{
|
||||
fmt.Sprintf("--%s=%s", cli.FlagProposal, invalidPropFile.Name()), //nolint:staticcheck // we are intentionally using a deprecated flag here.
|
||||
fmt.Sprintf("--%s=%s", cli.FlagProposal, invalidPropFile.Name()), //nolint:staticcheck // keep deprecated flag for test
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10))).String()),
|
||||
@ -221,8 +221,8 @@ func (s *E2ETestSuite) TestNewCmdSubmitLegacyProposal() {
|
||||
{
|
||||
"invalid proposal",
|
||||
[]string{
|
||||
fmt.Sprintf("--%s='Where is the title!?'", cli.FlagDescription), //nolint:staticcheck // we are intentionally using a deprecated flag here.
|
||||
fmt.Sprintf("--%s=%s", cli.FlagProposalType, v1beta1.ProposalTypeText), //nolint:staticcheck // we are intentionally using a deprecated flag here.
|
||||
fmt.Sprintf("--%s='Where is the title!?'", cli.FlagDescription), //nolint:staticcheck // keep deprecated flag for test
|
||||
fmt.Sprintf("--%s=%s", cli.FlagProposalType, v1beta1.ProposalTypeText), //nolint:staticcheck // keep deprecated flag for test
|
||||
fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10000)).String()),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
@ -232,9 +232,9 @@ func (s *E2ETestSuite) TestNewCmdSubmitLegacyProposal() {
|
||||
},
|
||||
{
|
||||
"valid transaction (file)",
|
||||
//nolint:staticcheck // we are intentionally using a deprecated flag here.
|
||||
|
||||
[]string{
|
||||
fmt.Sprintf("--%s=%s", cli.FlagProposal, validPropFile.Name()),
|
||||
fmt.Sprintf("--%s=%s", cli.FlagProposal, validPropFile.Name()), //nolint:staticcheck // keep deprecated flag for test
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
|
||||
@ -246,8 +246,8 @@ func (s *E2ETestSuite) TestNewCmdSubmitLegacyProposal() {
|
||||
"valid transaction",
|
||||
[]string{
|
||||
fmt.Sprintf("--%s='Text Proposal'", cli.FlagTitle),
|
||||
fmt.Sprintf("--%s='Where is the title!?'", cli.FlagDescription), //nolint:staticcheck // we are intentionally using a deprecated flag here.
|
||||
fmt.Sprintf("--%s=%s", cli.FlagProposalType, v1beta1.ProposalTypeText), //nolint:staticcheck // we are intentionally using a deprecated flag here.
|
||||
fmt.Sprintf("--%s='Where is the title!?'", cli.FlagDescription), //nolint:staticcheck // keep deprecated flag for test
|
||||
fmt.Sprintf("--%s=%s", cli.FlagProposalType, v1beta1.ProposalTypeText), //nolint:staticcheck // keep deprecated flag for test
|
||||
fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin(s.cfg.BondDenom, math.NewInt(100000)).String()),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
|
||||
141
x/gov/depinject.go
Normal file
141
x/gov/depinject.go
Normal file
@ -0,0 +1,141 @@
|
||||
package gov
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
modulev1 "cosmossdk.io/api/cosmos/gov/module/v1"
|
||||
"cosmossdk.io/core/appmodule"
|
||||
"cosmossdk.io/core/store"
|
||||
"cosmossdk.io/depinject"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
|
||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
appmodule.Register(
|
||||
&modulev1.Module{},
|
||||
appmodule.Provide(ProvideModule, ProvideKeyTable),
|
||||
appmodule.Invoke(InvokeAddRoutes, InvokeSetHooks))
|
||||
}
|
||||
|
||||
type ModuleInputs struct {
|
||||
depinject.In
|
||||
|
||||
Config *modulev1.Module
|
||||
Cdc codec.Codec
|
||||
StoreService store.KVStoreService
|
||||
ModuleKey depinject.OwnModuleKey
|
||||
MsgServiceRouter baseapp.MessageRouter
|
||||
|
||||
AccountKeeper govtypes.AccountKeeper
|
||||
BankKeeper govtypes.BankKeeper
|
||||
StakingKeeper govtypes.StakingKeeper
|
||||
DistributionKeeper govtypes.DistributionKeeper
|
||||
|
||||
// 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"`
|
||||
|
||||
// LegacySubspace is used solely for migration of x/params managed parameters
|
||||
LegacySubspace govtypes.ParamSubspace `optional:"true"`
|
||||
}
|
||||
|
||||
type ModuleOutputs struct {
|
||||
depinject.Out
|
||||
|
||||
Module appmodule.AppModule
|
||||
Keeper *keeper.Keeper
|
||||
HandlerRoute v1beta1.HandlerRoute
|
||||
}
|
||||
|
||||
func ProvideModule(in ModuleInputs) ModuleOutputs {
|
||||
defaultConfig := govtypes.DefaultConfig()
|
||||
if in.Config.MaxMetadataLen != 0 {
|
||||
defaultConfig.MaxMetadataLen = in.Config.MaxMetadataLen
|
||||
}
|
||||
|
||||
// default to governance authority if not provided
|
||||
authority := authtypes.NewModuleAddress(govtypes.ModuleName)
|
||||
if in.Config.Authority != "" {
|
||||
authority = authtypes.NewModuleAddressOrBech32Address(in.Config.Authority)
|
||||
}
|
||||
|
||||
var opts []keeper.InitOption
|
||||
if in.CustomCalculateVoteResultsAndVotingPowerFn != nil {
|
||||
opts = append(opts, keeper.WithCustomCalculateVoteResultsAndVotingPowerFn(in.CustomCalculateVoteResultsAndVotingPowerFn))
|
||||
}
|
||||
|
||||
k := keeper.NewKeeper(
|
||||
in.Cdc,
|
||||
in.StoreService,
|
||||
in.AccountKeeper,
|
||||
in.BankKeeper,
|
||||
in.StakingKeeper,
|
||||
in.DistributionKeeper,
|
||||
in.MsgServiceRouter,
|
||||
defaultConfig,
|
||||
authority.String(),
|
||||
opts...,
|
||||
)
|
||||
m := NewAppModule(in.Cdc, k, in.AccountKeeper, in.BankKeeper, in.LegacySubspace)
|
||||
hr := v1beta1.HandlerRoute{Handler: v1beta1.ProposalHandler, RouteKey: govtypes.RouterKey}
|
||||
|
||||
return ModuleOutputs{Module: m, Keeper: k, HandlerRoute: hr}
|
||||
}
|
||||
|
||||
func ProvideKeyTable() paramtypes.KeyTable {
|
||||
return v1.ParamKeyTable() //nolint:staticcheck // we still need this for upgrades
|
||||
}
|
||||
|
||||
func InvokeAddRoutes(keeper *keeper.Keeper, routes []v1beta1.HandlerRoute) {
|
||||
if keeper == nil || routes == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Default route order is a lexical sort by RouteKey.
|
||||
// Explicit ordering can be added to the module config if required.
|
||||
slices.SortFunc(routes, func(x, y v1beta1.HandlerRoute) int {
|
||||
return strings.Compare(x.RouteKey, y.RouteKey)
|
||||
})
|
||||
|
||||
router := v1beta1.NewRouter()
|
||||
for _, r := range routes {
|
||||
router.AddRoute(r.RouteKey, r.Handler)
|
||||
}
|
||||
keeper.SetLegacyRouter(router)
|
||||
}
|
||||
|
||||
func InvokeSetHooks(keeper *keeper.Keeper, govHooks map[string]govtypes.GovHooksWrapper) error {
|
||||
if keeper == nil || govHooks == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Default ordering is lexical by module name.
|
||||
// Explicit ordering can be added to the module config if required.
|
||||
modNames := slices.Sorted(maps.Keys(govHooks))
|
||||
order := modNames
|
||||
sort.Strings(order)
|
||||
|
||||
var multiHooks govtypes.MultiGovHooks
|
||||
for _, modName := range order {
|
||||
hook, ok := govHooks[modName]
|
||||
if !ok {
|
||||
return fmt.Errorf("can't find staking hooks for module %s", modName)
|
||||
}
|
||||
multiHooks = append(multiHooks, hook)
|
||||
}
|
||||
|
||||
keeper.SetHooks(multiHooks)
|
||||
return nil
|
||||
}
|
||||
@ -17,17 +17,17 @@ import (
|
||||
)
|
||||
|
||||
// SetDeposit sets a Deposit to the gov store
|
||||
func (keeper Keeper) SetDeposit(ctx context.Context, deposit v1.Deposit) error {
|
||||
depositor, err := keeper.authKeeper.AddressCodec().StringToBytes(deposit.Depositor)
|
||||
func (k Keeper) SetDeposit(ctx context.Context, deposit v1.Deposit) error {
|
||||
depositor, err := k.authKeeper.AddressCodec().StringToBytes(deposit.Depositor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return keeper.Deposits.Set(ctx, collections.Join(deposit.ProposalId, sdk.AccAddress(depositor)), deposit)
|
||||
return k.Deposits.Set(ctx, collections.Join(deposit.ProposalId, sdk.AccAddress(depositor)), deposit)
|
||||
}
|
||||
|
||||
// GetDeposits returns all the deposits of a proposal
|
||||
func (keeper Keeper) GetDeposits(ctx context.Context, proposalID uint64) (deposits v1.Deposits, err error) {
|
||||
err = keeper.IterateDeposits(ctx, proposalID, func(_ collections.Pair[uint64, sdk.AccAddress], deposit v1.Deposit) (bool, error) {
|
||||
func (k Keeper) GetDeposits(ctx context.Context, proposalID uint64) (deposits v1.Deposits, err error) {
|
||||
err = k.IterateDeposits(ctx, proposalID, func(_ collections.Pair[uint64, sdk.AccAddress], deposit v1.Deposit) (bool, error) {
|
||||
deposits = append(deposits, &deposit)
|
||||
return false, nil
|
||||
})
|
||||
@ -35,23 +35,23 @@ func (keeper Keeper) GetDeposits(ctx context.Context, proposalID uint64) (deposi
|
||||
}
|
||||
|
||||
// DeleteAndBurnDeposits deletes and burns all the deposits on a specific proposal.
|
||||
func (keeper Keeper) DeleteAndBurnDeposits(ctx context.Context, proposalID uint64) error {
|
||||
func (k Keeper) DeleteAndBurnDeposits(ctx context.Context, proposalID uint64) error {
|
||||
coinsToBurn := sdk.NewCoins()
|
||||
err := keeper.IterateDeposits(ctx, proposalID, func(key collections.Pair[uint64, sdk.AccAddress], deposit v1.Deposit) (stop bool, err error) {
|
||||
err := k.IterateDeposits(ctx, proposalID, func(key collections.Pair[uint64, sdk.AccAddress], deposit v1.Deposit) (stop bool, err error) {
|
||||
coinsToBurn = coinsToBurn.Add(deposit.Amount...)
|
||||
return false, keeper.Deposits.Remove(ctx, key)
|
||||
return false, k.Deposits.Remove(ctx, key)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return keeper.bankKeeper.BurnCoins(ctx, types.ModuleName, coinsToBurn)
|
||||
return k.bankKeeper.BurnCoins(ctx, types.ModuleName, coinsToBurn)
|
||||
}
|
||||
|
||||
// IterateDeposits iterates over all the proposals deposits and performs a callback function
|
||||
func (keeper Keeper) IterateDeposits(ctx context.Context, proposalID uint64, cb func(key collections.Pair[uint64, sdk.AccAddress], value v1.Deposit) (bool, error)) error {
|
||||
func (k Keeper) IterateDeposits(ctx context.Context, proposalID uint64, cb func(key collections.Pair[uint64, sdk.AccAddress], value v1.Deposit) (bool, error)) error {
|
||||
rng := collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposalID)
|
||||
err := keeper.Deposits.Walk(ctx, rng, cb)
|
||||
err := k.Deposits.Walk(ctx, rng, cb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -60,9 +60,9 @@ func (keeper Keeper) IterateDeposits(ctx context.Context, proposalID uint64, cb
|
||||
|
||||
// AddDeposit adds or updates a deposit of a specific depositor on a specific proposal.
|
||||
// Activates voting period when appropriate and returns true in that case, else returns false.
|
||||
func (keeper Keeper) AddDeposit(ctx context.Context, proposalID uint64, depositorAddr sdk.AccAddress, depositAmount sdk.Coins) (bool, error) {
|
||||
func (k Keeper) AddDeposit(ctx context.Context, proposalID uint64, depositorAddr sdk.AccAddress, depositAmount sdk.Coins) (bool, error) {
|
||||
// Checks to see if proposal exists
|
||||
proposal, err := keeper.Proposals.Get(ctx, proposalID)
|
||||
proposal, err := k.Proposals.Get(ctx, proposalID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -73,7 +73,7 @@ func (keeper Keeper) AddDeposit(ctx context.Context, proposalID uint64, deposito
|
||||
}
|
||||
|
||||
// Check coins to be deposited match the proposal's deposit params
|
||||
params, err := keeper.Params.Get(ctx)
|
||||
params, err := k.Params.Get(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -85,7 +85,7 @@ func (keeper Keeper) AddDeposit(ctx context.Context, proposalID uint64, deposito
|
||||
}
|
||||
|
||||
// the deposit must only contain valid denoms (listed in the min deposit param)
|
||||
if err := keeper.validateDepositDenom(ctx, params, depositAmount); err != nil {
|
||||
if err := k.validateDepositDenom(ctx, params, depositAmount); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@ -121,14 +121,14 @@ func (keeper Keeper) AddDeposit(ctx context.Context, proposalID uint64, deposito
|
||||
}
|
||||
|
||||
// update the governance module's account coins pool
|
||||
err = keeper.bankKeeper.SendCoinsFromAccountToModule(ctx, depositorAddr, types.ModuleName, depositAmount)
|
||||
err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, depositorAddr, types.ModuleName, depositAmount)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Update proposal
|
||||
proposal.TotalDeposit = sdk.NewCoins(proposal.TotalDeposit...).Add(depositAmount...)
|
||||
err = keeper.SetProposal(ctx, proposal)
|
||||
err = k.SetProposal(ctx, proposal)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -136,7 +136,7 @@ func (keeper Keeper) AddDeposit(ctx context.Context, proposalID uint64, deposito
|
||||
// Check if deposit has provided sufficient total funds to transition the proposal into the voting period
|
||||
activatedVotingPeriod := false
|
||||
if proposal.Status == v1.StatusDepositPeriod && sdk.NewCoins(proposal.TotalDeposit...).IsAllGTE(minDepositAmount) {
|
||||
err = keeper.ActivateVotingPeriod(ctx, proposal)
|
||||
err = k.ActivateVotingPeriod(ctx, proposal)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -145,7 +145,7 @@ func (keeper Keeper) AddDeposit(ctx context.Context, proposalID uint64, deposito
|
||||
}
|
||||
|
||||
// Add or update deposit object
|
||||
deposit, err := keeper.Deposits.Get(ctx, collections.Join(proposalID, depositorAddr))
|
||||
deposit, err := k.Deposits.Get(ctx, collections.Join(proposalID, depositorAddr))
|
||||
switch {
|
||||
case err == nil:
|
||||
// deposit exists
|
||||
@ -159,7 +159,7 @@ func (keeper Keeper) AddDeposit(ctx context.Context, proposalID uint64, deposito
|
||||
}
|
||||
|
||||
// called when deposit has been added to a proposal, however the proposal may not be active
|
||||
err = keeper.Hooks().AfterProposalDeposit(ctx, proposalID, depositorAddr)
|
||||
err = k.Hooks().AfterProposalDeposit(ctx, proposalID, depositorAddr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -174,7 +174,7 @@ func (keeper Keeper) AddDeposit(ctx context.Context, proposalID uint64, deposito
|
||||
),
|
||||
)
|
||||
|
||||
err = keeper.SetDeposit(ctx, deposit)
|
||||
err = k.SetDeposit(ctx, deposit)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -185,17 +185,17 @@ func (keeper Keeper) AddDeposit(ctx context.Context, proposalID uint64, deposito
|
||||
// ChargeDeposit will charge proposal cancellation fee (deposits * proposal_cancel_burn_rate) and
|
||||
// send to a destAddress if defined or burn otherwise.
|
||||
// Remaining funds are send back to the depositor.
|
||||
func (keeper Keeper) ChargeDeposit(ctx context.Context, proposalID uint64, destAddress, proposalCancelRate string) error {
|
||||
func (k Keeper) ChargeDeposit(ctx context.Context, proposalID uint64, destAddress, proposalCancelRate string) error {
|
||||
rate := sdkmath.LegacyMustNewDecFromStr(proposalCancelRate)
|
||||
var cancellationCharges sdk.Coins
|
||||
|
||||
deposits, err := keeper.GetDeposits(ctx, proposalID)
|
||||
deposits, err := k.GetDeposits(ctx, proposalID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, deposit := range deposits {
|
||||
depositerAddress, err := keeper.authKeeper.AddressCodec().StringToBytes(deposit.Depositor)
|
||||
depositerAddress, err := k.authKeeper.AddressCodec().StringToBytes(deposit.Depositor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -220,14 +220,14 @@ func (keeper Keeper) ChargeDeposit(ctx context.Context, proposalID uint64, destA
|
||||
}
|
||||
|
||||
if !remainingAmount.IsZero() {
|
||||
err := keeper.bankKeeper.SendCoinsFromModuleToAccount(
|
||||
err := k.bankKeeper.SendCoinsFromModuleToAccount(
|
||||
ctx, types.ModuleName, depositerAddress, remainingAmount,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = keeper.Deposits.Remove(ctx, collections.Join(deposit.ProposalId, sdk.AccAddress(depositerAddress)))
|
||||
err = k.Deposits.Remove(ctx, collections.Join(deposit.ProposalId, sdk.AccAddress(depositerAddress)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -236,25 +236,25 @@ func (keeper Keeper) ChargeDeposit(ctx context.Context, proposalID uint64, destA
|
||||
// burn the cancellation fee or sent the cancellation charges to destination address.
|
||||
if !cancellationCharges.IsZero() {
|
||||
// get the distribution module account address
|
||||
distributionAddress := keeper.authKeeper.GetModuleAddress(disttypes.ModuleName)
|
||||
distributionAddress := k.authKeeper.GetModuleAddress(disttypes.ModuleName)
|
||||
switch {
|
||||
case destAddress == "":
|
||||
// burn the cancellation charges from deposits
|
||||
err := keeper.bankKeeper.BurnCoins(ctx, types.ModuleName, cancellationCharges)
|
||||
err := k.bankKeeper.BurnCoins(ctx, types.ModuleName, cancellationCharges)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case distributionAddress.String() == destAddress:
|
||||
err := keeper.distrKeeper.FundCommunityPool(ctx, cancellationCharges, keeper.ModuleAccountAddress())
|
||||
err := k.distrKeeper.FundCommunityPool(ctx, cancellationCharges, k.ModuleAccountAddress())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
destAccAddress, err := keeper.authKeeper.AddressCodec().StringToBytes(destAddress)
|
||||
destAccAddress, err := k.authKeeper.AddressCodec().StringToBytes(destAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = keeper.bankKeeper.SendCoinsFromModuleToAccount(
|
||||
err = k.bankKeeper.SendCoinsFromModuleToAccount(
|
||||
ctx, types.ModuleName, destAccAddress, cancellationCharges,
|
||||
)
|
||||
if err != nil {
|
||||
@ -267,14 +267,14 @@ func (keeper Keeper) ChargeDeposit(ctx context.Context, proposalID uint64, destA
|
||||
}
|
||||
|
||||
// RefundAndDeleteDeposits refunds and deletes all the deposits on a specific proposal.
|
||||
func (keeper Keeper) RefundAndDeleteDeposits(ctx context.Context, proposalID uint64) error {
|
||||
return keeper.IterateDeposits(ctx, proposalID, func(key collections.Pair[uint64, sdk.AccAddress], deposit v1.Deposit) (bool, error) {
|
||||
func (k Keeper) RefundAndDeleteDeposits(ctx context.Context, proposalID uint64) error {
|
||||
return k.IterateDeposits(ctx, proposalID, func(key collections.Pair[uint64, sdk.AccAddress], deposit v1.Deposit) (bool, error) {
|
||||
depositor := key.K2()
|
||||
err := keeper.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, depositor, deposit.Amount)
|
||||
err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, depositor, deposit.Amount)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = keeper.Deposits.Remove(ctx, key)
|
||||
err = k.Deposits.Remove(ctx, key)
|
||||
return false, err
|
||||
})
|
||||
}
|
||||
@ -282,7 +282,7 @@ func (keeper Keeper) RefundAndDeleteDeposits(ctx context.Context, proposalID uin
|
||||
// validateInitialDeposit validates if initial deposit is greater than or equal to the minimum
|
||||
// required at the time of proposal submission. This threshold amount is determined by
|
||||
// the deposit parameters. Returns nil on success, error otherwise.
|
||||
func (keeper Keeper) validateInitialDeposit(ctx context.Context, params v1.Params, initialDeposit sdk.Coins, expedited bool) error {
|
||||
func (k Keeper) validateInitialDeposit(ctx context.Context, params v1.Params, initialDeposit sdk.Coins, expedited bool) error {
|
||||
if !initialDeposit.IsValid() || initialDeposit.IsAnyNegative() {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidCoins, initialDeposit.String())
|
||||
}
|
||||
@ -312,7 +312,7 @@ func (keeper Keeper) validateInitialDeposit(ctx context.Context, params v1.Param
|
||||
}
|
||||
|
||||
// validateDepositDenom validates if the deposit denom is accepted by the governance module.
|
||||
func (keeper Keeper) validateDepositDenom(ctx context.Context, params v1.Params, depositAmount sdk.Coins) error {
|
||||
func (k Keeper) validateDepositDenom(ctx context.Context, params v1.Params, depositAmount sdk.Coins) error {
|
||||
denoms := []string{}
|
||||
acceptedDenoms := make(map[string]bool, len(params.MinDeposit))
|
||||
for _, coin := range params.MinDeposit {
|
||||
|
||||
@ -43,6 +43,8 @@ type Keeper struct {
|
||||
|
||||
config types.Config
|
||||
|
||||
calculateVoteResultsAndVotingPowerFn CalculateVoteResultsAndVotingPowerFn
|
||||
|
||||
// the address capable of executing a MsgUpdateParams message. Typically, this
|
||||
// should be the x/gov module account.
|
||||
authority string
|
||||
@ -59,6 +61,20 @@ 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
|
||||
@ -74,7 +90,7 @@ func (k Keeper) GetAuthority() string {
|
||||
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,
|
||||
router baseapp.MessageRouter, config types.Config, authority string, initOptions ...InitOption,
|
||||
) *Keeper {
|
||||
// ensure governance module account is set
|
||||
if addr := authKeeper.GetModuleAddress(types.ModuleName); addr == nil {
|
||||
@ -92,25 +108,31 @@ func NewKeeper(
|
||||
|
||||
sb := collections.NewSchemaBuilder(storeService)
|
||||
k := &Keeper{
|
||||
storeService: storeService,
|
||||
authKeeper: authKeeper,
|
||||
bankKeeper: bankKeeper,
|
||||
distrKeeper: distrKeeper,
|
||||
sk: sk,
|
||||
cdc: cdc,
|
||||
router: router,
|
||||
config: config,
|
||||
authority: authority,
|
||||
Constitution: collections.NewItem(sb, types.ConstitutionKey, "constitution", collections.StringValue),
|
||||
Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[v1.Params](cdc)),
|
||||
Deposits: collections.NewMap(sb, types.DepositsKeyPrefix, "deposits", collections.PairKeyCodec(collections.Uint64Key, sdk.LengthPrefixedAddressKey(sdk.AccAddressKey)), codec.CollValue[v1.Deposit](cdc)), // nolint: staticcheck // sdk.LengthPrefixedAddressKey is needed to retain state compatibility
|
||||
Votes: collections.NewMap(sb, types.VotesKeyPrefix, "votes", collections.PairKeyCodec(collections.Uint64Key, sdk.LengthPrefixedAddressKey(sdk.AccAddressKey)), codec.CollValue[v1.Vote](cdc)), // nolint: staticcheck // sdk.LengthPrefixedAddressKey is needed to retain state compatibility
|
||||
ProposalID: collections.NewSequence(sb, types.ProposalIDKey, "proposal_id"),
|
||||
Proposals: collections.NewMap(sb, types.ProposalsKeyPrefix, "proposals", collections.Uint64Key, codec.CollValue[v1.Proposal](cdc)),
|
||||
ActiveProposalsQueue: collections.NewMap(sb, types.ActiveProposalQueuePrefix, "active_proposals_queue", collections.PairKeyCodec(sdk.TimeKey, collections.Uint64Key), collections.Uint64Value), // sdk.TimeKey is needed to retain state compatibility
|
||||
InactiveProposalsQueue: collections.NewMap(sb, types.InactiveProposalQueuePrefix, "inactive_proposals_queue", collections.PairKeyCodec(sdk.TimeKey, collections.Uint64Key), collections.Uint64Value), // sdk.TimeKey is needed to retain state compatibility
|
||||
VotingPeriodProposals: collections.NewMap(sb, types.VotingPeriodProposalKeyPrefix, "voting_period_proposals", collections.Uint64Key, collections.BytesValue),
|
||||
storeService: storeService,
|
||||
authKeeper: authKeeper,
|
||||
bankKeeper: bankKeeper,
|
||||
distrKeeper: distrKeeper,
|
||||
sk: sk,
|
||||
cdc: cdc,
|
||||
router: router,
|
||||
config: config,
|
||||
calculateVoteResultsAndVotingPowerFn: defaultCalculateVoteResultsAndVotingPower,
|
||||
authority: authority,
|
||||
Constitution: collections.NewItem(sb, types.ConstitutionKey, "constitution", collections.StringValue),
|
||||
Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[v1.Params](cdc)),
|
||||
Deposits: collections.NewMap(sb, types.DepositsKeyPrefix, "deposits", collections.PairKeyCodec(collections.Uint64Key, sdk.LengthPrefixedAddressKey(sdk.AccAddressKey)), codec.CollValue[v1.Deposit](cdc)), // nolint: staticcheck // sdk.LengthPrefixedAddressKey is needed to retain state compatibility
|
||||
Votes: collections.NewMap(sb, types.VotesKeyPrefix, "votes", collections.PairKeyCodec(collections.Uint64Key, sdk.LengthPrefixedAddressKey(sdk.AccAddressKey)), codec.CollValue[v1.Vote](cdc)), // nolint: staticcheck // sdk.LengthPrefixedAddressKey is needed to retain state compatibility
|
||||
ProposalID: collections.NewSequence(sb, types.ProposalIDKey, "proposal_id"),
|
||||
Proposals: collections.NewMap(sb, types.ProposalsKeyPrefix, "proposals", collections.Uint64Key, codec.CollValue[v1.Proposal](cdc)),
|
||||
ActiveProposalsQueue: collections.NewMap(sb, types.ActiveProposalQueuePrefix, "active_proposals_queue", collections.PairKeyCodec(sdk.TimeKey, collections.Uint64Key), collections.Uint64Value), // sdk.TimeKey is needed to retain state compatibility
|
||||
InactiveProposalsQueue: collections.NewMap(sb, types.InactiveProposalQueuePrefix, "inactive_proposals_queue", collections.PairKeyCodec(sdk.TimeKey, collections.Uint64Key), collections.Uint64Value), // sdk.TimeKey is needed to retain state compatibility
|
||||
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)
|
||||
|
||||
@ -16,21 +16,21 @@ import (
|
||||
)
|
||||
|
||||
// SubmitProposal creates a new proposal given an array of messages
|
||||
func (keeper Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, metadata, title, summary string, proposer sdk.AccAddress, expedited bool) (v1.Proposal, error) {
|
||||
func (k Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, metadata, title, summary string, proposer sdk.AccAddress, expedited bool) (v1.Proposal, error) {
|
||||
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
||||
err := keeper.assertMetadataLength(metadata)
|
||||
err := k.assertMetadataLength(metadata)
|
||||
if err != nil {
|
||||
return v1.Proposal{}, err
|
||||
}
|
||||
|
||||
// assert summary is no longer than predefined max length of metadata
|
||||
err = keeper.assertSummaryLength(summary)
|
||||
err = k.assertSummaryLength(summary)
|
||||
if err != nil {
|
||||
return v1.Proposal{}, err
|
||||
}
|
||||
|
||||
// assert title is no longer than predefined max length of metadata
|
||||
err = keeper.assertMetadataLength(title)
|
||||
err = k.assertMetadataLength(title)
|
||||
if err != nil {
|
||||
return v1.Proposal{}, err
|
||||
}
|
||||
@ -50,7 +50,7 @@ func (keeper Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, met
|
||||
}
|
||||
}
|
||||
|
||||
signers, _, err := keeper.cdc.GetMsgV1Signers(msg)
|
||||
signers, _, err := k.cdc.GetMsgV1Signers(msg)
|
||||
if err != nil {
|
||||
return v1.Proposal{}, err
|
||||
}
|
||||
@ -59,12 +59,12 @@ func (keeper Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, met
|
||||
}
|
||||
|
||||
// assert that the governance module account is the only signer of the messages
|
||||
if !bytes.Equal(signers[0], keeper.GetGovernanceAccount(ctx).GetAddress()) {
|
||||
if !bytes.Equal(signers[0], k.GetGovernanceAccount(ctx).GetAddress()) {
|
||||
return v1.Proposal{}, errorsmod.Wrapf(types.ErrInvalidSigner, sdk.AccAddress(signers[0]).String())
|
||||
}
|
||||
|
||||
// use the msg service router to see that there is a valid route for that message.
|
||||
handler := keeper.router.Handler(msg)
|
||||
handler := k.router.Handler(msg)
|
||||
if handler == nil {
|
||||
return v1.Proposal{}, errorsmod.Wrap(types.ErrUnroutableProposalMsg, sdk.MsgTypeURL(msg))
|
||||
}
|
||||
@ -86,12 +86,12 @@ func (keeper Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, met
|
||||
|
||||
}
|
||||
|
||||
proposalID, err := keeper.ProposalID.Next(ctx)
|
||||
proposalID, err := k.ProposalID.Next(ctx)
|
||||
if err != nil {
|
||||
return v1.Proposal{}, err
|
||||
}
|
||||
|
||||
params, err := keeper.Params.Get(ctx)
|
||||
params, err := k.Params.Get(ctx)
|
||||
if err != nil {
|
||||
return v1.Proposal{}, err
|
||||
}
|
||||
@ -104,17 +104,17 @@ func (keeper Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, met
|
||||
return v1.Proposal{}, err
|
||||
}
|
||||
|
||||
err = keeper.SetProposal(ctx, proposal)
|
||||
err = k.SetProposal(ctx, proposal)
|
||||
if err != nil {
|
||||
return v1.Proposal{}, err
|
||||
}
|
||||
err = keeper.InactiveProposalsQueue.Set(ctx, collections.Join(*proposal.DepositEndTime, proposalID), proposalID)
|
||||
err = k.InactiveProposalsQueue.Set(ctx, collections.Join(*proposal.DepositEndTime, proposalID), proposalID)
|
||||
if err != nil {
|
||||
return v1.Proposal{}, err
|
||||
}
|
||||
|
||||
// called right after a proposal is submitted
|
||||
err = keeper.Hooks().AfterProposalSubmission(ctx, proposalID)
|
||||
err = k.Hooks().AfterProposalSubmission(ctx, proposalID)
|
||||
if err != nil {
|
||||
return v1.Proposal{}, err
|
||||
}
|
||||
@ -132,9 +132,9 @@ func (keeper Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, met
|
||||
}
|
||||
|
||||
// CancelProposal will cancel proposal before the voting period ends
|
||||
func (keeper Keeper) CancelProposal(ctx context.Context, proposalID uint64, proposer string) error {
|
||||
func (k Keeper) CancelProposal(ctx context.Context, proposalID uint64, proposer string) error {
|
||||
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
||||
proposal, err := keeper.Proposals.Get(ctx, proposalID)
|
||||
proposal, err := k.Proposals.Get(ctx, proposalID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -162,29 +162,29 @@ func (keeper Keeper) CancelProposal(ctx context.Context, proposalID uint64, prop
|
||||
|
||||
// burn the (deposits * proposal_cancel_rate) amount or sent to cancellation destination address.
|
||||
// and deposits * (1 - proposal_cancel_rate) will be sent to depositors.
|
||||
params, err := keeper.Params.Get(ctx)
|
||||
params, err := k.Params.Get(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = keeper.ChargeDeposit(ctx, proposal.Id, params.ProposalCancelDest, params.ProposalCancelRatio)
|
||||
err = k.ChargeDeposit(ctx, proposal.Id, params.ProposalCancelDest, params.ProposalCancelRatio)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if proposal.VotingStartTime != nil {
|
||||
err = keeper.deleteVotes(ctx, proposal.Id)
|
||||
err = k.deleteVotes(ctx, proposal.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = keeper.DeleteProposal(ctx, proposal.Id)
|
||||
err = k.DeleteProposal(ctx, proposal.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keeper.Logger(ctx).Info(
|
||||
k.Logger(ctx).Info(
|
||||
"proposal is canceled by proposer",
|
||||
"proposal", proposal.Id,
|
||||
"proposer", proposal.Proposer,
|
||||
@ -194,57 +194,57 @@ func (keeper Keeper) CancelProposal(ctx context.Context, proposalID uint64, prop
|
||||
}
|
||||
|
||||
// SetProposal sets a proposal to store.
|
||||
func (keeper Keeper) SetProposal(ctx context.Context, proposal v1.Proposal) error {
|
||||
func (k Keeper) SetProposal(ctx context.Context, proposal v1.Proposal) error {
|
||||
if proposal.Status == v1.StatusVotingPeriod {
|
||||
err := keeper.VotingPeriodProposals.Set(ctx, proposal.Id, []byte{1})
|
||||
err := k.VotingPeriodProposals.Set(ctx, proposal.Id, []byte{1})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := keeper.VotingPeriodProposals.Remove(ctx, proposal.Id)
|
||||
err := k.VotingPeriodProposals.Remove(ctx, proposal.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return keeper.Proposals.Set(ctx, proposal.Id, proposal)
|
||||
return k.Proposals.Set(ctx, proposal.Id, proposal)
|
||||
}
|
||||
|
||||
// DeleteProposal deletes a proposal from store.
|
||||
func (keeper Keeper) DeleteProposal(ctx context.Context, proposalID uint64) error {
|
||||
proposal, err := keeper.Proposals.Get(ctx, proposalID)
|
||||
func (k Keeper) DeleteProposal(ctx context.Context, proposalID uint64) error {
|
||||
proposal, err := k.Proposals.Get(ctx, proposalID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if proposal.DepositEndTime != nil {
|
||||
err := keeper.InactiveProposalsQueue.Remove(ctx, collections.Join(*proposal.DepositEndTime, proposalID))
|
||||
err := k.InactiveProposalsQueue.Remove(ctx, collections.Join(*proposal.DepositEndTime, proposalID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if proposal.VotingEndTime != nil {
|
||||
err := keeper.ActiveProposalsQueue.Remove(ctx, collections.Join(*proposal.VotingEndTime, proposalID))
|
||||
err := k.ActiveProposalsQueue.Remove(ctx, collections.Join(*proposal.VotingEndTime, proposalID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = keeper.VotingPeriodProposals.Remove(ctx, proposalID)
|
||||
err = k.VotingPeriodProposals.Remove(ctx, proposalID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return keeper.Proposals.Remove(ctx, proposalID)
|
||||
return k.Proposals.Remove(ctx, proposalID)
|
||||
}
|
||||
|
||||
// ActivateVotingPeriod activates the voting period of a proposal
|
||||
func (keeper Keeper) ActivateVotingPeriod(ctx context.Context, proposal v1.Proposal) error {
|
||||
func (k Keeper) ActivateVotingPeriod(ctx context.Context, proposal v1.Proposal) error {
|
||||
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
||||
startTime := sdkCtx.BlockHeader().Time
|
||||
proposal.VotingStartTime = &startTime
|
||||
var votingPeriod *time.Duration
|
||||
params, err := keeper.Params.Get(ctx)
|
||||
params, err := k.Params.Get(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -257,15 +257,15 @@ func (keeper Keeper) ActivateVotingPeriod(ctx context.Context, proposal v1.Propo
|
||||
endTime := proposal.VotingStartTime.Add(*votingPeriod)
|
||||
proposal.VotingEndTime = &endTime
|
||||
proposal.Status = v1.StatusVotingPeriod
|
||||
err = keeper.SetProposal(ctx, proposal)
|
||||
err = k.SetProposal(ctx, proposal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = keeper.InactiveProposalsQueue.Remove(ctx, collections.Join(*proposal.DepositEndTime, proposal.Id))
|
||||
err = k.InactiveProposalsQueue.Remove(ctx, collections.Join(*proposal.DepositEndTime, proposal.Id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return keeper.ActiveProposalsQueue.Set(ctx, collections.Join(*proposal.VotingEndTime, proposal.Id), proposal.Id)
|
||||
return k.ActiveProposalsQueue.Set(ctx, collections.Join(*proposal.VotingEndTime, proposal.Id), proposal.Id)
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/collections"
|
||||
"cosmossdk.io/math"
|
||||
@ -11,66 +12,57 @@ import (
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
// TODO: Break into several smaller functions for clarity
|
||||
// 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
|
||||
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)
|
||||
|
||||
// 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 context.Context, proposal v1.Proposal) (passes, burnDeposits bool, tallyResults v1.TallyResult, err error) {
|
||||
results := make(map[v1.VoteOption]math.LegacyDec)
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
totalVotingPower := math.LegacyZeroDec()
|
||||
currValidators := make(map[string]v1.ValidatorGovInfo)
|
||||
|
||||
// fetch all the bonded validators, insert them into currValidators
|
||||
err = keeper.sk.IterateBondedValidatorsByPower(ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) {
|
||||
valBz, err := keeper.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
|
||||
})
|
||||
if err != nil {
|
||||
return false, false, tallyResults, err
|
||||
}
|
||||
|
||||
rng := collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposal.Id)
|
||||
err = keeper.Votes.Walk(ctx, rng, func(key collections.Pair[uint64, sdk.AccAddress], vote v1.Vote) (bool, error) {
|
||||
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 := keeper.authKeeper.AddressCodec().StringToBytes(vote.Voter)
|
||||
voter, err := k.authKeeper.AddressCodec().StringToBytes(vote.Voter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
valAddrStr, err := keeper.sk.ValidatorAddressCodec().BytesToString(voter)
|
||||
valAddrStr, err := k.sk.ValidatorAddressCodec().BytesToString(voter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if val, ok := currValidators[valAddrStr]; ok {
|
||||
if val, ok := validators[valAddrStr]; ok {
|
||||
val.Vote = vote.Options
|
||||
currValidators[valAddrStr] = val
|
||||
validators[valAddrStr] = val
|
||||
}
|
||||
|
||||
// iterate over all delegations from voter, deduct from any delegated-to validators
|
||||
err = keeper.sk.IterateDelegations(ctx, voter, func(index int64, delegation stakingtypes.DelegationI) (stop bool) {
|
||||
err = k.sk.IterateDelegations(ctx, voter, func(index int64, delegation stakingtypes.DelegationI) (stop bool) {
|
||||
valAddrStr := delegation.GetValidatorAddr()
|
||||
|
||||
if val, ok := currValidators[valAddrStr]; ok {
|
||||
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())
|
||||
currValidators[valAddrStr] = val
|
||||
validators[valAddrStr] = val
|
||||
|
||||
// delegation shares * bonded / total shares
|
||||
votingPower := delegation.GetShares().MulInt(val.BondedTokens).Quo(val.DelegatorShares)
|
||||
@ -89,14 +81,14 @@ func (keeper Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, b
|
||||
return false, err
|
||||
}
|
||||
|
||||
return false, keeper.Votes.Remove(ctx, collections.Join(vote.ProposalId, sdk.AccAddress(voter)))
|
||||
return false, k.Votes.Remove(ctx, collections.Join(vote.ProposalId, sdk.AccAddress(voter)))
|
||||
})
|
||||
if err != nil {
|
||||
return false, false, tallyResults, err
|
||||
return math.LegacyZeroDec(), nil, fmt.Errorf("error while iterating delegations: %w", err)
|
||||
}
|
||||
|
||||
// iterate over the validators again to tally their voting power
|
||||
for _, val := range currValidators {
|
||||
for _, val := range validators {
|
||||
if len(val.Vote) == 0 {
|
||||
continue
|
||||
}
|
||||
@ -112,15 +104,52 @@ func (keeper Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, b
|
||||
totalVotingPower = totalVotingPower.Add(votingPower)
|
||||
}
|
||||
|
||||
params, err := keeper.Params.Get(ctx)
|
||||
if err != nil {
|
||||
return false, false, tallyResults, err
|
||||
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)
|
||||
if err != nil {
|
||||
return false, false, tallyResults, fmt.Errorf("error while calculating tally results: %w", err)
|
||||
}
|
||||
|
||||
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 := keeper.sk.TotalBondedTokens(ctx)
|
||||
totalBonded, err := k.sk.TotalBondedTokens(ctx)
|
||||
if err != nil {
|
||||
return false, false, tallyResults, err
|
||||
}
|
||||
@ -129,6 +158,11 @@ func (keeper Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, b
|
||||
return false, false, tallyResults, nil
|
||||
}
|
||||
|
||||
params, err := k.Params.Get(ctx)
|
||||
if err != nil {
|
||||
return false, false, tallyResults, fmt.Errorf("error while getting params: %w", err)
|
||||
}
|
||||
|
||||
// If there is not enough quorum of votes, the proposal fails
|
||||
percentVoting := totalVotingPower.Quo(math.LegacyNewDecFromInt(totalBonded))
|
||||
quorum, _ := math.LegacyNewDecFromStr(params.Quorum)
|
||||
|
||||
@ -13,9 +13,9 @@ import (
|
||||
)
|
||||
|
||||
// AddVote adds a vote on a specific proposal
|
||||
func (keeper Keeper) AddVote(ctx context.Context, proposalID uint64, voterAddr sdk.AccAddress, options v1.WeightedVoteOptions, metadata string) error {
|
||||
func (k Keeper) AddVote(ctx context.Context, proposalID uint64, voterAddr sdk.AccAddress, options v1.WeightedVoteOptions, metadata string) error {
|
||||
// Check if proposal is in voting period.
|
||||
inVotingPeriod, err := keeper.VotingPeriodProposals.Has(ctx, proposalID)
|
||||
inVotingPeriod, err := k.VotingPeriodProposals.Has(ctx, proposalID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -24,7 +24,7 @@ func (keeper Keeper) AddVote(ctx context.Context, proposalID uint64, voterAddr s
|
||||
return errors.Wrapf(types.ErrInactiveProposal, "%d", proposalID)
|
||||
}
|
||||
|
||||
err = keeper.assertMetadataLength(metadata)
|
||||
err = k.assertMetadataLength(metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -36,13 +36,13 @@ func (keeper Keeper) AddVote(ctx context.Context, proposalID uint64, voterAddr s
|
||||
}
|
||||
|
||||
vote := v1.NewVote(proposalID, voterAddr, options, metadata)
|
||||
err = keeper.Votes.Set(ctx, collections.Join(proposalID, voterAddr), vote)
|
||||
err = k.Votes.Set(ctx, collections.Join(proposalID, voterAddr), vote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// called after a vote on a proposal is cast
|
||||
err = keeper.Hooks().AfterProposalVote(ctx, proposalID, voterAddr)
|
||||
err = k.Hooks().AfterProposalVote(ctx, proposalID, voterAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -61,9 +61,9 @@ func (keeper Keeper) AddVote(ctx context.Context, proposalID uint64, voterAddr s
|
||||
}
|
||||
|
||||
// deleteVotes deletes all the votes from a given proposalID.
|
||||
func (keeper Keeper) deleteVotes(ctx context.Context, proposalID uint64) error {
|
||||
func (k Keeper) deleteVotes(ctx context.Context, proposalID uint64) error {
|
||||
rng := collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposalID)
|
||||
err := keeper.Votes.Clear(ctx, rng)
|
||||
err := k.Votes.Clear(ctx, rng)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
118
x/gov/module.go
118
x/gov/module.go
@ -4,21 +4,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
modulev1 "cosmossdk.io/api/cosmos/gov/module/v1"
|
||||
"cosmossdk.io/core/address"
|
||||
"cosmossdk.io/core/appmodule"
|
||||
store "cosmossdk.io/core/store"
|
||||
"cosmossdk.io/depinject"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
@ -26,7 +18,6 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
govclient "github.com/cosmos/cosmos-sdk/x/gov/client"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/client/cli"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
|
||||
@ -34,7 +25,6 @@ import (
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
|
||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||
)
|
||||
|
||||
const ConsensusVersion = 5
|
||||
@ -153,114 +143,6 @@ func (am AppModule) IsOnePerModuleType() {}
|
||||
// IsAppModule implements the appmodule.AppModule interface.
|
||||
func (am AppModule) IsAppModule() {}
|
||||
|
||||
func init() {
|
||||
appmodule.Register(
|
||||
&modulev1.Module{},
|
||||
appmodule.Provide(ProvideModule, ProvideKeyTable),
|
||||
appmodule.Invoke(InvokeAddRoutes, InvokeSetHooks))
|
||||
}
|
||||
|
||||
type ModuleInputs struct {
|
||||
depinject.In
|
||||
|
||||
Config *modulev1.Module
|
||||
Cdc codec.Codec
|
||||
StoreService store.KVStoreService
|
||||
ModuleKey depinject.OwnModuleKey
|
||||
MsgServiceRouter baseapp.MessageRouter
|
||||
|
||||
AccountKeeper govtypes.AccountKeeper
|
||||
BankKeeper govtypes.BankKeeper
|
||||
StakingKeeper govtypes.StakingKeeper
|
||||
DistributionKeeper govtypes.DistributionKeeper
|
||||
|
||||
// LegacySubspace is used solely for migration of x/params managed parameters
|
||||
LegacySubspace govtypes.ParamSubspace `optional:"true"`
|
||||
}
|
||||
|
||||
type ModuleOutputs struct {
|
||||
depinject.Out
|
||||
|
||||
Module appmodule.AppModule
|
||||
Keeper *keeper.Keeper
|
||||
HandlerRoute v1beta1.HandlerRoute
|
||||
}
|
||||
|
||||
func ProvideModule(in ModuleInputs) ModuleOutputs {
|
||||
defaultConfig := govtypes.DefaultConfig()
|
||||
if in.Config.MaxMetadataLen != 0 {
|
||||
defaultConfig.MaxMetadataLen = in.Config.MaxMetadataLen
|
||||
}
|
||||
|
||||
// default to governance authority if not provided
|
||||
authority := authtypes.NewModuleAddress(govtypes.ModuleName)
|
||||
if in.Config.Authority != "" {
|
||||
authority = authtypes.NewModuleAddressOrBech32Address(in.Config.Authority)
|
||||
}
|
||||
|
||||
k := keeper.NewKeeper(
|
||||
in.Cdc,
|
||||
in.StoreService,
|
||||
in.AccountKeeper,
|
||||
in.BankKeeper,
|
||||
in.StakingKeeper,
|
||||
in.DistributionKeeper,
|
||||
in.MsgServiceRouter,
|
||||
defaultConfig,
|
||||
authority.String(),
|
||||
)
|
||||
m := NewAppModule(in.Cdc, k, in.AccountKeeper, in.BankKeeper, in.LegacySubspace)
|
||||
hr := v1beta1.HandlerRoute{Handler: v1beta1.ProposalHandler, RouteKey: govtypes.RouterKey}
|
||||
|
||||
return ModuleOutputs{Module: m, Keeper: k, HandlerRoute: hr}
|
||||
}
|
||||
|
||||
func ProvideKeyTable() paramtypes.KeyTable {
|
||||
return v1.ParamKeyTable() //nolint:staticcheck // we still need this for upgrades
|
||||
}
|
||||
|
||||
func InvokeAddRoutes(keeper *keeper.Keeper, routes []v1beta1.HandlerRoute) {
|
||||
if keeper == nil || routes == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Default route order is a lexical sort by RouteKey.
|
||||
// Explicit ordering can be added to the module config if required.
|
||||
slices.SortFunc(routes, func(x, y v1beta1.HandlerRoute) int {
|
||||
return strings.Compare(x.RouteKey, y.RouteKey)
|
||||
})
|
||||
|
||||
router := v1beta1.NewRouter()
|
||||
for _, r := range routes {
|
||||
router.AddRoute(r.RouteKey, r.Handler)
|
||||
}
|
||||
keeper.SetLegacyRouter(router)
|
||||
}
|
||||
|
||||
func InvokeSetHooks(keeper *keeper.Keeper, govHooks map[string]govtypes.GovHooksWrapper) error {
|
||||
if keeper == nil || govHooks == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Default ordering is lexical by module name.
|
||||
// Explicit ordering can be added to the module config if required.
|
||||
modNames := slices.Sorted(maps.Keys(govHooks))
|
||||
order := modNames
|
||||
sort.Strings(order)
|
||||
|
||||
var multiHooks govtypes.MultiGovHooks
|
||||
for _, modName := range order {
|
||||
hook, ok := govHooks[modName]
|
||||
if !ok {
|
||||
return fmt.Errorf("can't find staking hooks for module %s", modName)
|
||||
}
|
||||
multiHooks = append(multiHooks, hook)
|
||||
}
|
||||
|
||||
keeper.SetHooks(multiHooks)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterServices registers module services.
|
||||
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
||||
msgServer := keeper.NewMsgServerImpl(am.keeper)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user