-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SendTx -> SendMsg, CreditTx -> IssueMsg
- Loading branch information
Showing
4 changed files
with
166 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,167 +1,206 @@ | ||
package coin | ||
|
||
// TODO rename this to msg.go | ||
|
||
import ( | ||
"fmt" | ||
"github.com/cosmos/cosmos-sdk/types" | ||
cmn "github.com/tendermint/tmlibs/common" | ||
) | ||
|
||
// NOTE: How the app decodes a SendMsg or IssueMsg is up to | ||
// the app implementation. Do not include parsing logic | ||
// here. | ||
type CoinMsg interface { | ||
AssertIsCoinMsg() | ||
Type() string // "send", "credit" | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
func (_ SendMsg) AssertIsCoinMsg() {} | ||
func (_ IssueMsg) AssertIsCoinMsg() {} | ||
|
||
// Input is a source of coins in a transaction. | ||
type Input struct { | ||
Address cmn.Bytes | ||
Coins Coins | ||
} | ||
//---------------------------------------- | ||
// SendMsg | ||
|
||
func (in Input) ValidateBasic() error { | ||
if !auth.IsValidAddress(in.Address) { | ||
return ErrInvalidAddress() | ||
} | ||
if !in.Coins.IsValid() { | ||
return ErrInvalidInput() | ||
} | ||
if !in.Coins.IsPositive() { | ||
return ErrInvalidInput() | ||
} | ||
return nil | ||
// SendMsg - high level transaction of the coin module | ||
type SendMsg struct { | ||
Inputs []Input `json:"inputs"` | ||
Outputs []Output `json:"outputs"` | ||
} | ||
|
||
func (txIn TxInput) String() string { | ||
return fmt.Sprintf("TxInput{%v,%v}", txIn.Address, txIn.Coins) | ||
var _ CoinMsg = SendMsg | ||
|
||
func NewSendMsg(in []Input, out []Output) SendMsg { | ||
return SendMsg{Inputs: in, Outputs: out} | ||
} | ||
|
||
// NewTxInput - create a transaction input, used with SendTx | ||
func NewTxInput(addr Actor, coins Coins) TxInput { | ||
input := TxInput{ | ||
Address: addr, | ||
Coins: coins, | ||
} | ||
return input | ||
// Implements types.Msg. | ||
func (msg SendMsg) Get(key interface{}) (value interface{}) { | ||
panic("not implemented yet") // XXX | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
// Implements types.Msg. | ||
func (msg SendMsg) SignBytes() []byte { | ||
panic("SendMsg does not implement SignBytes. Implement it by embedding SendMsg in a custom struct") | ||
} | ||
|
||
// TxOutput - expected coin movement output, used with SendTx | ||
type TxOutput struct { | ||
Address Actor `json:"address"` | ||
Coins Coins `json:"coins"` | ||
// Implements types.Msg. | ||
func (msg SendMsg) ValidateBasic() error { | ||
return nil | ||
} | ||
|
||
// ValidateBasic - validate transaction output | ||
func (txOut TxOutput) ValidateBasic() error { | ||
if txOut.Address.App == "" { | ||
return ErrInvalidAddress() | ||
// Implements types.Msg. | ||
func (msg SendMsg) ValidateBasic() error { | ||
if len(msg.Inputs) == 0 { | ||
return ErrInvalidInput("SendMsg needs 1 or more inputs") | ||
} | ||
// TODO: knowledge of app-specific codings? | ||
if len(txOut.Address.Address) == 0 { | ||
return ErrInvalidAddress() | ||
if len(msg.Outputs) == 0 { | ||
return ErrInvalidOutput("SendMsg needs 1 or more outputs") | ||
} | ||
if !txOut.Coins.IsValid() { | ||
return ErrInvalidCoins() | ||
|
||
// While tallying totals, validate inputs and outputs. | ||
var totalIn, totalOut Coins | ||
for _, in := range msg.Inputs { | ||
if err := in.ValidateBasic(); err != nil { | ||
return ErrInvalidInput("").WithCause(err) | ||
} | ||
totalIn = totalIn.Plus(in.Coins) | ||
} | ||
for _, out := range msg.Outputs { | ||
if err := out.ValidateBasic(); err != nil { | ||
return ErrInvalidOutput("").WithCause(err) | ||
} | ||
totalOut = totalOut.Plus(out.Coins) | ||
} | ||
if !txOut.Coins.IsPositive() { | ||
return ErrInvalidCoins() | ||
|
||
// Ensure that totals match. | ||
// TODO: Handle fees, whether too low, legative, or too high. | ||
if !totalIn.IsEqual(totalOut) { | ||
return ErrInsufficientFunds("") | ||
} | ||
|
||
// All good! | ||
return nil | ||
} | ||
|
||
func (txOut TxOutput) String() string { | ||
return fmt.Sprintf("TxOutput{%X,%v}", txOut.Address, txOut.Coins) | ||
// Implements types.Msg. | ||
func (msg SendMsg) Signers() [][]byte { | ||
panic("not implemented yet") // XXX | ||
} | ||
|
||
// NewTxOutput - create a transaction output, used with SendTx | ||
func NewTxOutput(addr Actor, coins Coins) TxOutput { | ||
output := TxOutput{ | ||
Address: addr, | ||
Coins: coins, | ||
func (msg SendMsg) String() string { | ||
return fmt.Sprintf("SendMsg{%v->%v}", msg.Inputs, msg.Outputs) | ||
} | ||
|
||
//---------------------------------------- | ||
// IssueMsg | ||
|
||
// IssueMsg allows issuer to issue new coins. | ||
type IssueMsg struct { | ||
Issuer []byte `json:"issuer"` | ||
Target []byte `json:"target"` | ||
Coins `json:"coins"` | ||
} | ||
|
||
var _ CoinMsg = IssueMsg | ||
|
||
func NewIssueMsg(issuer []byte, target []byte, coins Coins) IssueMsg { | ||
return IssueMsg{ | ||
Issuer: issuer, | ||
Target: target, | ||
Coins: coins, | ||
} | ||
return output | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
// Implements types.Msg. | ||
func (msg IssueMsg) Get(key interface{}) (value interface{}) { | ||
panic("not implemented yet") // XXX | ||
} | ||
|
||
// SendTx - high level transaction of the coin module | ||
// Satisfies: TxInner | ||
type SendTx struct { | ||
Inputs []TxInput `json:"inputs"` | ||
Outputs []TxOutput `json:"outputs"` | ||
// Implements types.Msg. | ||
func (msg IssueMsg) SignBytes() []byte { | ||
panic("IssueMsg does not implement SignBytes. Implement it by embedding IssueMsg in a custom struct") | ||
} | ||
|
||
// var _ types.Tx = NewSendTx(nil, nil) | ||
// Implements types.Msg. | ||
func (msg IssueMsg) ValidateBasic() error { | ||
return nil | ||
} | ||
|
||
// NewSendTx - construct arbitrary multi-in, multi-out sendtx | ||
func NewSendTx(in []TxInput, out []TxOutput) SendTx { // types.Tx { | ||
return SendTx{Inputs: in, Outputs: out} | ||
// Implements types.Msg. | ||
func (msg IssueMsg) Signers() [][]byte { | ||
panic("not implemented yet") // XXX | ||
} | ||
|
||
// NewSendOneTx is a helper for the standard (?) case where there is exactly | ||
// one sender and one recipient | ||
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} | ||
func (msg IssueMsg) String() string { | ||
return fmt.Sprintf("IssueMsg{%X:%v->%X}", | ||
msg.Issuer, msg.Coins, msg.Target) | ||
} | ||
|
||
// ValidateBasic - validate the send transaction | ||
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 ErrNoInputs() | ||
} | ||
if len(tx.Outputs) == 0 { | ||
return ErrNoOutputs() | ||
//---------------------------------------- | ||
// Input | ||
|
||
// Input is a source of coins in a transaction. | ||
type Input struct { | ||
Address cmn.Bytes `json:"address"` | ||
Coins Coins `json:"coins"` | ||
} | ||
|
||
func NewInput(addr []byte, coins Coins) Input { | ||
return Input{ | ||
Address: addr, | ||
Coins: coins, | ||
} | ||
// make sure all inputs and outputs are individually valid | ||
var totalIn, totalOut Coins | ||
for _, in := range tx.Inputs { | ||
if err := in.ValidateBasic(); err != nil { | ||
return err | ||
} | ||
totalIn = totalIn.Plus(in.Coins) | ||
} | ||
|
||
func (inp Input) ValidateBasic() error { | ||
if !auth.IsValidAddress(inp.Address) { | ||
return ErrInvalidAddress(fmt.Sprintf( | ||
"Invalid input address %X", inp.Address)) | ||
} | ||
for _, out := range tx.Outputs { | ||
if err := out.ValidateBasic(); err != nil { | ||
return err | ||
} | ||
totalOut = totalOut.Plus(out.Coins) | ||
if !inp.Coins.IsValid() { | ||
return ErrInvalidInput(fmt.Sprintf( | ||
"Input coins not valid: %v", inp.Coins)) | ||
} | ||
// make sure inputs and outputs match | ||
if !totalIn.IsEqual(totalOut) { | ||
return ErrInvalidCoins() | ||
if !inp.Coins.IsPositive() { | ||
return ErrInvalidInput("Input coins must be positive") | ||
} | ||
return nil | ||
} | ||
|
||
func (tx SendTx) String() string { | ||
return fmt.Sprintf("SendTx{%v->%v}", tx.Inputs, tx.Outputs) | ||
func (inp Input) String() string { | ||
return fmt.Sprintf("Input{%v,%v}", inp.Address, inp.Coins) | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
//---------------------------------------- | ||
// Output | ||
|
||
// CreditTx - this allows a special issuer to give an account credit | ||
// Satisfies: TxInner | ||
type CreditTx struct { | ||
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 | ||
Credit Coins `json:"credit"` | ||
// Output is a destination of coins in a transaction. | ||
type Output struct { | ||
Address cmn.Bytes `json:"address"` | ||
Coins Coins `json:"coins"` | ||
} | ||
|
||
// NewCreditTx - modify the credit granted to a given account | ||
func NewCreditTx(debitor Actor, credit Coins) CreditTx { | ||
return CreditTx{Debitor: debitor, Credit: credit} | ||
func NewOutput(addr []byte, coins Coins) Output { | ||
output := Output{ | ||
Address: addr, | ||
Coins: coins, | ||
} | ||
return output | ||
} | ||
|
||
// ValidateBasic - used to satisfy TxInner | ||
func (tx CreditTx) ValidateBasic() error { | ||
func (out Output) ValidateBasic() error { | ||
if !auth.IsValidAddress(out.Address) { | ||
return ErrInvalidAddress(fmt.Sprintf( | ||
"Invalid output address %X", out.Address)) | ||
} | ||
if !out.Coins.IsValid() { | ||
return ErrInvalidOutput(fmt.Sprintf( | ||
"Output coins not valid: %v", out.Coins)) | ||
} | ||
if !out.Coins.IsPositive() { | ||
return ErrInvalidOutput("Output coins must be positive") | ||
} | ||
return nil | ||
} | ||
|
||
func (out Output) String() string { | ||
return fmt.Sprintf("Output{%X,%v}", out.Address, out.Coins) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
package coin | ||
|
||
type Coinser interface { | ||
type CoinHolder interface { | ||
GetCoins() Coins | ||
SetCoins(Coins) | ||
} |