214 lines
6.9 KiB
Go
214 lines
6.9 KiB
Go
package bank
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
)
|
|
|
|
const (
|
|
costGetCoins sdk.Gas = 10
|
|
costHasCoins sdk.Gas = 10
|
|
costSetCoins sdk.Gas = 100
|
|
costSubtractCoins sdk.Gas = 10
|
|
costAddCoins sdk.Gas = 10
|
|
)
|
|
|
|
// Keeper manages transfers between accounts
|
|
type Keeper struct {
|
|
am auth.AccountMapper
|
|
}
|
|
|
|
// NewKeeper returns a new Keeper
|
|
func NewKeeper(am auth.AccountMapper) Keeper {
|
|
return Keeper{am: am}
|
|
}
|
|
|
|
// GetCoins returns the coins at the addr.
|
|
func (keeper Keeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins {
|
|
return getCoins(ctx, keeper.am, addr)
|
|
}
|
|
|
|
// SetCoins sets the coins at the addr.
|
|
func (keeper Keeper) SetCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) sdk.Error {
|
|
return setCoins(ctx, keeper.am, addr, amt)
|
|
}
|
|
|
|
// HasCoins returns whether or not an account has at least amt coins.
|
|
func (keeper Keeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool {
|
|
return hasCoins(ctx, keeper.am, addr, amt)
|
|
}
|
|
|
|
// SubtractCoins subtracts amt from the coins at the addr.
|
|
func (keeper Keeper) SubtractCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) {
|
|
return subtractCoins(ctx, keeper.am, addr, amt)
|
|
}
|
|
|
|
// AddCoins adds amt to the coins at the addr.
|
|
func (keeper Keeper) AddCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) {
|
|
return addCoins(ctx, keeper.am, addr, amt)
|
|
}
|
|
|
|
// SendCoins moves coins from one account to another
|
|
func (keeper Keeper) SendCoins(ctx sdk.Context, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) (sdk.Tags, sdk.Error) {
|
|
return sendCoins(ctx, keeper.am, fromAddr, toAddr, amt)
|
|
}
|
|
|
|
// InputOutputCoins handles a list of inputs and outputs
|
|
func (keeper Keeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) {
|
|
return inputOutputCoins(ctx, keeper.am, inputs, outputs)
|
|
}
|
|
|
|
//______________________________________________________________________________________________
|
|
|
|
// SendKeeper only allows transfers between accounts, without the possibility of creating coins
|
|
type SendKeeper struct {
|
|
am auth.AccountMapper
|
|
}
|
|
|
|
// NewSendKeeper returns a new Keeper
|
|
func NewSendKeeper(am auth.AccountMapper) SendKeeper {
|
|
return SendKeeper{am: am}
|
|
}
|
|
|
|
// GetCoins returns the coins at the addr.
|
|
func (keeper SendKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins {
|
|
return getCoins(ctx, keeper.am, addr)
|
|
}
|
|
|
|
// HasCoins returns whether or not an account has at least amt coins.
|
|
func (keeper SendKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool {
|
|
return hasCoins(ctx, keeper.am, addr, amt)
|
|
}
|
|
|
|
// SendCoins moves coins from one account to another
|
|
func (keeper SendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) (sdk.Tags, sdk.Error) {
|
|
return sendCoins(ctx, keeper.am, fromAddr, toAddr, amt)
|
|
}
|
|
|
|
// InputOutputCoins handles a list of inputs and outputs
|
|
func (keeper SendKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) {
|
|
return inputOutputCoins(ctx, keeper.am, inputs, outputs)
|
|
}
|
|
|
|
//______________________________________________________________________________________________
|
|
|
|
// ViewKeeper only allows reading of balances
|
|
type ViewKeeper struct {
|
|
am auth.AccountMapper
|
|
}
|
|
|
|
// NewViewKeeper returns a new Keeper
|
|
func NewViewKeeper(am auth.AccountMapper) ViewKeeper {
|
|
return ViewKeeper{am: am}
|
|
}
|
|
|
|
// GetCoins returns the coins at the addr.
|
|
func (keeper ViewKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins {
|
|
return getCoins(ctx, keeper.am, addr)
|
|
}
|
|
|
|
// HasCoins returns whether or not an account has at least amt coins.
|
|
func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool {
|
|
return hasCoins(ctx, keeper.am, addr, amt)
|
|
}
|
|
|
|
//______________________________________________________________________________________________
|
|
|
|
func getCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.Address) sdk.Coins {
|
|
ctx.GasMeter().ConsumeGas(costGetCoins, "getCoins")
|
|
acc := am.GetAccount(ctx, addr)
|
|
if acc == nil {
|
|
return sdk.Coins{}
|
|
}
|
|
return acc.GetCoins()
|
|
}
|
|
|
|
func setCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.Address, amt sdk.Coins) sdk.Error {
|
|
ctx.GasMeter().ConsumeGas(costSetCoins, "setCoins")
|
|
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)
|
|
}
|
|
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.AccountMapper, addr sdk.Address, amt sdk.Coins) bool {
|
|
ctx.GasMeter().ConsumeGas(costHasCoins, "hasCoins")
|
|
return getCoins(ctx, am, addr).IsGTE(amt)
|
|
}
|
|
|
|
// SubtractCoins subtracts amt from the coins at the addr.
|
|
func subtractCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) {
|
|
ctx.GasMeter().ConsumeGas(costSubtractCoins, "subtractCoins")
|
|
oldCoins := getCoins(ctx, am, addr)
|
|
newCoins := oldCoins.Minus(amt)
|
|
if !newCoins.IsNotNegative() {
|
|
return amt, nil, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt))
|
|
}
|
|
err := setCoins(ctx, am, addr, newCoins)
|
|
tags := sdk.NewTags("sender", []byte(addr.String()))
|
|
return newCoins, tags, err
|
|
}
|
|
|
|
// AddCoins adds amt to the coins at the addr.
|
|
func addCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) {
|
|
ctx.GasMeter().ConsumeGas(costAddCoins, "addCoins")
|
|
oldCoins := getCoins(ctx, am, addr)
|
|
newCoins := oldCoins.Plus(amt)
|
|
if !newCoins.IsNotNegative() {
|
|
return amt, nil, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt))
|
|
}
|
|
err := setCoins(ctx, am, addr, newCoins)
|
|
tags := sdk.NewTags("recipient", []byte(addr.String()))
|
|
return newCoins, tags, err
|
|
}
|
|
|
|
// 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.AccountMapper, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) (sdk.Tags, sdk.Error) {
|
|
_, subTags, err := subtractCoins(ctx, am, fromAddr, amt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, addTags, err := addCoins(ctx, am, toAddr, amt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return subTags.AppendTags(addTags), nil
|
|
}
|
|
|
|
// 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.AccountMapper, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) {
|
|
allTags := sdk.EmptyTags()
|
|
|
|
for _, in := range inputs {
|
|
_, tags, err := subtractCoins(ctx, am, in.Address, in.Coins)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
allTags = allTags.AppendTags(tags)
|
|
}
|
|
|
|
for _, out := range outputs {
|
|
_, tags, err := addCoins(ctx, am, out.Address, out.Coins)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
allTags = allTags.AppendTags(tags)
|
|
}
|
|
|
|
return allTags, nil
|
|
}
|