move coins and accounts out of types

This commit is contained in:
Ethan Buchman 2018-01-06 17:22:21 -05:00 committed by Jae Kwon
parent 3235b2d647
commit 3ba3d6b02f
31 changed files with 345 additions and 350 deletions

View File

@ -25,7 +25,7 @@ type testTx struct {
func (tx testTx) Get(key interface{}) (value interface{}) { return nil }
func (tx testTx) SignBytes() []byte { return nil }
func (tx testTx) ValidateBasic() error { return nil }
func (tx testTx) Signers() []types.Address { return nil }
func (tx testTx) Signers() []crypto.Address { return nil }
func (tx testTx) TxBytes() []byte { return nil }
func (tx testTx) Signatures() []types.StdSignature { return nil }

View File

@ -1,71 +0,0 @@
package main
import (
"github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
)
// AppAccount - coin account structure
type AppAccount struct {
Address_ crypto.Address `json:"address"`
Coins types.Coins `json:"coins"`
PubKey_ crypto.PubKey `json:"public_key"` // can't conflict with PubKey()
Sequence int64 `json:"sequence"`
}
// Implements auth.Account
func (a *AppAccount) Get(key interface{}) (value interface{}, err error) {
switch key.(type) {
case string:
//
default:
panic("HURAH!")
}
return nil, nil
}
// Implements auth.Account
func (a *AppAccount) Set(key interface{}, value interface{}) error {
switch key.(type) {
case string:
//
default:
panic("HURAH!")
}
return nil
}
// Implements auth.Account
func (a *AppAccount) Address() types.Address {
return a.PubKey_.Address()
}
// Implements auth.Account
func (a *AppAccount) PubKey() crypto.PubKey {
return a.PubKey_
}
func (a *AppAccount) SetPubKey(pubKey crypto.PubKey) error {
a.PubKey_ = pubKey
return nil
}
// Implements coinstore.Coinser
func (a *AppAccount) GetCoins() types.Coins {
return a.Coins
}
// Implements coinstore.Coinser
func (a *AppAccount) SetCoins(coins types.Coins) error {
a.Coins = coins
return nil
}
func (a *AppAccount) GetSequence() int64 {
return a.Sequence
}
func (a *AppAccount) SetSequence(seq int64) error {
a.Sequence = seq
return nil
}

View File

@ -1,55 +0,0 @@
package main
import (
"encoding/json"
"path"
"github.com/cosmos/cosmos-sdk/types"
)
type AppAccountStore struct {
kvStore types.KVStore
}
func newAccountStore(kvStore types.KVStore) types.AccountStore {
return AppAccountStore{kvStore}
}
func (accStore AppAccountStore) NewAccountWithAddress(addr types.Address) types.Account {
return &AppAccount{
Address_: addr,
}
}
func (accStore AppAccountStore) GetAccount(addr types.Address) types.Account {
v := accStore.kvStore.Get(keyAccount(addr))
if len(v) == 0 {
return nil
}
acc := new(AppAccount)
if err := json.Unmarshal(v, acc); err != nil {
panic(err)
}
return acc
}
func (accStore AppAccountStore) SetAccount(acc types.Account) {
b, err := json.Marshal(acc)
if err != nil {
panic(err)
}
appAcc, ok := acc.(*AppAccount)
if !ok {
panic("acc is not *AppAccount") // XXX
}
accStore.kvStore.Set(keyAccount(appAcc.Address_), b)
}
func keyAccount(addr types.Address) []byte {
return []byte(path.Join("account", string(addr)))
}

66
examples/basecoin/app.go Normal file
View File

@ -0,0 +1,66 @@
package main
import (
"encoding/json"
"path"
crypto "github.com/tendermint/go-crypto"
"github.com/cosmos/cosmos-sdk/types"
acm "github.com/cosmos/cosmos-sdk/x/account"
"github.com/cosmos/cosmos-sdk/x/sendtx"
"github.com/cosmos/cosmos-sdk/x/store"
)
func txParser(txBytes []byte) (types.Tx, error) {
var tx sendtx.SendTx
err := json.Unmarshal(txBytes, &tx)
return tx, err
}
//-----------------------------------------------------------------------------
type AccountStore struct {
kvStore types.KVStore
}
func newAccountStore(kvStore types.KVStore) store.AccountStore {
return AccountStore{kvStore}
}
func (accStore AccountStore) NewAccountWithAddress(addr crypto.Address) store.Account {
return acm.NewBaseAccountWithAddress(addr)
}
func (accStore AccountStore) GetAccount(addr crypto.Address) store.Account {
v := accStore.kvStore.Get(keyAccount(addr))
if len(v) == 0 {
return nil
}
acc := new(acm.BaseAccount)
if err := json.Unmarshal(v, acc); err != nil {
panic(err)
}
return acc
}
func (accStore AccountStore) SetAccount(acc store.Account) {
b, err := json.Marshal(acc)
if err != nil {
panic(err)
}
appAcc, ok := acc.(*acm.BaseAccount)
if !ok {
panic("acc is not *acm.BaseAccount") // XXX
}
accStore.kvStore.Set(keyAccount(appAcc.Address()), b)
}
func keyAccount(addr crypto.Address) []byte {
return []byte(path.Join("account", string(addr)))
}

View File

@ -1,7 +1,6 @@
package main
import (
"encoding/json"
"fmt"
"os"
@ -73,11 +72,3 @@ func main() {
})
return
}
// create ctx in begin block to be used as background for txs ...
func txParser(txBytes []byte) (types.Tx, error) {
var tx sendtx.SendTx
err := json.Unmarshal(txBytes, &tx)
return tx, err
}

View File

@ -6,6 +6,7 @@ import (
"os"
"github.com/tendermint/abci/server"
crypto "github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
@ -89,7 +90,7 @@ func (tx dummyTx) ValidateBasic() error {
return nil
}
func (tx dummyTx) Signers() []types.Address {
func (tx dummyTx) Signers() []crypto.Address {
return nil
}

View File

@ -1,6 +0,0 @@
package main
import (
_ "github.com/tendermint/go-wire/gen"
_ "github.com/clipperhouse/stringer"
)

View File

@ -1,29 +0,0 @@
package types
import (
crypto "github.com/tendermint/go-crypto"
)
type Address = crypto.Address
type Account interface {
Address() Address
PubKey() crypto.PubKey
SetPubKey(crypto.PubKey) error
GetCoins() Coins
SetCoins(Coins) error
GetSequence() int64
SetSequence(int64) error
Get(key interface{}) (value interface{}, err error)
Set(key interface{}, value interface{}) error
}
type AccountStore interface {
NewAccountWithAddress(addr Address) Account
GetAccount(addr Address) Account
SetAccount(acc Account)
}

View File

@ -1,5 +1,7 @@
package types
import crypto "github.com/tendermint/go-crypto"
type Msg interface {
// Get some property of the Msg.
@ -15,7 +17,7 @@ type Msg interface {
// Signers returns the addrs of signers that must sign.
// CONTRACT: All signatures must be present to be valid.
// CONTRACT: Returns addrs in some deterministic order.
Signers() []Address
Signers() []crypto.Address
}
type Tx interface {

113
x/account/account.go Normal file
View File

@ -0,0 +1,113 @@
package account
import (
"encoding/json"
crypto "github.com/tendermint/go-crypto"
"github.com/cosmos/cosmos-sdk/x/coin"
)
//-----------------------------------------------------------
// BaseAccount
// BaseAccount - coin account structure
type BaseAccount struct {
address crypto.Address
coins coin.Coins
pubKey crypto.PubKey
sequence int64
}
func NewBaseAccountWithAddress(addr crypto.Address) *BaseAccount {
return &BaseAccount{
address: addr,
}
}
// BaseAccountWire is the account structure used for serialization
type BaseAccountWire struct {
Address crypto.Address `json:"address"`
Coins coin.Coins `json:"coins"`
PubKey crypto.PubKey `json:"public_key"` // can't conflict with PubKey()
Sequence int64 `json:"sequence"`
}
func (acc *BaseAccount) MarshalJSON() ([]byte, error) {
return json.Marshal(BaseAccountWire{
Address: acc.address,
Coins: acc.coins,
PubKey: acc.pubKey,
Sequence: acc.sequence,
})
}
func (acc *BaseAccount) UnmarshalJSON(bz []byte) error {
accWire := new(BaseAccountWire)
err := json.Unmarshal(bz, accWire)
if err != nil {
return err
}
acc.address = accWire.Address
acc.coins = accWire.Coins
acc.pubKey = accWire.PubKey
acc.sequence = accWire.Sequence
return nil
}
// Implements Account
func (acc *BaseAccount) Get(key interface{}) (value interface{}, err error) {
switch key.(type) {
case string:
//
default:
panic("HURAH!")
}
return nil, nil
}
// Implements Account
func (acc *BaseAccount) Set(key interface{}, value interface{}) error {
switch key.(type) {
case string:
//
default:
panic("HURAH!")
}
return nil
}
// Implements Account
func (acc *BaseAccount) Address() crypto.Address {
return acc.pubKey.Address()
}
// Implements Account
func (acc *BaseAccount) GetPubKey() crypto.PubKey {
return acc.pubKey
}
func (acc *BaseAccount) SetPubKey(pubKey crypto.PubKey) error {
acc.pubKey = pubKey
return nil
}
// Implements coinstore.Coinser
func (acc *BaseAccount) GetCoins() coin.Coins {
return acc.coins
}
// Implements coinstore.Coinser
func (acc *BaseAccount) SetCoins(coins coin.Coins) error {
acc.coins = coins
return nil
}
func (acc *BaseAccount) GetSequence() int64 {
return acc.sequence
}
func (acc *BaseAccount) SetSequence(seq int64) error {
acc.sequence = seq
return nil
}

View File

@ -2,6 +2,7 @@ package auth
import (
"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/store"
)
/*
@ -24,10 +25,10 @@ const (
contextKeyAccount contextKey = iota
)
func SetAccount(ctx types.Context, account types.Account) types.Context {
func SetAccount(ctx types.Context, account store.Account) types.Context {
return ctx.WithValueUnsafe(contextKeyAccount, account)
}
func GetAccount(ctx types.Context) types.Account {
return ctx.Value(contextKeyAccount).(types.Account)
func GetAccount(ctx types.Context) store.Account {
return ctx.Value(contextKeyAccount).(store.Account)
}

View File

@ -2,15 +2,14 @@ package auth
import (
"github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
"github.com/cosmos/cosmos-sdk/x/store"
)
func DecoratorFn(newAccountStore func(types.KVStore) types.AccountStore) types.Decorator {
func DecoratorFn(newAccountStore func(types.KVStore) store.AccountStore) types.Decorator {
return func(ctx types.Context, ms types.MultiStore, tx types.Tx, next types.Handler) types.Result {
accountStore := newAccountStore(ms.GetKVStore("main"))
// NOTE: we actually dont need Signers() since we have pubkeys in Signatures()
signers := tx.Signers()
signatures := tx.Signatures()
@ -68,11 +67,3 @@ func DecoratorFn(newAccountStore func(types.KVStore) types.AccountStore) types.D
return next(ctx, ms, tx)
}
}
type Auther interface {
GetPubKey() crypto.PubKey
SetPubKey(crypto.PubKey) error
GetSequence() int64
SetSequence() (int64, error)
}

View File

@ -1,9 +1,16 @@
package auth
import (
"github.com/cosmos/cosmos-sdk/x/store"
crypto "github.com/tendermint/go-crypto"
)
type SetPubKeyer interface {
SetPubKey(crypto.PubKey)
var _ Auther = (store.Account)(nil)
type Auther interface {
GetPubKey() crypto.PubKey
SetPubKey(crypto.PubKey) error
GetSequence() int64
SetSequence(int64) error
}

View File

@ -1,4 +1,4 @@
package types
package coin
import (
"fmt"
@ -202,14 +202,6 @@ var _ sort.Interface = Coins{}
// Sort is a helper function to sort the set of coins inplace
func (coins Coins) Sort() { sort.Sort(coins) }
//----------------------------------------
// Misc
type Coinser interface {
GetCoins() Coins
SetCoins(Coins)
}
//----------------------------------------
// Parsing

View File

@ -1,4 +1,4 @@
package types
package coin
import (
"testing"

View File

@ -1,117 +0,0 @@
package coin
import (
"fmt"
"github.com/cosmos/cosmos-sdk/types"
)
type Coins = types.Coins
type Coinser interface {
GetCoins() Coins
SetCoins(Coins)
}
// CoinStore manages transfers between accounts
type CoinStore struct {
types.AccountStore
}
// get the account as a Coinser. if the account doesn't exist, return nil.
// if it's not a Coinser, return error.
func (cs CoinStore) getCoinserAccount(addr types.Address) (types.Coinser, error) {
_acc := cs.GetAccount(addr)
if _acc == nil {
return nil, nil
}
acc, ok := _acc.(Coinser)
if !ok {
return nil, fmt.Errorf("Account %s is not a Coinser", addr)
}
return acc, nil
}
func (cs CoinStore) SubtractCoins(addr types.Address, amt Coins) (Coins, error) {
acc, err := cs.getCoinserAccount(addr)
if err != nil {
return amt, err
} else if acc == nil {
return amt, fmt.Errorf("Sending account (%s) does not exist", addr)
}
coins := acc.GetCoins()
newCoins := coins.Minus(amt)
if !newCoins.IsNotNegative() {
return amt, ErrInsufficientCoins(fmt.Sprintf("%s < %s", coins, amt))
}
acc.SetCoins(newCoins)
cs.SetAccount(acc.(types.Account))
return newCoins, nil
}
func (cs CoinStore) AddCoins(addr types.Address, amt Coins) (Coins, error) {
acc, err := cs.getCoinserAccount(addr)
if err != nil {
return amt, err
} else if acc == nil {
acc = cs.AccountStore.NewAccountWithAddress(addr).(Coinser)
}
coins := acc.GetCoins()
newCoins := coins.Plus(amt)
acc.SetCoins(newCoins)
cs.SetAccount(acc.(types.Account))
return newCoins, nil
}
/*
// TransferCoins transfers coins from fromAddr to toAddr.
// It returns an error if the from account doesn't exist,
// if the accounts doin't implement Coinser,
// or if the from account does not have enough coins.
func (cs CoinStore) TransferCoins(fromAddr, toAddr types.Address, amt Coins) error {
var fromAcc, toAcc types.Account
// Get the accounts
_fromAcc := cs.GetAccount(fromAddr)
if _fromAcc == nil {
return ErrUnknownAccount(fromAddr)
}
_toAcc := cs.GetAccount(to)
if _toAcc == nil {
toAcc = cs.AccountStore.NewAccountWithAddress(to)
}
// Ensure they are Coinser
fromAcc, ok := _fromAcc.(Coinser)
if !ok {
return ErrAccountNotCoinser(from)
}
toAcc, ok = _toAcc.(Coinser)
if !ok {
return ErrAccountNotCoinser(from)
}
// Coin math
fromCoins := fromAcc.GetCoins()
newFromCoins := fromCoins.Minus(amt)
if newFromCoins.Negative() {
return ErrInsufficientCoins(fromCoins, amt)
}
toCoins := toAcc.GetCoins()
newToCoins := toCoins.Plus(amt)
// Set everything!
fromAcc.SetCoins(newFromCoins)
toAcc.SetCoins(newToCoins)
cs.SetAccount(fromAcc)
cs.SetAccount(toAcc)
return nil
}*/

View File

@ -2,14 +2,14 @@ package sendtx
import (
"github.com/cosmos/cosmos-sdk/types"
coinstore "github.com/cosmos/cosmos-sdk/x/coinstore"
"github.com/cosmos/cosmos-sdk/x/store"
)
func TransferHandlerFn(newAccStore func(types.KVStore) types.AccountStore) types.Handler {
func TransferHandlerFn(newAccStore func(types.KVStore) store.AccountStore) types.Handler {
return func(ctx types.Context, ms types.MultiStore, tx types.Tx) types.Result {
accStore := newAccStore(ms.GetKVStore("main"))
cs := coinstore.CoinStore{accStore}
cs := store.CoinStore{accStore}
sendTx, ok := tx.(SendTx)
if !ok {

View File

@ -4,14 +4,16 @@ import (
"encoding/json"
"fmt"
"github.com/cosmos/cosmos-sdk/types"
coinstore "github.com/cosmos/cosmos-sdk/x/coinstore"
crypto "github.com/tendermint/go-crypto"
"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/coin"
"github.com/cosmos/cosmos-sdk/x/store"
)
type (
Address = types.Address
Coins = types.Coins
Address = crypto.Address
Coins = coin.Coins
)
//-----------------------------------------------------------------------------
@ -28,16 +30,16 @@ type TxInput struct {
// ValidateBasic - validate transaction input
func (txIn TxInput) ValidateBasic() error {
if len(txIn.Address) == 0 {
return coinstore.ErrInvalidAddress(txIn.Address.String())
return store.ErrInvalidAddress(txIn.Address.String())
}
if txIn.Sequence < 0 {
return ErrInvalidSequence(txIn.Sequence)
}
if !txIn.Coins.IsValid() {
return coinstore.ErrInvalidCoins(txIn.Coins.String())
return store.ErrInvalidCoins(txIn.Coins.String())
}
if !txIn.Coins.IsPositive() {
return coinstore.ErrInvalidCoins(txIn.Coins.String())
return store.ErrInvalidCoins(txIn.Coins.String())
}
return nil
}
@ -66,13 +68,13 @@ type TxOutput struct {
// ValidateBasic - validate transaction output
func (txOut TxOutput) ValidateBasic() error {
if len(txOut.Address) == 0 {
return coinstore.ErrInvalidAddress(txOut.Address.String())
return store.ErrInvalidAddress(txOut.Address.String())
}
if !txOut.Coins.IsValid() {
return coinstore.ErrInvalidCoins(txOut.Coins.String())
return store.ErrInvalidCoins(txOut.Coins.String())
}
if !txOut.Coins.IsPositive() {
return coinstore.ErrInvalidCoins(txOut.Coins.String())
return store.ErrInvalidCoins(txOut.Coins.String())
}
return nil
}
@ -92,28 +94,14 @@ func NewTxOutput(addr Address, coins Coins) TxOutput {
//-----------------------------------------------------------------------------
var _ types.Tx = (*SendTx)(nil)
// SendTx - high level transaction of the coin module
// Satisfies: TxInner
type SendTx struct {
Inputs []TxInput `json:"inputs"`
Outputs []TxOutput `json:"outputs"`
}
var _ types.Tx = (*SendTx)(nil)
// NewSendTx - construct arbitrary multi-in, multi-out sendtx
func NewSendTx(in []TxInput, out []TxOutput) types.Tx {
return SendTx{Inputs: in, Outputs: out}
}
// NewSendOneTx is a helper for the standard (?) case where there is exactly
// one sender and one recipient
func NewSendOneTx(sender, recipient types.Address, amount types.Coins) types.Tx {
in := []TxInput{{Address: sender, Coins: amount}}
out := []TxOutput{{Address: recipient, Coins: amount}}
return SendTx{Inputs: in, Outputs: out}
}
// ValidateBasic - validate the send transaction
func (tx SendTx) ValidateBasic() error {
// this just makes sure all the inputs and outputs are properly formatted,
@ -140,7 +128,7 @@ func (tx SendTx) ValidateBasic() error {
}
// make sure inputs and outputs match
if !totalIn.IsEqual(totalOut) {
return coinstore.ErrInvalidCoins(totalIn.String()) // TODO
return store.ErrInvalidCoins(totalIn.String()) // TODO
}
return nil
}
@ -149,8 +137,22 @@ func (tx SendTx) String() string {
return fmt.Sprintf("SendTx{%v->%v}", tx.Inputs, tx.Outputs)
}
// TODO
// NewSendTx - construct arbitrary multi-in, multi-out sendtx
func NewSendTx(in []TxInput, out []TxOutput) types.Tx {
return SendTx{Inputs: in, Outputs: out}
}
// NewSendOneTx is a helper for the standard (?) case where there is exactly
// one sender and one recipient
func NewSendOneTx(sender, recipient crypto.Address, amount coin.Coins) types.Tx {
in := []TxInput{{Address: sender, Coins: amount}}
out := []TxOutput{{Address: recipient, Coins: amount}}
return SendTx{Inputs: in, Outputs: out}
}
//------------------------
// Implements types.Tx
func (tx SendTx) Get(key interface{}) (value interface{}) {
switch k := key.(type) {
case string:
@ -170,8 +172,8 @@ func (tx SendTx) SignBytes() []byte {
return b
}
func (tx SendTx) Signers() []types.Address {
addrs := make([]types.Address, len(tx.Inputs))
func (tx SendTx) Signers() []crypto.Address {
addrs := make([]crypto.Address, len(tx.Inputs))
for i, in := range tx.Inputs {
addrs[i] = in.Address
}

34
x/store/account.go Normal file
View File

@ -0,0 +1,34 @@
package store
import (
crypto "github.com/tendermint/go-crypto"
"github.com/cosmos/cosmos-sdk/x/coin"
)
// AccountStore indexes accounts by address.
type AccountStore interface {
NewAccountWithAddress(addr crypto.Address) Account
GetAccount(addr crypto.Address) Account
SetAccount(acc Account)
}
// Account is a standard balance account
// using a sequence number for replay protection
// and a single pubkey for authentication.
// TODO: multisig accounts?
type Account interface {
Address() crypto.Address
GetPubKey() crypto.PubKey
SetPubKey(crypto.PubKey) error
GetCoins() coin.Coins
SetCoins(coin.Coins) error
GetSequence() int64
SetSequence(int64) error
Get(key interface{}) (value interface{}, err error)
Set(key interface{}, value interface{}) error
}

73
x/store/coin.go Normal file
View File

@ -0,0 +1,73 @@
package store
import (
"fmt"
"github.com/cosmos/cosmos-sdk/x/coin"
crypto "github.com/tendermint/go-crypto"
)
type Coins = coin.Coins
// Coinser can get and set coins
type Coinser interface {
GetCoins() Coins
SetCoins(Coins)
}
// CoinStore manages transfers between accounts
type CoinStore struct {
AccountStore
}
// SubtractCoins subtracts amt from the coins at the addr.
func (cs CoinStore) SubtractCoins(addr crypto.Address, amt Coins) (Coins, error) {
acc, err := cs.getCoinserAccount(addr)
if err != nil {
return amt, err
} else if acc == nil {
return amt, fmt.Errorf("Sending account (%s) does not exist", addr)
}
coins := acc.GetCoins()
newCoins := coins.Minus(amt)
if !newCoins.IsNotNegative() {
return amt, ErrInsufficientCoins(fmt.Sprintf("%s < %s", coins, amt))
}
acc.SetCoins(newCoins)
cs.SetAccount(acc.(Account))
return newCoins, nil
}
// AddCoins adds amt to the coins at the addr.
func (cs CoinStore) AddCoins(addr crypto.Address, amt Coins) (Coins, error) {
acc, err := cs.getCoinserAccount(addr)
if err != nil {
return amt, err
} else if acc == nil {
acc = cs.AccountStore.NewAccountWithAddress(addr).(Coinser)
}
coins := acc.GetCoins()
newCoins := coins.Plus(amt)
acc.SetCoins(newCoins)
cs.SetAccount(acc.(Account))
return newCoins, nil
}
// get the account as a Coinser. if the account doesn't exist, return nil.
// if it's not a Coinser, return error.
func (cs CoinStore) getCoinserAccount(addr crypto.Address) (Coinser, error) {
_acc := cs.GetAccount(addr)
if _acc == nil {
return nil, nil
}
acc, ok := _acc.(Coinser)
if !ok {
return nil, fmt.Errorf("Account %s is not a Coinser", addr)
}
return acc, nil
}

View File

@ -1,5 +1,5 @@
//nolint
package coin
package store
import (
"github.com/cosmos/cosmos-sdk/errors"