From bcca045caf49e9d6dc2b586415422b217ee686e0 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 10 Dec 2019 23:02:15 -0500 Subject: [PATCH] Merge PR #5384: Fund Community Pool -- Part II --- simapp/params/weights.go | 1 + x/distribution/alias.go | 2 + x/distribution/client/cli/tx.go | 11 +++-- x/distribution/client/rest/tx.go | 8 +++- x/distribution/handler.go | 8 ++-- x/distribution/keeper/keeper.go | 7 ++- x/distribution/keeper/keeper_test.go | 4 +- x/distribution/simulation/operations.go | 61 +++++++++++++++++++++++++ x/distribution/types/msg.go | 35 +++++++++----- x/distribution/types/msg_test.go | 2 +- 10 files changed, 113 insertions(+), 26 deletions(-) diff --git a/simapp/params/weights.go b/simapp/params/weights.go index adff26b8a..0ba377b00 100644 --- a/simapp/params/weights.go +++ b/simapp/params/weights.go @@ -7,6 +7,7 @@ const ( DefaultWeightMsgSetWithdrawAddress int = 50 DefaultWeightMsgWithdrawDelegationReward int = 50 DefaultWeightMsgWithdrawValidatorCommission int = 50 + DefaultWeightMsgFundCommunityPool int = 50 DefaultWeightMsgDeposit int = 100 DefaultWeightMsgVote int = 67 DefaultWeightMsgUnjail int = 100 diff --git a/x/distribution/alias.go b/x/distribution/alias.go index 466674a49..d55d88dd3 100644 --- a/x/distribution/alias.go +++ b/x/distribution/alias.go @@ -37,6 +37,7 @@ const ( ParamBaseProposerReward = types.ParamBaseProposerReward ParamBonusProposerReward = types.ParamBonusProposerReward ParamWithdrawAddrEnabled = types.ParamWithdrawAddrEnabled + TypeMsgFundCommunityPool = types.TypeMsgFundCommunityPool ) var ( @@ -90,6 +91,7 @@ var ( NewMsgSetWithdrawAddress = types.NewMsgSetWithdrawAddress NewMsgWithdrawDelegatorReward = types.NewMsgWithdrawDelegatorReward NewMsgWithdrawValidatorCommission = types.NewMsgWithdrawValidatorCommission + MsgFundCommunityPool = types.NewMsgFundCommunityPool NewCommunityPoolSpendProposal = types.NewCommunityPoolSpendProposal NewQueryValidatorOutstandingRewardsParams = types.NewQueryValidatorOutstandingRewardsParams NewQueryValidatorCommissionParams = types.NewQueryValidatorCommissionParams diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index b73244d61..29d0c17fc 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -261,12 +261,13 @@ Where proposal.json contains: return cmd } -// command to fund the community pool +// GetCmdFundCommunityPool returns a command implementation that supports directly +// funding the community pool. func GetCmdFundCommunityPool(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "fund-community-pool [amount]", Args: cobra.ExactArgs(1), - Short: "funds the community pool with the specified amount", + Short: "Funds the community pool with the specified amount", Long: strings.TrimSpace( fmt.Sprintf(`Funds the community pool with the specified amount @@ -287,7 +288,11 @@ $ %s tx fund-community-pool 100uatom --from mykey return err } - msg := types.NewMsgDepositIntoCommunityPool(amount, depositorAddr) + msg := types.NewMsgFundCommunityPool(amount, depositorAddr) + if err := msg.ValidateBasic(); err != nil { + return err + } + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go index 6044e150e..ace2c11a7 100644 --- a/x/distribution/client/rest/tx.go +++ b/x/distribution/client/rest/tx.go @@ -188,7 +188,6 @@ func withdrawValidatorRewardsHandlerFn(cliCtx context.CLIContext) http.HandlerFu } } -// Fund the community pool func fundCommunityPoolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req fundCommunityPoolReq @@ -207,7 +206,12 @@ func fundCommunityPoolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - msg := types.NewMsgDepositIntoCommunityPool(req.Amount, fromAddr) + msg := types.NewMsgFundCommunityPool(req.Amount, fromAddr) + if err := msg.ValidateBasic(); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) } } diff --git a/x/distribution/handler.go b/x/distribution/handler.go index d25612411..90c4bc14d 100644 --- a/x/distribution/handler.go +++ b/x/distribution/handler.go @@ -23,8 +23,8 @@ func NewHandler(k keeper.Keeper) sdk.Handler { case types.MsgWithdrawValidatorCommission: return handleMsgWithdrawValidatorCommission(ctx, msg, k) - case types.MsgDepositIntoCommunityPool: - return handleMsgDepositIntoCommunityPool(ctx, msg, k) + case types.MsgFundCommunityPool: + return handleMsgFundCommunityPool(ctx, msg, k) default: errMsg := fmt.Sprintf("unrecognized distribution message type: %T", msg) @@ -86,8 +86,8 @@ func handleMsgWithdrawValidatorCommission(ctx sdk.Context, msg types.MsgWithdraw return sdk.Result{Events: ctx.EventManager().Events()} } -func handleMsgDepositIntoCommunityPool(ctx sdk.Context, msg types.MsgDepositIntoCommunityPool, k keeper.Keeper) sdk.Result { - if err := k.DepositCommunityPoolFunds(ctx, msg.Amount, msg.Depositor); err != nil { +func handleMsgFundCommunityPool(ctx sdk.Context, msg types.MsgFundCommunityPool, k keeper.Keeper) sdk.Result { + if err := k.FundCommunityPool(ctx, msg.Amount, msg.Depositor); err != nil { return sdk.ResultFromError(err) } diff --git a/x/distribution/keeper/keeper.go b/x/distribution/keeper/keeper.go index a36834fb7..6aca0d86f 100644 --- a/x/distribution/keeper/keeper.go +++ b/x/distribution/keeper/keeper.go @@ -150,8 +150,11 @@ func (k Keeper) GetTotalRewards(ctx sdk.Context) (totalRewards sdk.DecCoins) { return totalRewards } -// DepositCommunityPoolFunds allows to transfer the specified amount from the sender into the community pool -func (k Keeper) DepositCommunityPoolFunds(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error { +// FundCommunityPool allows an account to directly fund the community fund pool. +// The amount is first added to the distribution module account and then directly +// added to the pool. An error is returned if the amount cannot be sent to the +// module account. +func (k Keeper) FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error { if err := k.supplyKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil { return err } diff --git a/x/distribution/keeper/keeper_test.go b/x/distribution/keeper/keeper_test.go index a493e4fea..f0421d80e 100644 --- a/x/distribution/keeper/keeper_test.go +++ b/x/distribution/keeper/keeper_test.go @@ -92,7 +92,7 @@ func TestGetTotalRewards(t *testing.T) { require.Equal(t, expectedRewards, totalRewards) } -func TestDepositCommunityPoolFunds(t *testing.T) { +func TestFundCommunityPool(t *testing.T) { // nolint dogsled ctx, _, bk, keeper, _, _, _ := CreateTestInputAdvanced(t, false, 1000, sdk.NewDecWithPrec(2, 2)) @@ -102,7 +102,7 @@ func TestDepositCommunityPoolFunds(t *testing.T) { initPool := keeper.GetFeePool(ctx) assert.Empty(t, initPool.CommunityPool) - err := keeper.DepositCommunityPoolFunds(ctx, amount, delAddr1) + err := keeper.FundCommunityPool(ctx, amount, delAddr1) assert.Nil(t, err) assert.Equal(t, initPool.CommunityPool.Add(sdk.NewDecCoins(amount)), keeper.GetFeePool(ctx).CommunityPool) diff --git a/x/distribution/simulation/operations.go b/x/distribution/simulation/operations.go index f3875e256..2ec483cbc 100644 --- a/x/distribution/simulation/operations.go +++ b/x/distribution/simulation/operations.go @@ -21,6 +21,7 @@ const ( OpWeightMsgSetWithdrawAddress = "op_weight_msg_set_withdraw_address" OpWeightMsgWithdrawDelegationReward = "op_weight_msg_withdraw_delegation_reward" OpWeightMsgWithdrawValidatorCommission = "op_weight_msg_withdraw_validator_commission" + OpWeightMsgFundCommunityPool = "op_weight_msg_fund_community_pool" ) // WeightedOperations returns all the operations from the module with their respective weights @@ -50,6 +51,13 @@ func WeightedOperations( }, ) + var weightMsgFundCommunityPool int + appParams.GetOrGenerate(cdc, OpWeightMsgFundCommunityPool, &weightMsgFundCommunityPool, nil, + func(_ *rand.Rand) { + weightMsgFundCommunityPool = simappparams.DefaultWeightMsgFundCommunityPool + }, + ) + return simulation.WeightedOperations{ simulation.NewWeightedOperation( weightMsgSetWithdrawAddress, @@ -63,6 +71,10 @@ func WeightedOperations( weightMsgWithdrawValidatorCommission, SimulateMsgWithdrawValidatorCommission(ak, k, sk), ), + simulation.NewWeightedOperation( + weightMsgFundCommunityPool, + SimulateMsgFundCommunityPool(ak, k, sk), + ), } } @@ -197,3 +209,52 @@ func SimulateMsgWithdrawValidatorCommission(ak types.AccountKeeper, k keeper.Kee return simulation.NewOperationMsg(msg, true, ""), nil, nil } } + +// SimulateMsgFundCommunityPool simulates MsgFundCommunityPool execution where +// a random account sends a random amount of its funds to the community pool. +func SimulateMsgFundCommunityPool(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simulation.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, + ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + + funder, _ := simulation.RandomAcc(r, accs) + + account := ak.GetAccount(ctx, funder.Address) + coins := account.SpendableCoins(ctx.BlockTime()) + + fundAmount := simulation.RandSubsetCoins(r, coins) + if fundAmount.Empty() { + return simulation.NoOpMsg(types.ModuleName), nil, nil + } + + var ( + fees sdk.Coins + err error + ) + + coins, hasNeg := coins.SafeSub(fundAmount) + if !hasNeg { + fees, err = simulation.RandomFees(r, ctx, coins) + if err != nil { + return simulation.NoOpMsg(types.ModuleName), nil, err + } + } + + msg := types.NewMsgFundCommunityPool(fundAmount, funder.Address) + tx := helpers.GenTx( + []sdk.Msg{msg}, + fees, + chainID, + []uint64{account.GetAccountNumber()}, + []uint64{account.GetSequence()}, + funder.PrivKey, + ) + + res := app.Deliver(tx) + if !res.IsOK() { + return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log) + } + + return simulation.NewOperationMsg(msg, true, ""), nil, nil + } +} diff --git a/x/distribution/types/msg.go b/x/distribution/types/msg.go index 8bc8b1a0d..408983cb0 100644 --- a/x/distribution/types/msg.go +++ b/x/distribution/types/msg.go @@ -117,40 +117,51 @@ func (msg MsgWithdrawValidatorCommission) ValidateBasic() sdk.Error { return nil } -// msg struct for delegation withdraw from a single validator -type MsgDepositIntoCommunityPool struct { +const TypeMsgFundCommunityPool = "fund_community_pool" + +// MsgFundCommunityPool defines a Msg type that allows an account to directly +// fund the community pool. +type MsgFundCommunityPool struct { Amount sdk.Coins `json:"amount" yaml:"amount"` Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` } -func NewMsgDepositIntoCommunityPool(amount sdk.Coins, depositor sdk.AccAddress) MsgDepositIntoCommunityPool { - return MsgDepositIntoCommunityPool{ +// NewMsgFundCommunityPool returns a new MsgFundCommunityPool with a sender and +// a funding amount. +func NewMsgFundCommunityPool(amount sdk.Coins, depositor sdk.AccAddress) MsgFundCommunityPool { + return MsgFundCommunityPool{ Amount: amount, Depositor: depositor, } } -func (msg MsgDepositIntoCommunityPool) Route() string { return ModuleName } -func (msg MsgDepositIntoCommunityPool) Type() string { return "deposit_into_community_pool" } +// Route returns the MsgFundCommunityPool message route. +func (msg MsgFundCommunityPool) Route() string { return ModuleName } -// Return address that must sign over msg.GetSignBytes() -func (msg MsgDepositIntoCommunityPool) GetSigners() []sdk.AccAddress { +// Type returns the MsgFundCommunityPool message type. +func (msg MsgFundCommunityPool) Type() string { return TypeMsgFundCommunityPool } + +// GetSigners returns the signer addresses that are expected to sign the result +// of GetSignBytes. +func (msg MsgFundCommunityPool) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Depositor} } -// get the bytes for the message signer to sign on -func (msg MsgDepositIntoCommunityPool) GetSignBytes() []byte { +// GetSignBytes returns the raw bytes for a MsgFundCommunityPool message that +// the expected signer needs to sign. +func (msg MsgFundCommunityPool) GetSignBytes() []byte { bz := ModuleCdc.MustMarshalJSON(msg) return sdk.MustSortJSON(bz) } -// quick validity check -func (msg MsgDepositIntoCommunityPool) ValidateBasic() sdk.Error { +// ValidateBasic performs basic MsgFundCommunityPool message validation. +func (msg MsgFundCommunityPool) ValidateBasic() sdk.Error { if !msg.Amount.IsValid() { return sdk.ErrInvalidCoins(msg.Amount.String()) } if msg.Depositor.Empty() { return sdk.ErrInvalidAddress(msg.Depositor.String()) } + return nil } diff --git a/x/distribution/types/msg_test.go b/x/distribution/types/msg_test.go index c78931191..324626178 100644 --- a/x/distribution/types/msg_test.go +++ b/x/distribution/types/msg_test.go @@ -85,7 +85,7 @@ func TestMsgDepositIntoCommunityPool(t *testing.T) { {sdk.NewCoins(sdk.NewInt64Coin("uatom", 1000)), delAddr1, true}, } for i, tc := range tests { - msg := NewMsgDepositIntoCommunityPool(tc.amount, tc.depositor) + msg := NewMsgFundCommunityPool(tc.amount, tc.depositor) if tc.expectPass { require.Nil(t, msg.ValidateBasic(), "test index: %v", i) } else {