Rough draft of the new coins storage
This commit is contained in:
parent
673b51f3b0
commit
2fc4da1076
|
@ -1,6 +1,7 @@
|
||||||
package basecoin
|
package basecoin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
"github.com/tendermint/go-wire/data"
|
"github.com/tendermint/go-wire/data"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
@ -20,6 +21,10 @@ func NewActor(app string, addr []byte) Actor {
|
||||||
return Actor{App: app, Address: addr}
|
return Actor{App: app, Address: addr}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a Actor) Bytes() []byte {
|
||||||
|
return wire.BinaryBytes(a)
|
||||||
|
}
|
||||||
|
|
||||||
// Context is an interface, so we can implement "secure" variants that
|
// Context is an interface, so we can implement "secure" variants that
|
||||||
// rely on private fields to control the actions
|
// rely on private fields to control the actions
|
||||||
type Context interface {
|
type Context interface {
|
||||||
|
|
|
@ -20,6 +20,7 @@ const (
|
||||||
msgInvalidSequence = "Invalid Sequence"
|
msgInvalidSequence = "Invalid Sequence"
|
||||||
msgInvalidSignature = "Invalid Signature"
|
msgInvalidSignature = "Invalid Signature"
|
||||||
msgInsufficientFees = "Insufficient Fees"
|
msgInsufficientFees = "Insufficient Fees"
|
||||||
|
msgInsufficientFunds = "Insufficient Funds"
|
||||||
msgNoInputs = "No Input Coins"
|
msgNoInputs = "No Input Coins"
|
||||||
msgNoOutputs = "No Output Coins"
|
msgNoOutputs = "No Output Coins"
|
||||||
msgTooLarge = "Input size too large"
|
msgTooLarge = "Input size too large"
|
||||||
|
@ -88,6 +89,10 @@ func InsufficientFees() TMError {
|
||||||
return New(msgInsufficientFees, abci.CodeType_BaseInvalidInput)
|
return New(msgInsufficientFees, abci.CodeType_BaseInvalidInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InsufficientFunds() TMError {
|
||||||
|
return New(msgInsufficientFunds, abci.CodeType_BaseInvalidInput)
|
||||||
|
}
|
||||||
|
|
||||||
func NoInputs() TMError {
|
func NoInputs() TMError {
|
||||||
return New(msgNoInputs, abci.CodeType_BaseInvalidInput)
|
return New(msgNoInputs, abci.CodeType_BaseInvalidInput)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Accountant) GetAccount(store types.KVStore, addr basecoin.Actor) (Account, error) {
|
||||||
|
// TODO: how to handle empty accounts??
|
||||||
|
return loadAccount(store, a.makeKey(addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
err = storeAccount(store, a.makeKey(addr), acct)
|
||||||
|
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) {
|
||||||
|
acct, err = loadAccount(store, a.makeKey(addr))
|
||||||
|
if err != nil {
|
||||||
|
return acct, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check sequence
|
||||||
|
if seq != acct.Sequence+1 {
|
||||||
|
return acct, errors.InvalidSequence()
|
||||||
|
}
|
||||||
|
acct.Sequence += 1
|
||||||
|
|
||||||
|
// check amount
|
||||||
|
final := acct.Coins.Minus(coins)
|
||||||
|
if !final.IsNonnegative() {
|
||||||
|
return acct, errors.InsufficientFunds()
|
||||||
|
}
|
||||||
|
|
||||||
|
acct.Coins = final
|
||||||
|
return acct, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Accountant) makeKey(addr basecoin.Actor) []byte {
|
||||||
|
key := addr.Bytes()
|
||||||
|
if len(a.Prefix) > 0 {
|
||||||
|
key = append(a.Prefix, key...)
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
Coins types.Coins `json:"coins"`
|
||||||
|
Sequence int `json:"seq"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadAccount(store types.KVStore, key []byte) (acct Account, err error) {
|
||||||
|
data := store.Get(key)
|
||||||
|
if len(data) == 0 {
|
||||||
|
// TODO: error or empty????
|
||||||
|
return acct, errors.InternalError("No account found")
|
||||||
|
}
|
||||||
|
err = wire.ReadBinaryBytes(data, &acct)
|
||||||
|
if err != nil {
|
||||||
|
msg := fmt.Sprintf("Error reading account %X", key)
|
||||||
|
return acct, errors.InternalError(msg)
|
||||||
|
}
|
||||||
|
return acct, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeAccount(store types.KVStore, key []byte, acct Account) error {
|
||||||
|
bin := wire.BinaryBytes(acct)
|
||||||
|
store.Set(key, bin)
|
||||||
|
return nil // real stores can return error...
|
||||||
|
}
|
Loading…
Reference in New Issue