Coin (#312)
* wip: tests and fixes for kvstore iteration * update for latest tmlibs * types compiles * x/coin almost compiles * x/coin: move things out of the way so it builds * rebase fixes * update glide * add test for ChainDecorators
This commit is contained in:
parent
72b0ed004b
commit
a9b2636439
|
@ -1,15 +1,19 @@
|
|||
package errors
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// ABCI Response Codes
|
||||
CodeInternalError = 1
|
||||
CodeTxParseError = 2
|
||||
CodeBadNonce = 3
|
||||
CodeUnauthorized = 4
|
||||
CodeInsufficientFunds = 5
|
||||
CodeUnknownRequest = 6
|
||||
CodeInternalError uint32 = 1
|
||||
CodeTxParseError = 2
|
||||
CodeBadNonce = 3
|
||||
CodeUnauthorized = 4
|
||||
CodeInsufficientFunds = 5
|
||||
CodeUnknownRequest = 6
|
||||
)
|
||||
|
||||
// NOTE: Don't stringer this, we'll put better messages in later.
|
||||
|
@ -65,6 +69,8 @@ func UnknownRequest(log string) sdkError {
|
|||
type ABCIError interface {
|
||||
ABCICode() uint32
|
||||
ABCILog() string
|
||||
|
||||
Error() string
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -132,3 +138,24 @@ func (err sdkError) WithCause(cause error) sdkError {
|
|||
copy.cause = cause
|
||||
return copy
|
||||
}
|
||||
|
||||
// HasErrorCode checks if this error would return the named error code
|
||||
func HasErrorCode(err error, code uint32) bool {
|
||||
// XXX Get the cause if not ABCIError
|
||||
if abciErr, ok := err.(ABCIError); ok {
|
||||
return abciErr.ABCICode() == code
|
||||
}
|
||||
return code == CodeInternalError
|
||||
}
|
||||
|
||||
func IsSameError(pattern error, err error) bool {
|
||||
return err != nil && (errors.Cause(err) == errors.Cause(pattern))
|
||||
}
|
||||
|
||||
func WithCode(err error, code uint32) sdkError {
|
||||
return sdkError{
|
||||
code: code,
|
||||
cause: err,
|
||||
log: "",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,5 +14,5 @@ type (
|
|||
Decorator = types.Decorator
|
||||
|
||||
// Type aliases for other modules.
|
||||
MultiStore = store.MultiStore modules.
|
||||
MultiStore = store.MultiStore
|
||||
)
|
||||
|
|
|
@ -112,7 +112,7 @@ imports:
|
|||
- leveldb/table
|
||||
- leveldb/util
|
||||
- name: github.com/tendermint/abci
|
||||
version: bb9bb4aa465a31fd6a272765be381888e6898c74
|
||||
version: e4b9f1abe794a2117a59738a1294e09b46d0fa00
|
||||
subpackages:
|
||||
- client
|
||||
- example/dummy
|
||||
|
|
|
@ -2,6 +2,7 @@ package types
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
)
|
||||
|
||||
|
@ -11,12 +12,12 @@ type Context struct {
|
|||
// it's probably not what you want to do.
|
||||
}
|
||||
|
||||
func NewContext(header tm.Header, isCheckTx bool, txBytes []byte) Context {
|
||||
func NewContext(header abci.Header, isCheckTx bool, txBytes []byte) Context {
|
||||
c := Context{
|
||||
Context: context.Background(),
|
||||
}
|
||||
c = c.setBlockHeader(header)
|
||||
c = c.setBlockHeight(int64(header.Height))
|
||||
c = c.setBlockHeight(header.Height)
|
||||
c = c.setChainID(header.ChainID)
|
||||
c = c.setIsCheckTx(isCheckTx)
|
||||
c = c.setTxBytes(txBytes)
|
||||
|
@ -48,8 +49,8 @@ const (
|
|||
contextKeyTxBytes
|
||||
)
|
||||
|
||||
func (c Context) BlockHeader() tm.Header {
|
||||
return c.Value(contextKeyBlockHeader).(tm.Header)
|
||||
func (c Context) BlockHeader() abci.Header {
|
||||
return c.Value(contextKeyBlockHeader).(abci.Header)
|
||||
}
|
||||
|
||||
func (c Context) BlockHeight() int64 {
|
||||
|
@ -69,18 +70,18 @@ func (c Context) TxBytes() []byte {
|
|||
}
|
||||
|
||||
// Unexposed to prevent overriding.
|
||||
func (c Context) setBlockHeader(header tm.Header) Context {
|
||||
func (c Context) setBlockHeader(header abci.Header) Context {
|
||||
return c.WithValueSDK(contextKeyBlockHeader, header)
|
||||
}
|
||||
|
||||
// Unexposed to prevent overriding.
|
||||
func (c Context) setBlockHeight(height int64) Context {
|
||||
return c.WithValueSDK(contextKeyBlockHeight, header)
|
||||
return c.WithValueSDK(contextKeyBlockHeight, height)
|
||||
}
|
||||
|
||||
// Unexposed to prevent overriding.
|
||||
func (c Context) setChainID(chainID string) Context {
|
||||
return c.WithValueSDK(contextKeyChainID, header)
|
||||
return c.WithValueSDK(contextKeyChainID, chainID)
|
||||
}
|
||||
|
||||
// Unexposed to prevent overriding.
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
)
|
||||
|
||||
// A Decorator executes before/during/after a handler to enhance functionality.
|
||||
type Decorator func(ctx Context, ms MultiStore, tx Tx, next Handler) Result
|
||||
type Decorator func(ctx Context, ms store.MultiStore, tx Tx, next Handler) Result
|
||||
|
||||
// Return a decorated handler
|
||||
func Decorate(dec Decorator, next Handler) Handler {
|
||||
return func(ctx Context, ms MultiStore, tx Tx) Result {
|
||||
return func(ctx Context, ms store.MultiStore, tx Tx) Result {
|
||||
return dec(ctx, ms, tx, next)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDecorate(t *testing.T) {
|
||||
|
||||
var calledDec1, calledDec2, calledHandler bool
|
||||
dec1 := func(ctx Context, ms store.MultiStore, tx Tx, next Handler) Result {
|
||||
calledDec1 = true
|
||||
next(ctx, ms, tx)
|
||||
return Result{}
|
||||
}
|
||||
|
||||
dec2 := func(ctx Context, ms store.MultiStore, tx Tx, next Handler) Result {
|
||||
calledDec2 = true
|
||||
next(ctx, ms, tx)
|
||||
return Result{}
|
||||
}
|
||||
|
||||
handler := func(ctx Context, ms store.MultiStore, tx Tx) Result {
|
||||
calledHandler = true
|
||||
return Result{}
|
||||
}
|
||||
|
||||
decoratedHandler := ChainDecorators(dec1, dec2).WithHandler(handler)
|
||||
|
||||
var ctx Context
|
||||
var ms store.MultiStore
|
||||
var tx Tx
|
||||
decoratedHandler(ctx, ms, tx)
|
||||
assert.True(t, calledDec1)
|
||||
assert.True(t, calledDec2)
|
||||
assert.True(t, calledHandler)
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
)
|
||||
|
||||
// Handler handles both ABCI DeliverTx and CheckTx requests.
|
||||
// Iff ABCI.CheckTx, ctx.IsCheckTx() returns true.
|
||||
type Handler func(ctx Context, ms MultiStore, tx Tx)
|
||||
type Handler func(ctx Context, ms store.MultiStore, tx Tx) Result
|
||||
|
|
|
@ -2,22 +2,7 @@ package types
|
|||
|
||||
import crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
type Signature interface {
|
||||
CryptoSig() crypto.Signature
|
||||
Sequence() int
|
||||
}
|
||||
|
||||
// StdSignature is a simple way to prevent replay attacks.
|
||||
// There must be better strategies, but this is simplest.
|
||||
type StdSignature struct {
|
||||
crypto.Signature
|
||||
Sequence int
|
||||
}
|
||||
|
||||
func (ss StdSignature) CryptoSig() crypto.Signature {
|
||||
return ss.Signature
|
||||
}
|
||||
|
||||
func (ss StdSignature) Sequence() int {
|
||||
return ss.Sequence
|
||||
Sequence int64
|
||||
}
|
||||
|
|
|
@ -32,5 +32,5 @@ type Tx interface {
|
|||
// CONTRACT: If the signature is missing (ie the Msg is
|
||||
// invalid), then the corresponding signature is
|
||||
// .Empty().
|
||||
Signatures() []Signature
|
||||
Signatures() []StdSignature
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -13,7 +13,7 @@ import (
|
|||
var acc accounts.Account
|
||||
|
||||
accounts.SetAccount(ctx, acc)
|
||||
acc2, ok := accounts.GetAccount(ctx)
|
||||
acc2 := accounts.GetAccount(ctx)
|
||||
|
||||
*/
|
||||
|
||||
|
@ -24,10 +24,10 @@ const (
|
|||
contextKeyAccount contextKey = iota
|
||||
)
|
||||
|
||||
func SetAccount(ctx sdk.Context, account Account) sdk.Context {
|
||||
return ctx.WithValue(contextKeyAccount, account)
|
||||
func SetAccount(ctx types.Context, account Account) types.Context {
|
||||
return ctx.WithValueSDK(contextKeyAccount, account)
|
||||
}
|
||||
|
||||
func GetAccount(ctx sdk.Context) (Account, bool) {
|
||||
func GetAccount(ctx types.Context) Account {
|
||||
return ctx.Value(contextKeyAccount).(Account)
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
)
|
||||
|
||||
type CheckSignatures struct{}
|
||||
|
||||
var _ sdk.Decorator = CheckSignatures{}
|
||||
|
||||
// CheckTx verifies the signatures are correct - fulfills Middlware interface
|
||||
func (Signatures) CheckTx(ctx sdk.Context, store sdk.SimpleDB,
|
||||
tx interface{}, next sdk.Checker) (res sdk.CheckResult, err error) {
|
||||
|
||||
// Check that signatures match
|
||||
// TODO
|
||||
|
||||
// Add info to context
|
||||
// TODO
|
||||
|
||||
return next.CheckTx(ctx2, store, tx)
|
||||
}
|
||||
|
||||
// DeliverTx verifies the signatures are correct - fulfills Middlware interface
|
||||
func (Signatures) DeliverTx(ctx sdk.Context, store sdk.SimpleDB,
|
||||
tx interface{}, next sdk.Deliverer) (res sdk.DeliverResult, err error) {
|
||||
|
||||
// Check that signatures match
|
||||
// TODO
|
||||
|
||||
// Add info to context
|
||||
// TODO
|
||||
|
||||
return next.DeliverTx(ctx2, store, tx)
|
||||
}
|
|
@ -6,10 +6,11 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/errors"
|
||||
"github.com/cosmos/cosmos-sdk/modules/auth"
|
||||
"github.com/cosmos/cosmos-sdk/modules/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/stack"
|
||||
"github.com/cosmos/cosmos-sdk/state"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
// "github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
// "github.com/cosmos/cosmos-sdk/stack"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -23,10 +24,10 @@ const (
|
|||
|
||||
// Handler includes an accountant
|
||||
type Handler struct {
|
||||
stack.PassInitValidate
|
||||
// stack.PassInitValidate
|
||||
}
|
||||
|
||||
var _ stack.Dispatchable = Handler{}
|
||||
// var _ stack.Dispatchable = Handler{}
|
||||
|
||||
// NewHandler - new accountant handler for the coin module
|
||||
func NewHandler() Handler {
|
||||
|
@ -42,8 +43,8 @@ func (Handler) Name() string {
|
|||
func (Handler) AssertDispatcher() {}
|
||||
|
||||
// CheckTx checks if there is enough money in the account
|
||||
func (h Handler) CheckTx(ctx sdk.Context, store state.SimpleDB,
|
||||
tx sdk.Tx, _ sdk.Checker) (res sdk.CheckResult, err error) {
|
||||
func (h Handler) CheckTx(ctx types.Context, store store.MultiStore,
|
||||
tx types.Tx, _ sdk.Checker) (res sdk.CheckResult, err error) {
|
||||
|
||||
err = tx.ValidateBasic()
|
||||
if err != nil {
|
||||
|
@ -63,8 +64,8 @@ func (h Handler) CheckTx(ctx sdk.Context, store state.SimpleDB,
|
|||
}
|
||||
|
||||
// DeliverTx moves the money
|
||||
func (h Handler) DeliverTx(ctx sdk.Context, store state.SimpleDB,
|
||||
tx sdk.Tx, cb sdk.Deliver) (res sdk.DeliverResult, err error) {
|
||||
func (h Handler) DeliverTx(ctx types.Context, store store.MultiStore,
|
||||
tx types.Tx, cb sdk.Deliver) (res sdk.DeliverResult, err error) {
|
||||
|
||||
err = tx.ValidateBasic()
|
||||
if err != nil {
|
||||
|
@ -81,7 +82,7 @@ func (h Handler) DeliverTx(ctx sdk.Context, store state.SimpleDB,
|
|||
}
|
||||
|
||||
// InitState - sets the genesis account balance
|
||||
func (h Handler) InitState(l log.Logger, store state.SimpleDB,
|
||||
func (h Handler) InitState(l log.Logger, store store.MultiStore,
|
||||
module, key, value string, cb sdk.InitStater) (log string, err error) {
|
||||
if module != NameCoin {
|
||||
return "", errors.ErrUnknownModule(module)
|
||||
|
@ -95,7 +96,7 @@ func (h Handler) InitState(l log.Logger, store state.SimpleDB,
|
|||
return "", errors.ErrUnknownKey(key)
|
||||
}
|
||||
|
||||
func (h Handler) sendTx(ctx sdk.Context, store state.SimpleDB,
|
||||
func (h Handler) sendTx(ctx types.Context, store store.MultiStore,
|
||||
send SendTx, cb sdk.Deliver) error {
|
||||
|
||||
err := checkTx(ctx, send)
|
||||
|
@ -136,6 +137,8 @@ func (h Handler) sendTx(ctx sdk.Context, store state.SimpleDB,
|
|||
}
|
||||
|
||||
outTx := NewSendTx(inputs, []TxOutput{out})
|
||||
_ = outTx
|
||||
/* TODO
|
||||
packet := ibc.CreatePacketTx{
|
||||
DestChain: out.Address.ChainID,
|
||||
Permissions: senders,
|
||||
|
@ -146,6 +149,7 @@ func (h Handler) sendTx(ctx sdk.Context, store state.SimpleDB,
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,7 +157,7 @@ func (h Handler) sendTx(ctx sdk.Context, store state.SimpleDB,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h Handler) creditTx(ctx sdk.Context, store state.SimpleDB,
|
||||
func (h Handler) creditTx(ctx types.Context, store store.MultiStore,
|
||||
credit CreditTx) error {
|
||||
|
||||
// first check permissions!!
|
||||
|
@ -186,7 +190,7 @@ func (h Handler) creditTx(ctx sdk.Context, store state.SimpleDB,
|
|||
return err
|
||||
}
|
||||
|
||||
func checkTx(ctx sdk.Context, send SendTx) error {
|
||||
func checkTx(ctx types.Context, send SendTx) error {
|
||||
// check if all inputs have permission
|
||||
for _, in := range send.Inputs {
|
||||
if !ctx.HasPermission(in.Address) {
|
||||
|
@ -196,7 +200,7 @@ func checkTx(ctx sdk.Context, send SendTx) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (Handler) checkSendTx(ctx sdk.Context, store state.SimpleDB, send SendTx) error {
|
||||
func (Handler) checkSendTx(ctx types.Context, store store.MultiStore, send SendTx) error {
|
||||
err := checkTx(ctx, send)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -211,7 +215,7 @@ func (Handler) checkSendTx(ctx sdk.Context, store state.SimpleDB, send SendTx) e
|
|||
return nil
|
||||
}
|
||||
|
||||
func setAccount(store state.SimpleDB, value string) (log string, err error) {
|
||||
func setAccount(store store.MultiStore, value string) (log string, err error) {
|
||||
var acc GenesisAccount
|
||||
err = data.FromJSON([]byte(value), &acc)
|
||||
if err != nil {
|
||||
|
@ -233,7 +237,7 @@ func setAccount(store state.SimpleDB, value string) (log string, err error) {
|
|||
|
||||
// setIssuer sets a permission for some super-powerful account to
|
||||
// mint money
|
||||
func setIssuer(store state.SimpleDB, value string) (log string, err error) {
|
||||
func setIssuer(store store.MultiStore, value string) (log string, err error) {
|
||||
var issuer sdk.Actor
|
||||
err = data.FromJSON([]byte(value), &issuer)
|
||||
if err != nil {
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/errors"
|
||||
"github.com/cosmos/cosmos-sdk/modules/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/stack"
|
||||
"github.com/cosmos/cosmos-sdk/state"
|
||||
)
|
|
@ -5,7 +5,7 @@ import (
|
|||
"github.com/tendermint/go-wire/data"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/modules/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
)
|
||||
|
||||
// AccountWithKey is a helper for tests, that includes and account
|
|
@ -7,8 +7,8 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/errors"
|
||||
"github.com/cosmos/cosmos-sdk/modules/auth"
|
||||
"github.com/cosmos/cosmos-sdk/modules/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/stack"
|
||||
"github.com/cosmos/cosmos-sdk/state"
|
||||
wire "github.com/tendermint/go-wire"
|
|
@ -0,0 +1,145 @@
|
|||
package coin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
wire "github.com/tendermint/go-wire"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/errors"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
)
|
||||
|
||||
// GetAccount - Get account from store and address
|
||||
func GetAccount(store store.MultiStore, addr sdk.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())
|
||||
|
||||
// for empty accounts, don't return an error, but rather an empty account
|
||||
if IsNoAccountErr(err) {
|
||||
err = nil
|
||||
}
|
||||
return acct, err
|
||||
}
|
||||
|
||||
// CheckCoins makes sure there are funds, but doesn't change anything
|
||||
func CheckCoins(store store.MultiStore, addr sdk.Actor, coins Coins) (Coins, error) {
|
||||
// if the actor is another chain, we use one address for the chain....
|
||||
addr = ChainAddr(addr)
|
||||
|
||||
acct, err := updateCoins(store, addr, coins)
|
||||
return acct.Coins, err
|
||||
}
|
||||
|
||||
// ChangeCoins changes the money, returns error if it would be negative
|
||||
func ChangeCoins(store store.MultiStore, addr sdk.Actor, coins Coins) (Coins, error) {
|
||||
// if the actor is another chain, we use one address for the chain....
|
||||
addr = ChainAddr(addr)
|
||||
|
||||
acct, err := updateCoins(store, addr, coins)
|
||||
if err != nil {
|
||||
return acct.Coins, err
|
||||
}
|
||||
|
||||
err = storeAccount(store, addr.Bytes(), acct)
|
||||
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 sdk.Actor) sdk.Actor {
|
||||
if addr.ChainID == "" {
|
||||
return addr
|
||||
}
|
||||
addr.App = ""
|
||||
addr.Address = nil
|
||||
return addr
|
||||
}
|
||||
|
||||
// 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 store.MultiStore, addr sdk.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
|
||||
}
|
||||
if err != nil {
|
||||
return acct, err
|
||||
}
|
||||
|
||||
// check amount
|
||||
final := acct.Coins.Plus(coins)
|
||||
if !final.IsNonnegative() {
|
||||
return acct, ErrInsufficientFunds()
|
||||
}
|
||||
|
||||
acct.Coins = final
|
||||
return acct, nil
|
||||
}
|
||||
|
||||
// Account - coin account structure
|
||||
type Account struct {
|
||||
// Coins is how much is on the account
|
||||
Coins Coins `json:"coins"`
|
||||
// Credit is how much has been "fronted" to the account
|
||||
// (this is usually 0 except for trusted chains)
|
||||
Credit Coins `json:"credit"`
|
||||
}
|
||||
|
||||
func loadAccount(store store.MultiStore, key []byte) (acct Account, err error) {
|
||||
// fmt.Printf("load: %X\n", key)
|
||||
data := store.Get(key)
|
||||
if len(data) == 0 {
|
||||
return acct, ErrNoAccount()
|
||||
}
|
||||
err = wire.ReadBinaryBytes(data, &acct)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Error reading account %X", key)
|
||||
return acct, errors.ErrInternal(msg)
|
||||
}
|
||||
return acct, nil
|
||||
}
|
||||
|
||||
func storeAccount(store store.MultiStore, key []byte, acct Account) error {
|
||||
// fmt.Printf("store: %X\n", key)
|
||||
bin := wire.BinaryBytes(acct)
|
||||
store.Set(key, bin)
|
||||
return nil // real stores can return error...
|
||||
}
|
||||
|
||||
// HandlerInfo - this is global info on the coin handler
|
||||
type HandlerInfo struct {
|
||||
Issuer sdk.Actor `json:"issuer"`
|
||||
}
|
||||
|
||||
// TODO: where to store these special pieces??
|
||||
var handlerKey = []byte{12, 34}
|
||||
|
||||
func loadHandlerInfo(store store.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 store.KVStore, issuer sdk.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...
|
||||
}
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/commands"
|
||||
"github.com/cosmos/cosmos-sdk/client/commands/query"
|
||||
"github.com/cosmos/cosmos-sdk/modules/coin"
|
||||
"github.com/cosmos/cosmos-sdk/x/coin"
|
||||
"github.com/cosmos/cosmos-sdk/stack"
|
||||
)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/client/commands"
|
||||
txcmd "github.com/cosmos/cosmos-sdk/client/commands/txs"
|
||||
"github.com/cosmos/cosmos-sdk/modules/coin"
|
||||
"github.com/cosmos/cosmos-sdk/x/coin"
|
||||
)
|
||||
|
||||
// SendTxCmd is CLI command to send tokens between basecoin accounts
|
||||
|
|
|
@ -4,8 +4,6 @@ package coin
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/errors"
|
||||
)
|
||||
|
||||
|
@ -17,73 +15,39 @@ var (
|
|||
errNoOutputs = fmt.Errorf("No output coins")
|
||||
errInvalidAddress = fmt.Errorf("Invalid address")
|
||||
errInvalidCoins = fmt.Errorf("Invalid coins")
|
||||
|
||||
invalidInput = abci.CodeType_BaseInvalidInput
|
||||
invalidOutput = abci.CodeType_BaseInvalidOutput
|
||||
unknownAddress = abci.CodeType_BaseUnknownAddress
|
||||
unknownRequest = abci.CodeType_UnknownRequest
|
||||
)
|
||||
|
||||
// here are some generic handlers to grab classes of errors based on code
|
||||
func IsInputErr(err error) bool {
|
||||
return errors.HasErrorCode(err, invalidInput)
|
||||
}
|
||||
func IsOutputErr(err error) bool {
|
||||
return errors.HasErrorCode(err, invalidOutput)
|
||||
}
|
||||
func IsAddressErr(err error) bool {
|
||||
return errors.HasErrorCode(err, unknownAddress)
|
||||
}
|
||||
func IsCoinErr(err error) bool {
|
||||
return err != nil && (IsInputErr(err) || IsOutputErr(err) || IsAddressErr(err))
|
||||
const (
|
||||
CodeInvalidInput uint32 = 101
|
||||
CodeInvalidOutput uint32 = 102
|
||||
CodeUnknownAddress uint32 = 103
|
||||
CodeUnknownRequest uint32 = errors.CodeUnknownRequest
|
||||
)
|
||||
|
||||
func ErrNoAccount() errors.ABCIError {
|
||||
return errors.WithCode(errNoAccount, CodeUnknownAddress)
|
||||
}
|
||||
|
||||
func ErrNoAccount() errors.TMError {
|
||||
return errors.WithCode(errNoAccount, unknownAddress)
|
||||
func ErrInvalidAddress() errors.ABCIError {
|
||||
return errors.WithCode(errInvalidAddress, CodeInvalidInput)
|
||||
}
|
||||
|
||||
func IsNoAccountErr(err error) bool {
|
||||
return errors.IsSameError(errNoAccount, err)
|
||||
func ErrInvalidCoins() errors.ABCIError {
|
||||
return errors.WithCode(errInvalidCoins, CodeInvalidInput)
|
||||
}
|
||||
|
||||
func ErrInvalidAddress() errors.TMError {
|
||||
return errors.WithCode(errInvalidAddress, invalidInput)
|
||||
}
|
||||
func IsInvalidAddressErr(err error) bool {
|
||||
return errors.IsSameError(errInvalidAddress, err)
|
||||
func ErrInsufficientFunds() errors.ABCIError {
|
||||
return errors.WithCode(errInsufficientFunds, CodeInvalidInput)
|
||||
}
|
||||
|
||||
func ErrInvalidCoins() errors.TMError {
|
||||
return errors.WithCode(errInvalidCoins, invalidInput)
|
||||
}
|
||||
func IsInvalidCoinsErr(err error) bool {
|
||||
return errors.IsSameError(errInvalidCoins, err)
|
||||
func ErrInsufficientCredit() errors.ABCIError {
|
||||
return errors.WithCode(errInsufficientCredit, CodeInvalidInput)
|
||||
}
|
||||
|
||||
func ErrInsufficientFunds() errors.TMError {
|
||||
return errors.WithCode(errInsufficientFunds, invalidInput)
|
||||
}
|
||||
func IsInsufficientFundsErr(err error) bool {
|
||||
return errors.IsSameError(errInsufficientFunds, err)
|
||||
func ErrNoInputs() errors.ABCIError {
|
||||
return errors.WithCode(errNoInputs, CodeInvalidInput)
|
||||
}
|
||||
|
||||
func ErrInsufficientCredit() errors.TMError {
|
||||
return errors.WithCode(errInsufficientCredit, invalidInput)
|
||||
}
|
||||
func IsInsufficientCreditErr(err error) bool {
|
||||
return errors.IsSameError(errInsufficientCredit, err)
|
||||
}
|
||||
|
||||
func ErrNoInputs() errors.TMError {
|
||||
return errors.WithCode(errNoInputs, invalidInput)
|
||||
}
|
||||
func IsNoInputsErr(err error) bool {
|
||||
return errors.IsSameError(errNoInputs, err)
|
||||
}
|
||||
|
||||
func ErrNoOutputs() errors.TMError {
|
||||
return errors.WithCode(errNoOutputs, invalidOutput)
|
||||
}
|
||||
func IsNoOutputsErr(err error) bool {
|
||||
return errors.IsSameError(errNoOutputs, err)
|
||||
func ErrNoOutputs() errors.ABCIError {
|
||||
return errors.WithCode(errNoOutputs, CodeInvalidOutput)
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/commands"
|
||||
"github.com/cosmos/cosmos-sdk/client/commands/query"
|
||||
"github.com/cosmos/cosmos-sdk/modules/auth"
|
||||
"github.com/cosmos/cosmos-sdk/modules/base"
|
||||
"github.com/cosmos/cosmos-sdk/modules/coin"
|
||||
"github.com/cosmos/cosmos-sdk/modules/fee"
|
||||
"github.com/cosmos/cosmos-sdk/modules/nonce"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/base"
|
||||
"github.com/cosmos/cosmos-sdk/x/coin"
|
||||
"github.com/cosmos/cosmos-sdk/x/fee"
|
||||
"github.com/cosmos/cosmos-sdk/x/nonce"
|
||||
"github.com/cosmos/cosmos-sdk/stack"
|
||||
"github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
|
137
x/coin/store.go
137
x/coin/store.go
|
@ -1,85 +1,13 @@
|
|||
package coin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
import "github.com/tendermint/go-wire/data"
|
||||
|
||||
wire "github.com/tendermint/go-wire"
|
||||
// TEMP
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/errors"
|
||||
"github.com/cosmos/cosmos-sdk/state"
|
||||
)
|
||||
|
||||
// GetAccount - Get account from store and address
|
||||
func GetAccount(store state.SimpleDB, addr sdk.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())
|
||||
|
||||
// for empty accounts, don't return an error, but rather an empty account
|
||||
if IsNoAccountErr(err) {
|
||||
err = nil
|
||||
}
|
||||
return acct, err
|
||||
}
|
||||
|
||||
// CheckCoins makes sure there are funds, but doesn't change anything
|
||||
func CheckCoins(store state.SimpleDB, addr sdk.Actor, coins Coins) (Coins, error) {
|
||||
// if the actor is another chain, we use one address for the chain....
|
||||
addr = ChainAddr(addr)
|
||||
|
||||
acct, err := updateCoins(store, addr, coins)
|
||||
return acct.Coins, err
|
||||
}
|
||||
|
||||
// ChangeCoins changes the money, returns error if it would be negative
|
||||
func ChangeCoins(store state.SimpleDB, addr sdk.Actor, coins Coins) (Coins, error) {
|
||||
// if the actor is another chain, we use one address for the chain....
|
||||
addr = ChainAddr(addr)
|
||||
|
||||
acct, err := updateCoins(store, addr, coins)
|
||||
if err != nil {
|
||||
return acct.Coins, err
|
||||
}
|
||||
|
||||
err = storeAccount(store, addr.Bytes(), acct)
|
||||
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 sdk.Actor) sdk.Actor {
|
||||
if addr.ChainID == "" {
|
||||
return addr
|
||||
}
|
||||
addr.App = ""
|
||||
addr.Address = nil
|
||||
return addr
|
||||
}
|
||||
|
||||
// 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 sdk.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
|
||||
}
|
||||
if err != nil {
|
||||
return acct, err
|
||||
}
|
||||
|
||||
// check amount
|
||||
final := acct.Coins.Plus(coins)
|
||||
if !final.IsNonnegative() {
|
||||
return acct, ErrInsufficientFunds()
|
||||
}
|
||||
|
||||
acct.Coins = final
|
||||
return acct, nil
|
||||
type Actor struct {
|
||||
ChainID string
|
||||
App string
|
||||
Address data.Bytes
|
||||
}
|
||||
|
||||
// Account - coin account structure
|
||||
|
@ -90,56 +18,3 @@ type Account struct {
|
|||
// (this is usually 0 except for trusted chains)
|
||||
Credit Coins `json:"credit"`
|
||||
}
|
||||
|
||||
func loadAccount(store state.SimpleDB, key []byte) (acct Account, err error) {
|
||||
// fmt.Printf("load: %X\n", key)
|
||||
data := store.Get(key)
|
||||
if len(data) == 0 {
|
||||
return acct, ErrNoAccount()
|
||||
}
|
||||
err = wire.ReadBinaryBytes(data, &acct)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Error reading account %X", key)
|
||||
return acct, errors.ErrInternal(msg)
|
||||
}
|
||||
return acct, nil
|
||||
}
|
||||
|
||||
func storeAccount(store state.SimpleDB, key []byte, acct Account) error {
|
||||
// fmt.Printf("store: %X\n", key)
|
||||
bin := wire.BinaryBytes(acct)
|
||||
store.Set(key, bin)
|
||||
return nil // real stores can return error...
|
||||
}
|
||||
|
||||
// HandlerInfo - this is global info on the coin handler
|
||||
type HandlerInfo struct {
|
||||
Issuer sdk.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 sdk.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...
|
||||
}
|
||||
|
|
46
x/coin/tx.go
46
x/coin/tx.go
|
@ -2,18 +2,18 @@ package coin
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
)
|
||||
|
||||
func init() {
|
||||
/*func init() {
|
||||
sdk.TxMapper.
|
||||
RegisterImplementation(SendTx{}, TypeSend, ByteSend).
|
||||
RegisterImplementation(CreditTx{}, TypeCredit, ByteCredit)
|
||||
}
|
||||
}*/
|
||||
|
||||
// we reserve the 0x20-0x3f range for standard modules
|
||||
const (
|
||||
NameCoin = "coin"
|
||||
|
||||
ByteSend = 0x20
|
||||
TypeSend = NameCoin + "/send"
|
||||
ByteCredit = 0x21
|
||||
|
@ -24,8 +24,8 @@ const (
|
|||
|
||||
// TxInput - expected coin movement outputs, used with SendTx
|
||||
type TxInput struct {
|
||||
Address sdk.Actor `json:"address"`
|
||||
Coins Coins `json:"coins"`
|
||||
Address Actor `json:"address"`
|
||||
Coins Coins `json:"coins"`
|
||||
}
|
||||
|
||||
// ValidateBasic - validate transaction input
|
||||
|
@ -51,7 +51,7 @@ func (txIn TxInput) String() string {
|
|||
}
|
||||
|
||||
// NewTxInput - create a transaction input, used with SendTx
|
||||
func NewTxInput(addr sdk.Actor, coins Coins) TxInput {
|
||||
func NewTxInput(addr Actor, coins Coins) TxInput {
|
||||
input := TxInput{
|
||||
Address: addr,
|
||||
Coins: coins,
|
||||
|
@ -63,8 +63,8 @@ func NewTxInput(addr sdk.Actor, coins Coins) TxInput {
|
|||
|
||||
// TxOutput - expected coin movement output, used with SendTx
|
||||
type TxOutput struct {
|
||||
Address sdk.Actor `json:"address"`
|
||||
Coins Coins `json:"coins"`
|
||||
Address Actor `json:"address"`
|
||||
Coins Coins `json:"coins"`
|
||||
}
|
||||
|
||||
// ValidateBasic - validate transaction output
|
||||
|
@ -90,7 +90,7 @@ func (txOut TxOutput) String() string {
|
|||
}
|
||||
|
||||
// NewTxOutput - create a transaction output, used with SendTx
|
||||
func NewTxOutput(addr sdk.Actor, coins Coins) TxOutput {
|
||||
func NewTxOutput(addr Actor, coins Coins) TxOutput {
|
||||
output := TxOutput{
|
||||
Address: addr,
|
||||
Coins: coins,
|
||||
|
@ -107,19 +107,19 @@ type SendTx struct {
|
|||
Outputs []TxOutput `json:"outputs"`
|
||||
}
|
||||
|
||||
var _ sdk.Tx = NewSendTx(nil, nil)
|
||||
// var _ types.Tx = NewSendTx(nil, nil)
|
||||
|
||||
// NewSendTx - construct arbitrary multi-in, multi-out sendtx
|
||||
func NewSendTx(in []TxInput, out []TxOutput) sdk.Tx {
|
||||
return SendTx{Inputs: in, Outputs: out}.Wrap()
|
||||
func NewSendTx(in []TxInput, out []TxOutput) SendTx { // 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 sdk.Actor, amount Coins) sdk.Tx {
|
||||
func NewSendOneTx(sender, recipient Actor, amount Coins) SendTx {
|
||||
in := []TxInput{{Address: sender, Coins: amount}}
|
||||
out := []TxOutput{{Address: recipient, Coins: amount}}
|
||||
return SendTx{Inputs: in, Outputs: out}.Wrap()
|
||||
return SendTx{Inputs: in, Outputs: out}
|
||||
}
|
||||
|
||||
// ValidateBasic - validate the send transaction
|
||||
|
@ -157,17 +157,12 @@ func (tx SendTx) String() string {
|
|||
return fmt.Sprintf("SendTx{%v->%v}", tx.Inputs, tx.Outputs)
|
||||
}
|
||||
|
||||
// Wrap - used to satisfy TxInner
|
||||
func (tx SendTx) Wrap() sdk.Tx {
|
||||
return sdk.Tx{tx}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// CreditTx - this allows a special issuer to give an account credit
|
||||
// Satisfies: TxInner
|
||||
type CreditTx struct {
|
||||
Debitor sdk.Actor `json:"debitor"`
|
||||
Debitor Actor `json:"debitor"`
|
||||
// Credit is the amount to change the credit...
|
||||
// This may be negative to remove some over-issued credit,
|
||||
// but can never bring the credit or the balance to negative
|
||||
|
@ -175,13 +170,8 @@ type CreditTx struct {
|
|||
}
|
||||
|
||||
// NewCreditTx - modify the credit granted to a given account
|
||||
func NewCreditTx(debitor sdk.Actor, credit Coins) sdk.Tx {
|
||||
return CreditTx{Debitor: debitor, Credit: credit}.Wrap()
|
||||
}
|
||||
|
||||
// Wrap - used to satisfy TxInner
|
||||
func (tx CreditTx) Wrap() sdk.Tx {
|
||||
return sdk.Tx{tx}
|
||||
func NewCreditTx(debitor Actor, credit Coins) CreditTx {
|
||||
return CreditTx{Debitor: debitor, Credit: credit}
|
||||
}
|
||||
|
||||
// ValidateBasic - used to satisfy TxInner
|
||||
|
|
Loading…
Reference in New Issue