diff --git a/errors/common.go b/errors/common.go index 06e12d3ee..3ea5a409e 100644 --- a/errors/common.go +++ b/errors/common.go @@ -7,8 +7,13 @@ package errors import abci "github.com/tendermint/abci/types" const ( - msgDecoding = "Error decoding input" - msgUnauthorized = "Unauthorized" + msgDecoding = "Error decoding input" + msgUnauthorized = "Unauthorized" + msgInvalidAddress = "Invalid Address" + msgInvalidCoins = "Invalid Coins" + msgInvalidSequence = "Invalid Sequence" + msgNoInputs = "No Input Coins" + msgNoOutputs = "No Output Coins" ) func DecodingError() TMError { @@ -18,3 +23,23 @@ func DecodingError() TMError { func Unauthorized() TMError { return New(msgUnauthorized, abci.CodeType_Unauthorized) } + +func InvalidAddress() TMError { + return New(msgInvalidAddress, abci.CodeType_BaseInvalidInput) +} + +func InvalidCoins() TMError { + return New(msgInvalidCoins, abci.CodeType_BaseInvalidInput) +} + +func InvalidSequence() TMError { + return New(msgInvalidSequence, abci.CodeType_BaseInvalidInput) +} + +func NoInputs() TMError { + return New(msgNoInputs, abci.CodeType_BaseInvalidInput) +} + +func NoOutputs() TMError { + return New(msgNoOutputs, abci.CodeType_BaseInvalidOutput) +} diff --git a/txs/base.go b/txs/base.go index 5fe283b25..f67f09db1 100644 --- a/txs/base.go +++ b/txs/base.go @@ -11,6 +11,7 @@ const ( ByteRaw = 0x1 ByteFees = 0x2 ByteMulti = 0x3 + ByteChain = 0x4 // for signatures ByteSig = 0x16 @@ -22,6 +23,7 @@ const ( TypeRaw = "raw" TypeFees = "fee" TypeMulti = "multi" + TypeChain = "chain" // for signatures TypeSig = "sig" @@ -32,7 +34,8 @@ func init() { basecoin.TxMapper. RegisterImplementation(Raw{}, TypeRaw, ByteRaw). RegisterImplementation(&Fee{}, TypeFees, ByteFees). - RegisterImplementation(&MultiTx{}, TypeMulti, ByteMulti) + RegisterImplementation(&MultiTx{}, TypeMulti, ByteMulti). + RegisterImplementation(&Chain{}, TypeChain, ByteChain) } // Raw just contains bytes that can be hex-ified @@ -78,3 +81,19 @@ func NewMultiTx(txs ...basecoin.Tx) *MultiTx { func (mt *MultiTx) Wrap() basecoin.Tx { return basecoin.Tx{mt} } + +/*** Chain ****/ + +// Chain locks this tx to one chain, wrap with this before signing +type Chain struct { + Tx basecoin.Tx `json:"tx"` + ChainID string `json:"chain_id"` +} + +func NewChain(tx basecoin.Tx, chainID string) *Chain { + return &Chain{Tx: tx, ChainID: chainID} +} + +func (c *Chain) Wrap() basecoin.Tx { + return basecoin.Tx{c} +} diff --git a/txs/send.go b/txs/send.go new file mode 100644 index 000000000..cffcd47f1 --- /dev/null +++ b/txs/send.go @@ -0,0 +1,119 @@ +package txs + +import ( + "fmt" + + "github.com/tendermint/basecoin" + "github.com/tendermint/go-wire/data" + + "github.com/tendermint/basecoin/errors" + "github.com/tendermint/basecoin/types" +) + +//----------------------------------------------------------------------------- + +type TxInput struct { + Address data.Bytes `json:"address"` + Coins types.Coins `json:"coins"` + Sequence int `json:"sequence"` // Nonce: Must be 1 greater than the last committed TxInput +} + +func (txIn TxInput) ValidateBasic() error { + if len(txIn.Address) != 20 { + return errors.InvalidAddress() + } + if !txIn.Coins.IsValid() { + return errors.InvalidCoins() + } + if txIn.Coins.IsZero() { + return errors.InvalidCoins() + } + if txIn.Sequence <= 0 { + return errors.InvalidSequence() + } + return nil +} + +func (txIn TxInput) String() string { + return fmt.Sprintf("TxInput{%v,%v,%v}", txIn.Address, txIn.Coins, txIn.Sequence) +} + +func NewTxInput(addr []byte, coins types.Coins, sequence int) TxInput { + input := TxInput{ + Address: addr, + Coins: coins, + Sequence: sequence, + } + return input +} + +//----------------------------------------------------------------------------- + +type TxOutput struct { + Address data.Bytes `json:"address"` + Coins types.Coins `json:"coins"` +} + +func (txOut TxOutput) ValidateBasic() error { + if len(txOut.Address) != 20 { + return errors.InvalidAddress() + } + if !txOut.Coins.IsValid() { + return errors.InvalidCoins() + } + if txOut.Coins.IsZero() { + return errors.InvalidCoins() + } + return nil +} + +func (txOut TxOutput) String() string { + return fmt.Sprintf("TxOutput{%X,%v}", txOut.Address, txOut.Coins) +} + +func NewTxOutput(addr []byte, coins types.Coins) TxOutput { + output := TxOutput{ + Address: addr, + Coins: coins, + } + return output +} + +//----------------------------------------------------------------------------- + +type SendTx struct { + Inputs []TxInput `json:"inputs"` + Outputs []TxOutput `json:"outputs"` +} + +var _ basecoin.Tx = SendTx{}.Wrap() + +func (tx SendTx) ValidateBasic() error { + // this just makes sure all the inputs and outputs are properly formatted, + // not that they actually have the money inside + if len(tx.Inputs) == 0 { + return errors.NoInputs() + } + if len(tx.Outputs) == 0 { + return errors.NoOutputs() + } + for _, in := range tx.Inputs { + if err := in.ValidateBasic(); err != nil { + return err + } + } + for _, out := range tx.Outputs { + if err := out.ValidateBasic(); err != nil { + return err + } + } + return nil +} + +func (tx SendTx) String() string { + return fmt.Sprintf("SendTx{%v->%v}", tx.Inputs, tx.Outputs) +} + +func (tx SendTx) Wrap() basecoin.Tx { + return basecoin.Tx{tx} +}