diff --git a/client/client.go b/client/client.go index 1c9c3502a5dc46d661c654414c917257cc143e05..81584b291dd296740a7d667e1c3d6f194cb71a15 100644 --- a/client/client.go +++ b/client/client.go @@ -18,19 +18,23 @@ package client import ( "fmt" + // "strings" "github.com/tendermint/go-rpc/client" + log "github.com/eris-ltd/eris-logger" + acc "github.com/eris-ltd/eris-db/account" consensus_types "github.com/eris-ltd/eris-db/consensus/types" + core_types "github.com/eris-ltd/eris-db/core/types" tendermint_client "github.com/eris-ltd/eris-db/rpc/tendermint/client" tendermint_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" "github.com/eris-ltd/eris-db/txs" - core_types "github.com/eris-ltd/eris-db/core/types" ) type NodeClient interface { Broadcast(transaction txs.Tx) (*txs.Receipt, error) + DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) Status() (ChainId []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) @@ -38,11 +42,19 @@ type NodeClient interface { QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) - DumpStorage(address []byte) (storage *core_types.Storage, err error) + DumpStorage(address []byte) (storage *core_types.Storage, err error) GetName(name string) (owner []byte, data string, expirationBlock int, err error) ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error) } +type NodeWebsocketClient interface { + Subscribe(eventId string) error + Unsubscribe(eventId string) error + + WaitForConfirmation(tx txs.Tx, chainId string, inputAddr []byte) (chan Confirmation, error) + Close() +} + // NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements // eris-db/client.NodeClient var _ NodeClient = (*ErisNodeClient)(nil) @@ -75,6 +87,38 @@ func (erisNodeClient *ErisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) return &receipt, nil } +func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) { + var wsAddr string + // TODO: clean up this inherited mess on dealing with the address prefixes. + nodeAddr := erisNodeClient.broadcastRPC + // if strings.HasPrefix(nodeAddr, "http://") { + // wsAddr = strings.TrimPrefix(nodeAddr, "http://") + // } + // if strings.HasPrefix(nodeAddr, "tcp://") { + // wsAddr = strings.TrimPrefix(nodeAddr, "tcp://") + // } + // if strings.HasPrefix(nodeAddr, "unix://") { + // log.WithFields(log.Fields{ + // "node address": nodeAddr, + // }).Error("Unable to subscribe to websocket from unix socket.") + // return nil, fmt.Errorf("Unable to construct websocket from unix socket: %s", nodeAddr) + // } + // wsAddr = "ws://" + wsAddr + wsAddr = nodeAddr + log.WithFields(log.Fields{ + "websocket address": wsAddr, + "endpoint": "/websocket", + }).Debug("Subscribing to websocket address") + wsClient := rpcclient.NewWSClient(wsAddr, "/websocket") + if _, err = wsClient.Start(); err != nil { + return nil, err + } + derivedErisNodeWebsocketClient := &ErisNodeWebsocketClient{ + tendermintWebsocket: wsClient, + } + return derivedErisNodeWebsocketClient, nil +} + //------------------------------------------------------------------------------------ // RPC requests other than transaction related @@ -111,11 +155,11 @@ func (erisNodeClient *ErisNodeClient) ChainId() (ChainName, ChainId string, Gene ChainId = chainIdResult.ChainId GenesisHash = make([]byte, len(chainIdResult.GenesisHash)) copy(GenesisHash[:], chainIdResult.GenesisHash) - return + return } // QueryContract executes the contract code at address with the given data -// NOTE: there is no check on the caller; +// NOTE: there is no check on the caller; func (erisNodeClient *ErisNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) callResult, err := tendermint_client.Call(client, callerAddress, calleeAddress, data) @@ -207,6 +251,5 @@ func (erisNodeClient *ErisNodeClient) ListValidators() (blockHeight int, blockHeight = validatorsResult.BlockHeight bondedValidators = validatorsResult.BondedValidators unbondingValidators = validatorsResult.UnbondingValidators - return + return } - diff --git a/client/cmd/status.go b/client/cmd/status.go index 376976300942b6af0d41fead8a5f1eb21ad052d0..5b29803d848fba681b028bcc0cae3cf969853968 100644 --- a/client/cmd/status.go +++ b/client/cmd/status.go @@ -23,8 +23,8 @@ import ( ) var StatusCmd = &cobra.Command{ - Use: "status", - Short: "eris-client status returns the current status from a chain.", + Use: "status", + Short: "eris-client status returns the current status from a chain.", Long: `eris-client status returns the current status from a chain. `, Run: func(cmd *cobra.Command, args []string) { @@ -33,7 +33,7 @@ var StatusCmd = &cobra.Command{ } func buildStatusCommand() { - addStatusPersistentFlags() + addStatusPersistentFlags() } func addStatusPersistentFlags() { diff --git a/client/cmd/transaction.go b/client/cmd/transaction.go index 13076b0ec366a21a1e06082de52ff2dbf99f8e17..87a3e10c5ee99aeb84a97062280d98c4047d0016 100644 --- a/client/cmd/transaction.go +++ b/client/cmd/transaction.go @@ -149,7 +149,7 @@ func addTransactionPersistentFlags() { // TransactionCmd.PersistentFlags().BoolVarP(&clientDo.SignFlag, "sign", "s", false, "sign the transaction using the eris-keys daemon") TransactionCmd.PersistentFlags().BoolVarP(&clientDo.BroadcastFlag, "broadcast", "b", true, "broadcast the transaction to the blockchain") - TransactionCmd.PersistentFlags().BoolVarP(&clientDo.WaitFlag, "wait", "w", false, "wait for the transaction to be committed in a block") + TransactionCmd.PersistentFlags().BoolVarP(&clientDo.WaitFlag, "wait", "w", true, "wait for the transaction to be committed in a block") } //------------------------------------------------------------------------------ diff --git a/client/core/transaction_factory.go b/client/core/transaction_factory.go index e631144c11bc36f70327b86488bd0ce66347564c..0385dccc65c77978e6cf6473b486df8962629370 100644 --- a/client/core/transaction_factory.go +++ b/client/core/transaction_factory.go @@ -20,8 +20,7 @@ import ( "encoding/hex" "fmt" "strconv" - // "strings" - // "time" + log "github.com/eris-ltd/eris-logger" ptypes "github.com/eris-ltd/eris-db/permission/types" @@ -239,24 +238,24 @@ func Unbond(addrS, heightS string) (*txs.UnbondTx, error) { func Rebond(addrS, heightS string) (*txs.RebondTx, error) { return nil, fmt.Errorf("Rebond Transaction formation to be implemented on 0.12.0") -// 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 + // 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 } type TxResult struct { @@ -274,9 +273,9 @@ type TxResult struct { // Preserve func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient keys.KeyClient, tx txs.Tx, sign, broadcast, wait bool) (txResult *TxResult, err error) { - // var inputAddr []byte + var inputAddr []byte if sign { - _, tx, err = signTx(keyClient, chainID, tx) + inputAddr, tx, err = signTx(keyClient, chainID, tx) if err != nil { return nil, err } @@ -286,32 +285,46 @@ func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient ke } 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 - // } - // log.WithFields(log.Fields{ - // "", - // }).Debug("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 - // } - // }() - // } - // } + if wait { + wsClient, err := nodeClient.DeriveWebsocketClient() + if err != nil { + return nil, err + } + var confirmationChannel chan client.Confirmation + confirmationChannel, err = wsClient.WaitForConfirmation(tx, chainID, inputAddr) + if err != nil { + return nil, err + } else { + defer func() { + if err != nil { + // if broadcast threw an error, just return + return + } + log.Debug("Waiting for transaction to be confirmed.") + confirmation := <-confirmationChannel + if confirmation.Error != nil { + log.Errorf("Encountered error waiting for event: %s\n", confirmation.Error) + err = confirmation.Error + return + } + if confirmation.Exception != nil { + log.Errorf("Encountered Exception from chain w: %s\n", confirmation.Error) + err = confirmation.Exception + return + } + txResult.BlockHash = confirmation.BlockHash + txResult.Exception = "" + eventDataTx, ok := confirmation.Event.(*txs.EventDataTx) + if !ok { + log.Errorf("Received wrong event type.") + err = fmt.Errorf("Received wrong event type.") + return + } + txResult.Return = eventDataTx.Return + }() + } + } + var receipt *txs.Receipt receipt, err = nodeClient.Broadcast(tx) if err != nil { @@ -331,102 +344,3 @@ func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient ke } return } - -//------------------------------------------------------------------------------------ -// wait for events - -type Msg struct { - BlockHash []byte - Value []byte - Exception string - Error error -} - -// func subscribeAndWait(tx txs.Tx, chainID, nodeAddr string, inputAddr []byte) (chan Msg, error) { -// // subscribe to event and wait for tx to be committed -// var wsAddr string -// if strings.HasPrefix(nodeAddr, "http://") { -// wsAddr = strings.TrimPrefix(nodeAddr, "http://") -// } -// if strings.HasPrefix(nodeAddr, "tcp://") { -// wsAddr = strings.TrimPrefix(nodeAddr, "tcp://") -// } -// if strings.HasPrefix(nodeAddr, "unix://") { -// log.WithFields(log.Fields{ -// "node address": nodeAddr, -// }).Warn("Unable to subscribe to websocket from unix socket.") -// return nil, fmt.Errorf("Unable to subscribe to websocket from unix socket: %s", nodeAddr) -// } -// wsAddr = "ws://" + wsAddr -// log.WithFields(log.Fields{ -// "websocket address": wsAddr, -// "endpoint": "/websocket", -// }).Debug("Subscribing to websocket address") -// wsClient := rpcclient.NewWSClient(wsAddr, "/websocket") -// wsClient.Start() -// eid := txs.EventStringAccInput(inputAddr) -// if err := wsClient.Subscribe(eid); err != nil { -// return nil, fmt.Errorf("Error subscribing to AccInput event: %v", err) -// } -// if err := wsClient.Subscribe(txs.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.(txs.EventDataNewBlock) -// if ok { -// log.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 -// } diff --git a/client/core/transaction_factory_util.go b/client/core/transaction_factory_util.go index 01c2382618907eaaf8e7aa25903167ca0923d08b..3ad64a6ba36ad88ad5c11b6a0d70584285239095 100644 --- a/client/core/transaction_factory_util.go +++ b/client/core/transaction_factory_util.go @@ -120,10 +120,9 @@ func checkCommon(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, } pubKeyBytes, err2 = keyClient.PublicKey(addressBytes) if err2 != nil { - err = fmt.Errorf("Failed to fetch pubkey for address (%s): %v", addr, err) + err = fmt.Errorf("Failed to fetch pubkey for address (%s): %v", addr, err2) return } - } if len(pubKeyBytes) == 0 { diff --git a/client/methods/status.go b/client/methods/status.go index 2954fd1d283c759e24eabf66ed6499fd74ff397f..ba2470e7645ba0759445daed1042211c8f184401 100644 --- a/client/methods/status.go +++ b/client/methods/status.go @@ -25,7 +25,7 @@ import ( "github.com/eris-ltd/eris-db/definitions" ) -func Status(do *definitions.ClientDo) { +func Status(do *definitions.ClientDo) { erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag) genesisHash, validatorPublicKey, latestBlockHash, latestBlockHeight, latestBlockTime, err := erisNodeClient.Status() if err != nil { @@ -40,14 +40,14 @@ func Status(do *definitions.ClientDo) { } log.WithFields(log.Fields{ - "chain": do.NodeAddrFlag, - "genesisHash": fmt.Sprintf("%X", genesisHash), - "chainName": chainName, - "chainId": chainId, - "genesisHash from chainId":fmt.Sprintf("%X", genesisHashfromChainId), - "validator public key": fmt.Sprintf("%X", validatorPublicKey), - "latest block hash": fmt.Sprintf("%X", latestBlockHash), - "latest block height": latestBlockHeight, - "latest block time": latestBlockTime, + "chain": do.NodeAddrFlag, + "genesisHash": fmt.Sprintf("%X", genesisHash), + "chainName": chainName, + "chainId": chainId, + "genesisHash from chainId": fmt.Sprintf("%X", genesisHashfromChainId), + "validator public key": fmt.Sprintf("%X", validatorPublicKey), + "latest block hash": fmt.Sprintf("%X", latestBlockHash), + "latest block height": latestBlockHeight, + "latest block time": latestBlockTime, }).Info("status") } diff --git a/client/mock/client_mock.go b/client/mock/client_mock.go index 24a94e93a37e04b6a84cf1a59c34963fc0fcc998..12cdd76cf9b15f9575898224889a1528eb8a58b7 100644 --- a/client/mock/client_mock.go +++ b/client/mock/client_mock.go @@ -19,10 +19,10 @@ package mock import ( "github.com/tendermint/go-crypto" - consensus_types "github.com/eris-ltd/eris-db/consensus/types" - core_types "github.com/eris-ltd/eris-db/core/types" acc "github.com/eris-ltd/eris-db/account" . "github.com/eris-ltd/eris-db/client" + consensus_types "github.com/eris-ltd/eris-db/consensus/types" + core_types "github.com/eris-ltd/eris-db/core/types" "github.com/eris-ltd/eris-db/txs" ) @@ -50,6 +50,10 @@ func (mock *MockNodeClient) Broadcast(transaction txs.Tx) (*txs.Receipt, error) return txReceipt, nil } +func (mock *MockNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) { + return nil, nil +} + func (mock *MockNodeClient) GetAccount(address []byte) (*acc.Account, error) { // make zero account var zero [32]byte @@ -88,7 +92,6 @@ func (mock *MockNodeClient) Status() (ChainId []byte, return } - // QueryContract executes the contract code at address with the given data func (mock *MockNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) { // return zero @@ -103,7 +106,6 @@ func (mock *MockNodeClient) QueryContractCode(address, code, data []byte) (ret [ return ret, 0, nil } - func (mock *MockNodeClient) DumpStorage(address []byte) (storage *core_types.Storage, err error) { return nil, nil } @@ -112,6 +114,6 @@ func (mock *MockNodeClient) GetName(name string) (owner []byte, data string, exp return nil, "", 0, nil } -func (mock *MockNodeClient) ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error){ +func (mock *MockNodeClient) ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error) { return 0, nil, nil, nil } diff --git a/client/websocket_client.go b/client/websocket_client.go new file mode 100644 index 0000000000000000000000000000000000000000..42f1bf37d384f999cb5d913cbf741124e71f1736 --- /dev/null +++ b/client/websocket_client.go @@ -0,0 +1,208 @@ +// Copyright 2015, 2016 Eris Industries (UK) Ltd. +// This file is part of Eris-RT + +// Eris-RT is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Eris-RT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Eris-RT. If not, see <http://www.gnu.org/licenses/>. + +package client + +import ( + "bytes" + "fmt" + "time" + + "github.com/tendermint/go-rpc/client" + "github.com/tendermint/go-wire" + + log "github.com/eris-ltd/eris-logger" + + ctypes "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" + "github.com/eris-ltd/eris-db/txs" +) + +const ( + MaxCommitWaitTimeSeconds = 10 +) + +type Confirmation struct { + BlockHash []byte + Event txs.EventData + Exception error + Error error +} + +// NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements +// eris-db/client.NodeClient +var _ NodeWebsocketClient = (*ErisNodeWebsocketClient)(nil) + +type ErisNodeWebsocketClient struct { + // TODO: assert no memory leak on closing with open websocket + tendermintWebsocket *rpcclient.WSClient +} + +// Subscribe to an eventid +func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Subscribe(eventid string) error { + // TODO we can in the background listen to the subscription id and remember it to ease unsubscribing later. + return erisNodeWebsocketClient.tendermintWebsocket.Subscribe(eventid) +} + +// Unsubscribe from an eventid +func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Unsubscribe(subscriptionId string) error { + return erisNodeWebsocketClient.tendermintWebsocket.Unsubscribe(subscriptionId) +} + +// Returns a channel that will receive a confirmation with a result or the exception that +// has been confirmed; or an error is returned and the confirmation channel is nil. +func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx txs.Tx, chainId string, inputAddr []byte) (chan Confirmation, error) { + // check no errors are reported on the websocket + if err := erisNodeWebsocketClient.assertNoErrors(); err != nil { + return nil, err + } + + // Setup the confirmation channel to be returned + confirmationChannel := make(chan Confirmation, 1) + var latestBlockHash []byte + + eid := txs.EventStringAccInput(inputAddr) + if err := erisNodeWebsocketClient.tendermintWebsocket.Subscribe(eid); err != nil { + return nil, fmt.Errorf("Error subscribing to AccInput event (%s): %v", eid, err) + } + if err := erisNodeWebsocketClient.tendermintWebsocket.Subscribe(txs.EventStringNewBlock()); err != nil { + return nil, fmt.Errorf("Error subscribing to NewBlock event: %v", err) + } + // Read the incoming events + go func() { + var err error + for { + resultBytes := <-erisNodeWebsocketClient.tendermintWebsocket.ResultsCh + result := new(ctypes.ErisDBResult) + if wire.ReadJSONPtr(result, resultBytes, &err); err != nil { + // keep calm and carry on + log.Errorf("[eris-client] Failed to unmarshal json bytes for websocket event: %s", err) + continue + } + + subscription, ok := (*result).(*ctypes.ResultSubscribe) + if ok { + // Received confirmation of subscription to event streams + // TODO: collect subscription IDs, push into channel and on completion + // unsubscribe + log.Infof("[eris-client] recceived confirmation for event (%s) with subscription id (%s).", + subscription.Event, subscription.SubscriptionId) + continue + } + + event, ok := (*result).(*ctypes.ResultEvent) + if !ok { + // keep calm and carry on + log.Warnf("[eris-client] Failed to cast to ResultEvent for websocket event: %s", *result) + continue + } + + blockData, ok := event.Data.(txs.EventDataNewBlock) + if ok { + latestBlockHash = blockData.Block.Hash() + log.WithFields(log.Fields{ + "new block": blockData.Block, + "latest hash": latestBlockHash, + }).Debug("Registered new block") + continue + } + + // we don't accept events unless they came after a new block (ie. in) + if latestBlockHash == nil { + log.Infof("[eris-client] no first block has been registered, so ignoring event: %s", event.Event) + continue + } + + if event.Event != eid { + log.Warnf("[eris-client] received unsolicited event! Got %s, expected %s\n", event.Event, eid) + continue + } + + data, ok := event.Data.(txs.EventDataTx) + if !ok { + // We are on the lookout for EventDataTx + confirmationChannel <- Confirmation{ + BlockHash: latestBlockHash, + Event: nil, + Exception: fmt.Errorf("response error: expected result.Data to be *types.EventDataTx"), + Error: nil, + } + return + } + + if !bytes.Equal(txs.TxHash(chainId, data.Tx), txs.TxHash(chainId, tx)) { + log.WithFields(log.Fields{ + // TODO: consider re-implementing TxID again, or other more clear debug + "received transaction event": txs.TxHash(chainId, data.Tx), + }).Debug("Received different event") + continue + } + + if data.Exception != "" { + confirmationChannel <- Confirmation{ + BlockHash: latestBlockHash, + Event: &data, + Exception: fmt.Errorf("Transaction confirmed with exception:", data.Exception), + Error: nil, + } + return + } + // success, return the full event and blockhash and exit go-routine + confirmationChannel <- Confirmation{ + BlockHash: latestBlockHash, + Event: &data, + Exception: nil, + Error: nil, + } + return + } + + }() + + // TODO: [ben] this is a draft implementation as resources on time.After can not be + // recovered before the timeout. Close-down timeout at success properly. + timeout := time.After(time.Duration(MaxCommitWaitTimeSeconds) * time.Second) + + go func() { + <-timeout + confirmationChannel <- Confirmation{ + BlockHash: nil, + Event: nil, + Exception: nil, + Error: fmt.Errorf("timed out waiting for event"), + } + return + }() + return confirmationChannel, nil +} + +func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Close() { + if erisNodeWebsocketClient.tendermintWebsocket != nil { + erisNodeWebsocketClient.tendermintWebsocket.Stop() + } +} + +func (erisNodeWebsocketClient *ErisNodeWebsocketClient) assertNoErrors() error { + if erisNodeWebsocketClient.tendermintWebsocket != nil { + select { + case err := <-erisNodeWebsocketClient.tendermintWebsocket.ErrorsCh: + return err + default: + return nil + } + } else { + return fmt.Errorf("Eris-client has no websocket initialised.") + } +} diff --git a/client/ws_client.go b/client/ws_client.go index f2542c1a477eacb27d12f4e8f0f563c8e530b891..d60986b6f9a41575642dbf963aaceb281d52dfe5 100644 --- a/client/ws_client.go +++ b/client/ws_client.go @@ -1,6 +1,12 @@ // Websocket client implementation. This will be used in tests. package client +// NOTE: this websocket client acts on rpc/v0, +// uses github.com/gorilla/websocket +// and will be deprecated after 0.12 +// It is recommended to use the interfaces NodeClient +// and NodeWebsocketClient. + import ( "fmt" "net/http" diff --git a/keys/key_client.go b/keys/key_client.go index 44a60f1f5c9f94f0839ec58eb8e115f2d9ec1d1e..994f5a67010943a3c7ac27416d192cb6cd2be067 100644 --- a/keys/key_client.go +++ b/keys/key_client.go @@ -68,7 +68,7 @@ func (erisKeys *ErisKeyClient) Sign(signBytesString string, signAddress []byte) // the eris-keys server. func (erisKeys *ErisKeyClient) PublicKey(address []byte) (publicKey []byte, err error) { args := map[string]string{ - "addr": string(address), + "addr": fmt.Sprintf("%X", address), } pubS, err := RequestResponse(erisKeys.rpcString, "pub", args) if err != nil {