2018-01-12 11:49:53 -08:00
|
|
|
package bank
|
|
|
|
|
|
|
|
import (
|
2018-03-24 17:12:44 -07:00
|
|
|
"fmt"
|
|
|
|
|
2018-02-08 23:43:08 -08:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2018-05-23 19:26:54 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
2018-01-12 11:49:53 -08:00
|
|
|
)
|
|
|
|
|
2018-05-15 17:06:17 -07:00
|
|
|
const (
|
|
|
|
costGetCoins sdk.Gas = 10
|
|
|
|
costHasCoins sdk.Gas = 10
|
|
|
|
costSetCoins sdk.Gas = 100
|
|
|
|
costSubtractCoins sdk.Gas = 10
|
|
|
|
costAddCoins sdk.Gas = 10
|
|
|
|
)
|
|
|
|
|
2018-09-10 11:25:34 -07:00
|
|
|
// Keeper defines a module interface that facilitates the transfer of coins
|
|
|
|
// between accounts.
|
|
|
|
type Keeper interface {
|
|
|
|
SendKeeper
|
|
|
|
SetCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) sdk.Error
|
|
|
|
SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error)
|
|
|
|
AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error)
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ Keeper = (*BaseKeeper)(nil)
|
|
|
|
|
|
|
|
// BaseKeeper manages transfers between accounts. It implements the Keeper
|
|
|
|
// interface.
|
|
|
|
type BaseKeeper struct {
|
2018-05-23 19:26:54 -07:00
|
|
|
am auth.AccountMapper
|
2018-04-09 15:10:58 -07:00
|
|
|
}
|
|
|
|
|
2018-09-10 11:25:34 -07:00
|
|
|
// NewBaseKeeper returns a new BaseKeeper
|
|
|
|
func NewBaseKeeper(am auth.AccountMapper) BaseKeeper {
|
|
|
|
return BaseKeeper{am: am}
|
2018-04-09 15:10:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetCoins returns the coins at the addr.
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseKeeper) GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins {
|
2018-04-09 15:10:58 -07:00
|
|
|
return getCoins(ctx, keeper.am, addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetCoins sets the coins at the addr.
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseKeeper) SetCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) sdk.Error {
|
2018-04-09 15:10:58 -07:00
|
|
|
return setCoins(ctx, keeper.am, addr, amt)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasCoins returns whether or not an account has at least amt coins.
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseKeeper) HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool {
|
2018-04-09 15:10:58 -07:00
|
|
|
return hasCoins(ctx, keeper.am, addr, amt)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SubtractCoins subtracts amt from the coins at the addr.
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseKeeper) SubtractCoins(
|
|
|
|
ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins,
|
|
|
|
) (sdk.Coins, sdk.Tags, sdk.Error) {
|
|
|
|
|
2018-04-09 15:10:58 -07:00
|
|
|
return subtractCoins(ctx, keeper.am, addr, amt)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddCoins adds amt to the coins at the addr.
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseKeeper) AddCoins(
|
|
|
|
ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins,
|
|
|
|
) (sdk.Coins, sdk.Tags, sdk.Error) {
|
|
|
|
|
2018-04-09 15:10:58 -07:00
|
|
|
return addCoins(ctx, keeper.am, addr, amt)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SendCoins moves coins from one account to another
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseKeeper) SendCoins(
|
|
|
|
ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins,
|
|
|
|
) (sdk.Tags, sdk.Error) {
|
|
|
|
|
2018-04-09 15:10:58 -07:00
|
|
|
return sendCoins(ctx, keeper.am, fromAddr, toAddr, amt)
|
|
|
|
}
|
|
|
|
|
|
|
|
// InputOutputCoins handles a list of inputs and outputs
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) {
|
2018-04-09 15:10:58 -07:00
|
|
|
return inputOutputCoins(ctx, keeper.am, inputs, outputs)
|
|
|
|
}
|
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
//______________________________________________________________________________________________
|
2018-04-09 15:10:58 -07:00
|
|
|
|
2018-09-10 11:25:34 -07:00
|
|
|
// SendKeeper defines a module interface that facilitates the transfer of coins
|
|
|
|
// between accounts without the possibility of creating coins.
|
|
|
|
type SendKeeper interface {
|
|
|
|
ViewKeeper
|
|
|
|
SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.Tags, sdk.Error)
|
|
|
|
InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error)
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ SendKeeper = (*BaseSendKeeper)(nil)
|
|
|
|
|
|
|
|
// SendKeeper only allows transfers between accounts without the possibility of
|
|
|
|
// creating coins. It implements the SendKeeper interface.
|
|
|
|
type BaseSendKeeper struct {
|
2018-05-23 19:26:54 -07:00
|
|
|
am auth.AccountMapper
|
2018-04-09 15:10:58 -07:00
|
|
|
}
|
|
|
|
|
2018-09-10 11:25:34 -07:00
|
|
|
// NewBaseSendKeeper returns a new BaseSendKeeper.
|
|
|
|
func NewBaseSendKeeper(am auth.AccountMapper) BaseSendKeeper {
|
|
|
|
return BaseSendKeeper{am: am}
|
2018-04-09 15:10:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetCoins returns the coins at the addr.
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseSendKeeper) GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins {
|
2018-04-09 15:10:58 -07:00
|
|
|
return getCoins(ctx, keeper.am, addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasCoins returns whether or not an account has at least amt coins.
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseSendKeeper) HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool {
|
2018-04-09 15:10:58 -07:00
|
|
|
return hasCoins(ctx, keeper.am, addr, amt)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SendCoins moves coins from one account to another
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseSendKeeper) SendCoins(
|
|
|
|
ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins,
|
|
|
|
) (sdk.Tags, sdk.Error) {
|
|
|
|
|
2018-04-09 15:10:58 -07:00
|
|
|
return sendCoins(ctx, keeper.am, fromAddr, toAddr, amt)
|
|
|
|
}
|
|
|
|
|
|
|
|
// InputOutputCoins handles a list of inputs and outputs
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseSendKeeper) InputOutputCoins(
|
|
|
|
ctx sdk.Context, inputs []Input, outputs []Output,
|
|
|
|
) (sdk.Tags, sdk.Error) {
|
|
|
|
|
2018-04-09 15:10:58 -07:00
|
|
|
return inputOutputCoins(ctx, keeper.am, inputs, outputs)
|
|
|
|
}
|
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
//______________________________________________________________________________________________
|
2018-04-09 15:10:58 -07:00
|
|
|
|
2018-09-10 11:25:34 -07:00
|
|
|
// ViewKeeper defines a module interface that facilitates read only access to
|
|
|
|
// account balances.
|
|
|
|
type ViewKeeper interface {
|
|
|
|
GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
|
|
|
|
HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ViewKeeper = (*BaseViewKeeper)(nil)
|
|
|
|
|
|
|
|
// BaseViewKeeper implements a read only keeper implementation of ViewKeeper.
|
|
|
|
type BaseViewKeeper struct {
|
2018-05-23 19:26:54 -07:00
|
|
|
am auth.AccountMapper
|
2018-04-09 15:10:58 -07:00
|
|
|
}
|
|
|
|
|
2018-09-10 11:25:34 -07:00
|
|
|
// NewBaseViewKeeper returns a new BaseViewKeeper.
|
|
|
|
func NewBaseViewKeeper(am auth.AccountMapper) BaseViewKeeper {
|
|
|
|
return BaseViewKeeper{am: am}
|
2018-04-09 15:10:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetCoins returns the coins at the addr.
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseViewKeeper) GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins {
|
2018-04-09 15:10:58 -07:00
|
|
|
return getCoins(ctx, keeper.am, addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasCoins returns whether or not an account has at least amt coins.
|
2018-09-10 11:25:34 -07:00
|
|
|
func (keeper BaseViewKeeper) HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool {
|
2018-04-09 15:10:58 -07:00
|
|
|
return hasCoins(ctx, keeper.am, addr, amt)
|
|
|
|
}
|
2018-04-18 21:49:24 -07:00
|
|
|
|
|
|
|
//______________________________________________________________________________________________
|
|
|
|
|
2018-07-06 00:06:53 -07:00
|
|
|
func getCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.AccAddress) sdk.Coins {
|
2018-05-15 17:06:17 -07:00
|
|
|
ctx.GasMeter().ConsumeGas(costGetCoins, "getCoins")
|
2018-04-18 21:49:24 -07:00
|
|
|
acc := am.GetAccount(ctx, addr)
|
|
|
|
if acc == nil {
|
2018-05-08 08:34:09 -07:00
|
|
|
return sdk.Coins{}
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
2018-05-08 08:34:09 -07:00
|
|
|
return acc.GetCoins()
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
|
|
|
|
2018-07-06 00:06:53 -07:00
|
|
|
func setCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.AccAddress, amt sdk.Coins) sdk.Error {
|
2018-05-15 17:06:17 -07:00
|
|
|
ctx.GasMeter().ConsumeGas(costSetCoins, "setCoins")
|
2018-04-18 21:49:24 -07:00
|
|
|
acc := am.GetAccount(ctx, addr)
|
|
|
|
if acc == nil {
|
|
|
|
acc = am.NewAccountWithAddress(ctx, addr)
|
|
|
|
}
|
2018-06-28 15:52:10 -07:00
|
|
|
err := acc.SetCoins(amt)
|
|
|
|
if err != nil {
|
|
|
|
// Handle w/ #870
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-04-18 21:49:24 -07:00
|
|
|
am.SetAccount(ctx, acc)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasCoins returns whether or not an account has at least amt coins.
|
2018-07-06 00:06:53 -07:00
|
|
|
func hasCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.AccAddress, amt sdk.Coins) bool {
|
2018-05-15 17:06:17 -07:00
|
|
|
ctx.GasMeter().ConsumeGas(costHasCoins, "hasCoins")
|
2018-05-08 08:34:09 -07:00
|
|
|
return getCoins(ctx, am, addr).IsGTE(amt)
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// SubtractCoins subtracts amt from the coins at the addr.
|
2018-07-06 00:06:53 -07:00
|
|
|
func subtractCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) {
|
2018-05-15 17:06:17 -07:00
|
|
|
ctx.GasMeter().ConsumeGas(costSubtractCoins, "subtractCoins")
|
2018-04-18 21:49:24 -07:00
|
|
|
oldCoins := getCoins(ctx, am, addr)
|
|
|
|
newCoins := oldCoins.Minus(amt)
|
|
|
|
if !newCoins.IsNotNegative() {
|
2018-04-26 07:14:51 -07:00
|
|
|
return amt, nil, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt))
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
|
|
|
err := setCoins(ctx, am, addr, newCoins)
|
2018-06-11 13:09:29 -07:00
|
|
|
tags := sdk.NewTags("sender", []byte(addr.String()))
|
2018-04-26 07:14:51 -07:00
|
|
|
return newCoins, tags, err
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddCoins adds amt to the coins at the addr.
|
2018-07-06 00:06:53 -07:00
|
|
|
func addCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) {
|
2018-05-15 17:06:17 -07:00
|
|
|
ctx.GasMeter().ConsumeGas(costAddCoins, "addCoins")
|
2018-04-18 21:49:24 -07:00
|
|
|
oldCoins := getCoins(ctx, am, addr)
|
|
|
|
newCoins := oldCoins.Plus(amt)
|
|
|
|
if !newCoins.IsNotNegative() {
|
2018-04-26 07:14:51 -07:00
|
|
|
return amt, nil, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt))
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
|
|
|
err := setCoins(ctx, am, addr, newCoins)
|
2018-06-11 13:09:29 -07:00
|
|
|
tags := sdk.NewTags("recipient", []byte(addr.String()))
|
2018-04-26 07:14:51 -07:00
|
|
|
return newCoins, tags, err
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// SendCoins moves coins from one account to another
|
|
|
|
// NOTE: Make sure to revert state changes from tx on error
|
2018-07-06 00:06:53 -07:00
|
|
|
func sendCoins(ctx sdk.Context, am auth.AccountMapper, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.Tags, sdk.Error) {
|
2018-04-26 07:14:51 -07:00
|
|
|
_, subTags, err := subtractCoins(ctx, am, fromAddr, amt)
|
2018-04-18 21:49:24 -07:00
|
|
|
if err != nil {
|
2018-04-26 07:14:51 -07:00
|
|
|
return nil, err
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
|
|
|
|
2018-04-26 07:14:51 -07:00
|
|
|
_, addTags, err := addCoins(ctx, am, toAddr, amt)
|
2018-04-18 21:49:24 -07:00
|
|
|
if err != nil {
|
2018-04-26 07:14:51 -07:00
|
|
|
return nil, err
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
|
|
|
|
2018-05-10 08:14:46 -07:00
|
|
|
return subTags.AppendTags(addTags), nil
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// InputOutputCoins handles a list of inputs and outputs
|
|
|
|
// NOTE: Make sure to revert state changes from tx on error
|
2018-05-23 19:26:54 -07:00
|
|
|
func inputOutputCoins(ctx sdk.Context, am auth.AccountMapper, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) {
|
2018-04-26 07:14:51 -07:00
|
|
|
allTags := sdk.EmptyTags()
|
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
for _, in := range inputs {
|
2018-04-26 07:14:51 -07:00
|
|
|
_, tags, err := subtractCoins(ctx, am, in.Address, in.Coins)
|
2018-04-18 21:49:24 -07:00
|
|
|
if err != nil {
|
2018-04-26 07:14:51 -07:00
|
|
|
return nil, err
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
2018-05-10 08:14:46 -07:00
|
|
|
allTags = allTags.AppendTags(tags)
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, out := range outputs {
|
2018-04-26 07:14:51 -07:00
|
|
|
_, tags, err := addCoins(ctx, am, out.Address, out.Coins)
|
2018-04-18 21:49:24 -07:00
|
|
|
if err != nil {
|
2018-04-26 07:14:51 -07:00
|
|
|
return nil, err
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
2018-05-10 08:14:46 -07:00
|
|
|
allTags = allTags.AppendTags(tags)
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|
|
|
|
|
2018-04-26 07:14:51 -07:00
|
|
|
return allTags, nil
|
2018-04-18 21:49:24 -07:00
|
|
|
}
|