Skip to content

Commit

Permalink
Merge PR cosmos#5249: Support for sending funds to the community pool…
Browse files Browse the repository at this point in the history
… - Part I
  • Loading branch information
RiccardoM authored and xiangjianmeng committed Dec 18, 2019
1 parent 78abfd0 commit 5b30343
Show file tree
Hide file tree
Showing 13 changed files with 256 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ that allows for arbitrary vesting periods.
* `ValidateSigCountDecorator`: Validate the number of signatures in tx based on app-parameters.
* `IncrementSequenceDecorator`: Increments the account sequence for each signer to prevent replay attacks.
* (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.
* (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).

### Improvements

Expand Down
17 changes: 16 additions & 1 deletion simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ var (
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
gov.ModuleName: {supply.Burner},
}

// module accounts that are allowed to receive tokens
allowedReceivingModAcc = map[string]bool{
distr.ModuleName: true,
}
)

// MakeCodec - custom tx codec
Expand Down Expand Up @@ -167,7 +172,7 @@ func NewSimApp(
)
app.BankKeeper = bank.NewBaseKeeper(
app.AccountKeeper, app.subspaces[bank.ModuleName], bank.DefaultCodespace,
app.ModuleAccountAddrs(),
app.BlacklistedAccAddrs(),
)
app.SupplyKeeper = supply.NewKeeper(
app.cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms,
Expand Down Expand Up @@ -322,6 +327,16 @@ func (app *SimApp) ModuleAccountAddrs() map[string]bool {
return modAccAddrs
}

// BlacklistedAccAddrs returns all the app's module account addresses black listed for receiving tokens.
func (app *SimApp) BlacklistedAccAddrs() map[string]bool {
blacklistedAddrs := make(map[string]bool)
for acc := range maccPerms {
blacklistedAddrs[supply.NewModuleAddress(acc).String()] = !allowedReceivingModAcc[acc]
}

return blacklistedAddrs
}

// Codec returns SimApp's codec.
//
// NOTE: This is solely to be used for testing purposes as it may be desirable
Expand Down
2 changes: 1 addition & 1 deletion simapp/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestBlackListedAddrs(t *testing.T) {
app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)

for acc := range maccPerms {
require.True(t, app.BankKeeper.BlacklistedAddr(app.SupplyKeeper.GetModuleAddress(acc)))
require.Equal(t, !allowedReceivingModAcc[acc], app.BankKeeper.BlacklistedAddr(app.SupplyKeeper.GetModuleAddress(acc)))
}
}

Expand Down
2 changes: 1 addition & 1 deletion simapp/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, exp sdk.Coins)
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
res := app.AccountKeeper.GetAccount(ctxCheck, addr)

require.Equal(t, exp, res.GetCoins())
require.True(t, exp.IsEqual(res.GetCoins()))
}

// SignCheckDeliver checks a generated signed transaction and simulates a
Expand Down
76 changes: 56 additions & 20 deletions x/bank/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package bank_test
import (
"testing"

"github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/supply"
"github.com/stretchr/testify/require"

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

// A module account cannot be the recipient of bank sends
// A module account cannot be the recipient of bank sends unless it has been marked as such
func TestSendToModuleAcc(t *testing.T) {
acc := &auth.BaseAccount{
Address: addr1,
Coins: coins,
tests := []struct {
name string
fromBalance sdk.Coins
msg types.MsgSend
expSimPass bool
expPass bool
expFromBalance sdk.Coins
expToBalance sdk.Coins
}{
{
name: "Normal module account cannot be the recipient of bank sends",
fromBalance: coins,
msg: types.NewMsgSend(addr1, moduleAccAddr, coins),
expSimPass: false,
expPass: false,
expFromBalance: coins,
expToBalance: sdk.NewCoins(),
},
{
name: "Allowed module account can be the recipient of bank sends",
fromBalance: coins,
msg: types.NewMsgSend(addr1, supply.NewModuleAddress(distribution.ModuleName), coins),
expPass: true,
expSimPass: true,
expFromBalance: sdk.NewCoins(),
expToBalance: coins,
},
}

genAccs := []authexported.GenesisAccount{acc}
app := simapp.SetupWithGenesisAccounts(genAccs)
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
acc := &auth.BaseAccount{
Address: test.msg.FromAddress,
Coins: test.fromBalance,
}

ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
genAccs := []authexported.GenesisAccount{acc}
app := simapp.SetupWithGenesisAccounts(genAccs)

res1 := app.AccountKeeper.GetAccount(ctxCheck, addr1)
require.NotNil(t, res1)
require.Equal(t, acc, res1.(*auth.BaseAccount))
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})

origAccNum := res1.GetAccountNumber()
origSeq := res1.GetSequence()
res1 := app.AccountKeeper.GetAccount(ctxCheck, test.msg.FromAddress)
require.NotNil(t, res1)
require.Equal(t, acc, res1.(*auth.BaseAccount))

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

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

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

require.Equal(t, res2.GetAccountNumber(), origAccNum)
require.Equal(t, res2.GetSequence(), origSeq+1)
res2 := app.AccountKeeper.GetAccount(app.NewContext(true, abci.Header{}), addr1)
require.NotNil(t, res2)

require.Equal(t, res2.GetAccountNumber(), origAccNum)
require.Equal(t, res2.GetSequence(), origSeq+1)
})
}
}

func TestMsgMultiSendWithAccounts(t *testing.T) {
Expand Down
4 changes: 3 additions & 1 deletion x/bank/internal/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"
"time"

"github.com/cosmos/cosmos-sdk/x/supply"
"github.com/tendermint/tendermint/libs/common"

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

// Test retrieving black listed accounts
for acc := range simapp.GetMaccPerms() {
require.True(t, app.BankKeeper.BlacklistedAddr(app.SupplyKeeper.GetModuleAddress(acc)))
addr := supply.NewModuleAddress(acc)
require.Equal(t, app.BlacklistedAccAddrs()[addr.String()], app.BankKeeper.BlacklistedAddr(addr))
}
}

Expand Down
33 changes: 33 additions & 0 deletions x/distribution/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command {
GetCmdWithdrawRewards(cdc),
GetCmdSetWithdrawAddr(cdc),
GetCmdWithdrawAllRewards(cdc, storeKey),
GetCmdFundCommunityPool(cdc),
)...)

return distTxCmd
Expand Down Expand Up @@ -259,3 +260,35 @@ Where proposal.json contains:

return cmd
}

// command to fund 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",
Long: strings.TrimSpace(
fmt.Sprintf(`Funds the community pool with the specified amount
Example:
$ %s tx fund-community-pool 100uatom --from mykey
`,
version.ClientName,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
inBuf := bufio.NewReader(cmd.InOrStdin())
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)

depositorAddr := cliCtx.GetFromAddress()
amount, err := sdk.ParseCoins(args[0])
if err != nil {
return err
}

msg := types.NewMsgDepositIntoCommunityPool(amount, depositorAddr)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
}
35 changes: 35 additions & 0 deletions x/distribution/client/rest/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute strin
withdrawValidatorRewardsHandlerFn(cliCtx),
).Methods("POST")

// Fund the community pool
r.HandleFunc(
"/distribution/community_pool",
fundCommunityPoolHandlerFn(cliCtx),
).Methods("POST")

}

type (
Expand All @@ -50,6 +56,11 @@ type (
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
WithdrawAddress sdk.AccAddress `json:"withdraw_address" yaml:"withdraw_address"`
}

fundCommunityPoolReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Amount sdk.Coins `json:"amount" yaml:"amount"`
}
)

// Withdraw delegator rewards
Expand Down Expand Up @@ -177,6 +188,30 @@ 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
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}

req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}

fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

msg := types.NewMsgDepositIntoCommunityPool(req.Amount, fromAddr)
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}

// Auxiliary

func checkDelegatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.AccAddress, bool) {
Expand Down
19 changes: 19 additions & 0 deletions x/distribution/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
case types.MsgWithdrawValidatorCommission:
return handleMsgWithdrawValidatorCommission(ctx, msg, k)

case types.MsgDepositIntoCommunityPool:
return handleMsgDepositIntoCommunityPool(ctx, msg, k)

default:
errMsg := fmt.Sprintf("unrecognized distribution message type: %T", msg)
return sdk.ErrUnknownRequest(errMsg).Result()
Expand Down Expand Up @@ -83,6 +86,22 @@ 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 {
return sdk.ResultFromError(err)
}

ctx.EventManager().EmitEvent(
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, msg.Depositor.String()),
),
)

return sdk.Result{Events: ctx.EventManager().Events()}
}

func NewCommunityPoolSpendProposalHandler(k Keeper) govtypes.Handler {
return func(ctx sdk.Context, content govtypes.Content) sdk.Error {
switch c := content.(type) {
Expand Down
13 changes: 13 additions & 0 deletions x/distribution/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,16 @@ 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 {
if err := k.supplyKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil {
return err
}

feePool := k.GetFeePool(ctx)
feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoins(amount))
k.SetFeePool(ctx, feePool)

return nil
}
19 changes: 19 additions & 0 deletions x/distribution/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package keeper
import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/stretchr/testify/require"

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

require.Equal(t, expectedRewards, totalRewards)
}

func TestDepositCommunityPoolFunds(t *testing.T) {
// nolint dogsled
ctx, _, bk, keeper, _, _, _ := CreateTestInputAdvanced(t, false, 1000, sdk.NewDecWithPrec(2, 2))

amount := sdk.NewCoins(sdk.NewInt64Coin("stake", 100))
_ = bk.SetCoins(ctx, delAddr1, amount)

initPool := keeper.GetFeePool(ctx)
assert.Empty(t, initPool.CommunityPool)

err := keeper.DepositCommunityPoolFunds(ctx, amount, delAddr1)
assert.Nil(t, err)

assert.Equal(t, initPool.CommunityPool.Add(sdk.NewDecCoins(amount)), keeper.GetFeePool(ctx).CommunityPool)
assert.Empty(t, bk.GetCoins(ctx, delAddr1))
}
Loading

0 comments on commit 5b30343

Please sign in to comment.