This commit is contained in:
parent
ea095bdc7c
commit
8cff58c1f6
@ -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
|
||||
|
||||
@ -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)`.
|
||||
|
||||
14
x/bank/keeper/export_test.go
Normal file
14
x/bank/keeper/export_test.go
Normal 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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
99
x/bank/types/restrictions.go
Normal file
99
x/bank/types/restrictions.go
Normal 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
|
||||
}
|
||||
}
|
||||
919
x/bank/types/restrictions_test.go
Normal file
919
x/bank/types/restrictions_test.go
Normal 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")
|
||||
}
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user