Skip to content

blacklist module accounts from receiving txs #4797

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 31, 2019
2 changes: 2 additions & 0 deletions .pending/bugfixes/modules/_4795-restrict-txs-m
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#4795 restrict module accounts from receiving transactions.
Allowing this would cause an invariant on the module account coins.
2 changes: 1 addition & 1 deletion simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func NewSimApp(

// add keepers
app.accountKeeper = auth.NewAccountKeeper(app.cdc, app.keyAccount, authSubspace, auth.ProtoBaseAccount)
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace)
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace, app.ModuleAccountAddrs())
app.supplyKeeper = supply.NewKeeper(app.cdc, app.keySupply, app.accountKeeper, app.bankKeeper, supply.DefaultCodespace, maccPerms)
stakingKeeper := staking.NewKeeper(app.cdc, app.keyStaking, app.tkeyStaking,
app.supplyKeeper, stakingSubspace, staking.DefaultCodespace)
Expand Down
80 changes: 57 additions & 23 deletions x/bank/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/bank/internal/keeper"
"github.com/cosmos/cosmos-sdk/x/bank/internal/types"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/cosmos/cosmos-sdk/x/supply"

"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -50,6 +47,7 @@ var (
freeFee = auth.NewStdFee(100000, sdk.Coins{sdk.NewInt64Coin("foocoin", 0)})

sendMsg1 = types.NewMsgSend(addr1, addr2, coins)
sendMsg2 = types.NewMsgSend(addr1, moduleAccAddr, coins)

multiSendMsg1 = types.MsgMultiSend{
Inputs: []types.Input{types.NewInput(addr1, coins)},
Expand Down Expand Up @@ -88,26 +86,15 @@ var (
types.NewOutput(addr2, manyCoins),
},
}
)

// initialize the mock application for this module
func getMockApp(t *testing.T) *mock.App {
mapp, err := getBenchmarkMockApp()
supply.RegisterCodec(mapp.Cdc)
require.NoError(t, err)
return mapp
}

// overwrite the mock init chainer
func getInitChainer(mapp *mock.App, keeper keeper.BaseKeeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
mapp.InitChainer(ctx, req)
bankGenesis := bank.DefaultGenesisState()
bank.InitGenesis(ctx, keeper, bankGenesis)

return abci.ResponseInitChain{}
multiSendMsg6 = types.MsgMultiSend{
Inputs: []types.Input{
types.NewInput(addr1, coins),
},
Outputs: []types.Output{
types.NewOutput(moduleAccAddr, coins),
},
}
}
)

func TestSendNotEnoughBalance(t *testing.T) {
mapp := getMockApp(t)
Expand Down Expand Up @@ -140,14 +127,53 @@ func TestSendNotEnoughBalance(t *testing.T) {
require.True(t, res2.GetSequence() == origSeq+1)
}

func TestSendToModuleAcc(t *testing.T) {
mapp := getMockApp(t)
acc := &auth.BaseAccount{
Address: addr1,
Coins: coins,
}

macc := &auth.BaseAccount{
Address: moduleAccAddr,
}

mock.SetGenesis(mapp, []auth.Account{acc, macc})

ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})

res1 := mapp.AccountKeeper.GetAccount(ctxCheck, addr1)
require.NotNil(t, res1)
require.Equal(t, acc, res1.(*auth.BaseAccount))

origAccNum := res1.GetAccountNumber()
origSeq := res1.GetSequence()

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, []sdk.Msg{sendMsg2}, []uint64{origAccNum}, []uint64{origSeq}, false, false, priv1)

mock.CheckBalance(t, mapp, addr1, coins)
mock.CheckBalance(t, mapp, moduleAccAddr, sdk.Coins(nil))

res2 := mapp.AccountKeeper.GetAccount(mapp.NewContext(true, abci.Header{}), addr1)
require.NotNil(t, res2)

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

func TestMsgMultiSendWithAccounts(t *testing.T) {
mapp := getMockApp(t)
acc := &auth.BaseAccount{
Address: addr1,
Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 67)},
}

mock.SetGenesis(mapp, []auth.Account{acc})
macc := &auth.BaseAccount{
Address: moduleAccAddr,
}

mock.SetGenesis(mapp, []auth.Account{acc, macc})

ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})

Expand Down Expand Up @@ -176,6 +202,14 @@ func TestMsgMultiSendWithAccounts(t *testing.T) {
expPass: false,
privKeys: []crypto.PrivKey{priv1},
},
{
msgs: []sdk.Msg{multiSendMsg6},
accNums: []uint64{0},
accSeqs: []uint64{0},
expSimPass: false,
expPass: false,
privKeys: []crypto.PrivKey{priv1},
},
}

for _, tc := range testCases {
Expand Down
29 changes: 28 additions & 1 deletion x/bank/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bank_test
import (
"testing"

"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -11,18 +12,44 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank/internal/keeper"
"github.com/cosmos/cosmos-sdk/x/bank/internal/types"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/cosmos/cosmos-sdk/x/supply"
)

var moduleAccAddr = sdk.AccAddress([]byte("moduleAcc"))

// initialize the mock application for this module
func getMockApp(t *testing.T) *mock.App {
mapp, err := getBenchmarkMockApp()
supply.RegisterCodec(mapp.Cdc)
require.NoError(t, err)
return mapp
}

// overwrite the mock init chainer
func getInitChainer(mapp *mock.App, keeper keeper.BaseKeeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
mapp.InitChainer(ctx, req)
bankGenesis := bank.DefaultGenesisState()
bank.InitGenesis(ctx, keeper, bankGenesis)

return abci.ResponseInitChain{}
}
}

// getBenchmarkMockApp initializes a mock application for this module, for purposes of benchmarking
// Any long term API support commitments do not apply to this function.
func getBenchmarkMockApp() (*mock.App, error) {
mapp := mock.NewApp()

types.RegisterCodec(mapp.Cdc)

blacklistedAddrs := make(map[string]bool)
blacklistedAddrs[moduleAccAddr.String()] = true

bankKeeper := keeper.NewBaseKeeper(
mapp.AccountKeeper,
mapp.ParamsKeeper.Subspace(types.DefaultParamspace),
types.DefaultCodespace,
blacklistedAddrs,
)
mapp.Router().AddRoute(types.RouterKey, bank.NewHandler(bankKeeper))
mapp.SetInitChainer(getInitChainer(mapp, bankKeeper))
Expand Down
10 changes: 10 additions & 0 deletions x/bank/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ func handleMsgSend(ctx sdk.Context, k keeper.Keeper, msg types.MsgSend) sdk.Resu
return types.ErrSendDisabled(k.Codespace()).Result()
}

if k.BlacklistedAddrs()[msg.ToAddress.String()] {
return sdk.ErrUnauthorized(fmt.Sprintf("%s is not allowed to receive transactions", msg.ToAddress)).Result()
}

err := k.SendCoins(ctx, msg.FromAddress, msg.ToAddress, msg.Amount)
if err != nil {
return err.Result()
Expand All @@ -55,6 +59,12 @@ func handleMsgMultiSend(ctx sdk.Context, k keeper.Keeper, msg types.MsgMultiSend
return types.ErrSendDisabled(k.Codespace()).Result()
}

for _, out := range msg.Outputs {
if k.BlacklistedAddrs()[out.Address.String()] {
return sdk.ErrUnauthorized(fmt.Sprintf("%s is not allowed to receive transactions", out.Address)).Result()
}
}

err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs)
if err != nil {
return err.Result()
Expand Down
59 changes: 59 additions & 0 deletions x/bank/internal/keeper/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package keeper

// DONTCOVER

import (
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank/internal/types"
"github.com/cosmos/cosmos-sdk/x/params"
)

type testInput struct {
cdc *codec.Codec
ctx sdk.Context
k Keeper
ak auth.AccountKeeper
pk params.Keeper
}

func setupTestInput() testInput {
db := dbm.NewMemDB()

cdc := codec.New()
auth.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)

authCapKey := sdk.NewKVStoreKey("authCapKey")
keyParams := sdk.NewKVStoreKey("params")
tkeyParams := sdk.NewTransientStoreKey("transient_params")

ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(authCapKey, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
ms.LoadLatestVersion()

blacklistedAddrs := make(map[string]bool)
blacklistedAddrs[sdk.AccAddress([]byte("moduleAcc")).String()] = true

pk := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)

ak := auth.NewAccountKeeper(
cdc, authCapKey, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount,
)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "test-chain-id"}, false, log.NewNopLogger())

ak.SetParams(ctx, auth.DefaultParams())

bankKeeper := NewBaseKeeper(ak, pk.Subspace(types.DefaultParamspace), types.DefaultCodespace, blacklistedAddrs)
bankKeeper.SetSendEnabled(ctx, true)

return testInput{cdc: cdc, ctx: ctx, k: bankKeeper, ak: ak, pk: pk}
}
2 changes: 1 addition & 1 deletion x/bank/internal/keeper/invariants.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank/internal/types"
)

// register bank invariants
// RegisterInvariants registers bank the module invariants
func RegisterInvariants(ir sdk.InvariantRegistry, ak types.AccountKeeper) {
ir.RegisterRoute(types.ModuleName, "nonnegative-outstanding",
NonnegativeBalanceInvariant(ak))
Expand Down
18 changes: 15 additions & 3 deletions x/bank/internal/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ type BaseKeeper struct {
// NewBaseKeeper returns a new BaseKeeper
func NewBaseKeeper(ak types.AccountKeeper,
paramSpace params.Subspace,
codespace sdk.CodespaceType) BaseKeeper {
codespace sdk.CodespaceType, blacklistedAddrs map[string]bool) BaseKeeper {

ps := paramSpace.WithKeyTable(types.ParamKeyTable())
return BaseKeeper{
BaseSendKeeper: NewBaseSendKeeper(ak, ps, codespace),
BaseSendKeeper: NewBaseSendKeeper(ak, ps, codespace, blacklistedAddrs),
ak: ak,
paramSpace: ps,
}
Expand Down Expand Up @@ -145,6 +145,8 @@ type SendKeeper interface {

GetSendEnabled(ctx sdk.Context) bool
SetSendEnabled(ctx sdk.Context, enabled bool)

BlacklistedAddrs() map[string]bool
}

var _ SendKeeper = (*BaseSendKeeper)(nil)
Expand All @@ -156,16 +158,20 @@ type BaseSendKeeper struct {

ak types.AccountKeeper
paramSpace params.Subspace

// list of addresses that are restricted from receiving transactions
blacklistedAddrs map[string]bool
}

// NewBaseSendKeeper returns a new BaseSendKeeper.
func NewBaseSendKeeper(ak types.AccountKeeper,
paramSpace params.Subspace, codespace sdk.CodespaceType) BaseSendKeeper {
paramSpace params.Subspace, codespace sdk.CodespaceType, blacklistedAddrs map[string]bool) BaseSendKeeper {

return BaseSendKeeper{
BaseViewKeeper: NewBaseViewKeeper(ak, codespace),
ak: ak,
paramSpace: paramSpace,
blacklistedAddrs: blacklistedAddrs,
}
}

Expand Down Expand Up @@ -321,6 +327,12 @@ func (keeper BaseSendKeeper) SetSendEnabled(ctx sdk.Context, enabled bool) {
keeper.paramSpace.Set(ctx, types.ParamStoreKeySendEnabled, &enabled)
}

// BlacklistedAddrs returns the list of the blacklisted accounts addresses that
// are not allowed to receive funds
func (keeper BaseSendKeeper) BlacklistedAddrs() map[string]bool {
return keeper.blacklistedAddrs
}

var _ ViewKeeper = (*BaseViewKeeper)(nil)

// ViewKeeper defines a module interface that facilitates read only access to
Expand Down
Loading