Skip to content

Commit

Permalink
feat: Add CLI for new gov proposal (#11013)
Browse files Browse the repository at this point in the history
* feat: Add CLI for new proposal

* Add changelog

* Add comment

* Add metadata

* Add tests for parsing

* Add CLI test

* Use legacy submit proposal in other modules

* Update x/gov/client/cli/tx.go

* Update x/gov/client/cli/tx.go

* Update x/gov/client/cli/tx.go

* Remove deprecated cobra field

* Update x/gov/client/cli/tx.go

Co-authored-by: atheeshp <[email protected]>
  • Loading branch information
amaury1093 and atheeshp authored Jan 31, 2022
1 parent 24c97d5 commit 62d9790
Show file tree
Hide file tree
Showing 10 changed files with 333 additions and 39 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

* [\#9594](https://github.com/cosmos/cosmos-sdk/pull/9594) Remove legacy REST API. Please see the [REST Endpoints Migration guide](https://docs.cosmos.network/master/migrations/rest.html) to migrate to the new REST endpoints.
* [\#9995](https://github.com/cosmos/cosmos-sdk/pull/9995) Increased gas cost for creating proposals.
* [\#11013](https://github.com/cosmos/cosmos-sdk/pull/) The `tx gov submit-proposal` command has changed syntax to support the new Msg-based gov proposals. To access the old CLI command, please use `tx gov submit-legacy-proposal`.

### CLI Breaking Changes

Expand Down
4 changes: 2 additions & 2 deletions x/auth/client/testutil/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -1503,7 +1503,7 @@ func (s *IntegrationTestSuite) TestAuxSigner() {
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
_, err := govtestutil.MsgSubmitProposal(
_, err := govtestutil.MsgSubmitLegacyProposal(
val.ClientCtx,
val.Address.String(),
"test",
Expand Down Expand Up @@ -1747,7 +1747,7 @@ func (s *IntegrationTestSuite) TestAuxToFee() {
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
res, err := govtestutil.MsgSubmitProposal(
res, err := govtestutil.MsgSubmitLegacyProposal(
val.ClientCtx,
tipper.String(),
"test",
Expand Down
2 changes: 1 addition & 1 deletion x/authz/client/testutil/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.Require().NoError(err)

// create a proposal with deposit
_, err = govtestutil.MsgSubmitProposal(val.ClientCtx, val.Address.String(),
_, err = govtestutil.MsgSubmitLegacyProposal(val.ClientCtx, val.Address.String(),
"Text Proposal 1", "Where is the title!?", govv1beta1.ProposalTypeText,
fmt.Sprintf("--%s=%s", govcli.FlagDeposit, sdk.NewCoin(s.cfg.BondDenom, govv1beta2.DefaultMinDepositTokens).String()))
s.Require().NoError(err)
Expand Down
4 changes: 2 additions & 2 deletions x/feegrant/client/testutil/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ func (s *IntegrationTestSuite) TestTxWithFeeGrant() {

// granted fee allowance for an account which is not in state and creating
// any tx with it by using --fee-account shouldn't fail
out, err := govtestutil.MsgSubmitProposal(val.ClientCtx, grantee.String(),
out, err := govtestutil.MsgSubmitLegacyProposal(val.ClientCtx, grantee.String(),
"Text Proposal", "No desc", govv1beta1.ProposalTypeText,
fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter.String()),
)
Expand Down Expand Up @@ -892,7 +892,7 @@ func (s *IntegrationTestSuite) TestFilteredFeeAllowance() {
{
"valid proposal tx",
func() (testutil.BufferWriter, error) {
return govtestutil.MsgSubmitProposal(val.ClientCtx, grantee.String(),
return govtestutil.MsgSubmitLegacyProposal(val.ClientCtx, grantee.String(),
"Text Proposal", "No desc", govv1beta1.ProposalTypeText,
fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter.String()),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(100))).String()),
Expand Down
53 changes: 51 additions & 2 deletions x/gov/client/cli/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,20 @@ import (

"github.com/spf13/pflag"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
govutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils"
)

func parseSubmitProposalFlags(fs *pflag.FlagSet) (*proposal, error) {
proposal := &proposal{}
type legacyProposal struct {
Title string
Description string
Type string
Deposit string
}

func parseSubmitLegacyProposalFlags(fs *pflag.FlagSet) (*legacyProposal, error) {
proposal := &legacyProposal{}
proposalFile, _ := fs.GetString(FlagProposal)

if proposalFile == "" {
Expand Down Expand Up @@ -42,3 +51,43 @@ func parseSubmitProposalFlags(fs *pflag.FlagSet) (*proposal, error) {

return proposal, nil
}

// proposal defines the new Msg-based proposal.
type proposal struct {
// Msgs defines an array of sdk.Msgs proto-JSON-encoded as Anys.
Messages []json.RawMessage
Metadata []byte
Deposit string
}

func parseSubmitProposal(cdc codec.Codec, path string) ([]sdk.Msg, []byte, sdk.Coins, error) {
var proposal proposal

contents, err := os.ReadFile(path)
if err != nil {
return nil, nil, nil, err
}

err = json.Unmarshal(contents, &proposal)
if err != nil {
return nil, nil, nil, err
}

msgs := make([]sdk.Msg, len(proposal.Messages))
for i, anyJSON := range proposal.Messages {
var msg sdk.Msg
err := cdc.UnmarshalInterfaceJSON(anyJSON, &msg)
if err != nil {
return nil, nil, nil, err
}

msgs[i] = msg
}

deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit)
if err != nil {
return nil, nil, nil, err
}

return msgs, proposal.Metadata, deposit, nil
}
104 changes: 97 additions & 7 deletions x/gov/client/cli/parse_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
package cli

import (
"encoding/base64"
"fmt"
"testing"

"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

func TestParseSubmitProposalFlags(t *testing.T) {
func TestParseSubmitLegacyProposalFlags(t *testing.T) {
okJSON := testutil.WriteToNewTempFile(t, `
{
"title": "Test Proposal",
Expand All @@ -19,21 +29,21 @@ func TestParseSubmitProposalFlags(t *testing.T) {
`)

badJSON := testutil.WriteToNewTempFile(t, "bad json")
fs := NewCmdSubmitProposal().Flags()
fs := NewCmdSubmitLegacyProposal().Flags()

// nonexistent json
fs.Set(FlagProposal, "fileDoesNotExist")
_, err := parseSubmitProposalFlags(fs)
_, err := parseSubmitLegacyProposalFlags(fs)
require.Error(t, err)

// invalid json
fs.Set(FlagProposal, badJSON.Name())
_, err = parseSubmitProposalFlags(fs)
_, err = parseSubmitLegacyProposalFlags(fs)
require.Error(t, err)

// ok json
fs.Set(FlagProposal, okJSON.Name())
proposal1, err := parseSubmitProposalFlags(fs)
proposal1, err := parseSubmitLegacyProposalFlags(fs)
require.Nil(t, err, "unexpected error")
require.Equal(t, "Test Proposal", proposal1.Title)
require.Equal(t, "My awesome proposal", proposal1.Description)
Expand All @@ -43,7 +53,7 @@ func TestParseSubmitProposalFlags(t *testing.T) {
// flags that can't be used with --proposal
for _, incompatibleFlag := range ProposalFlags {
fs.Set(incompatibleFlag, "some value")
_, err := parseSubmitProposalFlags(fs)
_, err := parseSubmitLegacyProposalFlags(fs)
require.Error(t, err)
fs.Set(incompatibleFlag, "")
}
Expand All @@ -54,7 +64,7 @@ func TestParseSubmitProposalFlags(t *testing.T) {
fs.Set(FlagDescription, proposal1.Description)
fs.Set(FlagProposalType, proposal1.Type)
fs.Set(FlagDeposit, proposal1.Deposit)
proposal2, err := parseSubmitProposalFlags(fs)
proposal2, err := parseSubmitLegacyProposalFlags(fs)

require.Nil(t, err, "unexpected error")
require.Equal(t, proposal1.Title, proposal2.Title)
Expand All @@ -67,3 +77,83 @@ func TestParseSubmitProposalFlags(t *testing.T) {
err = badJSON.Close()
require.Nil(t, err, "unexpected error")
}

func TestParseSubmitProposal(t *testing.T) {
_, _, addr := testdata.KeyTestPubAddr()
interfaceRegistry := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
banktypes.RegisterInterfaces(interfaceRegistry)
stakingtypes.RegisterInterfaces(interfaceRegistry)
v1beta1.RegisterInterfaces(interfaceRegistry)
v1beta2.RegisterInterfaces(interfaceRegistry)
expectedMetadata := []byte{42}

okJSON := testutil.WriteToNewTempFile(t, fmt.Sprintf(`
{
"messages": [
{
"@type": "/cosmos.bank.v1beta1.MsgSend",
"from_address": "%s",
"to_address": "%s",
"amount":[{"denom": "stake","amount": "10"}]
},
{
"@type": "/cosmos.staking.v1beta1.MsgDelegate",
"delegator_address": "%s",
"validator_address": "%s",
"amount":{"denom": "stake","amount": "10"}
},
{
"@type": "/cosmos.gov.v1beta2.MsgExecLegacyContent",
"authority": "%s",
"content": {
"@type": "/cosmos.gov.v1beta1.TextProposal",
"title": "My awesome title",
"description": "My awesome description"
}
}
],
"metadata": "%s",
"deposit": "1000test"
}
`, addr, addr, addr, addr, addr, base64.StdEncoding.EncodeToString(expectedMetadata)))

badJSON := testutil.WriteToNewTempFile(t, "bad json")

// nonexistent json
_, _, _, err := parseSubmitProposal(cdc, "fileDoesNotExist")
require.Error(t, err)

// invalid json
_, _, _, err = parseSubmitProposal(cdc, badJSON.Name())
require.Error(t, err)

// ok json
msgs, metadata, deposit, err := parseSubmitProposal(cdc, okJSON.Name())
require.NoError(t, err, "unexpected error")
require.Equal(t, sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(1000))), deposit)
require.Equal(t, expectedMetadata, metadata)
require.Len(t, msgs, 3)
msg1, ok := msgs[0].(*banktypes.MsgSend)
require.True(t, ok)
require.Equal(t, addr.String(), msg1.FromAddress)
require.Equal(t, addr.String(), msg1.ToAddress)
require.Equal(t, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))), msg1.Amount)
msg2, ok := msgs[1].(*stakingtypes.MsgDelegate)
require.True(t, ok)
require.Equal(t, addr.String(), msg2.DelegatorAddress)
require.Equal(t, addr.String(), msg2.ValidatorAddress)
require.Equal(t, sdk.NewCoin("stake", sdk.NewInt(10)), msg2.Amount)
msg3, ok := msgs[2].(*v1beta2.MsgExecLegacyContent)
require.True(t, ok)
require.Equal(t, addr.String(), msg3.Authority)
textProp, ok := msg3.Content.GetCachedValue().(*v1beta1.TextProposal)
require.True(t, ok)
require.Equal(t, "My awesome title", textProp.Title)
require.Equal(t, "My awesome description", textProp.Description)

err = okJSON.Close()
require.Nil(t, err, "unexpected error")
err = badJSON.Close()
require.Nil(t, err, "unexpected error")
}
Loading

0 comments on commit 62d9790

Please sign in to comment.