Skip to content

Commit

Permalink
feat(cmd): Add support for governance operations
Browse files Browse the repository at this point in the history
  • Loading branch information
kostko authored and matevz committed Jun 21, 2023
1 parent e7970ca commit f12fa8c
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 6 deletions.
3 changes: 3 additions & 0 deletions cmd/account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package account

import (
"github.com/spf13/cobra"

"github.com/oasisprotocol/cli/cmd/account/governance"
)

var Cmd = &cobra.Command{
Expand All @@ -23,4 +25,5 @@ func init() {
Cmd.AddCommand(transferCmd)
Cmd.AddCommand(undelegateCmd)
Cmd.AddCommand(withdrawCmd)
Cmd.AddCommand(governance.Cmd)
}
129 changes: 129 additions & 0 deletions cmd/account/governance/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package governance

import (
"context"
"encoding/json"
"fmt"
"os"
"strconv"

"github.com/spf13/cobra"

governance "github.com/oasisprotocol/oasis-core/go/governance/api"
upgrade "github.com/oasisprotocol/oasis-core/go/upgrade/api"
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/connection"

"github.com/oasisprotocol/cli/cmd/common"
cliConfig "github.com/oasisprotocol/cli/config"
)

var (
govCreateProposalCmd = &cobra.Command{
Use: "create-proposal",
Short: "Create a governance proposal",
}

govCreateProposalUpgradeCmd = &cobra.Command{
Use: "upgrade <descriptor.json>",
Short: "Create an upgrade descriptor governance proposal",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
cfg := cliConfig.Global()
npa := common.GetNPASelection(cfg)
txCfg := common.GetTransactionConfig()
filename := args[0]

if npa.Account == nil {
cobra.CheckErr("no accounts configured in your wallet")
}

// When not in offline mode, connect to the given network endpoint.
ctx := context.Background()
var conn connection.Connection
if !txCfg.Offline {
var err error
conn, err = connection.Connect(ctx, npa.Network)
cobra.CheckErr(err)
}

// Load upgrade descriptor.
rawDescriptor, err := os.ReadFile(filename)
cobra.CheckErr(err)

// Parse upgrade descriptor.
var descriptor upgrade.Descriptor
if err = json.Unmarshal(rawDescriptor, &descriptor); err != nil {
cobra.CheckErr(fmt.Errorf("malformed upgrade descriptor: %w", err))
}
if err = descriptor.ValidateBasic(); err != nil {
cobra.CheckErr(fmt.Errorf("invalid upgrade descriptor: %w", err))
}

// Prepare transaction.
tx := governance.NewSubmitProposalTx(0, nil, &governance.ProposalContent{
Upgrade: &governance.UpgradeProposal{
Descriptor: descriptor,
},
})

acc := common.LoadAccount(cfg, npa.AccountName)
sigTx, err := common.SignConsensusTransaction(ctx, npa, acc, conn, tx)
cobra.CheckErr(err)

common.BroadcastOrExportTransaction(ctx, npa.ParaTime, conn, sigTx, nil, nil)
},
}

govCreateProposalCancelUpgradeCmd = &cobra.Command{
Use: "cancel-upgrade <proposal-id>",
Short: "Create a cancel upgrade governance proposal",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
cfg := cliConfig.Global()
npa := common.GetNPASelection(cfg)
txCfg := common.GetTransactionConfig()
rawProposalID := args[0]

if npa.Account == nil {
cobra.CheckErr("no accounts configured in your wallet")
}

// When not in offline mode, connect to the given network endpoint.
ctx := context.Background()
var conn connection.Connection
if !txCfg.Offline {
var err error
conn, err = connection.Connect(ctx, npa.Network)
cobra.CheckErr(err)
}

// Parse proposal ID.
proposalID, err := strconv.ParseUint(rawProposalID, 10, 64)
cobra.CheckErr(err)

// Prepare transaction.
tx := governance.NewSubmitProposalTx(0, nil, &governance.ProposalContent{
CancelUpgrade: &governance.CancelUpgradeProposal{
ProposalID: proposalID,
},
})

acc := common.LoadAccount(cfg, npa.AccountName)
sigTx, err := common.SignConsensusTransaction(ctx, npa, acc, conn, tx)
cobra.CheckErr(err)

common.BroadcastOrExportTransaction(ctx, npa.ParaTime, conn, sigTx, nil, nil)
},
}
)

func init() {
govCreateProposalUpgradeCmd.Flags().AddFlagSet(common.SelectorNAFlags)
govCreateProposalUpgradeCmd.Flags().AddFlagSet(common.TxFlags)

govCreateProposalCancelUpgradeCmd.Flags().AddFlagSet(common.SelectorNAFlags)
govCreateProposalCancelUpgradeCmd.Flags().AddFlagSet(common.TxFlags)

govCreateProposalCmd.AddCommand(govCreateProposalUpgradeCmd)
govCreateProposalCmd.AddCommand(govCreateProposalCancelUpgradeCmd)
}
18 changes: 18 additions & 0 deletions cmd/account/governance/governance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package governance

import (
"github.com/spf13/cobra"
)

var Cmd = &cobra.Command{
Use: "governance",
Short: "Governance operations",
Aliases: []string{"gov"},
}

func init() {
Cmd.AddCommand(govCreateProposalCmd)
Cmd.AddCommand(govCastVoteCmd)
Cmd.AddCommand(govShowCmd)
Cmd.AddCommand(govListCmd)
}
68 changes: 68 additions & 0 deletions cmd/account/governance/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package governance

import (
"context"
"fmt"

"github.com/spf13/cobra"

"github.com/oasisprotocol/oasis-sdk/client-sdk/go/connection"

"github.com/oasisprotocol/cli/cmd/common"
cliConfig "github.com/oasisprotocol/cli/config"
"github.com/oasisprotocol/cli/table"
)

var govListCmd = &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
Short: "List governance proposals",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
cfg := cliConfig.Global()
npa := common.GetNPASelection(cfg)

// When not in offline mode, connect to the given network endpoint.
ctx := context.Background()
conn, err := connection.Connect(ctx, npa.Network)
cobra.CheckErr(err)

table := table.New()
table.SetHeader([]string{"ID", "Kind", "Submitter", "Created At", "Closes At", "State"})

proposals, err := conn.Consensus().Governance().Proposals(ctx, common.GetHeight())
if err != nil {
cobra.CheckErr(fmt.Errorf("failed to fetch proposals: %w", err))
}

var output [][]string
for _, proposal := range proposals {
var kind string
switch {
case proposal.Content.Upgrade != nil:
kind = "upgrade"
case proposal.Content.CancelUpgrade != nil:
kind = fmt.Sprintf("cancel upgrade %d", proposal.Content.CancelUpgrade.ProposalID)
default:
kind = "unknown"
}

output = append(output, []string{
fmt.Sprintf("%d", proposal.ID),
kind,
proposal.Submitter.String(),
fmt.Sprintf("%d", proposal.CreatedAt),
fmt.Sprintf("%d", proposal.ClosesAt),
proposal.State.String(),
})
}

table.AppendBulk(output)
table.Render()
},
}

func init() {
govListCmd.Flags().AddFlagSet(common.SelectorNFlags)
govShowCmd.Flags().AddFlagSet(common.HeightFlag)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package network
package governance

import (
"context"
Expand All @@ -22,8 +22,8 @@ import (
"github.com/oasisprotocol/cli/metadata"
)

var governanceProposalCmd = &cobra.Command{
Use: "governance-proposal <proposal-id>",
var govShowCmd = &cobra.Command{
Use: "show <proposal-id>",
Short: "Show proposal status by ID",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
Expand Down Expand Up @@ -292,6 +292,6 @@ func (p entityStakes) Less(i, j int) bool { return p[i].Stake.Cmp(&p[j].Stake) <
func (p entityStakes) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func init() {
governanceProposalCmd.Flags().AddFlagSet(common.SelectorNFlags)
governanceProposalCmd.Flags().AddFlagSet(common.HeightFlag)
govShowCmd.Flags().AddFlagSet(common.SelectorNFlags)
govShowCmd.Flags().AddFlagSet(common.HeightFlag)
}
72 changes: 72 additions & 0 deletions cmd/account/governance/vote.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package governance

import (
"context"
"fmt"
"strconv"

"github.com/spf13/cobra"

governance "github.com/oasisprotocol/oasis-core/go/governance/api"
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/connection"

"github.com/oasisprotocol/cli/cmd/common"
cliConfig "github.com/oasisprotocol/cli/config"
)

var govCastVoteCmd = &cobra.Command{
Use: "cast-vote <proposal-id> { yes | no | abstain }",
Short: "Cast a governance vote on a proposal",
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
cfg := cliConfig.Global()
npa := common.GetNPASelection(cfg)
txCfg := common.GetTransactionConfig()
rawProposalID, rawVote := args[0], args[1]

if npa.Account == nil {
cobra.CheckErr("no accounts configured in your wallet")
}

// When not in offline mode, connect to the given network endpoint.
ctx := context.Background()
var conn connection.Connection
if !txCfg.Offline {
var err error
conn, err = connection.Connect(ctx, npa.Network)
cobra.CheckErr(err)
}

// Parse proposal ID.
var (
proposalID uint64
err error
)
if proposalID, err = strconv.ParseUint(rawProposalID, 10, 64); err != nil {
cobra.CheckErr(fmt.Errorf("bad proposal ID: %w", err))
}

// Parse vote.
var vote governance.Vote
if err = vote.UnmarshalText([]byte(rawVote)); err != nil {
cobra.CheckErr(fmt.Errorf("bad vote: %w", err))
}

// Prepare transaction.
tx := governance.NewCastVoteTx(0, nil, &governance.ProposalVote{
ID: proposalID,
Vote: vote,
})

acc := common.LoadAccount(cfg, npa.AccountName)
sigTx, err := common.SignConsensusTransaction(ctx, npa, acc, conn, tx)
cobra.CheckErr(err)

common.BroadcastOrExportTransaction(ctx, npa.ParaTime, conn, sigTx, nil, nil)
},
}

func init() {
govCastVoteCmd.Flags().AddFlagSet(common.SelectorNAFlags)
govCastVoteCmd.Flags().AddFlagSet(common.TxFlags)
}
1 change: 0 additions & 1 deletion cmd/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ func networkDetailsFromSurvey(net *config.Network) {
func init() {
Cmd.AddCommand(addCmd)
Cmd.AddCommand(addLocalCmd)
Cmd.AddCommand(governanceProposalCmd)
Cmd.AddCommand(listCmd)
Cmd.AddCommand(rmCmd)
Cmd.AddCommand(setDefaultCmd)
Expand Down

0 comments on commit f12fa8c

Please sign in to comment.