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