Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: add expedited propsals support #3149

Merged
merged 22 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func NewAnteHandler(opts HandlerOptions) (sdk.AnteHandler, error) {
ante.NewValidateMemoDecorator(opts.AccountKeeper),
ante.NewConsumeGasForTxSizeDecorator(opts.AccountKeeper),
NewGovVoteDecorator(opts.Codec, opts.StakingKeeper),
NewGovExpeditedProposalsDecorator(opts.Codec),
gaiafeeante.NewFeeDecorator(opts.GlobalFeeSubspace, opts.StakingKeeper),
ante.NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper, opts.TxFeeChecker),
ante.NewSetPubKeyDecorator(opts.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
Expand Down
83 changes: 83 additions & 0 deletions ante/gov_expedited_ante.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package ante

import (
errorsmod "cosmossdk.io/errors"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"

gaiaerrors "github.com/cosmos/gaia/v18/types/errors"
)

var expeditedPropDecoratorEnabled = true

// SetExpeditedProposalsEnabled toggles the expedited proposals decorator on/off.
// Should only be used in testing - this is to allow simtests to pass.
func SetExpeditedProposalsEnabled(val bool) {
expeditedPropDecoratorEnabled = val
}

var expeditedPropsWhitelist = map[string]struct{}{
"/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade": {},
"/cosmos.upgrade.v1beta1.MsgCancelUpgrade": {},
// legacy proposals can still be submitted using govv1.MsgSubmitProposal
"/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal": {},
"/cosmos.upgrade.v1beta1.CancelSoftwareUpgradeProposal": {},
}

// Check if the proposal is whitelisted for expedited voting.
type GovExpeditedProposalsDecorator struct {
cdc codec.BinaryCodec
}

func NewGovExpeditedProposalsDecorator(cdc codec.BinaryCodec) GovExpeditedProposalsDecorator {
return GovExpeditedProposalsDecorator{
cdc: cdc,
}
}

// AnteHandle checks if the proposal is whitelisted for expedited voting.
// Only proposals submitted using "gaiad tx gov submit-proposal" can be expedited.
// Legacy proposals submitted using "gaiad tx gov submit-legacy-proposal" cannot be marked as expedited.
func (g GovExpeditedProposalsDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
if expeditedPropDecoratorEnabled {
for _, msg := range tx.GetMsgs() {
prop, ok := msg.(*govv1.MsgSubmitProposal)
if !ok {
continue
}
if prop.Expedited {
if err := g.validateExpeditedGovProp(prop); err != nil {
return ctx, err
}
}
}
}
return next(ctx, tx, simulate)
}

func (g GovExpeditedProposalsDecorator) isWhitelisted(msgType string) bool {
_, ok := expeditedPropsWhitelist[msgType]
return ok
}

func (g GovExpeditedProposalsDecorator) validateExpeditedGovProp(prop *govv1.MsgSubmitProposal) error {
msgs := prop.GetMessages()
if len(msgs) == 0 {
return gaiaerrors.ErrInvalidExpeditedProposal
}
for _, message := range msgs {
// in case of legacy content submitted using govv1.MsgSubmitProposal
if sdkMsg, isLegacy := message.GetCachedValue().(*govv1.MsgExecLegacyContent); isLegacy {
if !g.isWhitelisted(sdkMsg.Content.TypeUrl) {
return errorsmod.Wrapf(gaiaerrors.ErrInvalidExpeditedProposal, "invalid Msg type: %s", sdkMsg.Content.TypeUrl)
}
continue
}
if !g.isWhitelisted(message.TypeUrl) {
return errorsmod.Wrapf(gaiaerrors.ErrInvalidExpeditedProposal, "invalid Msg type: %s", message.TypeUrl)
}
}
return nil
}
270 changes: 270 additions & 0 deletions ante/gov_expedited_ante_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
package ante_test

import (
"testing"

"github.com/stretchr/testify/require"

sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"

"github.com/cosmos/gaia/v18/ante"
"github.com/cosmos/gaia/v18/app/helpers"
)

func TestGovExpeditedProposalsDecorator(t *testing.T) {
gaiaApp := helpers.Setup(t)

testCases := []struct {
name string
ctx sdk.Context
msgs []sdk.Msg
expectErr bool
}{
// these cases should pass
{
name: "expedited - govv1.MsgSubmitProposal - MsgSoftwareUpgrade",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newGovProp([]sdk.Msg{&upgradetypes.MsgSoftwareUpgrade{
Authority: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
Plan: upgradetypes.Plan{
Name: "upgrade plan-plan",
Info: "some text here",
Height: 123456789,
},
}}, true),
},
expectErr: false,
},
{
name: "expedited - govv1.MsgSubmitProposal - LegacySoftwareUpgrade",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newLegacyUpgradeProp(true), // expedite
},
expectErr: false,
},
{
name: "expedited - govv1.MsgSubmitProposal - MsgCancelUpgrade",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newGovProp([]sdk.Msg{&upgradetypes.MsgCancelUpgrade{
Authority: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
}}, true),
},
expectErr: false,
},
{
name: "expedited - govv1.MsgSubmitProposal - LegacyCancelUpgrade",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newLegacyCancelProp(true), // expedite
},
expectErr: false,
},
{
name: "normal - govv1.MsgSubmitProposal - LegacySoftwareUpgrade",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newLegacyUpgradeProp(false), // normal
},
expectErr: false,
},
{
name: "normal - govv1.MsgSubmitProposal - LegacyCancelUpgrade",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newLegacyCancelProp(false), // normal
},
expectErr: false,
},
{
name: "normal - govv1.MsgSubmitProposal - TextProposal",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newLegacyTextProp(false), // normal
},
expectErr: false,
},
{
name: "normal - govv1.MsgSubmitProposal - MsgCommunityPoolSpend",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newGovProp([]sdk.Msg{&distrtypes.MsgCommunityPoolSpend{
Authority: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
Recipient: sdk.AccAddress{}.String(),
Amount: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100))),
}}, false), // normal
},
expectErr: false,
},
{
name: "normal - govv1.MsgSubmitProposal - MsgTransfer",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newGovProp([]sdk.Msg{&banktypes.MsgSend{
FromAddress: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
ToAddress: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
Amount: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100))),
}}, false), // normal
},
expectErr: false,
},
{
name: "normal - govv1.MsgSubmitProposal - MsgUpdateParams",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newGovProp([]sdk.Msg{&banktypes.MsgUpdateParams{
Authority: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
}}, false),
},
expectErr: false,
},
// legacy proposals - antehandler should not affect them
// submitted using "gaiad tx gov submit-legacy-proposal"
{
name: "normal - govv1beta.MsgSubmitProposal - LegacySoftwareUpgrade",
ctx: sdk.Context{},
msgs: []sdk.Msg{newGovV1BETA1LegacyUpgradeProp()},
expectErr: false,
},
{
name: "normal - govv1beta.MsgSubmitProposal - LegacyCancelSoftwareUpgrade",
ctx: sdk.Context{},
msgs: []sdk.Msg{newGovV1BETA1LegacyCancelUpgradeProp()},
expectErr: false,
},
// these cases should fail
// these are normal proposals, not whitelisted for expedited voting
{
name: "fail - expedited - govv1.MsgSubmitProposal - Empty",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newGovProp([]sdk.Msg{}, true),
},
expectErr: true,
},
{
name: "fail - expedited - govv1.MsgSubmitProposal - TextProposal",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newLegacyTextProp(true), // expedite
},
expectErr: true,
},
{
name: "fail - expedited - govv1.MsgSubmitProposal - MsgCommunityPoolSpend",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newGovProp([]sdk.Msg{&distrtypes.MsgCommunityPoolSpend{
Authority: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
Recipient: sdk.AccAddress{}.String(),
Amount: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100))),
}}, true),
},
expectErr: true,
},
{
name: "fail - expedited - govv1.MsgSubmitProposal - MsgTransfer",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newGovProp([]sdk.Msg{&banktypes.MsgSend{
FromAddress: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
ToAddress: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
Amount: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100))),
}}, true),
},
expectErr: true,
},
{
name: "fail - expedited - govv1.MsgSubmitProposal - MsgUpdateParams",
ctx: sdk.Context{},
msgs: []sdk.Msg{
newGovProp([]sdk.Msg{&banktypes.MsgUpdateParams{
Authority: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
}}, true),
},
expectErr: true,
},
}

for _, tc := range testCases {
tc := tc

t.Run(tc.name, func(t *testing.T) {
txCfg := gaiaApp.GetTxConfig()
decorator := ante.NewGovExpeditedProposalsDecorator(gaiaApp.AppCodec())

txBuilder := txCfg.NewTxBuilder()
require.NoError(t, txBuilder.SetMsgs(tc.msgs...))

_, err := decorator.AnteHandle(tc.ctx, txBuilder.GetTx(), false,
func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { return ctx, nil })
if tc.expectErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}

func newLegacyTextProp(expedite bool) *govv1.MsgSubmitProposal {
testProposal := govv1beta1.NewTextProposal("Proposal", "Test as normal proposal")
msgContent, err := govv1.NewLegacyContent(testProposal, "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn")
if err != nil {
return nil
}
return newGovProp([]sdk.Msg{msgContent}, expedite)
}

func newLegacyUpgradeProp(expedite bool) *govv1.MsgSubmitProposal {
prop := upgradetypes.NewSoftwareUpgradeProposal("test legacy upgrade", "test legacy upgrade", upgradetypes.Plan{
Name: "upgrade plan-plan",
Info: "some text here",
Height: 123456789,
})
msgContent, err := govv1.NewLegacyContent(prop, "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn")
if err != nil {
return nil
}
return newGovProp([]sdk.Msg{msgContent}, expedite)
}

func newGovV1BETA1LegacyUpgradeProp() *govv1beta1.MsgSubmitProposal {
legacyContent := upgradetypes.NewSoftwareUpgradeProposal("test legacy upgrade", "test legacy upgrade", upgradetypes.Plan{
Name: "upgrade plan-plan",
Info: "some text here",
Height: 123456789,
})

msg, _ := govv1beta1.NewMsgSubmitProposal(legacyContent, sdk.NewCoins(), sdk.AccAddress{})
return msg
}

func newGovV1BETA1LegacyCancelUpgradeProp() *govv1beta1.MsgSubmitProposal {
legacyContent := upgradetypes.NewCancelSoftwareUpgradeProposal("test legacy upgrade", "test legacy upgrade")

msg, _ := govv1beta1.NewMsgSubmitProposal(legacyContent, sdk.NewCoins(), sdk.AccAddress{})
return msg
}

func newLegacyCancelProp(expedite bool) *govv1.MsgSubmitProposal {
prop := upgradetypes.NewCancelSoftwareUpgradeProposal("test legacy upgrade", "test legacy upgrade")
msgContent, err := govv1.NewLegacyContent(prop, "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn")
if err != nil {
return nil
}
return newGovProp([]sdk.Msg{msgContent}, expedite)
}

func newGovProp(msgs []sdk.Msg, expedite bool) *govv1.MsgSubmitProposal {
msg, _ := govv1.NewMsgSubmitProposal(msgs, sdk.NewCoins(), sdk.AccAddress{}.String(), "", "expedite", "expedite", expedite)
// fmt.Println("### msg ###", msg, "err", err)
return msg
}
Loading
Loading