Skip to content

Commit

Permalink
SendTx -> SendMsg, CreditTx -> IssueMsg
Browse files Browse the repository at this point in the history
  • Loading branch information
jaekwon committed Dec 21, 2017
1 parent 5c06e56 commit 9c1f2d5
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 118 deletions.
10 changes: 7 additions & 3 deletions x/coin/decorator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import (
)

func Decorator(ctx sdk.Context, store sdk.MultiStore, tx sdk.Tx, next sdk.Handler) sdk.Result {
if msg, ok := tx.(CoinsMsg); ok {
handleCoinsMsg(ctx, store, msg)
if msg, ok := tx.(CoinMsg); ok {
return handleCoinsMsg(ctx, store, msg)
} else {
next(ctx, store, tx)
return next(ctx, store, tx)
}
}

func handleCoinsMsg(ctx sdk.Context, store sdk.MultiStore, tx sdk.Tx) sdk.Result {
panic("not implemented yet") // XXX
}
15 changes: 10 additions & 5 deletions x/coin/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import (

const (
// Coin errors reserve 100 ~ 199.
CodeInvalidInput uint32 = 101
CodeInvalidOutput uint32 = 102
CodeInvalidAddress uint32 = 103
CodeUnknownAddress uint32 = 103
CodeUnknownRequest uint32 = errors.CodeUnknownRequest
CodeInvalidInput uint32 = 101
CodeInvalidOutput uint32 = 102
CodeInvalidAddress uint32 = 103
CodeUnknownAddress uint32 = 103
CodeUnknownRequest uint32 = errors.CodeUnknownRequest
CodeInsufficientFunds uint32 = errors.CodeInsufficientFunds
)

// NOTE: Don't stringer this, we'll put better messages in later.
Expand Down Expand Up @@ -57,6 +58,10 @@ func ErrUnknownRequest(log string) error {
return newError(CodeUnknownRequest, log)
}

func ErrInsufficientFunds(log string) error {
return newError(CodeInsufficientFunds, log)
}

//----------------------------------------
// Misc

Expand Down
257 changes: 148 additions & 109 deletions x/coin/tx.go
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)
}
2 changes: 1 addition & 1 deletion x/coin/types.go
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)
}

0 comments on commit 9c1f2d5

Please sign in to comment.