cosmos-sdk/modules/coin/store.go

146 lines
3.8 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"
2017-07-06 05:23:38 -07:00
"github.com/tendermint/basecoin/state"
2017-06-30 11:55:23 -07:00
)
2017-07-06 02:30:03 -07:00
// GetAccount - Get account from store and address
func GetAccount(store state.SimpleDB, addr basecoin.Actor) (Account, error) {
// if the actor is another chain, we use one address for the chain....
addr = ChainAddr(addr)
acct, err := loadAccount(store, addr.Bytes())
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 CheckCoins(store state.SimpleDB, addr basecoin.Actor, coins Coins) (Coins, error) {
// if the actor is another chain, we use one address for the chain....
addr = ChainAddr(addr)
2017-07-12 09:54:07 -07:00
acct, err := updateCoins(store, addr, coins)
2017-06-30 11:55:23 -07:00
return acct.Coins, err
}
// ChangeCoins changes the money, returns error if it would be negative
func ChangeCoins(store state.SimpleDB, addr basecoin.Actor, coins Coins) (Coins, error) {
// if the actor is another chain, we use one address for the chain....
addr = ChainAddr(addr)
2017-07-12 09:54:07 -07:00
acct, err := updateCoins(store, addr, coins)
2017-06-30 11:55:23 -07:00
if err != nil {
return acct.Coins, err
}
err = storeAccount(store, addr.Bytes(), acct)
2017-06-30 11:55:23 -07:00
return acct.Coins, err
}
// ChainAddr collapses all addresses from another chain into one, so we can
// keep an over-all balance
//
// TODO: is there a better way to do this?
func ChainAddr(addr basecoin.Actor) basecoin.Actor {
if addr.ChainID == "" {
return addr
}
addr.App = ""
addr.Address = nil
return addr
}
2017-06-30 11:55:23 -07:00
// 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 updateCoins(store state.SimpleDB, addr basecoin.Actor, coins Coins) (acct Account, err error) {
acct, err = loadAccount(store, addr.Bytes())
// 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 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-06 02:30:03 -07:00
// Account - coin account structure
2017-06-30 11:55:23 -07:00
type Account struct {
2017-07-21 11:24:53 -07:00
// Coins is how much is on the account
Coins Coins `json:"coins"`
2017-07-21 11:24:53 -07:00
// Credit is how much has been "fronted" to the account
// (this is usually 0 except for trusted chains)
Credit Coins `json:"credit"`
2017-06-30 11:55:23 -07:00
}
func loadAccount(store state.SimpleDB, 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 state.SimpleDB, 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...
}
2017-07-21 11:24:53 -07:00
// HandlerInfo - this is global info on the coin handler
type HandlerInfo struct {
Issuer basecoin.Actor `json:"issuer"`
}
// TODO: where to store these special pieces??
var handlerKey = []byte{12, 34}
func loadHandlerInfo(store state.KVStore) (info HandlerInfo, err error) {
data := store.Get(handlerKey)
if len(data) == 0 {
return info, nil
}
err = wire.ReadBinaryBytes(data, &info)
if err != nil {
msg := "Error reading handler info"
return info, errors.ErrInternal(msg)
}
return info, nil
}
func storeIssuer(store state.KVStore, issuer basecoin.Actor) error {
info, err := loadHandlerInfo(store)
if err != nil {
return err
}
info.Issuer = issuer
d := wire.BinaryBytes(info)
store.Set(handlerKey, d)
return nil // real stores can return error...
}