Skip to content

Commit

Permalink
Support a proposal JSON file in submit-proposal
Browse files Browse the repository at this point in the history
Closes #1852.
Closes #1776.
  • Loading branch information
mslipper committed Aug 21, 2018
1 parent 204762b commit f7d8675
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 7 deletions.
1 change: 1 addition & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ FEATURES

* Gaia CLI (`gaiacli`)
* [cli] Cmds to query staking pool and params
* [gov][cli] #2062 added `--proposal` flag to `submit-proposal` that allows a JSON file containing a proposal to be passed in

* Gaia

Expand Down
17 changes: 17 additions & 0 deletions docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ Uuse the CLI to create a new proposal:
simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms
```

Or, via a json file:

```bash
simplegovcli propose --proposal="path/to/proposal.json"
```

Where proposal.json contains:

```json
{
"title": "Voting Period Update",
"description": "Should we change the proposal voting period to 3 weeks?",
"type": "Text",
"deposit": "300Atoms"
}
```

Get the details of your newly created proposal:

```bash
Expand Down
82 changes: 75 additions & 7 deletions x/gov/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"io/ioutil"
"encoding/json"
"strings"
)

const (
Expand All @@ -28,18 +31,51 @@ const (
flagDepositer = "depositer"
flagStatus = "status"
flagLatestProposalIDs = "latest"
flagProposal = "proposal"
)

type proposal struct {
Title string
Description string
Type string
Deposit string
}

var proposalFlags = []string{
flagTitle,
flagDescription,
flagProposalType,
flagDeposit,
}

// GetCmdSubmitProposal implements submitting a proposal transaction command.
func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "submit-proposal",
Short: "Submit a proposal along with an initial deposit",
Long: strings.TrimSpace(`
Submit a proposal along with an initial deposit. Proposal title, description, type and deposit can be given directly or through a proposal JSON file. For example:
$ gaiacli gov submit-proposal --proposal="path/to/proposal.json"
where proposal.json contains:
{
"title": "Test Proposal",
"description": "My awesome proposal",
"type": "Text",
"deposit": "1000test"
}
is equivalent to
$ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="1000test"
`),
RunE: func(cmd *cobra.Command, args []string) error {
title := viper.GetString(flagTitle)
description := viper.GetString(flagDescription)
strProposalType := viper.GetString(flagProposalType)
initialDeposit := viper.GetString(flagDeposit)
proposal, err := parseSubmitProposalFlags()
if err != nil {
return err
}

txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
Expand All @@ -52,17 +88,17 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
return err
}

amount, err := sdk.ParseCoins(initialDeposit)
amount, err := sdk.ParseCoins(proposal.Deposit)
if err != nil {
return err
}

proposalType, err := gov.ProposalTypeFromString(strProposalType)
proposalType, err := gov.ProposalTypeFromString(proposal.Type)
if err != nil {
return err
}

msg := gov.NewMsgSubmitProposal(title, description, proposalType, fromAddr, amount)
msg := gov.NewMsgSubmitProposal(proposal.Title, proposal.Description, proposalType, fromAddr, amount)

err = msg.ValidateBasic()
if err != nil {
Expand All @@ -80,10 +116,42 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
cmd.Flags().String(flagDescription, "", "description of proposal")
cmd.Flags().String(flagProposalType, "", "proposalType of proposal")
cmd.Flags().String(flagDeposit, "", "deposit of proposal")
cmd.Flags().String(flagProposal, "", "proposal file path (if this path is given, other proposal flags are ignored)")

return cmd
}

func parseSubmitProposalFlags() (*proposal, error) {
proposal := &proposal{}
proposalFile := viper.GetString(flagProposal)

if proposalFile == "" {
proposal.Title = viper.GetString(flagTitle)
proposal.Description = viper.GetString(flagDescription)
proposal.Type = viper.GetString(flagProposalType)
proposal.Deposit = viper.GetString(flagDeposit)
return proposal, nil
}

for _, flag := range proposalFlags {
if viper.GetString(flag) != "" {
return nil, fmt.Errorf("--%s flag provided alongside --proposal, which is a noop", flag)
}
}

contents, err := ioutil.ReadFile(proposalFile)
if err != nil {
return nil, err
}

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

return proposal, nil
}

// GetCmdDeposit implements depositing tokens for an active proposal.
func GetCmdDeposit(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Expand Down
70 changes: 70 additions & 0 deletions x/gov/client/cli/tx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package cli

import (
"testing"
"github.com/spf13/viper"
"io/ioutil"
"github.com/stretchr/testify/require"
)

func TestParseSubmitProposalFlags(t *testing.T) {
okJSON, err := ioutil.TempFile("", "proposal")
require.Nil(t, err, "unexpected error")
okJSON.WriteString(`
{
"title": "Test Proposal",
"description": "My awesome proposal",
"type": "Text",
"deposit": "1000test"
}
`)

badJSON, err := ioutil.TempFile("", "proposal")
require.Nil(t, err, "unexpected error")
badJSON.WriteString("bad json")

// nonexistent json
viper.Set(flagProposal, "fileDoesNotExist")
_, err = parseSubmitProposalFlags()
require.Error(t, err)

// invalid json
viper.Set(flagProposal, badJSON.Name())
_, err = parseSubmitProposalFlags()
require.Error(t, err)

// ok json
viper.Set(flagProposal, okJSON.Name())
proposal1, err := parseSubmitProposalFlags()
require.Nil(t, err, "unexpected error")
require.Equal(t, "Test Proposal", proposal1.Title)
require.Equal(t, "My awesome proposal", proposal1.Description)
require.Equal(t, "Text", proposal1.Type)
require.Equal(t, "1000test", proposal1.Deposit)

// flags that can't be used with --proposal
for _, incompatibleFlag := range proposalFlags {
viper.Set(incompatibleFlag, "some value")
_, err := parseSubmitProposalFlags()
require.Error(t, err)
viper.Set(incompatibleFlag, "")
}

// no --proposal, only flags
viper.Set(flagProposal, "")
viper.Set(flagTitle, proposal1.Title)
viper.Set(flagDescription, proposal1.Description)
viper.Set(flagProposalType, proposal1.Type)
viper.Set(flagDeposit, proposal1.Deposit)
proposal2, err := parseSubmitProposalFlags()
require.Nil(t, err, "unexpected error")
require.Equal(t, proposal1.Title, proposal2.Title)
require.Equal(t, proposal1.Description, proposal2.Description)
require.Equal(t, proposal1.Type, proposal2.Type)
require.Equal(t, proposal1.Deposit, proposal2.Deposit)

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

0 comments on commit f7d8675

Please sign in to comment.