add gov client handlers

This commit is contained in:
rhuairahrighairigh 2020-03-23 14:32:50 +00:00
parent 0275b21173
commit b31cfbe39b
10 changed files with 132 additions and 25 deletions

View File

@ -55,7 +55,7 @@ var (
staking.AppModuleBasic{}, staking.AppModuleBasic{},
mint.AppModuleBasic{}, mint.AppModuleBasic{},
distr.AppModuleBasic{}, distr.AppModuleBasic{},
gov.NewAppModuleBasic(paramsclient.ProposalHandler, distr.ProposalHandler), gov.NewAppModuleBasic(paramsclient.ProposalHandler, distr.ProposalHandler, committee.ProposalHandler),
params.AppModuleBasic{}, params.AppModuleBasic{},
crisis.AppModuleBasic{}, crisis.AppModuleBasic{},
slashing.AppModuleBasic{}, slashing.AppModuleBasic{},
@ -207,11 +207,23 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
invCheckPeriod, invCheckPeriod,
app.supplyKeeper, app.supplyKeeper,
auth.FeeCollectorName) auth.FeeCollectorName)
committeeGovRouter := gov.NewRouter()
committeeGovRouter.
AddRoute(gov.RouterKey, gov.ProposalHandler).
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper))
// Note: the committee proposal handler is not registered on the committee router. This means committees cannot create or update other committees.
// Adding the committee proposal handler to the router is possible but awkward as the handler depends on the keeper which depends on the handler.
app.committeeKeeper = committee.NewKeeper(
app.cdc,
keys[committee.StoreKey],
committeeGovRouter) // TODO blacklist module addresses?)
govRouter := gov.NewRouter() govRouter := gov.NewRouter()
govRouter. govRouter.
AddRoute(gov.RouterKey, gov.ProposalHandler). AddRoute(gov.RouterKey, gov.ProposalHandler).
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)). AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)) AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)).
AddRoute(committee.RouterKey, committee.NewProposalHandler(app.committeeKeeper))
app.govKeeper = gov.NewKeeper( app.govKeeper = gov.NewKeeper(
app.cdc, app.cdc,
keys[gov.StoreKey], keys[gov.StoreKey],
@ -246,12 +258,6 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
app.auctionKeeper, app.auctionKeeper,
app.supplyKeeper, app.supplyKeeper,
cdp.DefaultCodespace) cdp.DefaultCodespace)
app.committeeKeeper = committee.NewKeeper(
app.cdc,
keys[committee.StoreKey],
govRouter,
// TODO blacklist module addresses?
)
// register the staking hooks // register the staking hooks
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
@ -330,7 +336,8 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
// initialize the app // initialize the app
app.SetInitChainer(app.InitChainer) app.SetInitChainer(app.InitChainer)
app.SetBeginBlocker(app.BeginBlocker) app.SetBeginBlocker(app.BeginBlocker)
// app.SetAnteHandler(NewAnteHandler(app.accountKeeper, app.supplyKeeper, app.shutdownKeeper, auth.DefaultSigVerificationGasConsumer)) // TODO app.SetAnteHandler(NewAnteHandler(app.accountKeeper, app.supplyKeeper, app.shutdownKeeper, auth.DefaultSigVerificationGasConsumer))
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.supplyKeeper, auth.DefaultSigVerificationGasConsumer))
app.SetEndBlocker(app.EndBlocker) app.SetEndBlocker(app.EndBlocker)
// load store // load store

View File

@ -3,6 +3,7 @@
package committee package committee
import ( import (
"github.com/kava-labs/kava/x/committee/client"
"github.com/kava-labs/kava/x/committee/keeper" "github.com/kava-labs/kava/x/committee/keeper"
"github.com/kava-labs/kava/x/committee/types" "github.com/kava-labs/kava/x/committee/types"
) )
@ -49,6 +50,7 @@ var (
Uint64FromBytes = types.Uint64FromBytes Uint64FromBytes = types.Uint64FromBytes
// variable aliases // variable aliases
ProposalHandler = client.ProposalHandler
CommitteeKeyPrefix = types.CommitteeKeyPrefix CommitteeKeyPrefix = types.CommitteeKeyPrefix
MaxProposalDuration = types.MaxProposalDuration MaxProposalDuration = types.MaxProposalDuration
ModuleCdc = types.ModuleCdc ModuleCdc = types.ModuleCdc

View File

@ -10,7 +10,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
comclient "github.com/kava-labs/kava/x/committee/client" "github.com/kava-labs/kava/x/committee/client/common"
"github.com/kava-labs/kava/x/committee/types" "github.com/kava-labs/kava/x/committee/types"
) )
@ -381,7 +381,7 @@ func GetCmdQueryProposer(queryRoute string, cdc *codec.Codec) *cobra.Command {
return fmt.Errorf("proposal-id %s is not a valid uint", args[0]) return fmt.Errorf("proposal-id %s is not a valid uint", args[0])
} }
prop, err := comclient.QueryProposer(cliCtx, proposalID) prop, err := common.QueryProposer(cliCtx, proposalID)
if err != nil { if err != nil {
return err return err
} }

View File

@ -13,6 +13,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils" "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/kava-labs/kava/x/committee/types" "github.com/kava-labs/kava/x/committee/types"
) )
@ -139,7 +140,7 @@ func GetCmdSubmitProposal(cdc *codec.Codec) *cobra.Command {
func GetCmdVote(cdc *codec.Codec) *cobra.Command { func GetCmdVote(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: "vote [proposal-id]", Use: "vote [proposal-id]",
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(1),
Short: "Vote for an active proposal", // TODO Short: "Vote for an active proposal", // TODO
// Long: strings.TrimSpace( // Long: strings.TrimSpace(
// fmt.Sprintf(`Submit a vote for an active proposal. You can // fmt.Sprintf(`Submit a vote for an active proposal. You can
@ -175,3 +176,51 @@ func GetCmdVote(cdc *codec.Codec) *cobra.Command {
}, },
} }
} }
// TODO this could replace the whole gov submit-proposal cmd, remove and replace the gov cmd in kvcli main.go
// would want the documentation/examples though
func GetGovCmdSubmitProposal(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "committee [proposal-file] [deposit]",
Short: "Submit a governance proposal to change a committee.",
Long: "This command will work with either CommitteeChange proposals or CommitteeDelete proposals.", // TODO
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
cliCtx := context.NewCLIContext().WithCodec(cdc)
// Get proposing address
proposer := cliCtx.GetFromAddress()
// Get the deposit
deposit, err := sdk.ParseCoins(args[0])
if err != nil {
return err
}
// Get the proposal
bz, err := ioutil.ReadFile(args[0])
if err != nil {
return err
}
var content govtypes.Content
if err := cdc.UnmarshalJSON(bz, &content); err != nil {
return err
}
if err = content.ValidateBasic(); err != nil {
return err
}
// Build message and run basic validation
msg := govtypes.NewMsgSubmitProposal(content, deposit, proposer)
err = msg.ValidateBasic()
if err != nil {
return err
}
// Sign and broadcast message
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
return cmd
}

View File

@ -1,4 +1,4 @@
package client package common
import ( import (
"fmt" "fmt"

View File

@ -0,0 +1,11 @@
package client
import (
govclient "github.com/cosmos/cosmos-sdk/x/gov/client"
"github.com/kava-labs/kava/x/committee/client/cli"
"github.com/kava-labs/kava/x/committee/client/rest"
)
// ProposalHandler is a struct containing handler funcs for submiting CommitteeChange/Delete proposal txs to the gov module through the cli or rest.
var ProposalHandler = govclient.NewProposalHandler(cli.GetGovCmdSubmitProposal, rest.ProposalRESTHandler)

View File

@ -10,7 +10,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/types/rest"
"github.com/kava-labs/kava/x/committee/client" "github.com/kava-labs/kava/x/committee/client/common"
"github.com/kava-labs/kava/x/committee/types" "github.com/kava-labs/kava/x/committee/types"
) )
@ -181,7 +181,7 @@ func queryProposerHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
} }
// Query // Query
res, err := client.QueryProposer(cliCtx, proposalID) res, err := common.QueryProposer(cliCtx, proposalID)
if err != nil { if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return return

View File

@ -10,6 +10,8 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils" "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/kava-labs/kava/x/committee/types" "github.com/kava-labs/kava/x/committee/types"
) )
@ -108,3 +110,46 @@ func postVoteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
} }
} }
// -------- --------
// TODO this could replace the POST gov/proposals endpoint, would need to overwrite routes in kvcli main, hacky
type PostGovProposalReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Content govtypes.Content `json:"content" yaml:"content"` //TODO use same PubProposal name?
Proposer sdk.AccAddress `json:"proposer" yaml:"proposer"`
Deposit sdk.Coins `json:"deposit" yaml:"deposit"`
}
func ProposalRESTHandler(cliCtx context.CLIContext) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "committee",
Handler: postGovProposalHandlerFn(cliCtx),
}
}
func postGovProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Parse and validate http request body
var req PostGovProposalReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
if err := req.Content.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// Create and return a StdTx
msg := govtypes.NewMsgSubmitProposal(req.Content, req.Deposit, req.Proposer)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}

View File

@ -22,8 +22,7 @@ type Keeper struct {
func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, router govtypes.Router) Keeper { func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, router govtypes.Router) Keeper {
// Logic in the keeper methods assume the set of gov handlers is fixed. // Logic in the keeper methods assume the set of gov handlers is fixed.
// So the gov router must be sealed so no handlers can be added or removed after the keeper is created. // So the gov router must be sealed so no handlers can be added or removed after the keeper is created.
// Note: for some reason the gov router panics if it has already been sealed, so a helper func is used to make sealing idempotent. router.Seal()
sealGovRouterIdempotently(router)
return Keeper{ return Keeper{
cdc: cdc, cdc: cdc,
@ -32,13 +31,6 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, router govtypes.Router)
} }
} }
func sealGovRouterIdempotently(router govtypes.Router) {
defer func() {
recover()
}()
router.Seal()
}
// ---------- Committees ---------- // ---------- Committees ----------
// GetCommittee gets a committee from the store. // GetCommittee gets a committee from the store.

View File

@ -70,6 +70,7 @@ func (suite *ProposalHandlerTestSuite) TestProposalHandler_ChangeCommittee() {
"A proposal description.", "A proposal description.",
committee.Committee{ committee.Committee{
ID: 34, ID: 34,
Members: suite.addresses[:1],
}, },
), ),
expectPass: true, expectPass: true,