Skip to content

Commit 5b30343

Browse files
RiccardoMxiangjianmeng
authored andcommitted
Merge PR cosmos#5249: Support for sending funds to the community pool - Part I
1 parent 78abfd0 commit 5b30343

File tree

13 files changed

+256
-24
lines changed

13 files changed

+256
-24
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ that allows for arbitrary vesting periods.
139139
* `ValidateSigCountDecorator`: Validate the number of signatures in tx based on app-parameters.
140140
* `IncrementSequenceDecorator`: Increments the account sequence for each signer to prevent replay attacks.
141141
* (cli) [\#5223](https://github.com/cosmos/cosmos-sdk/issues/5223) Cosmos Ledger App v2.0.0 is now supported. The changes are backwards compatible and App v1.5.x is still supported.
142+
* (modules) [\#5249](https://github.com/cosmos/cosmos-sdk/pull/5249) Funds are now allowed to be directly sent to the community pool (via the distribution module account).
142143

143144
### Improvements
144145

simapp/app.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ var (
7272
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
7373
gov.ModuleName: {supply.Burner},
7474
}
75+
76+
// module accounts that are allowed to receive tokens
77+
allowedReceivingModAcc = map[string]bool{
78+
distr.ModuleName: true,
79+
}
7580
)
7681

7782
// MakeCodec - custom tx codec
@@ -167,7 +172,7 @@ func NewSimApp(
167172
)
168173
app.BankKeeper = bank.NewBaseKeeper(
169174
app.AccountKeeper, app.subspaces[bank.ModuleName], bank.DefaultCodespace,
170-
app.ModuleAccountAddrs(),
175+
app.BlacklistedAccAddrs(),
171176
)
172177
app.SupplyKeeper = supply.NewKeeper(
173178
app.cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms,
@@ -322,6 +327,16 @@ func (app *SimApp) ModuleAccountAddrs() map[string]bool {
322327
return modAccAddrs
323328
}
324329

330+
// BlacklistedAccAddrs returns all the app's module account addresses black listed for receiving tokens.
331+
func (app *SimApp) BlacklistedAccAddrs() map[string]bool {
332+
blacklistedAddrs := make(map[string]bool)
333+
for acc := range maccPerms {
334+
blacklistedAddrs[supply.NewModuleAddress(acc).String()] = !allowedReceivingModAcc[acc]
335+
}
336+
337+
return blacklistedAddrs
338+
}
339+
325340
// Codec returns SimApp's codec.
326341
//
327342
// NOTE: This is solely to be used for testing purposes as it may be desirable

simapp/app_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func TestBlackListedAddrs(t *testing.T) {
4242
app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)
4343

4444
for acc := range maccPerms {
45-
require.True(t, app.BankKeeper.BlacklistedAddr(app.SupplyKeeper.GetModuleAddress(acc)))
45+
require.Equal(t, !allowedReceivingModAcc[acc], app.BankKeeper.BlacklistedAddr(app.SupplyKeeper.GetModuleAddress(acc)))
4646
}
4747
}
4848

simapp/test_helpers.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, exp sdk.Coins)
105105
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
106106
res := app.AccountKeeper.GetAccount(ctxCheck, addr)
107107

108-
require.Equal(t, exp, res.GetCoins())
108+
require.True(t, exp.IsEqual(res.GetCoins()))
109109
}
110110

111111
// SignCheckDeliver checks a generated signed transaction and simulates a

x/bank/app_test.go

+56-20
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package bank_test
33
import (
44
"testing"
55

6+
"github.com/cosmos/cosmos-sdk/x/distribution"
7+
"github.com/cosmos/cosmos-sdk/x/supply"
68
"github.com/stretchr/testify/require"
79

810
abci "github.com/tendermint/tendermint/abci/types"
@@ -118,36 +120,70 @@ func TestSendNotEnoughBalance(t *testing.T) {
118120
require.Equal(t, res2.GetSequence(), origSeq+1)
119121
}
120122

121-
// A module account cannot be the recipient of bank sends
123+
// A module account cannot be the recipient of bank sends unless it has been marked as such
122124
func TestSendToModuleAcc(t *testing.T) {
123-
acc := &auth.BaseAccount{
124-
Address: addr1,
125-
Coins: coins,
125+
tests := []struct {
126+
name string
127+
fromBalance sdk.Coins
128+
msg types.MsgSend
129+
expSimPass bool
130+
expPass bool
131+
expFromBalance sdk.Coins
132+
expToBalance sdk.Coins
133+
}{
134+
{
135+
name: "Normal module account cannot be the recipient of bank sends",
136+
fromBalance: coins,
137+
msg: types.NewMsgSend(addr1, moduleAccAddr, coins),
138+
expSimPass: false,
139+
expPass: false,
140+
expFromBalance: coins,
141+
expToBalance: sdk.NewCoins(),
142+
},
143+
{
144+
name: "Allowed module account can be the recipient of bank sends",
145+
fromBalance: coins,
146+
msg: types.NewMsgSend(addr1, supply.NewModuleAddress(distribution.ModuleName), coins),
147+
expPass: true,
148+
expSimPass: true,
149+
expFromBalance: sdk.NewCoins(),
150+
expToBalance: coins,
151+
},
126152
}
127153

128-
genAccs := []authexported.GenesisAccount{acc}
129-
app := simapp.SetupWithGenesisAccounts(genAccs)
154+
for _, test := range tests {
155+
test := test
156+
t.Run(test.name, func(t *testing.T) {
157+
acc := &auth.BaseAccount{
158+
Address: test.msg.FromAddress,
159+
Coins: test.fromBalance,
160+
}
130161

131-
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
162+
genAccs := []authexported.GenesisAccount{acc}
163+
app := simapp.SetupWithGenesisAccounts(genAccs)
132164

133-
res1 := app.AccountKeeper.GetAccount(ctxCheck, addr1)
134-
require.NotNil(t, res1)
135-
require.Equal(t, acc, res1.(*auth.BaseAccount))
165+
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
136166

137-
origAccNum := res1.GetAccountNumber()
138-
origSeq := res1.GetSequence()
167+
res1 := app.AccountKeeper.GetAccount(ctxCheck, test.msg.FromAddress)
168+
require.NotNil(t, res1)
169+
require.Equal(t, acc, res1.(*auth.BaseAccount))
139170

140-
header := abci.Header{Height: app.LastBlockHeight() + 1}
141-
simapp.SignCheckDeliver(t, app.Codec(), app.BaseApp, header, []sdk.Msg{sendMsg2}, []uint64{origAccNum}, []uint64{origSeq}, false, false, priv1)
171+
origAccNum := res1.GetAccountNumber()
172+
origSeq := res1.GetSequence()
142173

143-
simapp.CheckBalance(t, app, addr1, coins)
144-
simapp.CheckBalance(t, app, moduleAccAddr, sdk.Coins(nil))
174+
header := abci.Header{Height: app.LastBlockHeight() + 1}
175+
simapp.SignCheckDeliver(t, app.Codec(), app.BaseApp, header, []sdk.Msg{test.msg}, []uint64{origAccNum}, []uint64{origSeq}, test.expSimPass, test.expPass, priv1)
145176

146-
res2 := app.AccountKeeper.GetAccount(app.NewContext(true, abci.Header{}), addr1)
147-
require.NotNil(t, res2)
177+
simapp.CheckBalance(t, app, test.msg.FromAddress, test.expFromBalance)
178+
simapp.CheckBalance(t, app, test.msg.ToAddress, test.expToBalance)
148179

149-
require.Equal(t, res2.GetAccountNumber(), origAccNum)
150-
require.Equal(t, res2.GetSequence(), origSeq+1)
180+
res2 := app.AccountKeeper.GetAccount(app.NewContext(true, abci.Header{}), addr1)
181+
require.NotNil(t, res2)
182+
183+
require.Equal(t, res2.GetAccountNumber(), origAccNum)
184+
require.Equal(t, res2.GetSequence(), origSeq+1)
185+
})
186+
}
151187
}
152188

153189
func TestMsgMultiSendWithAccounts(t *testing.T) {

x/bank/internal/keeper/keeper_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"testing"
55
"time"
66

7+
"github.com/cosmos/cosmos-sdk/x/supply"
78
"github.com/tendermint/tendermint/libs/common"
89

910
"github.com/stretchr/testify/require"
@@ -97,7 +98,8 @@ func TestKeeper(t *testing.T) {
9798

9899
// Test retrieving black listed accounts
99100
for acc := range simapp.GetMaccPerms() {
100-
require.True(t, app.BankKeeper.BlacklistedAddr(app.SupplyKeeper.GetModuleAddress(acc)))
101+
addr := supply.NewModuleAddress(acc)
102+
require.Equal(t, app.BlacklistedAccAddrs()[addr.String()], app.BankKeeper.BlacklistedAddr(addr))
101103
}
102104
}
103105

x/distribution/client/cli/tx.go

+33
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command {
4747
GetCmdWithdrawRewards(cdc),
4848
GetCmdSetWithdrawAddr(cdc),
4949
GetCmdWithdrawAllRewards(cdc, storeKey),
50+
GetCmdFundCommunityPool(cdc),
5051
)...)
5152

5253
return distTxCmd
@@ -259,3 +260,35 @@ Where proposal.json contains:
259260

260261
return cmd
261262
}
263+
264+
// command to fund the community pool
265+
func GetCmdFundCommunityPool(cdc *codec.Codec) *cobra.Command {
266+
return &cobra.Command{
267+
Use: "fund-community-pool [amount]",
268+
Args: cobra.ExactArgs(1),
269+
Short: "funds the community pool with the specified amount",
270+
Long: strings.TrimSpace(
271+
fmt.Sprintf(`Funds the community pool with the specified amount
272+
273+
Example:
274+
$ %s tx fund-community-pool 100uatom --from mykey
275+
`,
276+
version.ClientName,
277+
),
278+
),
279+
RunE: func(cmd *cobra.Command, args []string) error {
280+
inBuf := bufio.NewReader(cmd.InOrStdin())
281+
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
282+
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
283+
284+
depositorAddr := cliCtx.GetFromAddress()
285+
amount, err := sdk.ParseCoins(args[0])
286+
if err != nil {
287+
return err
288+
}
289+
290+
msg := types.NewMsgDepositIntoCommunityPool(amount, depositorAddr)
291+
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
292+
},
293+
}
294+
}

x/distribution/client/rest/tx.go

+35
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute strin
3939
withdrawValidatorRewardsHandlerFn(cliCtx),
4040
).Methods("POST")
4141

42+
// Fund the community pool
43+
r.HandleFunc(
44+
"/distribution/community_pool",
45+
fundCommunityPoolHandlerFn(cliCtx),
46+
).Methods("POST")
47+
4248
}
4349

4450
type (
@@ -50,6 +56,11 @@ type (
5056
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
5157
WithdrawAddress sdk.AccAddress `json:"withdraw_address" yaml:"withdraw_address"`
5258
}
59+
60+
fundCommunityPoolReq struct {
61+
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
62+
Amount sdk.Coins `json:"amount" yaml:"amount"`
63+
}
5364
)
5465

5566
// Withdraw delegator rewards
@@ -177,6 +188,30 @@ func withdrawValidatorRewardsHandlerFn(cliCtx context.CLIContext) http.HandlerFu
177188
}
178189
}
179190

191+
// Fund the community pool
192+
func fundCommunityPoolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
193+
return func(w http.ResponseWriter, r *http.Request) {
194+
var req fundCommunityPoolReq
195+
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
196+
return
197+
}
198+
199+
req.BaseReq = req.BaseReq.Sanitize()
200+
if !req.BaseReq.ValidateBasic(w) {
201+
return
202+
}
203+
204+
fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
205+
if err != nil {
206+
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
207+
return
208+
}
209+
210+
msg := types.NewMsgDepositIntoCommunityPool(req.Amount, fromAddr)
211+
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
212+
}
213+
}
214+
180215
// Auxiliary
181216

182217
func checkDelegatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.AccAddress, bool) {

x/distribution/handler.go

+19
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
2323
case types.MsgWithdrawValidatorCommission:
2424
return handleMsgWithdrawValidatorCommission(ctx, msg, k)
2525

26+
case types.MsgDepositIntoCommunityPool:
27+
return handleMsgDepositIntoCommunityPool(ctx, msg, k)
28+
2629
default:
2730
errMsg := fmt.Sprintf("unrecognized distribution message type: %T", msg)
2831
return sdk.ErrUnknownRequest(errMsg).Result()
@@ -83,6 +86,22 @@ func handleMsgWithdrawValidatorCommission(ctx sdk.Context, msg types.MsgWithdraw
8386
return sdk.Result{Events: ctx.EventManager().Events()}
8487
}
8588

89+
func handleMsgDepositIntoCommunityPool(ctx sdk.Context, msg types.MsgDepositIntoCommunityPool, k keeper.Keeper) sdk.Result {
90+
if err := k.DepositCommunityPoolFunds(ctx, msg.Amount, msg.Depositor); err != nil {
91+
return sdk.ResultFromError(err)
92+
}
93+
94+
ctx.EventManager().EmitEvent(
95+
sdk.NewEvent(
96+
sdk.EventTypeMessage,
97+
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
98+
sdk.NewAttribute(sdk.AttributeKeySender, msg.Depositor.String()),
99+
),
100+
)
101+
102+
return sdk.Result{Events: ctx.EventManager().Events()}
103+
}
104+
86105
func NewCommunityPoolSpendProposalHandler(k Keeper) govtypes.Handler {
87106
return func(ctx sdk.Context, content govtypes.Content) sdk.Error {
88107
switch c := content.(type) {

x/distribution/keeper/keeper.go

+13
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,16 @@ func (k Keeper) GetTotalRewards(ctx sdk.Context) (totalRewards sdk.DecCoins) {
149149
)
150150
return totalRewards
151151
}
152+
153+
// DepositCommunityPoolFunds allows to transfer the specified amount from the sender into the community pool
154+
func (k Keeper) DepositCommunityPoolFunds(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error {
155+
if err := k.supplyKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil {
156+
return err
157+
}
158+
159+
feePool := k.GetFeePool(ctx)
160+
feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoins(amount))
161+
k.SetFeePool(ctx, feePool)
162+
163+
return nil
164+
}

x/distribution/keeper/keeper_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package keeper
33
import (
44
"testing"
55

6+
"github.com/stretchr/testify/assert"
7+
68
"github.com/stretchr/testify/require"
79

810
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -89,3 +91,20 @@ func TestGetTotalRewards(t *testing.T) {
8991

9092
require.Equal(t, expectedRewards, totalRewards)
9193
}
94+
95+
func TestDepositCommunityPoolFunds(t *testing.T) {
96+
// nolint dogsled
97+
ctx, _, bk, keeper, _, _, _ := CreateTestInputAdvanced(t, false, 1000, sdk.NewDecWithPrec(2, 2))
98+
99+
amount := sdk.NewCoins(sdk.NewInt64Coin("stake", 100))
100+
_ = bk.SetCoins(ctx, delAddr1, amount)
101+
102+
initPool := keeper.GetFeePool(ctx)
103+
assert.Empty(t, initPool.CommunityPool)
104+
105+
err := keeper.DepositCommunityPoolFunds(ctx, amount, delAddr1)
106+
assert.Nil(t, err)
107+
108+
assert.Equal(t, initPool.CommunityPool.Add(sdk.NewDecCoins(amount)), keeper.GetFeePool(ctx).CommunityPool)
109+
assert.Empty(t, bk.GetCoins(ctx, delAddr1))
110+
}

0 commit comments

Comments
 (0)