Skip to content

Commit

Permalink
CNS-422: add proposal for plans delete
Browse files Browse the repository at this point in the history
  • Loading branch information
orenl-lava committed May 23, 2023
1 parent 4c25499 commit f6181fa
Show file tree
Hide file tree
Showing 14 changed files with 958 additions and 484 deletions.
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ func getGovProposalHandlers() []govclient.ProposalHandler {
ibcclientclient.UpgradeProposalHandler,
specmoduleclient.SpecAddProposalHandler,
plansmoduleclient.PlansAddProposalHandler,
plansmoduleclient.PlansDelProposalHandler,
// this line is used by starport scaffolding # stargate/app/govProposalHandler
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,12 @@ message PlansAddProposal {
string description = 2;
repeated Plan plans = 3 [(gogoproto.nullable) = false];
}

message PlansDelProposal {
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;

string title = 1;
string description = 2;
repeated string plans = 3 [(gogoproto.nullable) = false];
}
60 changes: 56 additions & 4 deletions x/plans/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@ func GetTxCmd() *cobra.Command {
return cmd
}

// NewSubmitParamChangeProposalTxCmd returns a CLI command handler for creating
// a parameter change proposal governance transaction.
// NewSubmitPlansAddProposalTxCmd returns a CLI command handler for creating
// a new plan proposal governance transaction.
func NewSubmitPlansAddProposalTxCmd() *cobra.Command {
return &cobra.Command{
Use: "plans-add [proposal-file,proposal-file,...]",
Args: cobra.ExactArgs(1),
Short: "Submit a plans add proposal",
Long: strings.TrimSpace(
fmt.Sprintf(`Submit a parameter proposal along with an initial deposit.
fmt.Sprintf(`Submit a plans add proposal.
The proposal details must be supplied via a JSON file. For values that contains
objects, only non-empty fields will be updated.
IMPORTANT: Currently changes are evaluated but not validated, so it is
IMPORTANT: Currently changes are evaluated but not validated, so it is
very important that any "value" change is valid (ie. correct type and within bounds)
Proper vetting of a plans add proposal should prevent this from happening
Expand Down Expand Up @@ -91,3 +91,55 @@ $ %s tx gov plans-proposal plans-add <path/to/proposal.json> --from=<key_or_addr
},
}
}

// NewSubmitPlansDelProposalTxCmd returns a CLI command handler for creating
// a delete plan proposal governance transaction.
func NewSubmitPlansDelProposalTxCmd() *cobra.Command {
return &cobra.Command{
Use: "plans-del [proposal-file,proposal-file,...]",
Args: cobra.ExactArgs(1),
Short: "Submit a plans delete proposal",
Long: strings.TrimSpace(
fmt.Sprintf(`Submit a plans delete proposal.
The proposal details must be supplied via a JSON file. For values that contains
objects, only non-empty fields will be updated.
IMPORTANT: Currently changes are evaluated but not validated, so it is
very important that any "value" change is valid (ie. correct type and within bounds)
Proper vetting of a plans add proposal should prevent this from happening
(no deposits should occur during the governance process), but it should be noted
regardless.
Example:
$ %s tx gov plans-proposal plans-del <path/to/proposal.json> --from=<key_or_address>
`,
version.AppName,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
proposal, err := utils.ParsePlansDelProposalJSON(clientCtx.LegacyAmino, args[0])
if err != nil {
return err
}

from := clientCtx.GetFromAddress()
content := &proposal.Proposal
deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit)
if err != nil {
return err
}

msg, err := govtypes.NewMsgSubmitProposal(content, deposit, from)
if err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}
}
6 changes: 4 additions & 2 deletions x/plans/client/proposal_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package client
import (
govclient "github.com/cosmos/cosmos-sdk/x/gov/client"
"github.com/lavanet/lava/x/plans/client/cli"
"github.com/lavanet/lava/x/spec/client/rest"
"github.com/lavanet/lava/x/plans/client/rest"
)

var PlansAddProposalHandler = govclient.NewProposalHandler(cli.NewSubmitPlansAddProposalTxCmd, rest.ProposalRESTHandler)
var PlansAddProposalHandler = govclient.NewProposalHandler(cli.NewSubmitPlansAddProposalTxCmd, rest.PlansAddProposalRESTHandler)

var PlansDelProposalHandler = govclient.NewProposalHandler(cli.NewSubmitPlansDelProposalTxCmd, rest.PlansDelProposalRESTHandler)
21 changes: 17 additions & 4 deletions x/plans/client/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,28 @@ import (
govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest"
)

func ProposalRESTHandler(clientCtx client.Context) govrest.ProposalRESTHandler {
func PlansAddProposalRESTHandler(clientCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "plans_add",
Handler: postProposalHandlerFn(clientCtx),
Handler: postPlansAddProposalHandlerFn(clientCtx),
}
}

func postProposalHandlerFn(clientCtx client.Context) http.HandlerFunc {
func postPlansAddProposalHandlerFn(clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Println("postProposalHandlerFn")
log.Println("postPlansAddProposalHandlerFn")
}
}

func PlansDelProposalRESTHandler(clientCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "plans_del",
Handler: postPlansDelProposalHandlerFn(clientCtx),
}
}

func postPlansDelProposalHandlerFn(clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Println("postPlansDelProposalHandlerFn")
}
}
44 changes: 42 additions & 2 deletions x/plans/client/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,50 @@ type (
}
)

// Parse packages add proposal JSON form file
type (
PlansDelProposalJSON struct {
Proposal types.PlansDelProposal `json:"proposal"`
Deposit string `json:"deposit" yaml:"deposit"`
}
)

// Parse plans add proposal JSON form file
func ParsePlansAddProposalJSON(cdc *codec.LegacyAmino, proposalFile string) (ret PlansAddProposalJSON, err error) {
for _, fileName := range strings.Split(proposalFile, ",") {
proposal := PlansAddProposalJSON{}
var proposal PlansAddProposalJSON

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

if err := cdc.UnmarshalJSON(contents, &proposal); err != nil {
return proposal, err
}
if len(ret.Proposal.Plans) > 0 {
ret.Proposal.Plans = append(ret.Proposal.Plans, proposal.Proposal.Plans...)
ret.Proposal.Description = proposal.Proposal.Description + " " + ret.Proposal.Description
ret.Proposal.Title = proposal.Proposal.Title + " " + ret.Proposal.Title
retDeposit, err := sdk.ParseCoinNormalized(ret.Deposit)
if err != nil {
return proposal, err
}
proposalDeposit, err := sdk.ParseCoinNormalized(proposal.Deposit)
if err != nil {
return proposal, err
}
ret.Deposit = retDeposit.Add(proposalDeposit).String()
} else {
ret = proposal
}
}
return ret, nil
}

// Parse plans delete proposal JSON form file
func ParsePlansDelProposalJSON(cdc *codec.LegacyAmino, proposalFile string) (ret PlansDelProposalJSON, err error) {
for _, fileName := range strings.Split(proposalFile, ",") {
var proposal PlansDelProposalJSON

contents, err := os.ReadFile(fileName)
if err != nil {
Expand Down
9 changes: 2 additions & 7 deletions x/plans/keeper/plan.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package keeper

import (
"strconv"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/lavanet/lava/utils"
"github.com/lavanet/lava/x/plans/types"
Expand Down Expand Up @@ -31,11 +29,8 @@ func (k Keeper) DelPlan(ctx sdk.Context, index string) error {
// Deletions should take place at the end of epoch (beginning of next epoch).
// However, because deletion of plans occurs through gov proposal, we must be
// already at the beginning of that "next" epoch (after having waited for the
// the voting).

if !k.IsEpochStart(ctx) {
panic("DelPlan called not an epoch start block " + strconv.FormatInt(ctx.BlockHeight(), 10))
}
// already at the beginning of that "next" epoch (after having waited for the
// the voting). Hence, we delete at current block height.

return k.plansFS.DelEntry(ctx, index, uint64(ctx.BlockHeight()))
}
Expand Down
26 changes: 22 additions & 4 deletions x/plans/proposal_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ func NewPlansProposalsHandler(k keeper.Keeper) govtypes.Handler {
return func(ctx sdk.Context, content govtypes.Content) error {
switch c := content.(type) {
case *types.PlansAddProposal:
return handlePlansProposal(ctx, k, c)

return handlePlansAddProposal(ctx, k, c)
case *types.PlansDelProposal:
return handlePlansDelProposal(ctx, k, c)
default:
log.Println("unrecognized plans proposal content")
return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized plans proposal content type: %T", c)
}
}
}

func handlePlansProposal(ctx sdk.Context, k keeper.Keeper, p *types.PlansAddProposal) error {
func handlePlansAddProposal(ctx sdk.Context, k keeper.Keeper, p *types.PlansAddProposal) error {
// add the plans to the plan storage
for _, planElem := range p.Plans {
logger := k.Logger(ctx)
Expand All @@ -37,7 +38,24 @@ func handlePlansProposal(ctx sdk.Context, k keeper.Keeper, p *types.PlansAddProp
}

details := map[string]string{"planDetails": planElem.String()}
utils.LogLavaEvent(ctx, logger, "add_new_plan_to_storage", details, "Gov Proposal Accepted Package")
utils.LogLavaEvent(ctx, logger, "add_new_plan_to_storage", details, "Gov Proposal Accepted Plans")
}
return nil
}

func handlePlansDelProposal(ctx sdk.Context, k keeper.Keeper, p *types.PlansDelProposal) error {
// add the plans to the plan storage
for _, index := range p.Plans {
logger := k.Logger(ctx)
err := k.DelPlan(ctx, index)
if err != nil {
return utils.LavaFormatError("could not del existing plan", err,
utils.Attribute{Key: "planIndex", Value: index},
)
}

details := map[string]string{"index": index}
utils.LogLavaEvent(ctx, logger, "del_plan_from_storage", details, "Gov Proposal Accepted Plans")
}
return nil
}
6 changes: 2 additions & 4 deletions x/plans/types/addproposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ const (
ProposalPlansAdd = "PlansAdd"
)

var _ govtypes.Content = &PlansAddProposal{}

func init() {
govtypes.RegisterProposalType(ProposalPlansAdd)
}
Expand Down Expand Up @@ -42,7 +40,7 @@ func (pcp *PlansAddProposal) ValidateBasic() error {
}

if len(pcp.Plans) == 0 {
return sdkerrors.Wrap(ErrEmptyPlans, "proposal plans cannot be empty")
return sdkerrors.Wrap(ErrEmptyPlans, "proposal plans add cannot be empty")
}
for _, planElem := range pcp.Plans {
err := planElem.ValidatePlan()
Expand All @@ -65,7 +63,7 @@ func (pcp PlansAddProposal) String() string {
`, pcp.Title, pcp.Description))

for _, planElem := range pcp.Plans {
b = stringPlan(&planElem, b)
b.WriteString(planElem.String())
}

return b.String()
Expand Down
1 change: 1 addition & 0 deletions x/plans/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
registry.RegisterImplementations(
(*govtypes.Content)(nil),
&PlansAddProposal{},
&PlansDelProposal{},
)
}

Expand Down
66 changes: 66 additions & 0 deletions x/plans/types/delproposal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package types

import (
fmt "fmt"
"strings"

sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
commontypes "github.com/lavanet/lava/common/types"
)

const (
ProposalPlansDel = "PlansDel"
)

func init() {
govtypes.RegisterProposalType(ProposalPlansDel)
}

func NewPlansDelProposal(title, description string, indices []string) *PlansDelProposal {
return &PlansDelProposal{title, description, indices}
}

// GetTitle returns the title of a proposal.
func (pcp *PlansDelProposal) GetTitle() string { return pcp.Title }

// GetDescription returns the description of a proposal.
func (pcp *PlansDelProposal) GetDescription() string { return pcp.Description }

// ProposalRoute returns the routing key of a proposal.
func (pcp *PlansDelProposal) ProposalRoute() string { return ProposalsRouterKey }

// ProposalType returns the type of a proposal.
func (pcp *PlansDelProposal) ProposalType() string { return ProposalPlansDel }

// ValidateBasic validates the proposal
func (pcp *PlansDelProposal) ValidateBasic() error {
err := govtypes.ValidateAbstract(pcp)
if err != nil {
return err
}

if len(pcp.Plans) == 0 {
return sdkerrors.Wrap(ErrEmptyPlans, "proposal plans delete cannot be empty")
}
for _, index := range pcp.Plans {
if !commontypes.ValidateString(index, commontypes.NAME_RESTRICTIONS, nil) {
return fmt.Errorf("invalid plan name: %s", index)
}
}

return nil
}

// String implements the Stringer interface.
func (pcp PlansDelProposal) String() string {
var b strings.Builder

b.WriteString(fmt.Sprintf(`Plan Del Proposal:
Title: %s
Description: %s
Deleted: %s
`, pcp.Title, pcp.Description, strings.Join(pcp.Plans, ", ")))

return b.String()
}
Loading

0 comments on commit f6181fa

Please sign in to comment.