cosmos-sdk/x/bank/keeper.go

238 lines
7.3 KiB
Go
Raw Normal View History

package bank
import (
2018-03-24 17:12:44 -07:00
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
2018-05-23 19:26:54 -07:00
"github.com/cosmos/cosmos-sdk/x/auth"
)
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-11-07 06:27:20 -08:00
//-----------------------------------------------------------------------------
// Keeper
var _ Keeper = (*BaseKeeper)(nil)
// 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)
}
// BaseKeeper manages transfers between accounts. It implements the Keeper
// interface.
type BaseKeeper struct {
2018-11-07 06:27:20 -08:00
BaseSendKeeper
2018-04-09 15:10:58 -07:00
2018-11-07 06:27:20 -08:00
ak auth.AccountKeeper
2018-04-09 15:10:58 -07:00
}
2018-11-07 06:27:20 -08:00
// NewBaseKeeper returns a new BaseKeeper
func NewBaseKeeper(ak auth.AccountKeeper) BaseKeeper {
return BaseKeeper{BaseSendKeeper: NewBaseSendKeeper(ak), ak: ak}
2018-04-09 15:10:58 -07:00
}
// SetCoins sets the coins at the addr.
func (keeper BaseKeeper) SetCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) sdk.Error {
2018-11-07 06:27:20 -08:00
return setCoins(ctx, keeper.ak, addr, amt)
2018-04-09 15:10:58 -07:00
}
// SubtractCoins subtracts amt from the coins at the addr.
func (keeper BaseKeeper) SubtractCoins(
ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins,
) (sdk.Coins, sdk.Tags, sdk.Error) {
2018-11-07 06:27:20 -08:00
return subtractCoins(ctx, keeper.ak, addr, amt)
2018-04-09 15:10:58 -07:00
}
// AddCoins adds amt to the coins at the addr.
func (keeper BaseKeeper) AddCoins(
ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins,
) (sdk.Coins, sdk.Tags, sdk.Error) {
2018-11-07 06:27:20 -08:00
return addCoins(ctx, keeper.ak, addr, amt)
2018-04-09 15:10:58 -07:00
}
2018-11-07 06:27:20 -08:00
//-----------------------------------------------------------------------------
// Send Keeper
2018-04-09 15:10:58 -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
2018-11-07 06:27:20 -08:00
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-11-07 06:27:20 -08:00
BaseViewKeeper
2018-04-09 15:10:58 -07:00
2018-11-07 06:27:20 -08:00
ak auth.AccountKeeper
2018-04-09 15:10:58 -07:00
}
2018-11-07 06:27:20 -08:00
// NewBaseSendKeeper returns a new BaseSendKeeper.
func NewBaseSendKeeper(ak auth.AccountKeeper) BaseSendKeeper {
return BaseSendKeeper{BaseViewKeeper: NewBaseViewKeeper(ak), ak: ak}
2018-04-09 15:10:58 -07:00
}
// SendCoins moves coins from one account to another
func (keeper BaseSendKeeper) SendCoins(
ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins,
) (sdk.Tags, sdk.Error) {
2018-11-07 06:27:20 -08:00
return sendCoins(ctx, keeper.ak, fromAddr, toAddr, amt)
2018-04-09 15:10:58 -07:00
}
// InputOutputCoins handles a list of inputs and outputs
func (keeper BaseSendKeeper) InputOutputCoins(
ctx sdk.Context, inputs []Input, outputs []Output,
) (sdk.Tags, sdk.Error) {
2018-11-07 06:27:20 -08:00
return inputOutputCoins(ctx, keeper.ak, inputs, outputs)
2018-04-09 15:10:58 -07:00
}
2018-11-07 06:27:20 -08:00
//-----------------------------------------------------------------------------
// View Keeper
var _ ViewKeeper = (*BaseViewKeeper)(nil)
2018-04-09 15:10:58 -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
}
// BaseViewKeeper implements a read only keeper implementation of ViewKeeper.
type BaseViewKeeper struct {
2018-11-07 06:27:20 -08:00
ak auth.AccountKeeper
2018-04-09 15:10:58 -07:00
}
// NewBaseViewKeeper returns a new BaseViewKeeper.
2018-11-07 06:27:20 -08:00
func NewBaseViewKeeper(ak auth.AccountKeeper) BaseViewKeeper {
return BaseViewKeeper{ak: ak}
2018-04-09 15:10:58 -07:00
}
// GetCoins returns the coins at the addr.
func (keeper BaseViewKeeper) GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins {
2018-11-07 06:27:20 -08:00
return getCoins(ctx, keeper.ak, addr)
2018-04-09 15:10:58 -07:00
}
// HasCoins returns whether or not an account has at least amt coins.
func (keeper BaseViewKeeper) HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool {
2018-11-07 06:27:20 -08:00
return hasCoins(ctx, keeper.ak, addr, amt)
2018-04-09 15:10:58 -07:00
}
2018-04-18 21:49:24 -07:00
2018-11-07 06:27:20 -08:00
//-----------------------------------------------------------------------------
2018-04-18 21:49:24 -07:00
func getCoins(ctx sdk.Context, am auth.AccountKeeper, 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
}
func setCoins(ctx sdk.Context, am auth.AccountKeeper, 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)
}
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.
func hasCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) bool {
2018-05-15 17:06:17 -07:00
ctx.GasMeter().ConsumeGas(costHasCoins, "hasCoins")
return getCoins(ctx, am, addr).IsAllGTE(amt)
2018-04-18 21:49:24 -07:00
}
// SubtractCoins subtracts amt from the coins at the addr.
func subtractCoins(ctx sdk.Context, am auth.AccountKeeper, 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() {
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)
tags := sdk.NewTags("sender", []byte(addr.String()))
return newCoins, tags, err
2018-04-18 21:49:24 -07:00
}
// AddCoins adds amt to the coins at the addr.
func addCoins(ctx sdk.Context, am auth.AccountKeeper, 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() {
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)
tags := sdk.NewTags("recipient", []byte(addr.String()))
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
func sendCoins(ctx sdk.Context, am auth.AccountKeeper, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.Tags, sdk.Error) {
_, subTags, err := subtractCoins(ctx, am, fromAddr, amt)
2018-04-18 21:49:24 -07:00
if err != nil {
return nil, err
2018-04-18 21:49:24 -07:00
}
_, addTags, err := addCoins(ctx, am, toAddr, amt)
2018-04-18 21:49:24 -07:00
if err != nil {
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
func inputOutputCoins(ctx sdk.Context, am auth.AccountKeeper, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) {
allTags := sdk.EmptyTags()
2018-04-18 21:49:24 -07:00
for _, in := range inputs {
_, tags, err := subtractCoins(ctx, am, in.Address, in.Coins)
2018-04-18 21:49:24 -07:00
if err != nil {
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 {
_, tags, err := addCoins(ctx, am, out.Address, out.Coins)
2018-04-18 21:49:24 -07:00
if err != nil {
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
}
return allTags, nil
2018-04-18 21:49:24 -07:00
}