From 00fe5982ecfe3eaeb0e820ba61929ac8de68640c Mon Sep 17 00:00:00 2001
From: Benjamin Bollen <ben@erisindustries.com>
Date: Wed, 10 Aug 2016 14:28:32 +0200
Subject: [PATCH] client: call key server; retrieve nonce from node

---
 client/cmd/eris-client.go         |   1 -
 client/cmd/transaction.go         |  51 +++---
 client/core/core.go               | 278 ++++++++++++++++--------------
 client/transaction/transaction.go |   5 +-
 4 files changed, 177 insertions(+), 158 deletions(-)

diff --git a/client/cmd/eris-client.go b/client/cmd/eris-client.go
index dd960f6d..d68f0e08 100644
--- a/client/cmd/eris-client.go
+++ b/client/cmd/eris-client.go
@@ -42,7 +42,6 @@ Made with <3 by Eris Industries.
 Complete documentation is available at https://docs.erisindustries.com
 ` + "\nVERSION:\n " + version.VERSION,
 	PersistentPreRun: func(cmd *cobra.Command, args []string) {
-
 		log.SetLevel(log.WarnLevel)
 		if clientDo.Verbose {
 			log.SetLevel(log.InfoLevel)
diff --git a/client/cmd/transaction.go b/client/cmd/transaction.go
index 1106dab0..01476a3d 100644
--- a/client/cmd/transaction.go
+++ b/client/cmd/transaction.go
@@ -33,19 +33,6 @@ var TransactionCmd = &cobra.Command{
 	Long:  `eris-client tx formulates and signs a transaction to a chain.
 `,
 	Run:   func(cmd *cobra.Command, args []string) { cmd.Help() },
-	PersistentPreRun: func(cmd *cobra.Command, args []string) {
-		// 
-		if !strings.HasPrefix(clientDo.NodeAddrFlag, "http://") {
-			clientDo.NodeAddrFlag = "http://" + clientDo.NodeAddrFlag
-		}
-		if !strings.HasSuffix(clientDo.NodeAddrFlag, "/") {
-			clientDo.NodeAddrFlag += "/"
-		}
-
-		if !strings.HasPrefix(clientDo.SignAddrFlag, "http://") {
-			clientDo.SignAddrFlag = "http://" + clientDo.SignAddrFlag
-		}
-	},
 }
 
 func buildTransactionCommand() {
@@ -62,7 +49,7 @@ func buildTransactionCommand() {
 		Run:   func(cmd *cobra.Command, args []string) {
 			transaction.Send(clientDo)
 		},
-		PersistentPreRun: assertChainIdSet,
+		PreRun: assertParameters,
 	}
 	sendCmd.Flags().StringVarP(&clientDo.AmtFlag, "amt", "a", "", "specify an amount")
 	sendCmd.Flags().StringVarP(&clientDo.ToFlag, "to", "t", "", "specify an address to send to")
@@ -75,7 +62,7 @@ func buildTransactionCommand() {
 		Run:   func(cmd *cobra.Command, args []string) {
 			// transaction.Name(clientDo)
 		},
-		PersistentPreRun: assertChainIdSet,
+		PreRun: assertParameters,
 	}
 	nameCmd.Flags().StringVarP(&clientDo.AmtFlag, "amt", "a", "", "specify an amount")
 	nameCmd.Flags().StringVarP(&clientDo.NameFlag, "name", "n", "", "specify a name")
@@ -91,7 +78,7 @@ func buildTransactionCommand() {
 		Run:   func(cmd *cobra.Command, args []string) {
 			// transaction.Call(clientDo)
 		},
-		PersistentPreRun: assertChainIdSet,
+		PreRun: assertParameters,
 	}
 	callCmd.Flags().StringVarP(&clientDo.AmtFlag, "amt", "a", "", "specify an amount")
 	callCmd.Flags().StringVarP(&clientDo.ToFlag, "to", "t", "", "specify an address to send to")
@@ -107,7 +94,7 @@ func buildTransactionCommand() {
 		Run:   func(cmd *cobra.Command, args []string) {
 			// transaction.Bond(clientDo)
 		},
-		PersistentPreRun: assertChainIdSet,
+		PreRun: assertParameters,
 	}
 	bondCmd.Flags().StringVarP(&clientDo.AmtFlag, "amt", "a", "", "specify an amount")
 	bondCmd.Flags().StringVarP(&clientDo.UnbondtoFlag, "to", "t", "", "specify an address to unbond to")
@@ -120,7 +107,7 @@ func buildTransactionCommand() {
 		Run:   func(cmd *cobra.Command, args []string) {
 			// transaction.Unbond(clientDo)
 		},
-		PersistentPreRun: assertChainIdSet,
+		PreRun: assertParameters,
 	}
 	unbondCmd.Flags().StringVarP(&clientDo.AddrFlag, "addr", "a", "", "specify an address")
 	unbondCmd.Flags().StringVarP(&clientDo.HeightFlag, "height", "n", "", "specify a height to unbond at")
@@ -133,7 +120,7 @@ func buildTransactionCommand() {
 		Run:   func(cmd *cobra.Command, args []string) {
 			// transaction.Rebond(clientDo)
 		},
-		PersistentPreRun: assertChainIdSet,
+		PreRun: assertParameters,
 	}
 	rebondCmd.Flags().StringVarP(&clientDo.AddrFlag, "addr", "a", "", "specify an address")
 	rebondCmd.Flags().StringVarP(&clientDo.HeightFlag, "height", "n", "", "specify a height to unbond at")
@@ -146,7 +133,7 @@ func buildTransactionCommand() {
 		Run:   func(cmd *cobra.Command, args []string) {
 			// transaction.Permsissions(clientDo)
 		},
-		PersistentPreRun: assertChainIdSet,
+		PreRun: assertParameters,
 	}
 	permissionsCmd.Flags().StringVarP(&clientDo.AddrFlag, "addr", "a", "", "specify an address")
 	permissionsCmd.Flags().StringVarP(&clientDo.HeightFlag, "height", "n", "", "specify a height to unbond at")
@@ -158,7 +145,7 @@ func addTransactionPersistentFlags() {
 	TransactionCmd.PersistentFlags().StringVarP(&clientDo.SignAddrFlag, "sign-addr", "", defaultKeyDaemonAddress(), "set eris-keys daemon address (default respects $ERIS_CLIENT_SIGN_ADDRESS)")
 	TransactionCmd.PersistentFlags().StringVarP(&clientDo.NodeAddrFlag, "node-addr", "", defaultNodeRpcAddress(), "set the eris-db node rpc server address (default respects $ERIS_CLIENT_NODE_ADDRESS)")
 	TransactionCmd.PersistentFlags().StringVarP(&clientDo.PubkeyFlag, "pubkey", "", defaultPublicKey(), "specify the public key to sign with (defaults to $ERIS_CLIENT_PUBLIC_KEY)")
-	TransactionCmd.PersistentFlags().StringVarP(&clientDo.AddrFlag, "addr", "", defaultAddress(), "specify the account address (from which the public key can be fetch from eris-keys) (default respects $ERIS_CLIENT_ADDRESS)")
+	TransactionCmd.PersistentFlags().StringVarP(&clientDo.AddrFlag, "addr", "", defaultAddress(), "specify the account address (for which the public key can be found at eris-keys) (default respects $ERIS_CLIENT_ADDRESS)")
 	TransactionCmd.PersistentFlags().StringVarP(&clientDo.ChainidFlag, "chain-id", "", defaultChainId(), "specify the chainID (default respects $CHAIN_ID)")
 	TransactionCmd.PersistentFlags().StringVarP(&clientDo.NonceFlag, "nonce", "", "", "specify the nonce to use for the transaction (should equal the sender account's nonce + 1)")
 
@@ -175,11 +162,11 @@ func defaultChainId() string {
 }
 
 func defaultKeyDaemonAddress() string {
-	return setDefaultString("ERIS_CLIENT_SIGN_ADDRESS", "http://localhost:4767")
+	return setDefaultString("ERIS_CLIENT_SIGN_ADDRESS", "http://127.0.0.1:4767")
 }
 
 func defaultNodeRpcAddress() string {
-	return setDefaultString("ERIS_CLIENT_NODE_ADDRESS", "http://localhost:46657")
+	return setDefaultString("ERIS_CLIENT_NODE_ADDRESS", "http://127.0.0.1:46657")
 }
 
 func defaultPublicKey() string {
@@ -193,9 +180,25 @@ func defaultAddress() string {
 //------------------------------------------------------------------------------
 // Helper functions
 
-func assertChainIdSet(cmd *cobra.Command, args []string) {
+func assertParameters(cmd *cobra.Command, args []string) {
 	if clientDo.ChainidFlag == "" {
 		log.Fatalf(`Cannot run "eris-client tx" without chain id set in --chain-id or $CHAIN_ID.`)
 		os.Exit(1)
 	}
+	 
+	if !strings.HasPrefix(clientDo.NodeAddrFlag, "tcp://") {
+		clientDo.NodeAddrFlag = "tcp://" + clientDo.NodeAddrFlag
+	}
+	if !strings.HasSuffix(clientDo.NodeAddrFlag, "/") {
+		clientDo.NodeAddrFlag += "/"
+	}
+
+	if !strings.HasPrefix(clientDo.SignAddrFlag, "http://") {
+		clientDo.SignAddrFlag = "http://" + clientDo.SignAddrFlag
+	}
+	log.WithFields(log.Fields{
+		"signing address": clientDo.SignAddrFlag,
+		"node address": clientDo.NodeAddrFlag,
+		"chain id": clientDo.ChainidFlag,
+		}).Debug("Asserted parameters")
 }
\ No newline at end of file
diff --git a/client/core/core.go b/client/core/core.go
index c9907f5c..a31cd99a 100644
--- a/client/core/core.go
+++ b/client/core/core.go
@@ -30,12 +30,12 @@ import (
 	"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"
 
+	"github.com/eris-ltd/eris-db/account"
 	tendermint_client "github.com/eris-ltd/eris-db/rpc/tendermint/client"
 	"github.com/eris-ltd/eris-db/txs"
 )
@@ -312,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 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
-// }
+func Broadcast(tx txs.Tx, broadcastRPC string) (*txs.Receipt, error) {
+	client := rpcclient.NewClientURI(broadcastRPC)
+	receipt, err := tendermint_client.BroadcastTx(client, tx)
+	if err != nil {
+		return nil, err
+	}
+	return &receipt, nil
+}
 
 //------------------------------------------------------------------------------------
 // utils for talking to the key server
@@ -354,9 +354,9 @@ func RequestResponse(addr, method string, args map[string]string) (string, error
 	}
 	endpoint := fmt.Sprintf("%s/%s", addr, method)
 	log.WithFields(log.Fields{
-		"endpoint": endpoint,
+		"key server endpoint": endpoint,
 		"request body": string(b),
-		}).Debugf("Sending request body")
+		}).Debugf("Sending request body to key server")
 	req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(b))
 	if err != nil {
 		return "", err
@@ -369,6 +369,11 @@ func RequestResponse(addr, method string, args map[string]string) (string, error
 	if errS != "" {
 		return "", fmt.Errorf("Error (string) calling eris-keys at %s: %s", endpoint, errS)
 	}
+	log.WithFields(log.Fields{
+		"endpoint": endpoint,
+		"request body": string(b),
+		"response": res,
+		}).Debugf("Received response from key server")
 	return res, nil
 }
 
@@ -396,114 +401,119 @@ 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", 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
-// }
+//------------------------------------------------------------------------------------
+// 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 crypto.SignatureEd25519
+	switch tx := tx_.(type) {
+	case *txs.SendTx:
+		inputAddr = tx.Inputs[0].Address
+		defer func(s *crypto.SignatureEd25519) { tx.Inputs[0].Signature = *s }(&sigED)
+	case *txs.NameTx:
+		inputAddr = tx.Input.Address
+		defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+	case *txs.CallTx:
+		inputAddr = tx.Input.Address
+		defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+	case *txs.PermissionsTx:
+		inputAddr = tx.Input.Address
+		defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+	case *txs.BondTx:
+		inputAddr = tx.Inputs[0].Address
+		defer func(s *crypto.SignatureEd25519) {
+			tx.Signature = *s
+			tx.Inputs[0].Signature = *s
+		}(&sigED)
+	case *txs.UnbondTx:
+		inputAddr = tx.Address
+		defer func(s *crypto.SignatureEd25519) { tx.Signature = *s }(&sigED)
+	case *txs.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)
+	log.WithFields(log.Fields{
+		"signature": sig, 
+		}).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
+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
+	// 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
-// }
+	//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
-// 		}
-// 	}
+func SignAndBroadcast(chainID, nodeAddr, signAddr string, tx txs.Tx, sign, broadcast, wait bool) (txResult *TxResult, err error) {
+	// var inputAddr []byte
+	if sign {
+		_, 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
-// }
+	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 = Broadcast(tx, nodeAddr)
+		if err != nil {
+			return nil, err
+		}
+		txResult = &TxResult{
+			Hash: receipt.TxHash,
+		}
+		// NOTE: [ben] is this consistent with the Ethereum protocol?  It should seem
+		// reasonable to get this returned from the chain directly.  The returned benefit
+		// is that the we don't need to trust the chain node
+		if tx_, ok := tx.(*txs.CallTx); ok {
+			if len(tx_.Address) == 0 {
+				txResult.Address = txs.NewContractAddress(tx_.Input.Address, tx_.Input.Sequence)
+			}
+		}
+	}
+	return
+}
 
 // //------------------------------------------------------------------------------------
 // // wait for events
@@ -646,7 +656,11 @@ func checkCommon(nodeAddr, signAddr, pubkey, addr, amtS, nonceS string) (pub cry
 		}
 
 		// fetch nonce from node
-		client := rpcclient.NewClientJSONRPC(nodeAddr)
+		client := rpcclient.NewClientURI(nodeAddr)
+		log.WithFields(log.Fields{
+			"node address": nodeAddr,
+			"account address": fmt.Sprintf("%X", addrBytes),
+			}).Debug("Fetch nonce from node")
 		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())
diff --git a/client/transaction/transaction.go b/client/transaction/transaction.go
index d6f210f9..744f824f 100644
--- a/client/transaction/transaction.go
+++ b/client/transaction/transaction.go
@@ -27,9 +27,12 @@ import (
 )
 
 func Send(do *definitions.ClientDo) {
-	_, err := core.Send(do.NodeAddrFlag, do.SignAddrFlag,
+	sendTransaction, 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)
 	}
+	// unpackSignAndBroadcast(
+		core.SignAndBroadcast(do.ChainidFlag, do.NodeAddrFlag,
+		do.SignAddrFlag, sendTransaction, true, do.BroadcastFlag, do.WaitFlag)//)
 }
\ No newline at end of file
-- 
GitLab