diff --git a/app/app_test.go b/app/app_test.go index 3936359ab..e2c418dfe 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -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 } diff --git a/examples/basecoin/account.go b/examples/basecoin/account.go deleted file mode 100644 index 7985b3eb1..000000000 --- a/examples/basecoin/account.go +++ /dev/null @@ -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 -} diff --git a/examples/basecoin/account_store.go b/examples/basecoin/account_store.go deleted file mode 100644 index 53078ff6b..000000000 --- a/examples/basecoin/account_store.go +++ /dev/null @@ -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))) -} diff --git a/examples/basecoin/app.go b/examples/basecoin/app.go new file mode 100644 index 000000000..e1181d041 --- /dev/null +++ b/examples/basecoin/app.go @@ -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))) +} diff --git a/examples/basecoin/main.go b/examples/basecoin/main.go index 96e1bd43c..149f4a6dd 100644 --- a/examples/basecoin/main.go +++ b/examples/basecoin/main.go @@ -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 -} diff --git a/examples/dummy/main.go b/examples/dummy/main.go index ea478f066..dfacafa92 100644 --- a/examples/dummy/main.go +++ b/examples/dummy/main.go @@ -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 } diff --git a/types/_gen.go b/types/_gen.go deleted file mode 100644 index a98feaf4e..000000000 --- a/types/_gen.go +++ /dev/null @@ -1,6 +0,0 @@ -package main - -import ( - _ "github.com/tendermint/go-wire/gen" - _ "github.com/clipperhouse/stringer" -) diff --git a/types/account.go b/types/account.go deleted file mode 100644 index f25bc1d48..000000000 --- a/types/account.go +++ /dev/null @@ -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) -} diff --git a/types/tx_msg.go b/types/tx_msg.go index 2d432f845..b6d35d137 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -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 { diff --git a/x/account/account.go b/x/account/account.go new file mode 100644 index 000000000..980945220 --- /dev/null +++ b/x/account/account.go @@ -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 +} diff --git a/x/auth/context.go b/x/auth/context.go index 07e86b7d6..43c182a23 100644 --- a/x/auth/context.go +++ b/x/auth/context.go @@ -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) } diff --git a/x/auth/decorator.go b/x/auth/decorator.go index 38311563f..5bd443368 100644 --- a/x/auth/decorator.go +++ b/x/auth/decorator.go @@ -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) -} diff --git a/x/auth/types.go b/x/auth/types.go index 5f62fa5d3..aaa8018cf 100644 --- a/x/auth/types.go +++ b/x/auth/types.go @@ -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 } diff --git a/x/coinstore/_attic/bench_test.go b/x/coin/_attic/bench_test.go similarity index 100% rename from x/coinstore/_attic/bench_test.go rename to x/coin/_attic/bench_test.go diff --git a/x/coinstore/_attic/commands/query.go b/x/coin/_attic/commands/query.go similarity index 100% rename from x/coinstore/_attic/commands/query.go rename to x/coin/_attic/commands/query.go diff --git a/x/coinstore/_attic/commands/tx.go b/x/coin/_attic/commands/tx.go similarity index 100% rename from x/coinstore/_attic/commands/tx.go rename to x/coin/_attic/commands/tx.go diff --git a/x/coinstore/_attic/genesis.go b/x/coin/_attic/genesis.go similarity index 100% rename from x/coinstore/_attic/genesis.go rename to x/coin/_attic/genesis.go diff --git a/x/coinstore/_attic/handler.go b/x/coin/_attic/handler.go similarity index 100% rename from x/coinstore/_attic/handler.go rename to x/coin/_attic/handler.go diff --git a/x/coinstore/_attic/handler_test.go b/x/coin/_attic/handler_test.go similarity index 100% rename from x/coinstore/_attic/handler_test.go rename to x/coin/_attic/handler_test.go diff --git a/x/coinstore/_attic/helper.go b/x/coin/_attic/helper.go similarity index 100% rename from x/coinstore/_attic/helper.go rename to x/coin/_attic/helper.go diff --git a/x/coinstore/_attic/ibc_test.go b/x/coin/_attic/ibc_test.go similarity index 100% rename from x/coinstore/_attic/ibc_test.go rename to x/coin/_attic/ibc_test.go diff --git a/x/coinstore/_attic/rest/handlers.go b/x/coin/_attic/rest/handlers.go similarity index 100% rename from x/coinstore/_attic/rest/handlers.go rename to x/coin/_attic/rest/handlers.go diff --git a/x/coinstore/_attic/store.go b/x/coin/_attic/store.go similarity index 100% rename from x/coinstore/_attic/store.go rename to x/coin/_attic/store.go diff --git a/types/coin.go b/x/coin/coin.go similarity index 97% rename from types/coin.go rename to x/coin/coin.go index 2c525b26d..99ffc553d 100644 --- a/types/coin.go +++ b/x/coin/coin.go @@ -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 diff --git a/types/coin_test.go b/x/coin/coin_test.go similarity index 99% rename from types/coin_test.go rename to x/coin/coin_test.go index bf651c50d..0cb2b4f21 100644 --- a/types/coin_test.go +++ b/x/coin/coin_test.go @@ -1,4 +1,4 @@ -package types +package coin import ( "testing" diff --git a/x/coinstore/store.go b/x/coinstore/store.go deleted file mode 100644 index 7ee4467c3..000000000 --- a/x/coinstore/store.go +++ /dev/null @@ -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 -}*/ diff --git a/x/sendtx/handler.go b/x/sendtx/handler.go index fcbb66a00..fba7a5a3c 100644 --- a/x/sendtx/handler.go +++ b/x/sendtx/handler.go @@ -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 { diff --git a/x/sendtx/tx.go b/x/sendtx/tx.go index 839eaa3be..664507a0f 100644 --- a/x/sendtx/tx.go +++ b/x/sendtx/tx.go @@ -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 } diff --git a/x/store/account.go b/x/store/account.go new file mode 100644 index 000000000..456530170 --- /dev/null +++ b/x/store/account.go @@ -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 +} diff --git a/x/store/coin.go b/x/store/coin.go new file mode 100644 index 000000000..8626b235d --- /dev/null +++ b/x/store/coin.go @@ -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 +} diff --git a/x/coinstore/errors.go b/x/store/errors.go similarity index 99% rename from x/coinstore/errors.go rename to x/store/errors.go index a8fce42ac..bcdb3d3f0 100644 --- a/x/coinstore/errors.go +++ b/x/store/errors.go @@ -1,5 +1,5 @@ //nolint -package coin +package store import ( "github.com/cosmos/cosmos-sdk/errors"