Newer
Older
// Copyright 2017 Monax Industries Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package client
import (
Benjamin Bollen
committed
"fmt"
// "strings"
Benjamin Bollen
committed
"github.com/tendermint/go-rpc/client"
acc "github.com/hyperledger/burrow/account"
consensus_types "github.com/hyperledger/burrow/consensus/types"
core_types "github.com/hyperledger/burrow/core/types"
"github.com/hyperledger/burrow/logging"
"github.com/hyperledger/burrow/logging/loggers"
tendermint_client "github.com/hyperledger/burrow/rpc/tendermint/client"
tendermint_types "github.com/hyperledger/burrow/rpc/tendermint/core/types"
"github.com/hyperledger/burrow/txs"
tmLog15 "github.com/tendermint/log15"
)
type NodeClient interface {
Broadcast(transaction txs.Tx) (*txs.Receipt, error)
DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error)
Status() (ChainId []byte, ValidatorPublicKey []byte, LatestBlockHash []byte,
Benjamin Bollen
committed
LatestBlockHeight int, LatestBlockTime int64, err error)
GetAccount(address []byte) (*acc.Account, error)
Benjamin Bollen
committed
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)
GetName(name string) (owner []byte, data string, expirationBlock int, err error)
ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error)
// Logging context for this NodeClient
Logger() loggers.InfoTraceLogger
}
type NodeWebsocketClient interface {
Subscribe(eventId string) error
Unsubscribe(eventId string) error
WaitForConfirmation(tx txs.Tx, chainId string, inputAddr []byte) (chan Confirmation, error)
// NOTE [ben] Compiler check to ensure burrowNodeClient successfully implements
// burrow/client.NodeClient
var _ NodeClient = (*burrowNodeClient)(nil)
// burrow-client is a simple struct exposing the client rpc methods
type burrowNodeClient struct {
broadcastRPC string
}
// BurrowKeyClient.New returns a new monax-keys client for provided rpc location
// Monax-keys connects over http request-responses
func NewBurrowNodeClient(rpcString string, logger loggers.InfoTraceLogger) *burrowNodeClient {
return &burrowNodeClient{
broadcastRPC: rpcString,
// Note [Ben]: This is a hack to silence Tendermint logger from tendermint/go-rpc
// it needs to be initialised before go-rpc, hence it's placement here.
func init() {
h := tmLog15.LvlFilterHandler(tmLog15.LvlWarn, tmLog15.StdoutHandler)
tmLog15.Root().SetHandler(h)
}
//------------------------------------------------------------------------------------
// broadcast to blockchain node
func (burrowNodeClient *burrowNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) {
client := rpcclient.NewClientURI(burrowNodeClient.broadcastRPC)
receipt, err := tendermint_client.BroadcastTx(client, tx)
if err != nil {
return nil, err
}
return &receipt, nil
func (burrowNodeClient *burrowNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) {
// TODO: clean up this inherited mess on dealing with the address prefixes.
// 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
logging.TraceMsg(burrowNodeClient.logger, "Subscribing to websocket address",
"websocket address", wsAddr,
"endpoint", "/websocket",
)
wsClient := rpcclient.NewWSClient(wsAddr, "/websocket")
if _, err = wsClient.Start(); err != nil {
return nil, err
}
derivedBurrowNodeWebsocketClient := &burrowNodeWebsocketClient{
tendermintWebsocket: wsClient,
logger: logging.WithScope(burrowNodeClient.logger, "BurrowNodeWebsocketClient"),
//------------------------------------------------------------------------------------
// RPC requests other than transaction related
// Status returns the ChainId (GenesisHash), validator's PublicKey, latest block hash
// the block height and the latest block time.
func (burrowNodeClient *burrowNodeClient) Status() (GenesisHash []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) {
client := rpcclient.NewClientJSONRPC(burrowNodeClient.broadcastRPC)
res, err := tendermint_client.Status(client)
if err != nil {
err = fmt.Errorf("Error connecting to node (%s) to get status: %s",
Benjamin Bollen
committed
return nil, nil, nil, int(0), int64(0), err
}
Benjamin Bollen
committed
// unwrap return results
Benjamin Bollen
committed
ValidatorPublicKey = res.PubKey.Bytes()
LatestBlockHash = res.LatestBlockHash
LatestBlockHeight = res.LatestBlockHeight
LatestBlockTime = res.LatestBlockTime
return
}
func (burrowNodeClient *burrowNodeClient) ChainId() (ChainName, ChainId string, GenesisHash []byte, err error) {
client := rpcclient.NewClientJSONRPC(burrowNodeClient.broadcastRPC)
chainIdResult, err := tendermint_client.ChainId(client)
if err != nil {
err = fmt.Errorf("Error connecting to node (%s) to get chain id: %s",
return "", "", nil, err
}
// unwrap results
ChainName = chainIdResult.ChainName
ChainId = chainIdResult.ChainId
GenesisHash = make([]byte, len(chainIdResult.GenesisHash))
copy(GenesisHash[:], chainIdResult.GenesisHash)
Benjamin Bollen
committed
// QueryContract executes the contract code at address with the given data
func (burrowNodeClient *burrowNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) {
client := rpcclient.NewClientJSONRPC(burrowNodeClient.broadcastRPC)
Benjamin Bollen
committed
callResult, err := tendermint_client.Call(client, callerAddress, calleeAddress, data)
if err != nil {
err = fmt.Errorf("Error connnecting to node (%s) to query contract at (%X) with data (%X)",
burrowNodeClient.broadcastRPC, calleeAddress, data, err.Error())
Benjamin Bollen
committed
return nil, int64(0), err
Benjamin Bollen
committed
return callResult.Return, callResult.GasUsed, nil
Benjamin Bollen
committed
// QueryContractCode executes the contract code at address with the given data but with provided code
func (burrowNodeClient *burrowNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) {
client := rpcclient.NewClientJSONRPC(burrowNodeClient.broadcastRPC)
Benjamin Bollen
committed
// TODO: [ben] Call and CallCode have an inconsistent signature; it makes sense for both to only
// have a single address that is the contract to query.
callResult, err := tendermint_client.CallCode(client, address, code, data)
if err != nil {
err = fmt.Errorf("Error connnecting to node (%s) to query contract code at (%X) with data (%X) and code (%X)",
burrowNodeClient.broadcastRPC, address, data, code, err.Error())
Benjamin Bollen
committed
return nil, int64(0), err
}
return callResult.Return, callResult.GasUsed, nil
}
func (burrowNodeClient *burrowNodeClient) GetAccount(address []byte) (*acc.Account, error) {
client := rpcclient.NewClientJSONRPC(burrowNodeClient.broadcastRPC)
account, err := tendermint_client.GetAccount(client, address)
if err != nil {
err = fmt.Errorf("Error connecting to node (%s) to fetch account (%X): %s",
err = fmt.Errorf("Unknown account %X at node (%s)", address, burrowNodeClient.broadcastRPC)
return nil, err
}
return account.Copy(), nil
}
Benjamin Bollen
committed
// DumpStorage returns the full storage for an account.
func (burrowNodeClient *burrowNodeClient) DumpStorage(address []byte) (storage *core_types.Storage, err error) {
client := rpcclient.NewClientJSONRPC(burrowNodeClient.broadcastRPC)
Benjamin Bollen
committed
resultStorage, err := tendermint_client.DumpStorage(client, address)
if err != nil {
err = fmt.Errorf("Error connecting to node (%s) to get storage for account (%X): %s",
Benjamin Bollen
committed
}
// UnwrapResultDumpStorage is an inefficient full deep copy,
// to transform the type to /core/types.Storage
// TODO: removing go-wire and go-rpc allows us to collapse these types
storage = tendermint_types.UnwrapResultDumpStorage(resultStorage)
return
Benjamin Bollen
committed
}
//--------------------------------------------------------------------------------------------
// Name registry
func (burrowNodeClient *burrowNodeClient) GetName(name string) (owner []byte, data string, expirationBlock int, err error) {
client := rpcclient.NewClientJSONRPC(burrowNodeClient.broadcastRPC)
entryResult, err := tendermint_client.GetName(client, name)
if err != nil {
err = fmt.Errorf("Error connecting to node (%s) to get name registrar entry for name (%s)",
owner = entryResult.Owner
expirationBlock = entryResult.Expires
return
}
//--------------------------------------------------------------------------------------------
Benjamin Bollen
committed
func (burrowNodeClient *burrowNodeClient) ListValidators() (blockHeight int,
bondedValidators []consensus_types.Validator, unbondingValidators []consensus_types.Validator, err error) {
client := rpcclient.NewClientJSONRPC(burrowNodeClient.broadcastRPC)
validatorsResult, err := tendermint_client.ListValidators(client)
if err != nil {
err = fmt.Errorf("Error connecting to node (%s) to get validators",
return 0, nil, nil, err
}
// unwrap return results
blockHeight = validatorsResult.BlockHeight
bondedValidators = validatorsResult.BondedValidators
unbondingValidators = validatorsResult.UnbondingValidators
func (burrowNodeClient *burrowNodeClient) Logger() loggers.InfoTraceLogger {
return burrowNodeClient.logger