Community pool spend proposal (#4329)
Implement the "CommunityPoolSpendProposal" as described in Cosmos Hub proposal 7. Also a useful test of Git flow for merging features passed in governance proposals.
This commit is contained in:
parent
4b872d2eb4
commit
dd89c32951
|
@ -0,0 +1 @@
|
|||
Community pool spend proposal per Cosmos Hub governance proposal #7 "Activate the Community Pool"
|
|
@ -159,7 +159,8 @@ func NewSimApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bo
|
|||
// register the proposal types
|
||||
govRouter := gov.NewRouter()
|
||||
govRouter.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))
|
||||
app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper, govSubspace,
|
||||
app.bankKeeper, &stakingKeeper, gov.DefaultCodespace, govRouter)
|
||||
|
||||
|
|
|
@ -301,6 +301,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation {
|
|||
{50, distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper)},
|
||||
{50, distrsim.SimulateMsgWithdrawValidatorCommission(app.accountKeeper, app.distrKeeper)},
|
||||
{5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, govsim.SimulateTextProposalContent)},
|
||||
{5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, distrsim.SimulateCommunityPoolSpendProposalContent(app.distrKeeper))},
|
||||
{5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, paramsim.SimulateParamChangeProposalContent)},
|
||||
{100, govsim.SimulateMsgDeposit(app.govKeeper)},
|
||||
{100, stakingsim.SimulateMsgCreateValidator(app.accountKeeper, app.stakingKeeper)},
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
@ -55,7 +53,7 @@ func createMsgSend(r *rand.Rand, ctx sdk.Context, accs []simulation.Account, map
|
|||
}
|
||||
|
||||
denomIndex := r.Intn(len(initFromCoins))
|
||||
amt, goErr := randPositiveInt(r, initFromCoins[denomIndex].Amount)
|
||||
amt, goErr := simulation.RandPositiveInt(r, initFromCoins[denomIndex].Amount)
|
||||
if goErr != nil {
|
||||
return fromAcc, "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, msg, false
|
||||
}
|
||||
|
@ -150,7 +148,7 @@ func createSingleInputMsgMultiSend(r *rand.Rand, ctx sdk.Context, accs []simulat
|
|||
}
|
||||
|
||||
denomIndex := r.Intn(len(initFromCoins))
|
||||
amt, goErr := randPositiveInt(r, initFromCoins[denomIndex].Amount)
|
||||
amt, goErr := simulation.RandPositiveInt(r, initFromCoins[denomIndex].Amount)
|
||||
if goErr != nil {
|
||||
return fromAcc, "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, msg, false
|
||||
}
|
||||
|
@ -218,11 +216,3 @@ func sendAndVerifyMsgMultiSend(app *baseapp.BaseApp, mapper auth.AccountKeeper,
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func randPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) {
|
||||
if !max.GT(sdk.OneInt()) {
|
||||
return sdk.Int{}, errors.New("max too small")
|
||||
}
|
||||
max = max.Sub(sdk.OneInt())
|
||||
return sdk.NewIntFromBigInt(new(big.Int).Rand(r, max.BigInt())).Add(sdk.OneInt()), nil
|
||||
}
|
||||
|
|
|
@ -97,6 +97,7 @@ var (
|
|||
NewValidatorCurrentRewards = types.NewValidatorCurrentRewards
|
||||
InitialValidatorAccumulatedCommission = types.InitialValidatorAccumulatedCommission
|
||||
NewValidatorSlashEvent = types.NewValidatorSlashEvent
|
||||
NewCommunityPoolSpendProposal = types.NewCommunityPoolSpendProposal
|
||||
|
||||
// variable aliases
|
||||
FeePoolKey = keeper.FeePoolKey
|
||||
|
@ -145,6 +146,7 @@ type (
|
|||
ValidatorCurrentRewardsRecord = types.ValidatorCurrentRewardsRecord
|
||||
DelegatorStartingInfoRecord = types.DelegatorStartingInfoRecord
|
||||
ValidatorSlashEventRecord = types.ValidatorSlashEventRecord
|
||||
CommunityPoolSpendProposal = types.CommunityPoolSpendProposal
|
||||
GenesisState = types.GenesisState
|
||||
MsgSetWithdrawAddress = types.MsgSetWithdrawAddress
|
||||
MsgWithdrawDelegatorReward = types.MsgWithdrawDelegatorReward
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution/client/common"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||
|
@ -150,3 +151,65 @@ $ %s tx set-withdraw-addr cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --from m
|
|||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// GetCmdSubmitProposal implements the command to submit a community-pool-spend proposal
|
||||
func GetCmdSubmitProposal(cdc *codec.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "community-pool-spend [proposal-file]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "Submit a community pool spend proposal",
|
||||
Long: strings.TrimSpace(
|
||||
fmt.Sprintf(`Submit a community pool spend proposal along with an initial deposit.
|
||||
The proposal details must be supplied via a JSON file.
|
||||
|
||||
Example:
|
||||
$ %s tx gov submit-proposal community-pool-spend <path/to/proposal.json> --from=<key_or_address>
|
||||
|
||||
Where proposal.json contains:
|
||||
|
||||
{
|
||||
"title": "Community Pool Spend",
|
||||
"description": "Pay me some Atoms!",
|
||||
"recipient": "cosmos1s5afhd6gxevu37mkqcvvsj8qeylhn0rz46zdlq",
|
||||
"amount": [
|
||||
{
|
||||
"denom": "stake",
|
||||
"amount": "10000"
|
||||
}
|
||||
],
|
||||
"deposit": [
|
||||
{
|
||||
"denom": "stake",
|
||||
"amount": "10000"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
version.ClientName,
|
||||
),
|
||||
),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
|
||||
cliCtx := context.NewCLIContext().
|
||||
WithCodec(cdc).
|
||||
WithAccountDecoder(cdc)
|
||||
|
||||
proposal, err := ParseCommunityPoolSpendProposalJSON(cdc, args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
from := cliCtx.GetFromAddress()
|
||||
content := types.NewCommunityPoolSpendProposal(proposal.Title, proposal.Description, proposal.Recipient, proposal.Amount)
|
||||
|
||||
msg := gov.NewMsgSubmitProposal(content, proposal.Deposit, from)
|
||||
if err := msg.ValidateBasic(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
type (
|
||||
// CommunityPoolSpendProposalJSON defines a CommunityPoolSpendProposal with a deposit
|
||||
CommunityPoolSpendProposalJSON struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Recipient sdk.AccAddress `json:"recipient"`
|
||||
Amount sdk.Coins `json:"amount"`
|
||||
Deposit sdk.Coins `json:"deposit"`
|
||||
}
|
||||
)
|
||||
|
||||
// ParseCommunityPoolSpendProposalJSON reads and parses a CommunityPoolSpendProposalJSON from a file.
|
||||
func ParseCommunityPoolSpendProposalJSON(cdc *codec.Codec, proposalFile string) (CommunityPoolSpendProposalJSON, error) {
|
||||
proposal := CommunityPoolSpendProposalJSON{}
|
||||
|
||||
contents, err := ioutil.ReadFile(proposalFile)
|
||||
if err != nil {
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
if err := cdc.UnmarshalJSON(contents, &proposal); err != nil {
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
return proposal, nil
|
||||
}
|
|
@ -2,9 +2,16 @@ package rest
|
|||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
clientrest "github.com/cosmos/cosmos-sdk/client/rest"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest"
|
||||
)
|
||||
|
||||
// RegisterRoutes register distribution REST routes.
|
||||
|
@ -12,3 +19,35 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec,
|
|||
registerQueryRoutes(cliCtx, r, cdc, queryRoute)
|
||||
registerTxRoutes(cliCtx, r, cdc, queryRoute)
|
||||
}
|
||||
|
||||
// ProposalRESTHandler returns a ProposalRESTHandler that exposes the community pool spend REST handler with a given sub-route.
|
||||
func ProposalRESTHandler(cliCtx context.CLIContext, cdc *codec.Codec) govrest.ProposalRESTHandler {
|
||||
return govrest.ProposalRESTHandler{
|
||||
SubRoute: "community_pool_spend",
|
||||
Handler: postProposalHandlerFn(cdc, cliCtx),
|
||||
}
|
||||
}
|
||||
|
||||
func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req CommunityPoolSpendProposalReq
|
||||
if !rest.ReadRESTReq(w, r, cdc, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
req.BaseReq = req.BaseReq.Sanitize()
|
||||
if !req.BaseReq.ValidateBasic(w) {
|
||||
return
|
||||
}
|
||||
|
||||
content := distribution.NewCommunityPoolSpendProposal(req.Title, req.Description, req.Recipient, req.Amount)
|
||||
|
||||
msg := gov.NewMsgSubmitProposal(content, req.Deposit, req.Proposer)
|
||||
if err := msg.ValidateBasic(); err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
)
|
||||
|
||||
type (
|
||||
// CommunityPoolSpendProposalReq defines a community pool spend proposal request body.
|
||||
CommunityPoolSpendProposalReq struct {
|
||||
BaseReq rest.BaseReq `json:"base_req"`
|
||||
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Recipient sdk.AccAddress `json:"recipient"`
|
||||
Amount sdk.Coins `json:"amount"`
|
||||
Proposer sdk.AccAddress `json:"proposer"`
|
||||
Deposit sdk.Coins `json:"deposit"`
|
||||
}
|
||||
)
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution/tags"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
)
|
||||
|
||||
func NewHandler(k keeper.Keeper) sdk.Handler {
|
||||
|
@ -77,3 +78,16 @@ func handleMsgWithdrawValidatorCommission(ctx sdk.Context, msg types.MsgWithdraw
|
|||
),
|
||||
}
|
||||
}
|
||||
|
||||
func NewCommunityPoolSpendProposalHandler(k Keeper) govtypes.Handler {
|
||||
return func(ctx sdk.Context, content govtypes.Content) sdk.Error {
|
||||
switch c := content.(type) {
|
||||
case types.CommunityPoolSpendProposal:
|
||||
return keeper.HandleCommunityPoolSpendProposal(ctx, k, c)
|
||||
|
||||
default:
|
||||
errMsg := fmt.Sprintf("unrecognized distr proposal content type: %T", c)
|
||||
return sdk.ErrUnknownRequest(errMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||
)
|
||||
|
||||
func HandleCommunityPoolSpendProposal(ctx sdk.Context, k Keeper, p types.CommunityPoolSpendProposal) sdk.Error {
|
||||
feePool := k.GetFeePool(ctx)
|
||||
newPool, negative := feePool.CommunityPool.SafeSub(sdk.NewDecCoins(p.Amount))
|
||||
if negative {
|
||||
return types.ErrBadDistribution(k.codespace)
|
||||
}
|
||||
feePool.CommunityPool = newPool
|
||||
k.SetFeePool(ctx, feePool)
|
||||
_, err := k.bankKeeper.AddCoins(ctx, p.Recipient, p.Amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger := k.Logger(ctx)
|
||||
logger.Info(fmt.Sprintf("Spent %s coins from the community pool to recipient %s", p.Amount, p.Recipient))
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package distribution
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
delPk1 = ed25519.GenPrivKey().PubKey()
|
||||
delAddr1 = sdk.AccAddress(delPk1.Address())
|
||||
)
|
||||
|
||||
func testProposal(recipient sdk.AccAddress, amount sdk.Coins) types.CommunityPoolSpendProposal {
|
||||
return types.NewCommunityPoolSpendProposal(
|
||||
"Test",
|
||||
"description",
|
||||
recipient,
|
||||
amount,
|
||||
)
|
||||
}
|
||||
|
||||
func TestProposalHandlerPassed(t *testing.T) {
|
||||
ctx, accountKeeper, keeper, _, _ := CreateTestInputDefault(t, false, 10)
|
||||
recipient := delAddr1
|
||||
amount := sdk.NewCoin("stake", sdk.NewInt(1))
|
||||
|
||||
account := accountKeeper.NewAccountWithAddress(ctx, recipient)
|
||||
require.True(t, account.GetCoins().IsZero())
|
||||
accountKeeper.SetAccount(ctx, account)
|
||||
|
||||
feePool := keeper.GetFeePool(ctx)
|
||||
feePool.CommunityPool = sdk.DecCoins{sdk.NewDecCoinFromCoin(amount)}
|
||||
keeper.SetFeePool(ctx, feePool)
|
||||
|
||||
tp := testProposal(recipient, sdk.NewCoins(amount))
|
||||
hdlr := NewCommunityPoolSpendProposalHandler(keeper)
|
||||
require.NoError(t, hdlr(ctx, tp))
|
||||
require.Equal(t, accountKeeper.GetAccount(ctx, recipient).GetCoins(), sdk.NewCoins(amount))
|
||||
}
|
||||
|
||||
func TestProposalHandlerFailed(t *testing.T) {
|
||||
ctx, accountKeeper, keeper, _, _ := CreateTestInputDefault(t, false, 10)
|
||||
recipient := delAddr1
|
||||
amount := sdk.NewCoin("stake", sdk.NewInt(1))
|
||||
|
||||
account := accountKeeper.NewAccountWithAddress(ctx, recipient)
|
||||
require.True(t, account.GetCoins().IsZero())
|
||||
accountKeeper.SetAccount(ctx, account)
|
||||
|
||||
tp := testProposal(recipient, sdk.NewCoins(amount))
|
||||
hdlr := NewCommunityPoolSpendProposalHandler(keeper)
|
||||
require.Error(t, hdlr(ctx, tp))
|
||||
require.True(t, accountKeeper.GetAccount(ctx, recipient).GetCoins().IsZero())
|
||||
}
|
|
@ -8,6 +8,8 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
|
@ -84,3 +86,26 @@ func SimulateMsgWithdrawValidatorCommission(m auth.AccountKeeper, k distribution
|
|||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateCommunityPoolSpendProposalContent generates random community-pool-spend proposal content
|
||||
func SimulateCommunityPoolSpendProposalContent(k distribution.Keeper) govsim.ContentSimulator {
|
||||
return func(r *rand.Rand, _ *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) gov.Content {
|
||||
recipientAcc := simulation.RandomAcc(r, accs)
|
||||
coins := sdk.Coins{}
|
||||
balance := k.GetFeePool(ctx).CommunityPool
|
||||
if len(balance) > 0 {
|
||||
denomIndex := r.Intn(len(balance))
|
||||
amount, goErr := simulation.RandPositiveInt(r, balance[denomIndex].Amount.TruncateInt())
|
||||
if goErr == nil {
|
||||
denom := balance[denomIndex].Denom
|
||||
coins = sdk.NewCoins(sdk.NewCoin(denom, amount.Mul(sdk.NewInt(2))))
|
||||
}
|
||||
}
|
||||
return distribution.NewCommunityPoolSpendProposal(
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 100),
|
||||
recipientAcc.Address,
|
||||
coins,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ func RegisterCodec(cdc *codec.Codec) {
|
|||
cdc.RegisterConcrete(MsgWithdrawDelegatorReward{}, "cosmos-sdk/MsgWithdrawDelegationReward", nil)
|
||||
cdc.RegisterConcrete(MsgWithdrawValidatorCommission{}, "cosmos-sdk/MsgWithdrawValidatorCommission", nil)
|
||||
cdc.RegisterConcrete(MsgSetWithdrawAddress{}, "cosmos-sdk/MsgModifyWithdrawAddress", nil)
|
||||
cdc.RegisterConcrete(CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal", nil)
|
||||
}
|
||||
|
||||
// generic sealed codec to be used throughout module
|
||||
|
|
|
@ -39,3 +39,9 @@ func ErrSetWithdrawAddrDisabled(codespace sdk.CodespaceType) sdk.Error {
|
|||
func ErrBadDistribution(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "community pool does not have sufficient coins to distribute")
|
||||
}
|
||||
func ErrInvalidProposalAmount(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "invalid community pool spend proposal amount")
|
||||
}
|
||||
func ErrEmptyProposalRecipient(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "invalid community pool spend proposal recipient")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// ProposalTypeCommunityPoolSpend defines the type for a CommunityPoolSpendProposal
|
||||
ProposalTypeCommunityPoolSpend = "CommunityPoolSpend"
|
||||
)
|
||||
|
||||
// Assert CommunityPoolSpendProposal implements govtypes.Content at compile-time
|
||||
var _ govtypes.Content = CommunityPoolSpendProposal{}
|
||||
|
||||
func init() {
|
||||
govtypes.RegisterProposalType(ProposalTypeCommunityPoolSpend)
|
||||
govtypes.RegisterProposalTypeCodec(CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal")
|
||||
}
|
||||
|
||||
// CommunityPoolSpendProposal spends from the community pool
|
||||
type CommunityPoolSpendProposal struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Recipient sdk.AccAddress `json:"recipient"`
|
||||
Amount sdk.Coins `json:"amount"`
|
||||
}
|
||||
|
||||
// NewCommunityPoolSpendProposal creates a new community pool spned proposal.
|
||||
func NewCommunityPoolSpendProposal(title, description string, recipient sdk.AccAddress, amount sdk.Coins) CommunityPoolSpendProposal {
|
||||
return CommunityPoolSpendProposal{title, description, recipient, amount}
|
||||
}
|
||||
|
||||
// GetTitle returns the title of a community pool spend proposal.
|
||||
func (csp CommunityPoolSpendProposal) GetTitle() string { return csp.Title }
|
||||
|
||||
// GetDescription returns the description of a community pool spend proposal.
|
||||
func (csp CommunityPoolSpendProposal) GetDescription() string { return csp.Description }
|
||||
|
||||
// GetDescription returns the routing key of a community pool spend proposal.
|
||||
func (csp CommunityPoolSpendProposal) ProposalRoute() string { return RouterKey }
|
||||
|
||||
// ProposalType returns the type of a community pool spend proposal.
|
||||
func (csp CommunityPoolSpendProposal) ProposalType() string { return ProposalTypeCommunityPoolSpend }
|
||||
|
||||
// ValidateBasic runs basic stateless validity checks
|
||||
func (csp CommunityPoolSpendProposal) ValidateBasic() sdk.Error {
|
||||
err := govtypes.ValidateAbstract(DefaultCodespace, csp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !csp.Amount.IsValid() {
|
||||
return ErrInvalidProposalAmount(DefaultCodespace)
|
||||
}
|
||||
if csp.Recipient.Empty() {
|
||||
return ErrEmptyProposalRecipient(DefaultCodespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (csp CommunityPoolSpendProposal) String() string {
|
||||
var b strings.Builder
|
||||
b.WriteString(fmt.Sprintf(`Community Pool Spend Proposal:
|
||||
Title: %s
|
||||
Description: %s
|
||||
Recipient: %s
|
||||
Amount: %s
|
||||
`, csp.Title, csp.Description, csp.Recipient, csp.Amount))
|
||||
return b.String()
|
||||
}
|
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
// ContentSimulator defines a function type alias for generating random proposal
|
||||
// content.
|
||||
type ContentSimulator func(r *rand.Rand) gov.Content
|
||||
type ContentSimulator func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) gov.Content
|
||||
|
||||
// SimulateSubmittingVotingAndSlashingForProposal simulates creating a msg Submit Proposal
|
||||
// voting on the proposal, and subsequently slashing the proposal. It is implemented using
|
||||
|
@ -51,7 +51,7 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, contentSim Con
|
|||
|
||||
// 1) submit proposal now
|
||||
sender := simulation.RandomAcc(r, accs)
|
||||
content := contentSim(r)
|
||||
content := contentSim(r, app, ctx, accs)
|
||||
msg, err := simulationCreateMsgSubmitProposal(r, content, sender)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(), nil, err
|
||||
|
@ -102,7 +102,7 @@ func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, handler sdk.Hand
|
|||
}
|
||||
|
||||
// SimulateTextProposalContent returns random text proposal content.
|
||||
func SimulateTextProposalContent(r *rand.Rand) gov.Content {
|
||||
func SimulateTextProposalContent(r *rand.Rand, _ *baseapp.BaseApp, _ sdk.Context, _ []simulation.Account) gov.Content {
|
||||
return gov.NewTextProposal(
|
||||
simulation.RandStringOfLength(r, 140),
|
||||
simulation.RandStringOfLength(r, 5000),
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
|
@ -109,7 +110,7 @@ var paramChangePool = []simParamChange{
|
|||
// SimulateParamChangeProposalContent returns random parameter change content.
|
||||
// It will generate a ParameterChangeProposal object with anywhere between 1 and
|
||||
// 3 parameter changes all of which have random, but valid values.
|
||||
func SimulateParamChangeProposalContent(r *rand.Rand) gov.Content {
|
||||
func SimulateParamChangeProposalContent(r *rand.Rand, _ *baseapp.BaseApp, _ sdk.Context, _ []simulation.Account) gov.Content {
|
||||
numChanges := simulation.RandIntBetween(r, 1, len(paramChangePool)/2)
|
||||
paramChanges := make([]params.ParamChange, numChanges, numChanges)
|
||||
paramChangesKeys := make(map[string]struct{})
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
@ -35,6 +36,14 @@ func RandStringOfLength(r *rand.Rand, n int) string {
|
|||
return string(b)
|
||||
}
|
||||
|
||||
func RandPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) {
|
||||
if !max.GT(sdk.OneInt()) {
|
||||
return sdk.Int{}, errors.New("max too small")
|
||||
}
|
||||
max = max.Sub(sdk.OneInt())
|
||||
return sdk.NewIntFromBigInt(new(big.Int).Rand(r, max.BigInt())).Add(sdk.OneInt()), nil
|
||||
}
|
||||
|
||||
// Generate a random amount
|
||||
// Note: The range of RandomAmount includes max, and is, in fact, biased to return max as well as 0.
|
||||
func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int {
|
||||
|
|
Loading…
Reference in New Issue