Skip to content

Commit

Permalink
feat(group)!: read the decision policy from disk in group CLI (backport
Browse files Browse the repository at this point in the history
cosmos#12551) (cosmos#12565)

* feat(group)!: read the decision policy from disk in group CLI (cosmos#12551)

(cherry picked from commit a2744ea)

# Conflicts:
#	CHANGELOG.md

* fix conflict

Co-authored-by: Julien Robert <julien@rbrt.fr>
  • Loading branch information
2 people authored and JeancarloBarrios committed Sep 28, 2024
1 parent 6158b74 commit 9df9de3
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 26 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ It is strongly recommended to upgrade to these releases as well.
* [#12453](https://github.com/cosmos/cosmos-sdk/pull/12453) Add `NewInMemoryWithKeyring` function which allows the creation of in memory `keystore` instances with a specified set of existing items.
* [#11390](https://github.com/cosmos/cosmos-sdk/pull/11390) `LatestBlockResponse` & `BlockByHeightResponse` types' `Block` filed has been deprecated and they now contains new field `sdk_block` with `proposer_address` as `string`

### CLI Breaking Changes

* (x/group) [#12551](https://github.com/cosmos/cosmos-sdk/pull/12551) read the decision policy from disk in group CLI commands.

### Bug Fixes

* (testutil/sims) [#12374](https://github.com/cosmos/cosmos-sdk/pull/12374) fix the non-determinstic behavior in simulations caused by `GenSignedMockTx` and check empty coins slice before it is used to create `banktype.MsgSend`.
Expand Down
226 changes: 205 additions & 21 deletions x/group/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func TxCmd(name string) *cobra.Command {
// This command is being handled better here, not converting to autocli
func MsgCreateGroupCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create-group <admin> <metadata> <members-json-file>",
Use: "create-group [admin] [metadata] [members-json-file]",
Short: "Create a group which is an aggregation of member accounts with associated weights and an administrator account.",
Long: `Create a group which is an aggregation of member accounts with associated weights and an administrator account.
Note, the '--from' flag is ignored as it is implied from [admin]. Members accounts can be given through a members JSON file that contains an array of members.`,
Expand Down Expand Up @@ -204,7 +204,7 @@ Set a member's weight to "0" to delete it.
// This command is being handled better here, not converting to autocli
func MsgCreateGroupWithPolicyCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create-group-with-policy <admin> <group-metadata> <group-policy-metadata> <members-json-file> <decision-policy-json-file>",
Use: "create-group-with-policy [admin] [group-metadata] [group-policy-metadata] [members-json-file] [decision-policy-json-file]",
Short: "Create a group with policy which is an aggregation of member accounts with associated weights, an administrator account and decision policy.",
Long: `Create a group with policy which is an aggregation of member accounts with associated weights,
an administrator account and decision policy. Note, the '--from' flag is ignored as it is implied from [admin].
Expand Down Expand Up @@ -263,26 +263,11 @@ and policy.json contains:
return err
}

for _, member := range members {
if _, err := math.NewPositiveDecFromString(member.Weight); err != nil {
return fmt.Errorf("invalid weight %s for %s: weight must be positive", member.Weight, member.Address)
}
}

policy, err := parseDecisionPolicy(clientCtx.Codec, args[4])
if err != nil {
return err
}

if err := policy.ValidateBasic(); err != nil {
return err
}

admin, err := clientCtx.AddressCodec.BytesToString(clientCtx.GetFromAddress())
if err != nil {
return err
}

msg, err := group.NewMsgCreateGroupWithPolicy(
admin,
members,
Expand All @@ -309,7 +294,7 @@ and policy.json contains:
// This command is being handled better here, not converting to autocli
func MsgCreateGroupPolicyCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create-group-policy <admin> <group-id> <metadata> <decision-policy-json-file>",
Use: "create-group-policy [admin] [group-id] [metadata] [decision-policy-json-file]",
Short: `Create a group policy which is an account associated with a group and a decision policy. Note, the '--from' flag is ignored as it is implied from [admin].`,
Example: fmt.Sprintf(`
%s tx group create-group-policy [admin] [group-id] [metadata] policy.json
Expand Down Expand Up @@ -352,8 +337,9 @@ Here, we can use percentage decision policy when needed, where 0 < percentage <=
return err
}

if groupID == 0 {
return errZeroGroupID
policy, err := parseDecisionPolicy(clientCtx.Codec, args[3])
if err != nil {
return err
}

policy, err := parseDecisionPolicy(clientCtx.Codec, args[3])
Expand Down Expand Up @@ -394,7 +380,7 @@ Here, we can use percentage decision policy when needed, where 0 < percentage <=
// This command is being handled better here, not converting to autocli
func MsgUpdateGroupPolicyDecisionPolicyCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "update-group-policy-decision-policy <admin> <group-policy-account> <decision-policy-json-file>",
Use: "update-group-policy-decision-policy [admin] [group-policy-account] [decision-policy-json-file]",
Short: "Update a group policy's decision policy",
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -539,3 +525,201 @@ metadata example:

return cmd
}

// MsgWithdrawProposalCmd creates a CLI command for Msg/WithdrawProposal.
func MsgWithdrawProposalCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "withdraw-proposal [proposal-id] [group-policy-admin-or-proposer]",
Short: "Withdraw a submitted proposal",
Long: `Withdraw a submitted proposal.
Parameters:
proposal-id: unique ID of the proposal.
group-policy-admin-or-proposer: either admin of the group policy or one the proposer of the proposal.
Note: --from flag will be ignored here.
`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
err := cmd.Flags().Set(flags.FlagFrom, args[1])
if err != nil {
return err
}

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}

msg := &group.MsgWithdrawProposal{
ProposalId: proposalID,
Address: clientCtx.GetFromAddress().String(),
}

if err != nil {
return err
}

if err = msg.ValidateBasic(); err != nil {
return fmt.Errorf("message validation failed: %w", err)
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}

// MsgVoteCmd creates a CLI command for Msg/Vote.
func MsgVoteCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "vote [proposal-id] [voter] [vote-option] [metadata]",
Short: "Vote on a proposal",
Long: `Vote on a proposal.
Parameters:
proposal-id: unique ID of the proposal
voter: voter account addresses.
vote-option: choice of the voter(s)
VOTE_OPTION_UNSPECIFIED: no-op
VOTE_OPTION_NO: no
VOTE_OPTION_YES: yes
VOTE_OPTION_ABSTAIN: abstain
VOTE_OPTION_NO_WITH_VETO: no-with-veto
Metadata: metadata for the vote
`,
Args: cobra.ExactArgs(4),
RunE: func(cmd *cobra.Command, args []string) error {
err := cmd.Flags().Set(flags.FlagFrom, args[1])
if err != nil {
return err
}

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}

voteOption, err := group.VoteOptionFromString(args[2])
if err != nil {
return err
}

execStr, _ := cmd.Flags().GetString(FlagExec)

msg := &group.MsgVote{
ProposalId: proposalID,
Voter: args[1],
Option: voteOption,
Metadata: args[3],
Exec: execFromString(execStr),
}
if err != nil {
return err
}

if err = msg.ValidateBasic(); err != nil {
return fmt.Errorf("message validation failed: %w", err)
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

cmd.Flags().String(FlagExec, "", "Set to 1 to try to execute proposal immediately after voting")
flags.AddTxFlagsToCmd(cmd)

return cmd
}

// MsgExecCmd creates a CLI command for Msg/MsgExec.
func MsgExecCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "exec [proposal-id]",
Short: "Execute a proposal",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}

msg := &group.MsgExec{
ProposalId: proposalID,
Executor: clientCtx.GetFromAddress().String(),
}
if err != nil {
return err
}

if err = msg.ValidateBasic(); err != nil {
return fmt.Errorf("message validation failed: %w", err)
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}

// MsgLeaveGroupCmd creates a CLI command for Msg/LeaveGroup.
func MsgLeaveGroupCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "leave-group [member-address] [group-id]",
Short: "Remove member from the group",
Long: `Remove member from the group
Parameters:
group-id: unique id of the group
member-address: account address of the group member
Note, the '--from' flag is ignored as it is implied from [member-address]
`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
cmd.Flags().Set(flags.FlagFrom, args[0])
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

groupID, err := strconv.ParseUint(args[1], 10, 64)
if err != nil {
return err
}

msg := &group.MsgLeaveGroup{
Address: clientCtx.GetFromAddress().String(),
GroupId: groupID,
}
if err = msg.ValidateBasic(); err != nil {
return fmt.Errorf("message validation failed: %w", err)
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
8 changes: 3 additions & 5 deletions x/group/client/cli/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package cli

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"

"cosmossdk.io/x/group"
Expand All @@ -12,13 +12,12 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

// parseDecisionPolicy reads and parses the decision policy.
func parseDecisionPolicy(cdc codec.Codec, decisionPolicyFile string) (group.DecisionPolicy, error) {
if decisionPolicyFile == "" {
return nil, errors.New("decision policy is required")
return nil, fmt.Errorf("decision policy is required")
}

contents, err := os.ReadFile(decisionPolicyFile)
contents, err := ioutil.ReadFile(decisionPolicyFile)
if err != nil {
return nil, err
}
Expand All @@ -31,7 +30,6 @@ func parseDecisionPolicy(cdc codec.Codec, decisionPolicyFile string) (group.Deci
return policy, nil
}

// parseMembers reads and parses the members.
func parseMembers(membersFile string) ([]group.MemberRequest, error) {
members := struct {
Members []group.MemberRequest `json:"members"`
Expand Down

0 comments on commit 9df9de3

Please sign in to comment.