cosmos-sdk/modules/coin/store.go

117 lines
2.9 KiB
Go
Raw Normal View History

2017-06-30 11:55:23 -07:00
package coin
import (
"fmt"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/errors"
"github.com/tendermint/basecoin/types"
)
type Accountant struct {
Prefix []byte
}
2017-07-03 12:34:08 -07:00
func NewAccountant(prefix string) Accountant {
if prefix == "" {
prefix = NameCoin
}
return Accountant{
Prefix: []byte(prefix + "/"),
}
}
2017-06-30 11:55:23 -07:00
func (a Accountant) GetAccount(store types.KVStore, addr basecoin.Actor) (Account, error) {
2017-07-03 12:34:08 -07:00
acct, err := loadAccount(store, a.MakeKey(addr))
2017-07-04 20:28:27 -07:00
2017-07-03 05:50:33 -07:00
// for empty accounts, don't return an error, but rather an empty account
if IsNoAccountErr(err) {
err = nil
}
return acct, err
2017-06-30 11:55:23 -07:00
}
// CheckCoins makes sure there are funds, but doesn't change anything
func (a Accountant) CheckCoins(store types.KVStore, addr basecoin.Actor, coins types.Coins, seq int) (types.Coins, error) {
acct, err := a.updateCoins(store, addr, coins, seq)
return acct.Coins, err
}
// ChangeCoins changes the money, returns error if it would be negative
func (a Accountant) ChangeCoins(store types.KVStore, addr basecoin.Actor, coins types.Coins, seq int) (types.Coins, error) {
acct, err := a.updateCoins(store, addr, coins, seq)
if err != nil {
return acct.Coins, err
}
2017-07-03 12:34:08 -07:00
err = storeAccount(store, a.MakeKey(addr), acct)
2017-06-30 11:55:23 -07:00
return acct.Coins, err
}
// updateCoins will load the account, make all checks, and return the updated account.
//
// it doesn't save anything, that is up to you to decide (Check/Change Coins)
func (a Accountant) updateCoins(store types.KVStore, addr basecoin.Actor, coins types.Coins, seq int) (acct Account, err error) {
2017-07-03 12:34:08 -07:00
acct, err = loadAccount(store, a.MakeKey(addr))
// we can increase an empty account...
if IsNoAccountErr(err) && coins.IsPositive() {
err = nil
}
2017-06-30 11:55:23 -07:00
if err != nil {
return acct, err
}
// check sequence if we are deducting... ugh, need a cleaner replay protection
if !coins.IsPositive() {
if seq != acct.Sequence+1 {
return acct, ErrInvalidSequence()
}
acct.Sequence += 1
2017-06-30 11:55:23 -07:00
}
// check amount
final := acct.Coins.Plus(coins)
2017-06-30 11:55:23 -07:00
if !final.IsNonnegative() {
2017-07-03 05:50:33 -07:00
return acct, ErrInsufficientFunds()
2017-06-30 11:55:23 -07:00
}
acct.Coins = final
return acct, nil
}
2017-07-03 12:34:08 -07:00
func (a Accountant) MakeKey(addr basecoin.Actor) []byte {
2017-06-30 11:55:23 -07:00
key := addr.Bytes()
if len(a.Prefix) > 0 {
key = append(a.Prefix, key...)
}
return key
}
type Account struct {
Coins types.Coins `json:"coins"`
2017-07-03 13:30:12 -07:00
Sequence int `json:"sequence"`
2017-06-30 11:55:23 -07:00
}
func loadAccount(store types.KVStore, key []byte) (acct Account, err error) {
// fmt.Printf("load: %X\n", key)
2017-06-30 11:55:23 -07:00
data := store.Get(key)
if len(data) == 0 {
2017-07-03 05:50:33 -07:00
return acct, ErrNoAccount()
2017-06-30 11:55:23 -07:00
}
err = wire.ReadBinaryBytes(data, &acct)
if err != nil {
msg := fmt.Sprintf("Error reading account %X", key)
2017-07-03 05:50:33 -07:00
return acct, errors.ErrInternal(msg)
2017-06-30 11:55:23 -07:00
}
return acct, nil
}
func storeAccount(store types.KVStore, key []byte, acct Account) error {
// fmt.Printf("store: %X\n", key)
2017-06-30 11:55:23 -07:00
bin := wire.BinaryBytes(acct)
store.Set(key, bin)
return nil // real stores can return error...
}