From 574cd5be4a92c68049dd1af165a30bbb002cc729 Mon Sep 17 00:00:00 2001
From: Benjamin Bollen <ben@erisindustries.com>
Date: Fri, 5 Aug 2016 14:41:09 +0200
Subject: [PATCH] client: SendTransaction from client/core

---
 client/core/core.go               | 912 +++++++++++++++---------------
 client/transaction/transaction.go |  14 +-
 2 files changed, 467 insertions(+), 459 deletions(-)

diff --git a/client/core/core.go b/client/core/core.go
index df6dc8c0..c9907f5c 100644
--- a/client/core/core.go
+++ b/client/core/core.go
@@ -16,8 +16,6 @@
 
 package core
 
-package core
-
 import (
 	"bytes"
 	"encoding/hex"
@@ -26,14 +24,20 @@ import (
 	"io/ioutil"
 	"net/http"
 	"strconv"
-	"strings"
-	"time"
-
-	"github.com/eris-ltd/tendermint/account"
-	ptypes "github.com/eris-ltd/tendermint/permission/types"
-	rtypes "github.com/eris-ltd/tendermint/rpc/core/types"
-	cclient "github.com/eris-ltd/tendermint/rpc/core_client"
-	"github.com/eris-ltd/txs"
+	// "strings"
+	// "time"
+
+	"github.com/tendermint/go-crypto"
+	"github.com/tendermint/go-rpc/client"
+
+	// "github.com/eris-ltd/tendermint/account"
+	// ptypes "github.com/eris-ltd/permission/types"
+	// cclient "github.com/eris-ltd/tendermint/rpc/client"
+
+	log "github.com/eris-ltd/eris-logger"
+
+	tendermint_client "github.com/eris-ltd/eris-db/rpc/tendermint/client"
+	"github.com/eris-ltd/eris-db/txs"
 )
 
 var (
@@ -66,149 +70,149 @@ func Send(nodeAddr, signAddr, pubkey, addr, toAddr, amtS, nonceS string) (*txs.S
 	return tx, nil
 }
 
-func Call(nodeAddr, signAddr, pubkey, addr, toAddr, amtS, nonceS, gasS, feeS, data string) (*txs.CallTx, error) {
-	pub, amt, nonce, err := checkCommon(nodeAddr, signAddr, pubkey, addr, amtS, nonceS)
-	if err != nil {
-		return nil, err
-	}
-
-	toAddrBytes, err := hex.DecodeString(toAddr)
-	if err != nil {
-		return nil, fmt.Errorf("toAddr is bad hex: %v", err)
-	}
-
-	fee, err := strconv.ParseInt(feeS, 10, 64)
-	if err != nil {
-		return nil, fmt.Errorf("fee is misformatted: %v", err)
-	}
-
-	gas, err := strconv.ParseInt(gasS, 10, 64)
-	if err != nil {
-		return nil, fmt.Errorf("gas is misformatted: %v", err)
-	}
-
-	dataBytes, err := hex.DecodeString(data)
-	if err != nil {
-		return nil, fmt.Errorf("data is bad hex: %v", err)
-	}
-
-	tx := types.NewCallTxWithNonce(pub, toAddrBytes, dataBytes, amt, gas, fee, int(nonce))
-	return tx, nil
-}
-
-func Name(nodeAddr, signAddr, pubkey, addr, amtS, nonceS, feeS, name, data string) (*txs.NameTx, error) {
-	pub, amt, nonce, err := checkCommon(nodeAddr, signAddr, pubkey, addr, amtS, nonceS)
-	if err != nil {
-		return nil, err
-	}
-
-	fee, err := strconv.ParseInt(feeS, 10, 64)
-	if err != nil {
-		return nil, fmt.Errorf("fee is misformatted: %v", err)
-	}
-
-	tx := types.NewNameTxWithNonce(pub, name, data, amt, fee, int(nonce))
-	return tx, nil
-}
-
-type PermFunc struct {
-	Name string
-	Args string
-}
-
-var PermsFuncs = []PermFunc{
-	{"set_base", "address, permission flag, value"},
-	{"unset_base", "address, permission flag"},
-	{"set_global", "permission flag, value"},
-	{"add_role", "address, role"},
-	{"rm_role", "address, role"},
-}
-
-func Permissions(nodeAddr, signAddr, pubkey, addrS, nonceS, permFunc string, argsS []string) (*txs.PermissionsTx, error) {
-	pub, _, nonce, err := checkCommon(nodeAddr, signAddr, pubkey, addrS, "0", nonceS)
-	if err != nil {
-		return nil, err
-	}
-	var args ptypes.PermArgs
-	switch permFunc {
-	case "set_base":
-		addr, pF, err := decodeAddressPermFlag(argsS[0], argsS[1])
-		if err != nil {
-			return nil, err
-		}
-		if len(argsS) != 3 {
-			return nil, fmt.Errorf("set_base also takes a value (true or false)")
-		}
-		var value bool
-		if argsS[2] == "true" {
-			value = true
-		} else if argsS[2] == "false" {
-			value = false
-		} else {
-			return nil, fmt.Errorf("Unknown value %s", argsS[2])
-		}
-		args = &ptypes.SetBaseArgs{addr, pF, value}
-	case "unset_base":
-		addr, pF, err := decodeAddressPermFlag(argsS[0], argsS[1])
-		if err != nil {
-			return nil, err
-		}
-		args = &ptypes.UnsetBaseArgs{addr, pF}
-	case "set_global":
-		pF, err := ptypes.PermStringToFlag(argsS[0])
-		if err != nil {
-			return nil, err
-		}
-		var value bool
-		if argsS[1] == "true" {
-			value = true
-		} else if argsS[1] == "false" {
-			value = false
-		} else {
-			return nil, fmt.Errorf("Unknown value %s", argsS[1])
-		}
-		args = &ptypes.SetGlobalArgs{pF, value}
-	case "add_role":
-		addr, err := hex.DecodeString(argsS[0])
-		if err != nil {
-			return nil, err
-		}
-		args = &ptypes.AddRoleArgs{addr, argsS[1]}
-	case "rm_role":
-		addr, err := hex.DecodeString(argsS[0])
-		if err != nil {
-			return nil, err
-		}
-		args = &ptypes.RmRoleArgs{addr, argsS[1]}
-	default:
-		return nil, fmt.Errorf("Invalid permission function for use in PermissionsTx: %s", permFunc)
-	}
-	// args := snativeArgs(
-	tx := types.NewPermissionsTxWithNonce(pub, args, int(nonce))
-	return tx, nil
-}
-
-func decodeAddressPermFlag(addrS, permFlagS string) (addr []byte, pFlag ptypes.PermFlag, err error) {
-	if addr, err = hex.DecodeString(addrS); err != nil {
-		return
-	}
-	if pFlag, err = ptypes.PermStringToFlag(permFlagS); err != nil {
-		return
-	}
-	return
-}
-
-type NameGetter struct {
-	client cclient.Client
-}
-
-func (n NameGetter) GetNameRegEntry(name string) *txs.NameRegEntry {
-	entry, err := n.client.GetName(name)
-	if err != nil {
-		panic(err)
-	}
-	return entry.Entry
-}
+// func Call(nodeAddr, signAddr, pubkey, addr, toAddr, amtS, nonceS, gasS, feeS, data string) (*txs.CallTx, error) {
+// 	pub, amt, nonce, err := checkCommon(nodeAddr, signAddr, pubkey, addr, amtS, nonceS)
+// 	if err != nil {
+// 		return nil, err
+// 	}
+
+// 	toAddrBytes, err := hex.DecodeString(toAddr)
+// 	if err != nil {
+// 		return nil, fmt.Errorf("toAddr is bad hex: %v", err)
+// 	}
+
+// 	fee, err := strconv.ParseInt(feeS, 10, 64)
+// 	if err != nil {
+// 		return nil, fmt.Errorf("fee is misformatted: %v", err)
+// 	}
+
+// 	gas, err := strconv.ParseInt(gasS, 10, 64)
+// 	if err != nil {
+// 		return nil, fmt.Errorf("gas is misformatted: %v", err)
+// 	}
+
+// 	dataBytes, err := hex.DecodeString(data)
+// 	if err != nil {
+// 		return nil, fmt.Errorf("data is bad hex: %v", err)
+// 	}
+
+// 	tx := types.NewCallTxWithNonce(pub, toAddrBytes, dataBytes, amt, gas, fee, int(nonce))
+// 	return tx, nil
+// }
+
+// func Name(nodeAddr, signAddr, pubkey, addr, amtS, nonceS, feeS, name, data string) (*txs.NameTx, error) {
+// 	pub, amt, nonce, err := checkCommon(nodeAddr, signAddr, pubkey, addr, amtS, nonceS)
+// 	if err != nil {
+// 		return nil, err
+// 	}
+
+// 	fee, err := strconv.ParseInt(feeS, 10, 64)
+// 	if err != nil {
+// 		return nil, fmt.Errorf("fee is misformatted: %v", err)
+// 	}
+
+// 	tx := types.NewNameTxWithNonce(pub, name, data, amt, fee, int(nonce))
+// 	return tx, nil
+// }
+
+// type PermFunc struct {
+// 	Name string
+// 	Args string
+// }
+
+// var PermsFuncs = []PermFunc{
+// 	{"set_base", "address, permission flag, value"},
+// 	{"unset_base", "address, permission flag"},
+// 	{"set_global", "permission flag, value"},
+// 	{"add_role", "address, role"},
+// 	{"rm_role", "address, role"},
+// }
+
+// func Permissions(nodeAddr, signAddr, pubkey, addrS, nonceS, permFunc string, argsS []string) (*txs.PermissionsTx, error) {
+// 	pub, _, nonce, err := checkCommon(nodeAddr, signAddr, pubkey, addrS, "0", nonceS)
+// 	if err != nil {
+// 		return nil, err
+// 	}
+// 	var args ptypes.PermArgs
+// 	switch permFunc {
+// 	case "set_base":
+// 		addr, pF, err := decodeAddressPermFlag(argsS[0], argsS[1])
+// 		if err != nil {
+// 			return nil, err
+// 		}
+// 		if len(argsS) != 3 {
+// 			return nil, fmt.Errorf("set_base also takes a value (true or false)")
+// 		}
+// 		var value bool
+// 		if argsS[2] == "true" {
+// 			value = true
+// 		} else if argsS[2] == "false" {
+// 			value = false
+// 		} else {
+// 			return nil, fmt.Errorf("Unknown value %s", argsS[2])
+// 		}
+// 		args = &ptypes.SetBaseArgs{addr, pF, value}
+// 	case "unset_base":
+// 		addr, pF, err := decodeAddressPermFlag(argsS[0], argsS[1])
+// 		if err != nil {
+// 			return nil, err
+// 		}
+// 		args = &ptypes.UnsetBaseArgs{addr, pF}
+// 	case "set_global":
+// 		pF, err := ptypes.PermStringToFlag(argsS[0])
+// 		if err != nil {
+// 			return nil, err
+// 		}
+// 		var value bool
+// 		if argsS[1] == "true" {
+// 			value = true
+// 		} else if argsS[1] == "false" {
+// 			value = false
+// 		} else {
+// 			return nil, fmt.Errorf("Unknown value %s", argsS[1])
+// 		}
+// 		args = &ptypes.SetGlobalArgs{pF, value}
+// 	case "add_role":
+// 		addr, err := hex.DecodeString(argsS[0])
+// 		if err != nil {
+// 			return nil, err
+// 		}
+// 		args = &ptypes.AddRoleArgs{addr, argsS[1]}
+// 	case "rm_role":
+// 		addr, err := hex.DecodeString(argsS[0])
+// 		if err != nil {
+// 			return nil, err
+// 		}
+// 		args = &ptypes.RmRoleArgs{addr, argsS[1]}
+// 	default:
+// 		return nil, fmt.Errorf("Invalid permission function for use in PermissionsTx: %s", permFunc)
+// 	}
+// 	// args := snativeArgs(
+// 	tx := types.NewPermissionsTxWithNonce(pub, args, int(nonce))
+// 	return tx, nil
+// }
+
+// func decodeAddressPermFlag(addrS, permFlagS string) (addr []byte, pFlag ptypes.PermFlag, err error) {
+// 	if addr, err = hex.DecodeString(addrS); err != nil {
+// 		return
+// 	}
+// 	if pFlag, err = ptypes.PermStringToFlag(permFlagS); err != nil {
+// 		return
+// 	}
+// 	return
+// }
+
+// type NameGetter struct {
+// 	client cclient.Client
+// }
+
+// func (n NameGetter) GetNameRegEntry(name string) *txs.NameRegEntry {
+// 	entry, err := n.client.GetName(name)
+// 	if err != nil {
+// 		panic(err)
+// 	}
+// 	return entry.Entry
+// }
 
 /*
 func coreNewAccount(nodeAddr, pubkey, chainID string) (*types.NewAccountTx, error) {
@@ -222,77 +226,77 @@ func coreNewAccount(nodeAddr, pubkey, chainID string) (*types.NewAccountTx, erro
 }
 */
 
-func Bond(nodeAddr, signAddr, pubkey, unbondAddr, amtS, nonceS string) (*txs.BondTx, error) {
-	pub, amt, nonce, err := checkCommon(nodeAddr, signAddr, pubkey, "", amtS, nonceS)
-	if err != nil {
-		return nil, err
-	}
-	var pubKey account.PubKeyEd25519
-	var unbondAddrBytes []byte
-
-	if unbondAddr == "" {
-		pkb, _ := hex.DecodeString(pubkey)
-		copy(pubKey[:], pkb)
-		unbondAddrBytes = pubKey.Address()
-	} else {
-		unbondAddrBytes, err = hex.DecodeString(unbondAddr)
-		if err != nil {
-			return nil, fmt.Errorf("unbondAddr is bad hex: %v", err)
-		}
-
-	}
-
-	tx, err := types.NewBondTx(pub)
-	if err != nil {
-		return nil, err
-	}
-	tx.AddInputWithNonce(pub, amt, int(nonce))
-	tx.AddOutput(unbondAddrBytes, amt)
-
-	return tx, nil
-}
-
-func Unbond(addrS, heightS string) (*txs.UnbondTx, error) {
-	if addrS == "" {
-		return nil, fmt.Errorf("Validator address must be given with --addr flag")
-	}
-
-	addrBytes, err := hex.DecodeString(addrS)
-	if err != nil {
-		return nil, fmt.Errorf("addr is bad hex: %v", err)
-	}
-
-	height, err := strconv.ParseInt(heightS, 10, 32)
-	if err != nil {
-		return nil, fmt.Errorf("height is misformatted: %v", err)
-	}
-
-	return &types.UnbondTx{
-		Address: addrBytes,
-		Height:  int(height),
-	}, nil
-}
-
-func Rebond(addrS, heightS string) (*txs.RebondTx, error) {
-	if addrS == "" {
-		return nil, fmt.Errorf("Validator address must be given with --addr flag")
-	}
-
-	addrBytes, err := hex.DecodeString(addrS)
-	if err != nil {
-		return nil, fmt.Errorf("addr is bad hex: %v", err)
-	}
-
-	height, err := strconv.ParseInt(heightS, 10, 32)
-	if err != nil {
-		return nil, fmt.Errorf("height is misformatted: %v", err)
-	}
-
-	return &types.RebondTx{
-		Address: addrBytes,
-		Height:  int(height),
-	}, nil
-}
+// func Bond(nodeAddr, signAddr, pubkey, unbondAddr, amtS, nonceS string) (*txs.BondTx, error) {
+// 	pub, amt, nonce, err := checkCommon(nodeAddr, signAddr, pubkey, "", amtS, nonceS)
+// 	if err != nil {
+// 		return nil, err
+// 	}
+// 	var pubKey crypto.PubKeyEd25519
+// 	var unbondAddrBytes []byte
+
+// 	if unbondAddr == "" {
+// 		pkb, _ := hex.DecodeString(pubkey)
+// 		copy(pubKey[:], pkb)
+// 		unbondAddrBytes = pubKey.Address()
+// 	} else {
+// 		unbondAddrBytes, err = hex.DecodeString(unbondAddr)
+// 		if err != nil {
+// 			return nil, fmt.Errorf("unbondAddr is bad hex: %v", err)
+// 		}
+
+// 	}
+
+// 	tx, err := types.NewBondTx(pub)
+// 	if err != nil {
+// 		return nil, err
+// 	}
+// 	tx.AddInputWithNonce(pub, amt, int(nonce))
+// 	tx.AddOutput(unbondAddrBytes, amt)
+
+// 	return tx, nil
+// }
+
+// func Unbond(addrS, heightS string) (*txs.UnbondTx, error) {
+// 	if addrS == "" {
+// 		return nil, fmt.Errorf("Validator address must be given with --addr flag")
+// 	}
+
+// 	addrBytes, err := hex.DecodeString(addrS)
+// 	if err != nil {
+// 		return nil, fmt.Errorf("addr is bad hex: %v", err)
+// 	}
+
+// 	height, err := strconv.ParseInt(heightS, 10, 32)
+// 	if err != nil {
+// 		return nil, fmt.Errorf("height is misformatted: %v", err)
+// 	}
+
+// 	return &types.UnbondTx{
+// 		Address: addrBytes,
+// 		Height:  int(height),
+// 	}, nil
+// }
+
+// func Rebond(addrS, heightS string) (*txs.RebondTx, error) {
+// 	if addrS == "" {
+// 		return nil, fmt.Errorf("Validator address must be given with --addr flag")
+// 	}
+
+// 	addrBytes, err := hex.DecodeString(addrS)
+// 	if err != nil {
+// 		return nil, fmt.Errorf("addr is bad hex: %v", err)
+// 	}
+
+// 	height, err := strconv.ParseInt(heightS, 10, 32)
+// 	if err != nil {
+// 		return nil, fmt.Errorf("height is misformatted: %v", err)
+// 	}
+
+// 	return &types.RebondTx{
+// 		Address: addrBytes,
+// 		Height:  int(height),
+// 	}, nil
+// }
 
 //------------------------------------------------------------------------------------
 // sign and broadcast
@@ -308,32 +312,32 @@ func Pub(addr, rpcAddr string) (pubBytes []byte, err error) {
 	return hex.DecodeString(pubS)
 }
 
-func Sign(signBytes, signAddr, signRPC string) (sig [64]byte, err error) {
-	args := map[string]string{
-		"msg":  signBytes,
-		"hash": signBytes, // backwards compatibility
-		"addr": signAddr,
-	}
-	sigS, err := RequestResponse(signRPC, "sign", args)
-	if err != nil {
-		return
-	}
-	sigBytes, err := hex.DecodeString(sigS)
-	if err != nil {
-		return
-	}
-	copy(sig[:], sigBytes)
-	return
-}
-
-func Broadcast(tx types.Tx, broadcastRPC string) (*txs.Receipt, error) {
-	client := cclient.NewClient(broadcastRPC, "JSONRPC")
-	rec, err := client.BroadcastTx(tx)
-	if err != nil {
-		return nil, err
-	}
-	return &rec.Receipt, nil
-}
+// func Sign(signBytes, signAddr, signRPC string) (sig [64]byte, err error) {
+// 	args := map[string]string{
+// 		"msg":  signBytes,
+// 		"hash": signBytes, // backwards compatibility
+// 		"addr": signAddr,
+// 	}
+// 	sigS, err := RequestResponse(signRPC, "sign", args)
+// 	if err != nil {
+// 		return
+// 	}
+// 	sigBytes, err := hex.DecodeString(sigS)
+// 	if err != nil {
+// 		return
+// 	}
+// 	copy(sig[:], sigBytes)
+// 	return
+// }
+
+// func Broadcast(tx types.Tx, broadcastRPC string) (*txs.Receipt, error) {
+// 	client := rpclient.NewClientJSONRPC(broadcastRPC)
+// 	rec, err := client.BroadcastTx(tx)
+// 	if err != nil {
+// 		return nil, err
+// 	}
+// 	return &rec.Receipt, nil
+// }
 
 //------------------------------------------------------------------------------------
 // utils for talking to the key server
@@ -349,7 +353,10 @@ func RequestResponse(addr, method string, args map[string]string) (string, error
 		return "", err
 	}
 	endpoint := fmt.Sprintf("%s/%s", addr, method)
-	logger.Debugf("Sending request body (%s): %s\n", endpoint, string(b))
+	log.WithFields(log.Fields{
+		"endpoint": endpoint,
+		"request body": string(b),
+		}).Debugf("Sending request body")
 	req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(b))
 	if err != nil {
 		return "", err
@@ -389,203 +396,203 @@ func unpackResponse(resp *http.Response) (string, string, error) {
 	return r.Response, r.Error, nil
 }
 
-//------------------------------------------------------------------------------------
-// sign and broadcast convenience
-
-// tx has either one input or we default to the first one (ie for send/bond)
-// TODO: better support for multisig and bonding
-func signTx(signAddr, chainID string, tx_ txs.Tx) ([]byte, txs.Tx, error) {
-	signBytes := fmt.Sprintf("%X", account.SignBytes(chainID, tx_))
-	var inputAddr []byte
-	var sigED account.SignatureEd25519
-	switch tx := tx_.(type) {
-	case *types.SendTx:
-		inputAddr = tx.Inputs[0].Address
-		defer func(s *account.SignatureEd25519) { tx.Inputs[0].Signature = *s }(&sigED)
-	case *types.NameTx:
-		inputAddr = tx.Input.Address
-		defer func(s *account.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
-	case *types.CallTx:
-		inputAddr = tx.Input.Address
-		defer func(s *account.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
-	case *types.PermissionsTx:
-		inputAddr = tx.Input.Address
-		defer func(s *account.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
-	case *types.BondTx:
-		inputAddr = tx.Inputs[0].Address
-		defer func(s *account.SignatureEd25519) {
-			tx.Signature = *s
-			tx.Inputs[0].Signature = *s
-		}(&sigED)
-	case *types.UnbondTx:
-		inputAddr = tx.Address
-		defer func(s *account.SignatureEd25519) { tx.Signature = *s }(&sigED)
-	case *types.RebondTx:
-		inputAddr = tx.Address
-		defer func(s *account.SignatureEd25519) { tx.Signature = *s }(&sigED)
-	}
-	addrHex := fmt.Sprintf("%X", inputAddr)
-	sig, err := Sign(signBytes, addrHex, signAddr)
-	if err != nil {
-		return nil, nil, err
-	}
-	sigED = account.SignatureEd25519(sig)
-	logger.Debugf("SIG: %X\n", sig)
-	return inputAddr, tx_, nil
-}
-
-type TxResult struct {
-	BlockHash []byte // all txs get in a block
-	Hash      []byte // all txs get a hash
-
-	// only CallTx
-	Address   []byte // only for new contracts
-	Return    []byte
-	Exception string
-
-	//TODO: make Broadcast() errors more responsive so we
-	// can differentiate mempool errors from other
-}
-
-func SignAndBroadcast(chainID, nodeAddr, signAddr string, tx types.Tx, sign, broadcast, wait bool) (txResult *TxResult, err error) {
-	var inputAddr []byte
-	if sign {
-		inputAddr, tx, err = signTx(signAddr, chainID, tx)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	if broadcast {
-		if wait {
-			var ch chan Msg
-			ch, err = subscribeAndWait(tx, chainID, nodeAddr, inputAddr)
-			if err != nil {
-				return nil, err
-			} else {
-				defer func() {
-					if err != nil {
-						// if broadcast threw an error, just return
-						return
-					}
-					logger.Debugln("Waiting for tx to be committed ...")
-					msg := <-ch
-					if msg.Error != nil {
-						logger.Infof("Encountered error waiting for event: %v\n", msg.Error)
-						err = msg.Error
-					} else {
-						txResult.BlockHash = msg.BlockHash
-						txResult.Return = msg.Value
-						txResult.Exception = msg.Exception
-					}
-				}()
-			}
-		}
-		var receipt *rtypes.Receipt
-		receipt, err = Broadcast(tx, nodeAddr)
-		if err != nil {
-			return nil, err
-		}
-		txResult = &TxResult{
-			Hash: receipt.TxHash,
-		}
-		if tx_, ok := tx.(*types.CallTx); ok {
-			if len(tx_.Address) == 0 {
-				txResult.Address = types.NewContractAddress(tx_.Input.Address, tx_.Input.Sequence)
-			}
-		}
-	}
-	return
-}
-
-//------------------------------------------------------------------------------------
-// wait for events
-
-type Msg struct {
-	BlockHash []byte
-	Value     []byte
-	Exception string
-	Error     error
-}
-
-func subscribeAndWait(tx types.Tx, chainID, nodeAddr string, inputAddr []byte) (chan Msg, error) {
-	// subscribe to event and wait for tx to be committed
-	wsAddr := strings.TrimPrefix(nodeAddr, "http://")
-	wsAddr = "ws://" + wsAddr + "websocket"
-	logger.Debugf("Websocket Address %s\n", wsAddr)
-	wsClient := cclient.NewWSClient(wsAddr)
-	wsClient.Start()
-	eid := types.EventStringAccInput(inputAddr)
-	if err := wsClient.Subscribe(eid); err != nil {
-		return nil, fmt.Errorf("Error subscribing to AccInput event: %v", err)
-	}
-	if err := wsClient.Subscribe(types.EventStringNewBlock()); err != nil {
-		return nil, fmt.Errorf("Error subscribing to NewBlock event: %v", err)
-	}
-
-	resultChan := make(chan Msg, 1)
-
-	var latestBlockHash []byte
-
-	// Read message
-	go func() {
-		for {
-			result := <-wsClient.EventsCh
-			// if its a block, remember the block hash
-			blockData, ok := result.Data.(types.EventDataNewBlock)
-			if ok {
-				logger.Infoln(blockData.Block)
-				latestBlockHash = blockData.Block.Hash()
-				continue
-			}
-
-			// we don't accept events unless they came after a new block (ie. in)
-			if latestBlockHash == nil {
-				continue
-			}
-
-			if result.Event != eid {
-				logger.Debugf("received unsolicited event! Got %s, expected %s\n", result.Event, eid)
-				continue
-			}
-
-			data, ok := result.Data.(types.EventDataTx)
-			if !ok {
-				resultChan <- Msg{Error: fmt.Errorf("response error: expected result.Data to be *types.EventDataTx")}
-				return
-			}
-
-			if !bytes.Equal(types.TxID(chainID, data.Tx), types.TxID(chainID, tx)) {
-				logger.Debugf("Received event for same input from another transaction: %X\n", types.TxID(chainID, data.Tx))
-				continue
-			}
-
-			if data.Exception != "" {
-				resultChan <- Msg{BlockHash: latestBlockHash, Value: data.Return, Exception: data.Exception}
-				return
-			}
-
-			// GOOD!
-			resultChan <- Msg{BlockHash: latestBlockHash, Value: data.Return}
-			return
-		}
-	}()
-
-	// txs should take no more than 10 seconds
-	timeoutTicker := time.Tick(time.Duration(MaxCommitWaitTimeSeconds) * time.Second)
-
-	go func() {
-		<-timeoutTicker
-		resultChan <- Msg{Error: fmt.Errorf("timed out waiting for event")}
-		return
-	}()
-	return resultChan, nil
-}
+// //------------------------------------------------------------------------------------
+// // sign and broadcast convenience
+
+// // tx has either one input or we default to the first one (ie for send/bond)
+// // TODO: better support for multisig and bonding
+// func signTx(signAddr, chainID string, tx_ txs.Tx) ([]byte, txs.Tx, error) {
+// 	signBytes := fmt.Sprintf("%X", crypto.SignBytes(chainID, tx_))
+// 	var inputAddr []byte
+// 	var sigED crypto.SignatureEd25519
+// 	switch tx := tx_.(type) {
+// 	case *types.SendTx:
+// 		inputAddr = tx.Inputs[0].Address
+// 		defer func(s *crypto.SignatureEd25519) { tx.Inputs[0].Signature = *s }(&sigED)
+// 	case *types.NameTx:
+// 		inputAddr = tx.Input.Address
+// 		defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+// 	case *types.CallTx:
+// 		inputAddr = tx.Input.Address
+// 		defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+// 	case *types.PermissionsTx:
+// 		inputAddr = tx.Input.Address
+// 		defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+// 	case *types.BondTx:
+// 		inputAddr = tx.Inputs[0].Address
+// 		defer func(s *crypto.SignatureEd25519) {
+// 			tx.Signature = *s
+// 			tx.Inputs[0].Signature = *s
+// 		}(&sigED)
+// 	case *types.UnbondTx:
+// 		inputAddr = tx.Address
+// 		defer func(s *crypto.SignatureEd25519) { tx.Signature = *s }(&sigED)
+// 	case *types.RebondTx:
+// 		inputAddr = tx.Address
+// 		defer func(s *crypto.SignatureEd25519) { tx.Signature = *s }(&sigED)
+// 	}
+// 	addrHex := fmt.Sprintf("%X", inputAddr)
+// 	sig, err := Sign(signBytes, addrHex, signAddr)
+// 	if err != nil {
+// 		return nil, nil, err
+// 	}
+// 	sigED = crypto.SignatureEd25519(sig)
+// 	logger.Debugf("SIG: %X\n", sig)
+// 	return inputAddr, tx_, nil
+// }
+
+// type TxResult struct {
+// 	BlockHash []byte // all txs get in a block
+// 	Hash      []byte // all txs get a hash
+
+// 	// only CallTx
+// 	Address   []byte // only for new contracts
+// 	Return    []byte
+// 	Exception string
+
+// 	//TODO: make Broadcast() errors more responsive so we
+// 	// can differentiate mempool errors from other
+// }
+
+// func SignAndBroadcast(chainID, nodeAddr, signAddr string, tx types.Tx, sign, broadcast, wait bool) (txResult *TxResult, err error) {
+// 	var inputAddr []byte
+// 	if sign {
+// 		inputAddr, tx, err = signTx(signAddr, chainID, tx)
+// 		if err != nil {
+// 			return nil, err
+// 		}
+// 	}
+
+// 	if broadcast {
+// 		if wait {
+// 			var ch chan Msg
+// 			ch, err = subscribeAndWait(tx, chainID, nodeAddr, inputAddr)
+// 			if err != nil {
+// 				return nil, err
+// 			} else {
+// 				defer func() {
+// 					if err != nil {
+// 						// if broadcast threw an error, just return
+// 						return
+// 					}
+// 					logger.Debugln("Waiting for tx to be committed ...")
+// 					msg := <-ch
+// 					if msg.Error != nil {
+// 						logger.Infof("Encountered error waiting for event: %v\n", msg.Error)
+// 						err = msg.Error
+// 					} else {
+// 						txResult.BlockHash = msg.BlockHash
+// 						txResult.Return = msg.Value
+// 						txResult.Exception = msg.Exception
+// 					}
+// 				}()
+// 			}
+// 		}
+// 		var receipt *txs.Receipt
+// 		receipt, err = client.Broadcast(tx, nodeAddr)
+// 		if err != nil {
+// 			return nil, err
+// 		}
+// 		txResult = &TxResult{
+// 			Hash: receipt.TxHash,
+// 		}
+// 		if tx_, ok := tx.(*types.CallTx); ok {
+// 			if len(tx_.Address) == 0 {
+// 				txResult.Address = types.NewContractAddress(tx_.Input.Address, tx_.Input.Sequence)
+// 			}
+// 		}
+// 	}
+// 	return
+// }
+
+// //------------------------------------------------------------------------------------
+// // wait for events
+
+// type Msg struct {
+// 	BlockHash []byte
+// 	Value     []byte
+// 	Exception string
+// 	Error     error
+// }
+
+// func subscribeAndWait(tx types.Tx, chainID, nodeAddr string, inputAddr []byte) (chan Msg, error) {
+// 	// subscribe to event and wait for tx to be committed
+// 	wsAddr := strings.TrimPrefix(nodeAddr, "http://")
+// 	wsAddr = "ws://" + wsAddr + "websocket"
+// 	logger.Debugf("Websocket Address %s\n", wsAddr)
+// 	wsClient := cclient.NewWSClient(wsAddr)
+// 	wsClient.Start()
+// 	eid := types.EventStringAccInput(inputAddr)
+// 	if err := wsClient.Subscribe(eid); err != nil {
+// 		return nil, fmt.Errorf("Error subscribing to AccInput event: %v", err)
+// 	}
+// 	if err := wsClient.Subscribe(types.EventStringNewBlock()); err != nil {
+// 		return nil, fmt.Errorf("Error subscribing to NewBlock event: %v", err)
+// 	}
+
+// 	resultChan := make(chan Msg, 1)
+
+// 	var latestBlockHash []byte
+
+// 	// Read message
+// 	go func() {
+// 		for {
+// 			result := <-wsClient.EventsCh
+// 			// if its a block, remember the block hash
+// 			blockData, ok := result.Data.(types.EventDataNewBlock)
+// 			if ok {
+// 				logger.Infoln(blockData.Block)
+// 				latestBlockHash = blockData.Block.Hash()
+// 				continue
+// 			}
+
+// 			// we don't accept events unless they came after a new block (ie. in)
+// 			if latestBlockHash == nil {
+// 				continue
+// 			}
+
+// 			if result.Event != eid {
+// 				logger.Debugf("received unsolicited event! Got %s, expected %s\n", result.Event, eid)
+// 				continue
+// 			}
+
+// 			data, ok := result.Data.(types.EventDataTx)
+// 			if !ok {
+// 				resultChan <- Msg{Error: fmt.Errorf("response error: expected result.Data to be *types.EventDataTx")}
+// 				return
+// 			}
+
+// 			if !bytes.Equal(types.TxID(chainID, data.Tx), types.TxID(chainID, tx)) {
+// 				logger.Debugf("Received event for same input from another transaction: %X\n", types.TxID(chainID, data.Tx))
+// 				continue
+// 			}
+
+// 			if data.Exception != "" {
+// 				resultChan <- Msg{BlockHash: latestBlockHash, Value: data.Return, Exception: data.Exception}
+// 				return
+// 			}
+
+// 			// GOOD!
+// 			resultChan <- Msg{BlockHash: latestBlockHash, Value: data.Return}
+// 			return
+// 		}
+// 	}()
+
+// 	// txs should take no more than 10 seconds
+// 	timeoutTicker := time.Tick(time.Duration(MaxCommitWaitTimeSeconds) * time.Second)
+
+// 	go func() {
+// 		<-timeoutTicker
+// 		resultChan <- Msg{Error: fmt.Errorf("timed out waiting for event")}
+// 		return
+// 	}()
+// 	return resultChan, nil
+// }
 
 //------------------------------------------------------------------------------------
 // convenience function
 
-func checkCommon(nodeAddr, signAddr, pubkey, addr, amtS, nonceS string) (pub account.PubKey, amt int64, nonce int64, err error) {
+func checkCommon(nodeAddr, signAddr, pubkey, addr, amtS, nonceS string) (pub crypto.PubKey, amt int64, nonce int64, err error) {
 	if amtS == "" {
 		err = fmt.Errorf("input must specify an amount with the --amt flag")
 		return
@@ -597,9 +604,10 @@ func checkCommon(nodeAddr, signAddr, pubkey, addr, amtS, nonceS string) (pub acc
 		return
 	} else if pubkey != "" {
 		if addr != "" {
-			// NOTE: if --addr given byt MINTX_PUBKEY is set, the pubkey still wins
-			// TODO: fix this
-			logger.Infoln("you have specified both a pubkey and an address. the pubkey takes precedent")
+			log.WithFields(log.Fields{
+				"public key": pubkey,
+				"address": addr,
+				}).Info("you have specified both a pubkey and an address. the pubkey takes precedent")
 		}
 		pubKeyBytes, err = hex.DecodeString(pubkey)
 		if err != nil {
@@ -628,7 +636,7 @@ func checkCommon(nodeAddr, signAddr, pubkey, addr, amtS, nonceS string) (pub acc
 
 	var pubArray [32]byte
 	copy(pubArray[:], pubKeyBytes)
-	pub = account.PubKeyEd25519(pubArray)
+	pub = crypto.PubKeyEd25519(pubArray)
 	addrBytes := pub.Address()
 
 	if nonceS == "" {
@@ -638,17 +646,17 @@ func checkCommon(nodeAddr, signAddr, pubkey, addr, amtS, nonceS string) (pub acc
 		}
 
 		// fetch nonce from node
-		client := cclient.NewClient(nodeAddr, "HTTP")
-		ac, err2 := client.GetAccount(addrBytes)
+		client := rpcclient.NewClientJSONRPC(nodeAddr)
+		account, err2 := tendermint_client.GetAccount(client, addrBytes)
 		if err2 != nil {
 			err = fmt.Errorf("Error connecting to node (%s) to fetch nonce: %s", nodeAddr, err2.Error())
 			return
 		}
-		if ac == nil || ac.Account == nil {
+		if account == nil {
 			err = fmt.Errorf("unknown account %X", addrBytes)
 			return
 		}
-		nonce = int64(ac.Account.Sequence) + 1
+		nonce = int64(account.Sequence) + 1
 	} else {
 		nonce, err = strconv.ParseInt(nonceS, 10, 64)
 		if err != nil {
diff --git a/client/transaction/transaction.go b/client/transaction/transaction.go
index 17394144..d6f210f9 100644
--- a/client/transaction/transaction.go
+++ b/client/transaction/transaction.go
@@ -20,16 +20,16 @@ import (
 	// "fmt"
 	// "io/ioutil"
 
-	// "github.com/eris-ltd/eris-logger"
+	log "github.com/eris-ltd/eris-logger"
 
-	// // "github.com/eris-ltd/eris-db/client/core"
+	"github.com/eris-ltd/eris-db/client/core"
 	"github.com/eris-ltd/eris-db/definitions"
 )
 
 func Send(do *definitions.ClientDo) {
-	// transaction, err := core.Send(do.nodeAddrFlag, do.signAddrFlag,
-	// 	do.pubkeyFlag, do.toFlag, do.amtFlag, do.nonceFlag)
-	// if err != nil {
-	// 	log.Fatalf("Failed on Send Transaction: %s", err)
-	// }
+	_, err := core.Send(do.NodeAddrFlag, do.SignAddrFlag,
+		do.PubkeyFlag, do.AddrFlag, do.ToFlag, do.AmtFlag, do.NonceFlag)
+	if err != nil {
+		log.Fatalf("Failed on Send Transaction: %s", err)
+	}
 }
\ No newline at end of file
-- 
GitLab