feat(bank): Allow injectable restrictions on bank transfers (backport #14224) (#17457)

This commit is contained in:
mergify[bot] 2023-09-07 22:48:49 +02:00 committed by GitHub
parent ea095bdc7c
commit 8cff58c1f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1774 additions and 37 deletions

View File

@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Features
* (x/bank) [#14224](https://github.com/cosmos/cosmos-sdk/pull/14224) Allow injection of restrictions on transfers using `AppendSendRestriction` or `PrependSendRestriction`.
* (genutil) [#17571](https://github.com/cosmos/cosmos-sdk/pull/17571) Allow creation of `AppGenesis` without a file lookup.
### Improvments

View File

@ -236,8 +236,12 @@ accounts. The send keeper does not alter the total supply (mint or burn coins).
type SendKeeper interface {
ViewKeeper
InputOutputCoins(ctx context.Context, inputs types.Input, outputs []types.Output) error
SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
AppendSendRestriction(restriction SendRestrictionFn)
PrependSendRestriction(restriction SendRestrictionFn)
ClearSendRestriction()
InputOutputCoins(ctx context.Context, input types.Input, outputs []types.Output) error
SendCoins(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error
GetParams(ctx context.Context) types.Params
SetParams(ctx context.Context, params types.Params) error
@ -256,6 +260,86 @@ type SendKeeper interface {
}
```
#### Send Restrictions
The `SendKeeper` applies a `SendRestrictionFn` before each transfer of funds.
```golang
// A SendRestrictionFn can restrict sends and/or provide a new receiver address.
type SendRestrictionFn func(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (newToAddr sdk.AccAddress, err error)
```
After the `SendKeeper` (or `BaseKeeper`) has been created, send restrictions can be added to it using the `AppendSendRestriction` or `PrependSendRestriction` functions.
Both functions compose the provided restriction with any previously provided restrictions.
`AppendSendRestriction` adds the provided restriction to be run after any previously provided send restrictions.
`PrependSendRestriction` adds the restriction to be run before any previously provided send restrictions.
The composition will short-circuit when an error is encountered. I.e. if the first one returns an error, the second is not run.
During `SendCoins`, the send restriction is applied after coins are removed from the from address, but before adding them to the to address.
During `InputOutputCoins`, the send restriction is applied after the input coins are removed and once for each output before the funds are added.
A send restriction function should make use of a custom value in the context to allow bypassing that specific restriction.
For example, in your module's keeper package, you'd define the send restriction function:
```golang
var _ banktypes.SendRestrictionFn = Keeper{}.SendRestrictionFn
func (k Keeper) SendRestrictionFn(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.AccAddress, error) {
// Bypass if the context says to.
if mymodule.HasBypass(ctx) {
return toAddr, nil
}
// Your custom send restriction logic goes here.
return nil, errors.New("not implemented")
}
```
The bank keeper should be provided to your keeper's constructor so the send restriction can be added to it:
```golang
func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, bankKeeper mymodule.BankKeeper) Keeper {
rv := Keeper{/*...*/}
bankKeeper.AppendSendRestriction(rv.SendRestrictionFn)
return rv
}
```
Then, in the `mymodule` package, define the context helpers:
```golang
const bypassKey = "bypass-mymodule-restriction"
// WithBypass returns a new context that will cause the mymodule bank send restriction to be skipped.
func WithBypass(ctx context.Context) context.Context {
return sdk.UnwrapSDKContext(ctx).WithValue(bypassKey, true)
}
// WithoutBypass returns a new context that will cause the mymodule bank send restriction to not be skipped.
func WithoutBypass(ctx context.Context) context.Context {
return sdk.UnwrapSDKContext(ctx).WithValue(bypassKey, false)
}
// HasBypass checks the context to see if the mymodule bank send restriction should be skipped.
func HasBypass(ctx context.Context) bool {
bypassValue := ctx.Value(bypassKey)
if bypassValue == nil {
return false
}
bypass, isBool := bypassValue.(bool)
return isBool && bypass
}
```
Now, anywhere where you want to use `SendCoins` or `InputOutputCoins`, but you don't want your send restriction applied:
```golang
func (k Keeper) DoThing(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error {
return k.bankKeeper.SendCoins(mymodule.WithBypass(ctx), fromAddr, toAddr, amt)
}
```
### ViewKeeper
The view keeper provides read-only access to account balances. The view keeper does not have balance alteration functionality. All balance lookups are `O(1)`.

View File

@ -0,0 +1,14 @@
package keeper
import "github.com/cosmos/cosmos-sdk/x/bank/types"
// This file exists in the keeper package to expose some private things
// for the purpose of testing in the keeper_test package.
func (k BaseSendKeeper) SetSendRestriction(restriction types.SendRestrictionFn) {
k.sendRestriction.fn = restriction
}
func (k BaseSendKeeper) GetSendRestrictionFn() types.SendRestrictionFn {
return k.sendRestriction.fn
}

View File

@ -23,7 +23,7 @@ var _ Keeper = (*BaseKeeper)(nil)
// between accounts.
type Keeper interface {
SendKeeper
WithMintCoinsRestriction(MintingRestrictionFn) BaseKeeper
WithMintCoinsRestriction(types.MintingRestrictionFn) BaseKeeper
InitGenesis(context.Context, *types.GenesisState)
ExportGenesis(context.Context) *types.GenesisState
@ -59,12 +59,10 @@ type BaseKeeper struct {
ak types.AccountKeeper
cdc codec.BinaryCodec
storeService store.KVStoreService
mintCoinsRestrictionFn MintingRestrictionFn
mintCoinsRestrictionFn types.MintingRestrictionFn
logger log.Logger
}
type MintingRestrictionFn func(ctx context.Context, coins sdk.Coins) error
// GetPaginatedTotalSupply queries for the supply, ignoring 0 coins, with a given pagination
func (k BaseKeeper) GetPaginatedTotalSupply(ctx context.Context, pagination *query.PageRequest) (sdk.Coins, *query.PageResponse, error) {
coins, pageResp, err := query.CollectionPaginate(ctx, k.Supply, pagination, func(key string, value math.Int) (sdk.Coin, error) {
@ -103,7 +101,7 @@ func NewBaseKeeper(
ak: ak,
cdc: cdc,
storeService: storeService,
mintCoinsRestrictionFn: func(ctx context.Context, coins sdk.Coins) error { return nil },
mintCoinsRestrictionFn: types.NoOpMintingRestrictionFn,
logger: logger,
}
}
@ -113,19 +111,8 @@ func NewBaseKeeper(
// Previous restriction functions can be nested as such:
//
// bankKeeper.WithMintCoinsRestriction(restriction1).WithMintCoinsRestriction(restriction2)
func (k BaseKeeper) WithMintCoinsRestriction(check MintingRestrictionFn) BaseKeeper {
oldRestrictionFn := k.mintCoinsRestrictionFn
k.mintCoinsRestrictionFn = func(ctx context.Context, coins sdk.Coins) error {
err := check(ctx, coins)
if err != nil {
return err
}
err = oldRestrictionFn(ctx, coins)
if err != nil {
return err
}
return nil
}
func (k BaseKeeper) WithMintCoinsRestriction(check types.MintingRestrictionFn) BaseKeeper {
k.mintCoinsRestrictionFn = check.Then(k.mintCoinsRestrictionFn)
return k
}

View File

@ -4,6 +4,7 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"strings"
"testing"
@ -251,6 +252,64 @@ func (suite *KeeperTestSuite) mockUnDelegateCoins(ctx context.Context, acc, mAcc
suite.authKeeper.EXPECT().GetAccount(ctx, mAcc.GetAddress()).Return(mAcc)
}
func (suite *KeeperTestSuite) TestAppendSendRestriction() {
var calls []int
testRestriction := func(index int) banktypes.SendRestrictionFn {
return func(_ context.Context, _, _ sdk.AccAddress, _ sdk.Coins) (sdk.AccAddress, error) {
calls = append(calls, index)
return nil, nil
}
}
bk := suite.bankKeeper
// Initial append of the test restriction.
bk.SetSendRestriction(nil)
bk.AppendSendRestriction(testRestriction(1))
_, _ = bk.GetSendRestrictionFn()(suite.ctx, nil, nil, nil)
suite.Require().Equal([]int{1}, calls, "restriction calls after first append")
// Append the test restriction again.
calls = nil
bk.AppendSendRestriction(testRestriction(2))
_, _ = bk.GetSendRestrictionFn()(suite.ctx, nil, nil, nil)
suite.Require().Equal([]int{1, 2}, calls, "restriction calls after second append")
// make sure the original bank keeper has the restrictions too.
calls = nil
_, _ = suite.bankKeeper.GetSendRestrictionFn()(suite.ctx, nil, nil, nil)
suite.Require().Equal([]int{1, 2}, calls, "restriction calls from original bank keeper")
}
func (suite *KeeperTestSuite) TestPrependSendRestriction() {
var calls []int
testRestriction := func(index int) banktypes.SendRestrictionFn {
return func(_ context.Context, _, _ sdk.AccAddress, _ sdk.Coins) (sdk.AccAddress, error) {
calls = append(calls, index)
return nil, nil
}
}
bk := suite.bankKeeper
// Initial append of the test restriction.
bk.SetSendRestriction(nil)
bk.PrependSendRestriction(testRestriction(1))
_, _ = bk.GetSendRestrictionFn()(suite.ctx, nil, nil, nil)
suite.Require().Equal([]int{1}, calls, "restriction calls after first append")
// Append the test restriction again.
calls = nil
bk.PrependSendRestriction(testRestriction(2))
_, _ = bk.GetSendRestrictionFn()(suite.ctx, nil, nil, nil)
suite.Require().Equal([]int{2, 1}, calls, "restriction calls after second append")
// make sure the original bank keeper has the restrictions too.
calls = nil
_, _ = suite.bankKeeper.GetSendRestrictionFn()(suite.ctx, nil, nil, nil)
suite.Require().Equal([]int{2, 1}, calls, "restriction calls from original bank keeper")
}
func (suite *KeeperTestSuite) TestGetAuthority() {
storeService := runtime.NewKVStoreService(storetypes.NewKVStoreKey(banktypes.StoreKey))
NewKeeperWithAuthority := func(authority string) keeper.BaseKeeper {
@ -649,6 +708,294 @@ func (suite *KeeperTestSuite) TestInputOutputCoins() {
require.Equal(expected, acc3Balances)
}
func (suite *KeeperTestSuite) TestInputOutputCoinsWithRestrictions() {
type restrictionArgs struct {
ctx context.Context
fromAddr sdk.AccAddress
toAddr sdk.AccAddress
amt sdk.Coins
}
var actualRestrictionArgs []*restrictionArgs
restrictionError := func(messages ...string) banktypes.SendRestrictionFn {
i := -1
return func(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.AccAddress, error) {
actualRestrictionArgs = append(actualRestrictionArgs, &restrictionArgs{
ctx: ctx,
fromAddr: fromAddr,
toAddr: toAddr,
amt: amt,
})
i++
if i < len(messages) {
if len(messages[i]) > 0 {
return nil, errors.New(messages[i])
}
}
return toAddr, nil
}
}
restrictionPassthrough := func() banktypes.SendRestrictionFn {
return func(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.AccAddress, error) {
actualRestrictionArgs = append(actualRestrictionArgs, &restrictionArgs{
ctx: ctx,
fromAddr: fromAddr,
toAddr: toAddr,
amt: amt,
})
return toAddr, nil
}
}
restrictionNewTo := func(newToAddrs ...sdk.AccAddress) banktypes.SendRestrictionFn {
i := -1
return func(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.AccAddress, error) {
actualRestrictionArgs = append(actualRestrictionArgs, &restrictionArgs{
ctx: ctx,
fromAddr: fromAddr,
toAddr: toAddr,
amt: amt,
})
i++
if i < len(newToAddrs) {
if len(newToAddrs[i]) > 0 {
return newToAddrs[i], nil
}
}
return toAddr, nil
}
}
type expBals struct {
from sdk.Coins
to1 sdk.Coins
to2 sdk.Coins
}
setupCtx := suite.ctx
balances := sdk.NewCoins(newFooCoin(1000), newBarCoin(500))
fromAddr := accAddrs[0]
fromAcc := authtypes.NewBaseAccountWithAddress(fromAddr)
inputAccs := []sdk.AccountI{fromAcc}
toAddr1 := accAddrs[1]
toAddr2 := accAddrs[2]
suite.mockFundAccount(accAddrs[0])
suite.Require().NoError(banktestutil.FundAccount(setupCtx, suite.bankKeeper, accAddrs[0], balances))
tests := []struct {
name string
fn banktypes.SendRestrictionFn
inputCoins sdk.Coins
outputs []banktypes.Output
outputAddrs []sdk.AccAddress
expArgs []*restrictionArgs
expErr string
expBals expBals
}{
{
name: "nil restriction",
fn: nil,
inputCoins: sdk.NewCoins(newFooCoin(5)),
outputs: []banktypes.Output{{Address: toAddr1.String(), Coins: sdk.NewCoins(newFooCoin(5))}},
outputAddrs: []sdk.AccAddress{toAddr1},
expBals: expBals{
from: sdk.NewCoins(newFooCoin(995), newBarCoin(500)),
to1: sdk.NewCoins(newFooCoin(5)),
to2: sdk.Coins{},
},
},
{
name: "passthrough restriction single output",
fn: restrictionPassthrough(),
inputCoins: sdk.NewCoins(newFooCoin(10)),
outputs: []banktypes.Output{{Address: toAddr1.String(), Coins: sdk.NewCoins(newFooCoin(10))}},
outputAddrs: []sdk.AccAddress{toAddr1},
expArgs: []*restrictionArgs{
{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr1,
amt: sdk.NewCoins(newFooCoin(10)),
},
},
expBals: expBals{
from: sdk.NewCoins(newFooCoin(985), newBarCoin(500)),
to1: sdk.NewCoins(newFooCoin(15)),
to2: sdk.Coins{},
},
},
{
name: "new to restriction single output",
fn: restrictionNewTo(toAddr2),
inputCoins: sdk.NewCoins(newFooCoin(26)),
outputs: []banktypes.Output{{Address: toAddr1.String(), Coins: sdk.NewCoins(newFooCoin(26))}},
outputAddrs: []sdk.AccAddress{toAddr2},
expArgs: []*restrictionArgs{
{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr1,
amt: sdk.NewCoins(newFooCoin(26)),
},
},
expBals: expBals{
from: sdk.NewCoins(newFooCoin(959), newBarCoin(500)),
to1: sdk.NewCoins(newFooCoin(15)),
to2: sdk.NewCoins(newFooCoin(26)),
},
},
{
name: "error restriction single output",
fn: restrictionError("restriction test error"),
inputCoins: sdk.NewCoins(newBarCoin(88)),
outputs: []banktypes.Output{{Address: toAddr1.String(), Coins: sdk.NewCoins(newBarCoin(88))}},
outputAddrs: []sdk.AccAddress{},
expArgs: []*restrictionArgs{
{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr1,
amt: sdk.NewCoins(newBarCoin(88)),
},
},
expErr: "restriction test error",
expBals: expBals{
from: sdk.NewCoins(newFooCoin(959), newBarCoin(412)),
to1: sdk.NewCoins(newFooCoin(15)),
to2: sdk.NewCoins(newFooCoin(26)),
},
},
{
name: "passthrough restriction two outputs",
fn: restrictionPassthrough(),
inputCoins: sdk.NewCoins(newFooCoin(11), newBarCoin(12)),
outputs: []banktypes.Output{
{Address: toAddr1.String(), Coins: sdk.NewCoins(newFooCoin(11))},
{Address: toAddr2.String(), Coins: sdk.NewCoins(newBarCoin(12))},
},
outputAddrs: []sdk.AccAddress{toAddr1, toAddr2},
expArgs: []*restrictionArgs{
{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr1,
amt: sdk.NewCoins(newFooCoin(11)),
},
{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr2,
amt: sdk.NewCoins(newBarCoin(12)),
},
},
expBals: expBals{
from: sdk.NewCoins(newFooCoin(948), newBarCoin(400)),
to1: sdk.NewCoins(newFooCoin(26)),
to2: sdk.NewCoins(newFooCoin(26), newBarCoin(12)),
},
},
{
name: "error restriction two outputs error on second",
fn: restrictionError("", "second restriction error"),
inputCoins: sdk.NewCoins(newFooCoin(44)),
outputs: []banktypes.Output{
{Address: toAddr1.String(), Coins: sdk.NewCoins(newFooCoin(12))},
{Address: toAddr2.String(), Coins: sdk.NewCoins(newFooCoin(32))},
},
outputAddrs: []sdk.AccAddress{toAddr1},
expArgs: []*restrictionArgs{
{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr1,
amt: sdk.NewCoins(newFooCoin(12)),
},
{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr2,
amt: sdk.NewCoins(newFooCoin(32)),
},
},
expErr: "second restriction error",
expBals: expBals{
from: sdk.NewCoins(newFooCoin(904), newBarCoin(400)),
to1: sdk.NewCoins(newFooCoin(38)),
to2: sdk.NewCoins(newFooCoin(26), newBarCoin(12)),
},
},
{
name: "new to restriction two outputs",
fn: restrictionNewTo(toAddr2, toAddr1),
inputCoins: sdk.NewCoins(newBarCoin(35)),
outputs: []banktypes.Output{
{Address: toAddr1.String(), Coins: sdk.NewCoins(newBarCoin(10))},
{Address: toAddr2.String(), Coins: sdk.NewCoins(newBarCoin(25))},
},
outputAddrs: []sdk.AccAddress{toAddr1, toAddr2},
expArgs: []*restrictionArgs{
{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr1,
amt: sdk.NewCoins(newBarCoin(10)),
},
{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr2,
amt: sdk.NewCoins(newBarCoin(25)),
},
},
expBals: expBals{
from: sdk.NewCoins(newFooCoin(904), newBarCoin(365)),
to1: sdk.NewCoins(newFooCoin(38), newBarCoin(25)),
to2: sdk.NewCoins(newFooCoin(26), newBarCoin(22)),
},
},
}
for _, tc := range tests {
suite.Run(tc.name, func() {
existingSendRestrictionFn := suite.bankKeeper.GetSendRestrictionFn()
defer suite.bankKeeper.SetSendRestriction(existingSendRestrictionFn)
actualRestrictionArgs = nil
suite.bankKeeper.SetSendRestriction(tc.fn)
ctx := suite.ctx
suite.mockInputOutputCoins(inputAccs, tc.outputAddrs)
input := banktypes.Input{
Address: fromAddr.String(),
Coins: tc.inputCoins,
}
var err error
testFunc := func() {
err = suite.bankKeeper.InputOutputCoins(ctx, input, tc.outputs)
}
suite.Require().NotPanics(testFunc, "InputOutputCoins")
if len(tc.expErr) > 0 {
suite.Assert().EqualError(err, tc.expErr, "InputOutputCoins error")
} else {
suite.Assert().NoError(err, "InputOutputCoins error")
}
if len(tc.expArgs) > 0 {
for i, expArgs := range tc.expArgs {
suite.Assert().Equal(expArgs.ctx, actualRestrictionArgs[i].ctx, "[%d] ctx provided to restriction", i)
suite.Assert().Equal(expArgs.fromAddr, actualRestrictionArgs[i].fromAddr, "[%d] fromAddr provided to restriction", i)
suite.Assert().Equal(expArgs.toAddr, actualRestrictionArgs[i].toAddr, "[%d] toAddr provided to restriction", i)
suite.Assert().Equal(expArgs.amt.String(), actualRestrictionArgs[i].amt.String(), "[%d] amt provided to restriction", i)
}
} else {
suite.Assert().Nil(actualRestrictionArgs, "args provided to a restriction")
}
fromBal := suite.bankKeeper.GetAllBalances(ctx, fromAddr)
suite.Assert().Equal(tc.expBals.from.String(), fromBal.String(), "fromAddr balance")
to1Bal := suite.bankKeeper.GetAllBalances(ctx, toAddr1)
suite.Assert().Equal(tc.expBals.to1.String(), to1Bal.String(), "toAddr1 balance")
to2Bal := suite.bankKeeper.GetAllBalances(ctx, toAddr2)
suite.Assert().Equal(tc.expBals.to2.String(), to2Bal.String(), "toAddr2 balance")
})
}
}
func (suite *KeeperTestSuite) TestSendCoins() {
ctx := suite.ctx
require := suite.Require()
@ -686,6 +1033,184 @@ func (suite *KeeperTestSuite) TestSendCoins() {
require.Equal(newBarCoin(25), coins[0], "expected only bar coins in the account balance, got: %v", coins)
}
func (suite *KeeperTestSuite) TestSendCoinsWithRestrictions() {
type restrictionArgs struct {
ctx context.Context
fromAddr sdk.AccAddress
toAddr sdk.AccAddress
amt sdk.Coins
}
var actualRestrictionArgs *restrictionArgs
restrictionError := func(message string) banktypes.SendRestrictionFn {
return func(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.AccAddress, error) {
actualRestrictionArgs = &restrictionArgs{
ctx: ctx,
fromAddr: fromAddr,
toAddr: toAddr,
amt: amt,
}
return nil, errors.New(message)
}
}
restrictionPassthrough := func() banktypes.SendRestrictionFn {
return func(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.AccAddress, error) {
actualRestrictionArgs = &restrictionArgs{
ctx: ctx,
fromAddr: fromAddr,
toAddr: toAddr,
amt: amt,
}
return toAddr, nil
}
}
restrictionNewTo := func(newToAddr sdk.AccAddress) banktypes.SendRestrictionFn {
return func(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.AccAddress, error) {
actualRestrictionArgs = &restrictionArgs{
ctx: ctx,
fromAddr: fromAddr,
toAddr: toAddr,
amt: amt,
}
return newToAddr, nil
}
}
type expBals struct {
from sdk.Coins
to1 sdk.Coins
to2 sdk.Coins
}
setupCtx := suite.ctx
balances := sdk.NewCoins(newFooCoin(1000), newBarCoin(500))
fromAddr := accAddrs[0]
fromAcc := authtypes.NewBaseAccountWithAddress(fromAddr)
toAddr1 := accAddrs[1]
toAddr2 := accAddrs[2]
suite.mockFundAccount(accAddrs[0])
suite.Require().NoError(banktestutil.FundAccount(setupCtx, suite.bankKeeper, accAddrs[0], balances))
tests := []struct {
name string
fn banktypes.SendRestrictionFn
toAddr sdk.AccAddress
finalAddr sdk.AccAddress
amt sdk.Coins
expArgs *restrictionArgs
expErr string
expBals expBals
}{
{
name: "nil restriction",
fn: nil,
toAddr: toAddr1,
finalAddr: toAddr1,
amt: sdk.NewCoins(newFooCoin(5)),
expArgs: nil,
expBals: expBals{
from: sdk.NewCoins(newFooCoin(995), newBarCoin(500)),
to1: sdk.NewCoins(newFooCoin(5)),
to2: sdk.Coins{},
},
},
{
name: "passthrough restriction",
fn: restrictionPassthrough(),
toAddr: toAddr1,
finalAddr: toAddr1,
amt: sdk.NewCoins(newFooCoin(10)),
expArgs: &restrictionArgs{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr1,
amt: sdk.NewCoins(newFooCoin(10)),
},
expBals: expBals{
from: sdk.NewCoins(newFooCoin(985), newBarCoin(500)),
to1: sdk.NewCoins(newFooCoin(15)),
to2: sdk.Coins{},
},
},
{
name: "new to addr restriction",
fn: restrictionNewTo(toAddr2),
toAddr: toAddr1,
finalAddr: toAddr2,
amt: sdk.NewCoins(newBarCoin(27)),
expArgs: &restrictionArgs{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr1,
amt: sdk.NewCoins(newBarCoin(27)),
},
expBals: expBals{
from: sdk.NewCoins(newFooCoin(985), newBarCoin(473)),
to1: sdk.NewCoins(newFooCoin(15)),
to2: sdk.NewCoins(newBarCoin(27)),
},
},
{
name: "restriction returns error",
fn: restrictionError("test restriction error"),
toAddr: toAddr1,
finalAddr: toAddr1,
amt: sdk.NewCoins(newFooCoin(100), newBarCoin(200)),
expArgs: &restrictionArgs{
ctx: suite.ctx,
fromAddr: fromAddr,
toAddr: toAddr1,
amt: sdk.NewCoins(newFooCoin(100), newBarCoin(200)),
},
expErr: "test restriction error",
expBals: expBals{
from: sdk.NewCoins(newFooCoin(885), newBarCoin(273)),
to1: sdk.NewCoins(newFooCoin(15)),
to2: sdk.NewCoins(newBarCoin(27)),
},
},
}
for _, tc := range tests {
suite.Run(tc.name, func() {
existingSendRestrictionFn := suite.bankKeeper.GetSendRestrictionFn()
defer suite.bankKeeper.SetSendRestriction(existingSendRestrictionFn)
actualRestrictionArgs = nil
suite.bankKeeper.SetSendRestriction(tc.fn)
ctx := suite.ctx
if len(tc.expErr) > 0 {
suite.authKeeper.EXPECT().GetAccount(ctx, fromAddr).Return(fromAcc)
} else {
suite.mockSendCoins(ctx, fromAcc, tc.finalAddr)
}
var err error
testFunc := func() {
err = suite.bankKeeper.SendCoins(ctx, fromAddr, tc.toAddr, tc.amt)
}
suite.Require().NotPanics(testFunc, "SendCoins")
if len(tc.expErr) > 0 {
suite.Assert().EqualError(err, tc.expErr, "SendCoins error")
} else {
suite.Assert().NoError(err, "SendCoins error")
}
if tc.expArgs != nil {
suite.Assert().Equal(tc.expArgs.ctx, actualRestrictionArgs.ctx, "ctx provided to restriction")
suite.Assert().Equal(tc.expArgs.fromAddr, actualRestrictionArgs.fromAddr, "fromAddr provided to restriction")
suite.Assert().Equal(tc.expArgs.toAddr, actualRestrictionArgs.toAddr, "toAddr provided to restriction")
suite.Assert().Equal(tc.expArgs.amt.String(), actualRestrictionArgs.amt.String(), "amt provided to restriction")
} else {
suite.Assert().Nil(actualRestrictionArgs, "args provided to a restriction")
}
fromBal := suite.bankKeeper.GetAllBalances(ctx, fromAddr)
suite.Assert().Equal(tc.expBals.from.String(), fromBal.String(), "fromAddr balance")
to1Bal := suite.bankKeeper.GetAllBalances(ctx, toAddr1)
suite.Assert().Equal(tc.expBals.to1.String(), to1Bal.String(), "toAddr1 balance")
to2Bal := suite.bankKeeper.GetAllBalances(ctx, toAddr2)
suite.Assert().Equal(tc.expBals.to2.String(), to2Bal.String(), "toAddr2 balance")
})
}
}
func (suite *KeeperTestSuite) TestSendCoins_Invalid_SendLockedCoins() {
balances := sdk.NewCoins(newFooCoin(50))
@ -1464,7 +1989,7 @@ func (suite *KeeperTestSuite) TestMintCoinRestrictions() {
}
for _, test := range tests {
keeper := suite.bankKeeper.WithMintCoinsRestriction(keeper.MintingRestrictionFn(test.restrictionFn))
keeper := suite.bankKeeper.WithMintCoinsRestriction(banktypes.MintingRestrictionFn(test.restrictionFn))
for _, testCase := range test.testCases {
if testCase.expectPass {
suite.mockMintCoins(multiPermAcc)

View File

@ -22,7 +22,11 @@ import (
type SendKeeper interface {
ViewKeeper
InputOutputCoins(ctx context.Context, inputs types.Input, outputs []types.Output) error
AppendSendRestriction(restriction types.SendRestrictionFn)
PrependSendRestriction(restriction types.SendRestrictionFn)
ClearSendRestriction()
InputOutputCoins(ctx context.Context, input types.Input, outputs []types.Output) error
SendCoins(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error
GetParams(ctx context.Context) types.Params
@ -63,6 +67,8 @@ type BaseSendKeeper struct {
// the address capable of executing a MsgUpdateParams message. Typically, this
// should be the x/gov module account.
authority string
sendRestriction *sendRestriction
}
func NewBaseSendKeeper(
@ -78,16 +84,32 @@ func NewBaseSendKeeper(
}
return BaseSendKeeper{
BaseViewKeeper: NewBaseViewKeeper(cdc, storeService, ak, logger),
cdc: cdc,
ak: ak,
storeService: storeService,
blockedAddrs: blockedAddrs,
authority: authority,
logger: logger,
BaseViewKeeper: NewBaseViewKeeper(cdc, storeService, ak, logger),
cdc: cdc,
ak: ak,
storeService: storeService,
blockedAddrs: blockedAddrs,
authority: authority,
logger: logger,
sendRestriction: newSendRestriction(),
}
}
// AppendSendRestriction adds the provided SendRestrictionFn to run after previously provided restrictions.
func (k BaseSendKeeper) AppendSendRestriction(restriction types.SendRestrictionFn) {
k.sendRestriction.append(restriction)
}
// PrependSendRestriction adds the provided SendRestrictionFn to run before previously provided restrictions.
func (k BaseSendKeeper) PrependSendRestriction(restriction types.SendRestrictionFn) {
k.sendRestriction.prepend(restriction)
}
// ClearSendRestriction removes the send restriction (if there is one).
func (k BaseSendKeeper) ClearSendRestriction() {
k.sendRestriction.clear()
}
// GetAuthority returns the x/bank module's authority.
func (k BaseSendKeeper) GetAuthority() string {
return k.authority
@ -143,8 +165,14 @@ func (k BaseSendKeeper) InputOutputCoins(ctx context.Context, input types.Input,
),
)
var outAddress sdk.AccAddress
for _, out := range outputs {
outAddress, err := k.ak.AddressCodec().StringToBytes(out.Address)
outAddress, err = k.ak.AddressCodec().StringToBytes(out.Address)
if err != nil {
return err
}
outAddress, err = k.sendRestriction.apply(ctx, inAddress, outAddress, out.Coins)
if err != nil {
return err
}
@ -156,7 +184,7 @@ func (k BaseSendKeeper) InputOutputCoins(ctx context.Context, input types.Input,
sdkCtx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeTransfer,
sdk.NewAttribute(types.AttributeKeyRecipient, out.Address),
sdk.NewAttribute(types.AttributeKeyRecipient, outAddress.String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, out.Coins.String()),
),
)
@ -178,7 +206,13 @@ func (k BaseSendKeeper) InputOutputCoins(ctx context.Context, input types.Input,
// SendCoins transfers amt coins from a sending account to a receiving account.
// An error is returned upon failure.
func (k BaseSendKeeper) SendCoins(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error {
err := k.subUnlockedCoins(ctx, fromAddr, amt)
var err error
err = k.subUnlockedCoins(ctx, fromAddr, amt)
if err != nil {
return err
}
toAddr, err = k.sendRestriction.apply(ctx, fromAddr, toAddr, amt)
if err != nil {
return err
}
@ -433,3 +467,41 @@ func (k BaseSendKeeper) getSendEnabledOrDefault(ctx context.Context, denom strin
return defaultVal
}
// sendRestriction is a struct that houses a SendRestrictionFn.
// It exists so that the SendRestrictionFn can be updated in the SendKeeper without needing to have a pointer receiver.
type sendRestriction struct {
fn types.SendRestrictionFn
}
// newSendRestriction creates a new sendRestriction with nil send restriction.
func newSendRestriction() *sendRestriction {
return &sendRestriction{
fn: nil,
}
}
// append adds the provided restriction to this, to be run after the existing function.
func (r *sendRestriction) append(restriction types.SendRestrictionFn) {
r.fn = r.fn.Then(restriction)
}
// prepend adds the provided restriction to this, to be run before the existing function.
func (r *sendRestriction) prepend(restriction types.SendRestrictionFn) {
r.fn = restriction.Then(r.fn)
}
// clear removes the send restriction (sets it to nil).
func (r *sendRestriction) clear() {
r.fn = nil
}
var _ types.SendRestrictionFn = (*sendRestriction)(nil).apply
// apply applies the send restriction if there is one. If not, it's a no-op.
func (r *sendRestriction) apply(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.AccAddress, error) {
if r == nil || r.fn == nil {
return toAddr, nil
}
return r.fn(ctx, fromAddr, toAddr, amt)
}

View File

@ -0,0 +1,99 @@
package types
import (
"context"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// A MintingRestrictionFn can restrict minting of coins.
type MintingRestrictionFn func(ctx context.Context, coins sdk.Coins) error
var _ MintingRestrictionFn = NoOpMintingRestrictionFn
// NoOpMintingRestrictionFn is a no-op MintingRestrictionFn.
func NoOpMintingRestrictionFn(_ context.Context, _ sdk.Coins) error {
return nil
}
// Then creates a composite restriction that runs this one then the provided second one.
func (r MintingRestrictionFn) Then(second MintingRestrictionFn) MintingRestrictionFn {
return ComposeMintingRestrictions(r, second)
}
// ComposeMintingRestrictions combines multiple MintingRestrictionFn into one.
// nil entries are ignored.
// If all entries are nil, nil is returned.
// If exactly one entry is not nil, it is returned.
// Otherwise, a new MintingRestrictionFn is returned that runs the non-nil restrictions in the order they are given.
// The composition runs each minting restriction until an error is encountered and returns that error.
func ComposeMintingRestrictions(restrictions ...MintingRestrictionFn) MintingRestrictionFn {
toRun := make([]MintingRestrictionFn, 0, len(restrictions))
for _, r := range restrictions {
if r != nil {
toRun = append(toRun, r)
}
}
switch len(toRun) {
case 0:
return nil
case 1:
return toRun[0]
}
return func(ctx context.Context, coins sdk.Coins) error {
for _, r := range toRun {
err := r(ctx, coins)
if err != nil {
return err
}
}
return nil
}
}
// A SendRestrictionFn can restrict sends and/or provide a new receiver address.
type SendRestrictionFn func(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (newToAddr sdk.AccAddress, err error)
var _ SendRestrictionFn = NoOpSendRestrictionFn
// NoOpSendRestrictionFn is a no-op SendRestrictionFn.
func NoOpSendRestrictionFn(_ context.Context, _, toAddr sdk.AccAddress, _ sdk.Coins) (sdk.AccAddress, error) {
return toAddr, nil
}
// Then creates a composite restriction that runs this one then the provided second one.
func (r SendRestrictionFn) Then(second SendRestrictionFn) SendRestrictionFn {
return ComposeSendRestrictions(r, second)
}
// ComposeSendRestrictions combines multiple SendRestrictionFn into one.
// nil entries are ignored.
// If all entries are nil, nil is returned.
// If exactly one entry is not nil, it is returned.
// Otherwise, a new SendRestrictionFn is returned that runs the non-nil restrictions in the order they are given.
// The composition runs each send restriction until an error is encountered and returns that error,
// otherwise it returns the toAddr of the last send restriction.
func ComposeSendRestrictions(restrictions ...SendRestrictionFn) SendRestrictionFn {
toRun := make([]SendRestrictionFn, 0, len(restrictions))
for _, r := range restrictions {
if r != nil {
toRun = append(toRun, r)
}
}
switch len(toRun) {
case 0:
return nil
case 1:
return toRun[0]
}
return func(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.AccAddress, error) {
var err error
for _, r := range toRun {
toAddr, err = r(ctx, fromAddr, toAddr, amt)
if err != nil {
return toAddr, err
}
}
return toAddr, err
}
}

View File

@ -0,0 +1,919 @@
package types_test
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank/types"
)
// MintingRestrictionArgs are the args provided to a MintingRestrictionFn function.
type MintingRestrictionArgs struct {
Name string
Coins sdk.Coins
}
// MintingRestrictionTestHelper is a struct with stuff helpful for testing the MintingRestrictionFn stuff.
type MintingRestrictionTestHelper struct {
Calls []*MintingRestrictionArgs
}
func NewMintingRestrictionTestHelper() *MintingRestrictionTestHelper {
return &MintingRestrictionTestHelper{Calls: make([]*MintingRestrictionArgs, 0, 2)}
}
// RecordCall makes note that the provided args were used as a funcion call.
func (s *MintingRestrictionTestHelper) RecordCall(name string, coins sdk.Coins) {
s.Calls = append(s.Calls, s.NewArgs(name, coins))
}
// NewCalls is just a shorter way to create a []*MintingRestrictionArgs.
func (s *MintingRestrictionTestHelper) NewCalls(args ...*MintingRestrictionArgs) []*MintingRestrictionArgs {
return args
}
// NewArgs creates a new MintingRestrictionArgs.
func (s *MintingRestrictionTestHelper) NewArgs(name string, coins sdk.Coins) *MintingRestrictionArgs {
return &MintingRestrictionArgs{
Name: name,
Coins: coins,
}
}
// NamedRestriction creates a new MintingRestrictionFn function that records the arguments it's called with and returns nil.
func (s *MintingRestrictionTestHelper) NamedRestriction(name string) types.MintingRestrictionFn {
return func(_ context.Context, coins sdk.Coins) error {
s.RecordCall(name, coins)
return nil
}
}
// ErrorRestriction creates a new MintingRestrictionFn function that returns an error.
func (s *MintingRestrictionTestHelper) ErrorRestriction(message string) types.MintingRestrictionFn {
return func(_ context.Context, coins sdk.Coins) error {
s.RecordCall(message, coins)
return errors.New(message)
}
}
// MintingRestrictionTestParams are parameters to test regarding calling a MintingRestrictionFn.
type MintingRestrictionTestParams struct {
// ExpNil is whether to expect the provided MintingRestrictionFn to be nil.
// If it is true, the rest of these test params are ignored.
ExpNil bool
// Coins is the MintingRestrictionFn coins input.
Coins sdk.Coins
// ExpErr is the expected return error string.
ExpErr string
// ExpCalls is the args of all the MintingRestrictionFn calls that end up being made.
ExpCalls []*MintingRestrictionArgs
}
// TestActual tests the provided MintingRestrictionFn using the provided test parameters.
func (s *MintingRestrictionTestHelper) TestActual(t *testing.T, tp *MintingRestrictionTestParams, actual types.MintingRestrictionFn) {
t.Helper()
if tp.ExpNil {
require.Nil(t, actual, "resulting MintingRestrictionFn")
} else {
require.NotNil(t, actual, "resulting MintingRestrictionFn")
s.Calls = s.Calls[:0]
err := actual(sdk.Context{}, tp.Coins)
if len(tp.ExpErr) != 0 {
assert.EqualError(t, err, tp.ExpErr, "composite MintingRestrictionFn output error")
} else {
assert.NoError(t, err, "composite MintingRestrictionFn output error")
}
assert.Equal(t, tp.ExpCalls, s.Calls, "args given to funcs in composite MintingRestrictionFn")
}
}
func TestMintingRestriction_Then(t *testing.T) {
coins := sdk.NewCoins(sdk.NewInt64Coin("acoin", 2), sdk.NewInt64Coin("bcoin", 4))
h := NewMintingRestrictionTestHelper()
tests := []struct {
name string
base types.MintingRestrictionFn
second types.MintingRestrictionFn
exp *MintingRestrictionTestParams
}{
{
name: "nil nil",
base: nil,
second: nil,
exp: &MintingRestrictionTestParams{
ExpNil: true,
},
},
{
name: "nil noop",
base: nil,
second: h.NamedRestriction("noop"),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(h.NewArgs("noop", coins)),
},
},
{
name: "noop nil",
base: h.NamedRestriction("noop"),
second: nil,
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(h.NewArgs("noop", coins)),
},
},
{
name: "noop noop",
base: h.NamedRestriction("noop1"),
second: h.NamedRestriction("noop2"),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(h.NewArgs("noop1", coins), h.NewArgs("noop2", coins)),
},
},
{
name: "noop error",
base: h.NamedRestriction("noop"),
second: h.ErrorRestriction("this is a test error"),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpErr: "this is a test error",
ExpCalls: h.NewCalls(h.NewArgs("noop", coins), h.NewArgs("this is a test error", coins)),
},
},
{
name: "error noop",
base: h.ErrorRestriction("another test error"),
second: h.NamedRestriction("noop"),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpErr: "another test error",
ExpCalls: h.NewCalls(h.NewArgs("another test error", coins)),
},
},
{
name: "error error",
base: h.ErrorRestriction("first test error"),
second: h.ErrorRestriction("second test error"),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpErr: "first test error",
ExpCalls: h.NewCalls(h.NewArgs("first test error", coins)),
},
},
{
name: "double chain",
base: types.ComposeMintingRestrictions(h.NamedRestriction("r1"), h.NamedRestriction("r2")),
second: types.ComposeMintingRestrictions(h.NamedRestriction("r3"), h.NamedRestriction("r4")),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(
h.NewArgs("r1", coins),
h.NewArgs("r2", coins),
h.NewArgs("r3", coins),
h.NewArgs("r4", coins),
),
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var actual types.MintingRestrictionFn
testFunc := func() {
actual = tc.base.Then(tc.second)
}
require.NotPanics(t, testFunc, "MintingRestrictionFn.Then")
h.TestActual(t, tc.exp, actual)
})
}
}
func TestComposeMintingRestrictions(t *testing.T) {
rz := func(rs ...types.MintingRestrictionFn) []types.MintingRestrictionFn {
return rs
}
coins := sdk.NewCoins(sdk.NewInt64Coin("ccoin", 8), sdk.NewInt64Coin("dcoin", 16))
h := NewMintingRestrictionTestHelper()
tests := []struct {
name string
input []types.MintingRestrictionFn
exp *MintingRestrictionTestParams
}{
{
name: "nil list",
input: nil,
exp: &MintingRestrictionTestParams{
ExpNil: true,
},
},
{
name: "empty list",
input: rz(),
exp: &MintingRestrictionTestParams{
ExpNil: true,
},
},
{
name: "only nil entry",
input: rz(nil),
exp: &MintingRestrictionTestParams{
ExpNil: true,
},
},
{
name: "five nil entries",
input: rz(nil, nil, nil, nil, nil),
exp: &MintingRestrictionTestParams{
ExpNil: true,
},
},
{
name: "only noop entry",
input: rz(h.NamedRestriction("noop")),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(h.NewArgs("noop", coins)),
},
},
{
name: "only error entry",
input: rz(h.ErrorRestriction("test error")),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpErr: "test error",
ExpCalls: h.NewCalls(h.NewArgs("test error", coins)),
},
},
{
name: "noop nil nil",
input: rz(h.NamedRestriction("noop"), nil, nil),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(h.NewArgs("noop", coins)),
},
},
{
name: "nil noop nil",
input: rz(nil, h.NamedRestriction("noop"), nil),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(h.NewArgs("noop", coins)),
},
},
{
name: "nil nil noop",
input: rz(nil, nil, h.NamedRestriction("noop")),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(h.NewArgs("noop", coins)),
},
},
{
name: "noop noop nil",
input: rz(h.NamedRestriction("r1"), h.NamedRestriction("r2"), nil),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(h.NewArgs("r1", coins), h.NewArgs("r2", coins)),
},
},
{
name: "noop nil noop",
input: rz(h.NamedRestriction("r1"), nil, h.NamedRestriction("r2")),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(h.NewArgs("r1", coins), h.NewArgs("r2", coins)),
},
},
{
name: "nil noop noop",
input: rz(nil, h.NamedRestriction("r1"), h.NamedRestriction("r2")),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(h.NewArgs("r1", coins), h.NewArgs("r2", coins)),
},
},
{
name: "noop noop noop",
input: rz(h.NamedRestriction("r1"), h.NamedRestriction("r2"), h.NamedRestriction("r3")),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpCalls: h.NewCalls(h.NewArgs("r1", coins), h.NewArgs("r2", coins), h.NewArgs("r3", coins)),
},
},
{
name: "err noop noop",
input: rz(h.ErrorRestriction("first error"), h.NamedRestriction("r2"), h.NamedRestriction("r3")),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpErr: "first error",
ExpCalls: h.NewCalls(h.NewArgs("first error", coins)),
},
},
{
name: "noop err noop",
input: rz(h.NamedRestriction("r1"), h.ErrorRestriction("second error"), h.NamedRestriction("r3")),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpErr: "second error",
ExpCalls: h.NewCalls(h.NewArgs("r1", coins), h.NewArgs("second error", coins)),
},
},
{
name: "noop noop err",
input: rz(h.NamedRestriction("r1"), h.NamedRestriction("r2"), h.ErrorRestriction("third error")),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpErr: "third error",
ExpCalls: h.NewCalls(h.NewArgs("r1", coins), h.NewArgs("r2", coins), h.NewArgs("third error", coins)),
},
},
{
name: "noop err err",
input: rz(h.NamedRestriction("r1"), h.ErrorRestriction("second error"), h.ErrorRestriction("third error")),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpErr: "second error",
ExpCalls: h.NewCalls(h.NewArgs("r1", coins), h.NewArgs("second error", coins)),
},
},
{
name: "big bang",
input: rz(
h.NamedRestriction("r1"), nil, h.NamedRestriction("r2"), nil,
h.NamedRestriction("r3"), h.NamedRestriction("r4"), h.NamedRestriction("r5"),
nil, h.NamedRestriction("r6"), h.NamedRestriction("r7"), nil,
h.NamedRestriction("r8"), nil, nil, h.ErrorRestriction("oops, an error"),
h.NamedRestriction("r9"), nil, h.NamedRestriction("ra"), // Not called.
),
exp: &MintingRestrictionTestParams{
Coins: coins,
ExpErr: "oops, an error",
ExpCalls: h.NewCalls(
h.NewArgs("r1", coins),
h.NewArgs("r2", coins),
h.NewArgs("r3", coins),
h.NewArgs("r4", coins),
h.NewArgs("r5", coins),
h.NewArgs("r6", coins),
h.NewArgs("r7", coins),
h.NewArgs("r8", coins),
h.NewArgs("oops, an error", coins),
),
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var actual types.MintingRestrictionFn
testFunc := func() {
actual = types.ComposeMintingRestrictions(tc.input...)
}
require.NotPanics(t, testFunc, "ComposeMintingRestrictions")
h.TestActual(t, tc.exp, actual)
})
}
}
func TestNoOpMintingRestrictionFn(t *testing.T) {
var err error
testFunc := func() {
err = types.NoOpMintingRestrictionFn(sdk.Context{}, sdk.Coins{})
}
require.NotPanics(t, testFunc, "NoOpMintingRestrictionFn")
assert.NoError(t, err, "NoOpSendRestrictionFn error")
}
// SendRestrictionArgs are the args provided to a SendRestrictionFn function.
type SendRestrictionArgs struct {
Name string
FromAddr sdk.AccAddress
ToAddr sdk.AccAddress
Coins sdk.Coins
}
// SendRestrictionTestHelper is a struct with stuff helpful for testing the SendRestrictionFn stuff.
type SendRestrictionTestHelper struct {
Calls []*SendRestrictionArgs
}
func NewSendRestrictionTestHelper() *SendRestrictionTestHelper {
return &SendRestrictionTestHelper{Calls: make([]*SendRestrictionArgs, 0, 2)}
}
// RecordCall makes note that the provided args were used as a funcion call.
func (s *SendRestrictionTestHelper) RecordCall(name string, fromAddr, toAddr sdk.AccAddress, coins sdk.Coins) {
s.Calls = append(s.Calls, s.NewArgs(name, fromAddr, toAddr, coins))
}
// NewCalls is just a shorter way to create a []*SendRestrictionArgs.
func (s *SendRestrictionTestHelper) NewCalls(args ...*SendRestrictionArgs) []*SendRestrictionArgs {
return args
}
// NewArgs creates a new SendRestrictionArgs.
func (s *SendRestrictionTestHelper) NewArgs(name string, fromAddr, toAddr sdk.AccAddress, coins sdk.Coins) *SendRestrictionArgs {
return &SendRestrictionArgs{
Name: name,
FromAddr: fromAddr,
ToAddr: toAddr,
Coins: coins,
}
}
// NamedRestriction creates a new SendRestrictionFn function that records the arguments it's called with and returns the provided toAddr.
func (s *SendRestrictionTestHelper) NamedRestriction(name string) types.SendRestrictionFn {
return func(_ context.Context, fromAddr, toAddr sdk.AccAddress, coins sdk.Coins) (sdk.AccAddress, error) {
s.RecordCall(name, fromAddr, toAddr, coins)
return toAddr, nil
}
}
// NewToRestriction creates a new SendRestrictionFn function that returns a different toAddr than provided.
func (s *SendRestrictionTestHelper) NewToRestriction(name string, addr sdk.AccAddress) types.SendRestrictionFn {
return func(_ context.Context, fromAddr, toAddr sdk.AccAddress, coins sdk.Coins) (sdk.AccAddress, error) {
s.RecordCall(name, fromAddr, toAddr, coins)
return addr, nil
}
}
// ErrorRestriction creates a new SendRestrictionFn function that returns a nil toAddr and an error.
func (s *SendRestrictionTestHelper) ErrorRestriction(message string) types.SendRestrictionFn {
return func(_ context.Context, fromAddr, toAddr sdk.AccAddress, coins sdk.Coins) (sdk.AccAddress, error) {
s.RecordCall(message, fromAddr, toAddr, coins)
return nil, errors.New(message)
}
}
// SendRestrictionTestParams are parameters to test regarding calling a SendRestrictionFn.
type SendRestrictionTestParams struct {
// ExpNil is whether to expect the provided SendRestrictionFn to be nil.
// If it is true, the rest of these test params are ignored.
ExpNil bool
// FromAddr is the SendRestrictionFn fromAddr input.
FromAddr sdk.AccAddress
// ToAddr is the SendRestrictionFn toAddr input.
ToAddr sdk.AccAddress
// Coins is the SendRestrictionFn coins input.
Coins sdk.Coins
// ExpAddr is the expected return address.
ExpAddr sdk.AccAddress
// ExpErr is the expected return error string.
ExpErr string
// ExpCalls is the args of all the SendRestrictionFn calls that end up being made.
ExpCalls []*SendRestrictionArgs
}
// TestActual tests the provided SendRestrictionFn using the provided test parameters.
func (s *SendRestrictionTestHelper) TestActual(t *testing.T, tp *SendRestrictionTestParams, actual types.SendRestrictionFn) {
t.Helper()
if tp.ExpNil {
require.Nil(t, actual, "resulting SendRestrictionFn")
} else {
require.NotNil(t, actual, "resulting SendRestrictionFn")
s.Calls = s.Calls[:0]
addr, err := actual(sdk.Context{}, tp.FromAddr, tp.ToAddr, tp.Coins)
if len(tp.ExpErr) != 0 {
assert.EqualError(t, err, tp.ExpErr, "composite SendRestrictionFn output error")
} else {
assert.NoError(t, err, "composite SendRestrictionFn output error")
}
assert.Equal(t, tp.ExpAddr, addr, "composite SendRestrictionFn output address")
assert.Equal(t, tp.ExpCalls, s.Calls, "args given to funcs in composite SendRestrictionFn")
}
}
func TestSendRestriction_Then(t *testing.T) {
fromAddr := sdk.AccAddress("fromaddr____________")
addr0 := sdk.AccAddress("0addr_______________")
addr1 := sdk.AccAddress("1addr_______________")
addr2 := sdk.AccAddress("2addr_______________")
addr3 := sdk.AccAddress("3addr_______________")
addr4 := sdk.AccAddress("4addr_______________")
coins := sdk.NewCoins(sdk.NewInt64Coin("ecoin", 32), sdk.NewInt64Coin("fcoin", 64))
h := NewSendRestrictionTestHelper()
tests := []struct {
name string
base types.SendRestrictionFn
second types.SendRestrictionFn
exp *SendRestrictionTestParams
}{
{
name: "nil nil",
base: nil,
second: nil,
exp: &SendRestrictionTestParams{
ExpNil: true,
},
},
{
name: "nil noop",
base: nil,
second: h.NamedRestriction("noop"),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr1,
Coins: coins,
ExpAddr: addr1,
ExpCalls: h.NewCalls(h.NewArgs("noop", fromAddr, addr1, coins)),
},
},
{
name: "noop nil",
base: h.NamedRestriction("noop"),
second: nil,
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr1,
Coins: coins,
ExpAddr: addr1,
ExpCalls: h.NewCalls(h.NewArgs("noop", fromAddr, addr1, coins)),
},
},
{
name: "noop noop",
base: h.NamedRestriction("noop1"),
second: h.NamedRestriction("noop2"),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr1,
Coins: coins,
ExpAddr: addr1,
ExpCalls: h.NewCalls(
h.NewArgs("noop1", fromAddr, addr1, coins),
h.NewArgs("noop2", fromAddr, addr1, coins),
),
},
},
{
name: "setter setter",
base: h.NewToRestriction("r1", addr2),
second: h.NewToRestriction("r2", addr3),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr1,
Coins: coins,
ExpAddr: addr3,
ExpCalls: h.NewCalls(
h.NewArgs("r1", fromAddr, addr1, coins),
h.NewArgs("r2", fromAddr, addr2, coins),
),
},
},
{
name: "setter error",
base: h.NewToRestriction("r1", addr2),
second: h.ErrorRestriction("this is a test error"),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr1,
Coins: coins,
ExpAddr: nil,
ExpErr: "this is a test error",
ExpCalls: h.NewCalls(h.NewArgs(
"r1", fromAddr, addr1, coins),
h.NewArgs("this is a test error", fromAddr, addr2, coins),
),
},
},
{
name: "error setter",
base: h.ErrorRestriction("another test error"),
second: h.NewToRestriction("r2", addr3),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr1,
Coins: coins,
ExpAddr: nil,
ExpErr: "another test error",
ExpCalls: h.NewCalls(h.NewArgs("another test error", fromAddr, addr1, coins)),
},
},
{
name: "error error",
base: h.ErrorRestriction("first test error"),
second: h.ErrorRestriction("second test error"),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr1,
Coins: coins,
ExpAddr: nil,
ExpErr: "first test error",
ExpCalls: h.NewCalls(h.NewArgs("first test error", fromAddr, addr1, coins)),
},
},
{
name: "double chain",
base: types.ComposeSendRestrictions(h.NewToRestriction("r1", addr1), h.NewToRestriction("r2", addr2)),
second: types.ComposeSendRestrictions(h.NewToRestriction("r3", addr3), h.NewToRestriction("r4", addr4)),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr0,
Coins: coins,
ExpAddr: addr4,
ExpCalls: h.NewCalls(
h.NewArgs("r1", fromAddr, addr0, coins),
h.NewArgs("r2", fromAddr, addr1, coins),
h.NewArgs("r3", fromAddr, addr2, coins),
h.NewArgs("r4", fromAddr, addr3, coins),
),
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var actual types.SendRestrictionFn
testFunc := func() {
actual = tc.base.Then(tc.second)
}
require.NotPanics(t, testFunc, "SendRestrictionFn.Then")
h.TestActual(t, tc.exp, actual)
})
}
}
func TestComposeSendRestrictions(t *testing.T) {
rz := func(rs ...types.SendRestrictionFn) []types.SendRestrictionFn {
return rs
}
fromAddr := sdk.AccAddress("fromaddr____________")
addr0 := sdk.AccAddress("0addr_______________")
addr1 := sdk.AccAddress("1addr_______________")
addr2 := sdk.AccAddress("2addr_______________")
addr3 := sdk.AccAddress("3addr_______________")
addr4 := sdk.AccAddress("4addr_______________")
coins := sdk.NewCoins(sdk.NewInt64Coin("gcoin", 128), sdk.NewInt64Coin("hcoin", 256))
h := NewSendRestrictionTestHelper()
tests := []struct {
name string
input []types.SendRestrictionFn
exp *SendRestrictionTestParams
}{
{
name: "nil list",
input: nil,
exp: &SendRestrictionTestParams{
ExpNil: true,
},
},
{
name: "empty list",
input: rz(),
exp: &SendRestrictionTestParams{
ExpNil: true,
},
},
{
name: "only nil entry",
input: rz(nil),
exp: &SendRestrictionTestParams{
ExpNil: true,
},
},
{
name: "five nil entries",
input: rz(nil, nil, nil, nil, nil),
exp: &SendRestrictionTestParams{
ExpNil: true,
},
},
{
name: "only noop entry",
input: rz(h.NamedRestriction("noop")),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr0,
Coins: coins,
ExpAddr: addr0,
ExpCalls: h.NewCalls(h.NewArgs("noop", fromAddr, addr0, coins)),
},
},
{
name: "only error entry",
input: rz(h.ErrorRestriction("test error")),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr0,
Coins: coins,
ExpAddr: nil,
ExpErr: "test error",
ExpCalls: h.NewCalls(h.NewArgs("test error", fromAddr, addr0, coins)),
},
},
{
name: "noop nil nil",
input: rz(h.NamedRestriction("noop"), nil, nil),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr0,
Coins: coins,
ExpAddr: addr0,
ExpCalls: h.NewCalls(h.NewArgs("noop", fromAddr, addr0, coins)),
},
},
{
name: "nil noop nil",
input: rz(nil, h.NamedRestriction("noop"), nil),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr1,
Coins: coins,
ExpAddr: addr1,
ExpCalls: h.NewCalls(h.NewArgs("noop", fromAddr, addr1, coins)),
},
},
{
name: "nil nil noop",
input: rz(nil, nil, h.NamedRestriction("noop")),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr2,
Coins: coins,
ExpAddr: addr2,
ExpCalls: h.NewCalls(h.NewArgs("noop", fromAddr, addr2, coins)),
},
},
{
name: "noop noop nil",
input: rz(h.NamedRestriction("r1"), h.NamedRestriction("r2"), nil),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr0,
Coins: coins,
ExpAddr: addr0,
ExpCalls: h.NewCalls(
h.NewArgs("r1", fromAddr, addr0, coins),
h.NewArgs("r2", fromAddr, addr0, coins),
),
},
},
{
name: "noop nil noop",
input: rz(h.NamedRestriction("r1"), nil, h.NamedRestriction("r2")),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr1,
Coins: coins,
ExpAddr: addr1,
ExpCalls: h.NewCalls(
h.NewArgs("r1", fromAddr, addr1, coins),
h.NewArgs("r2", fromAddr, addr1, coins),
),
},
},
{
name: "nil noop noop",
input: rz(nil, h.NamedRestriction("r1"), h.NamedRestriction("r2")),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr2,
Coins: coins,
ExpAddr: addr2,
ExpCalls: h.NewCalls(
h.NewArgs("r1", fromAddr, addr2, coins),
h.NewArgs("r2", fromAddr, addr2, coins),
),
},
},
{
name: "noop noop noop",
input: rz(h.NamedRestriction("r1"), h.NamedRestriction("r2"), h.NamedRestriction("r3")),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr3,
Coins: coins,
ExpAddr: addr3,
ExpCalls: h.NewCalls(
h.NewArgs("r1", fromAddr, addr3, coins),
h.NewArgs("r2", fromAddr, addr3, coins),
h.NewArgs("r3", fromAddr, addr3, coins),
),
},
},
{
name: "err noop noop",
input: rz(h.ErrorRestriction("first error"), h.NamedRestriction("r2"), h.NamedRestriction("r3")),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr4,
Coins: coins,
ExpAddr: nil,
ExpErr: "first error",
ExpCalls: h.NewCalls(h.NewArgs("first error", fromAddr, addr4, coins)),
},
},
{
name: "noop err noop",
input: rz(h.NamedRestriction("r1"), h.ErrorRestriction("second error"), h.NamedRestriction("r3")),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr4,
Coins: coins,
ExpAddr: nil,
ExpErr: "second error",
ExpCalls: h.NewCalls(
h.NewArgs("r1", fromAddr, addr4, coins),
h.NewArgs("second error", fromAddr, addr4, coins),
),
},
},
{
name: "noop noop err",
input: rz(h.NamedRestriction("r1"), h.NamedRestriction("r2"), h.ErrorRestriction("third error")),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr4,
Coins: coins,
ExpAddr: nil,
ExpErr: "third error",
ExpCalls: h.NewCalls(
h.NewArgs("r1", fromAddr, addr4, coins),
h.NewArgs("r2", fromAddr, addr4, coins),
h.NewArgs("third error", fromAddr, addr4, coins),
),
},
},
{
name: "new-to err err",
input: rz(h.NewToRestriction("r1", addr0), h.ErrorRestriction("second error"), h.ErrorRestriction("third error")),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr4,
Coins: coins,
ExpAddr: nil,
ExpErr: "second error",
ExpCalls: h.NewCalls(
h.NewArgs("r1", fromAddr, addr4, coins),
h.NewArgs("second error", fromAddr, addr0, coins),
),
},
},
{
name: "big bang",
input: rz(
h.NamedRestriction("r1"), nil, h.NewToRestriction("r2", addr1), // Called with orig toAddr.
nil, h.NamedRestriction("r3"), h.NewToRestriction("r4", addr2), // Called with addr1 toAddr.
h.NewToRestriction("r5", addr3), // Called with addr2 toAddr.
nil, h.NamedRestriction("r6"), h.NewToRestriction("r7", addr4), // Called with addr3 toAddr.
nil, h.NamedRestriction("r8"), nil, nil, h.ErrorRestriction("oops, an error"), // Called with addr4 toAddr.
h.NewToRestriction("r9", addr0), nil, h.NamedRestriction("ra"), // Not called.
),
exp: &SendRestrictionTestParams{
FromAddr: fromAddr,
ToAddr: addr0,
Coins: coins,
ExpAddr: nil,
ExpErr: "oops, an error",
ExpCalls: h.NewCalls(
h.NewArgs("r1", fromAddr, addr0, coins),
h.NewArgs("r2", fromAddr, addr0, coins),
h.NewArgs("r3", fromAddr, addr1, coins),
h.NewArgs("r4", fromAddr, addr1, coins),
h.NewArgs("r5", fromAddr, addr2, coins),
h.NewArgs("r6", fromAddr, addr3, coins),
h.NewArgs("r7", fromAddr, addr3, coins),
h.NewArgs("r8", fromAddr, addr4, coins),
h.NewArgs("oops, an error", fromAddr, addr4, coins),
),
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var actual types.SendRestrictionFn
testFunc := func() {
actual = types.ComposeSendRestrictions(tc.input...)
}
require.NotPanics(t, testFunc, "ComposeSendRestrictions")
h.TestActual(t, tc.exp, actual)
})
}
}
func TestNoOpSendRestrictionFn(t *testing.T) {
expAddr := sdk.AccAddress("__expectedaddr__")
var addr sdk.AccAddress
var err error
testFunc := func() {
addr, err = types.NoOpSendRestrictionFn(sdk.Context{}, sdk.AccAddress("first_addr"), expAddr, sdk.Coins{})
}
require.NotPanics(t, testFunc, "NoOpSendRestrictionFn")
assert.NoError(t, err, "NoOpSendRestrictionFn error")
assert.Equal(t, expAddr, addr, "NoOpSendRestrictionFn addr")
}

View File

@ -159,6 +159,18 @@ func (mr *MockBankKeeperMockRecorder) AllBalances(arg0, arg1 interface{}) *gomoc
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllBalances", reflect.TypeOf((*MockBankKeeper)(nil).AllBalances), arg0, arg1)
}
// AppendSendRestriction mocks base method.
func (m *MockBankKeeper) AppendSendRestriction(restriction types0.SendRestrictionFn) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "AppendSendRestriction", restriction)
}
// AppendSendRestriction indicates an expected call of AppendSendRestriction.
func (mr *MockBankKeeperMockRecorder) AppendSendRestriction(restriction interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendSendRestriction", reflect.TypeOf((*MockBankKeeper)(nil).AppendSendRestriction), restriction)
}
// Balance mocks base method.
func (m *MockBankKeeper) Balance(arg0 context.Context, arg1 *types0.QueryBalanceRequest) (*types0.QueryBalanceResponse, error) {
m.ctrl.T.Helper()
@ -202,6 +214,18 @@ func (mr *MockBankKeeperMockRecorder) BurnCoins(ctx, moduleName, amt interface{}
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BurnCoins", reflect.TypeOf((*MockBankKeeper)(nil).BurnCoins), ctx, moduleName, amt)
}
// ClearSendRestriction mocks base method.
func (m *MockBankKeeper) ClearSendRestriction() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "ClearSendRestriction")
}
// ClearSendRestriction indicates an expected call of ClearSendRestriction.
func (mr *MockBankKeeperMockRecorder) ClearSendRestriction() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearSendRestriction", reflect.TypeOf((*MockBankKeeper)(nil).ClearSendRestriction))
}
// DelegateCoins mocks base method.
func (m *MockBankKeeper) DelegateCoins(ctx context.Context, delegatorAddr, moduleAccAddr types.AccAddress, amt types.Coins) error {
m.ctrl.T.Helper()
@ -548,17 +572,17 @@ func (mr *MockBankKeeperMockRecorder) InitGenesis(arg0, arg1 interface{}) *gomoc
}
// InputOutputCoins mocks base method.
func (m *MockBankKeeper) InputOutputCoins(ctx context.Context, inputs types0.Input, outputs []types0.Output) error {
func (m *MockBankKeeper) InputOutputCoins(ctx context.Context, input types0.Input, outputs []types0.Output) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InputOutputCoins", ctx, inputs, outputs)
ret := m.ctrl.Call(m, "InputOutputCoins", ctx, input, outputs)
ret0, _ := ret[0].(error)
return ret0
}
// InputOutputCoins indicates an expected call of InputOutputCoins.
func (mr *MockBankKeeperMockRecorder) InputOutputCoins(ctx, inputs, outputs interface{}) *gomock.Call {
func (mr *MockBankKeeperMockRecorder) InputOutputCoins(ctx, input, outputs interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InputOutputCoins", reflect.TypeOf((*MockBankKeeper)(nil).InputOutputCoins), ctx, inputs, outputs)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InputOutputCoins", reflect.TypeOf((*MockBankKeeper)(nil).InputOutputCoins), ctx, input, outputs)
}
// IsSendEnabledCoin mocks base method.
@ -711,6 +735,18 @@ func (mr *MockBankKeeperMockRecorder) Params(arg0, arg1 interface{}) *gomock.Cal
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Params", reflect.TypeOf((*MockBankKeeper)(nil).Params), arg0, arg1)
}
// PrependSendRestriction mocks base method.
func (m *MockBankKeeper) PrependSendRestriction(restriction types0.SendRestrictionFn) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "PrependSendRestriction", restriction)
}
// PrependSendRestriction indicates an expected call of PrependSendRestriction.
func (mr *MockBankKeeperMockRecorder) PrependSendRestriction(restriction interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrependSendRestriction", reflect.TypeOf((*MockBankKeeper)(nil).PrependSendRestriction), restriction)
}
// SendCoins mocks base method.
func (m *MockBankKeeper) SendCoins(ctx context.Context, fromAddr, toAddr types.AccAddress, amt types.Coins) error {
m.ctrl.T.Helper()
@ -963,7 +999,7 @@ func (mr *MockBankKeeperMockRecorder) ValidateBalance(ctx, addr interface{}) *go
}
// WithMintCoinsRestriction mocks base method.
func (m *MockBankKeeper) WithMintCoinsRestriction(arg0 keeper.MintingRestrictionFn) keeper.BaseKeeper {
func (m *MockBankKeeper) WithMintCoinsRestriction(arg0 types0.MintingRestrictionFn) keeper.BaseKeeper {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "WithMintCoinsRestriction", arg0)
ret0, _ := ret[0].(keeper.BaseKeeper)