Rough draft of the new coins storage

This commit is contained in:
Ethan Frey 2017-06-30 20:55:23 +02:00
parent 673b51f3b0
commit 2fc4da1076
3 changed files with 105 additions and 0 deletions

View File

@ -1,6 +1,7 @@
package basecoin
import (
wire "github.com/tendermint/go-wire"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/tmlibs/log"
)
@ -20,6 +21,10 @@ func NewActor(app string, addr []byte) Actor {
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
// rely on private fields to control the actions
type Context interface {

View File

@ -20,6 +20,7 @@ const (
msgInvalidSequence = "Invalid Sequence"
msgInvalidSignature = "Invalid Signature"
msgInsufficientFees = "Insufficient Fees"
msgInsufficientFunds = "Insufficient Funds"
msgNoInputs = "No Input Coins"
msgNoOutputs = "No Output Coins"
msgTooLarge = "Input size too large"
@ -88,6 +89,10 @@ func InsufficientFees() TMError {
return New(msgInsufficientFees, abci.CodeType_BaseInvalidInput)
}
func InsufficientFunds() TMError {
return New(msgInsufficientFunds, abci.CodeType_BaseInvalidInput)
}
func NoInputs() TMError {
return New(msgNoInputs, abci.CodeType_BaseInvalidInput)
}

95
modules/coin/store.go Normal file
View File

@ -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...
}