Newer
Older
// 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 (
Benjamin Bollen
committed
"fmt"
"github.com/tendermint/go-rpc/client"
acc "github.com/eris-ltd/eris-db/account"
consensus_types "github.com/eris-ltd/eris-db/consensus/types"
tendermint_client "github.com/eris-ltd/eris-db/rpc/tendermint/client"
Benjamin Bollen
committed
tendermint_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types"
"github.com/eris-ltd/eris-db/txs"
Benjamin Bollen
committed
core_types "github.com/eris-ltd/eris-db/core/types"
)
type NodeClient interface {
Broadcast(transaction txs.Tx) (*txs.Receipt, 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)
}
// NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements
// eris-db/client.NodeClient
Benjamin Bollen
committed
var _ NodeClient = (*ErisNodeClient)(nil)
// Eris-Client is a simple struct exposing the client rpc methods
type ErisNodeClient struct {
broadcastRPC string
}
// ErisKeyClient.New returns a new eris-keys client for provided rpc location
// Eris-keys connects over http request-responses
func NewErisNodeClient(rpcString string) *ErisNodeClient {
Benjamin Bollen
committed
return &ErisNodeClient{
broadcastRPC: rpcString,
}
}
//------------------------------------------------------------------------------------
// broadcast to blockchain node
// NOTE: [ben] Eris Client first continues from tendermint rpc, but will have handshake to negotiate
// protocol version for moving towards rpc/v1
func (erisNodeClient *ErisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) {
client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC)
receipt, err := tendermint_client.BroadcastTx(client, tx)
if err != nil {
return nil, err
}
return &receipt, nil
//------------------------------------------------------------------------------------
// 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.
Benjamin Bollen
committed
func (erisNodeClient *ErisNodeClient) Status() (ChainId []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) {
client := rpcclient.NewClientURI(erisNodeClient.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
erisNodeClient.broadcastRPC, err.Error())
return nil, nil, nil, int(0), int64(0), err
}
// unwrap return results
ChainId = res.GenesisHash
ValidatorPublicKey = res.PubKey.Bytes()
LatestBlockHash = res.LatestBlockHash
LatestBlockHeight = res.LatestBlockHeight
LatestBlockTime = res.LatestBlockTime
return
}
// QueryContract executes the contract code at address with the given data
Benjamin Bollen
committed
// NOTE: there is no check on the caller;
func (erisNodeClient *ErisNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) {
Benjamin Bollen
committed
client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC)
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)",
Benjamin Bollen
committed
erisNodeClient.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 (erisNodeClient *ErisNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) {
client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC)
// 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)",
erisNodeClient.broadcastRPC, address, data, code, err.Error())
return nil, int64(0), err
}
return callResult.Return, callResult.GasUsed, nil
}
// GetAccount returns a copy of the account
func (erisNodeClient *ErisNodeClient) GetAccount(address []byte) (*acc.Account, error) {
client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC)
account, err := tendermint_client.GetAccount(client, address)
if err != nil {
err = fmt.Errorf("Error connecting to node (%s) to fetch account (%X): %s",
erisNodeClient.broadcastRPC, address, err.Error())
return nil, err
}
if account == nil {
err = fmt.Errorf("Unknown account %X at node (%s)", address, erisNodeClient.broadcastRPC)
return nil, err
}
return account.Copy(), nil
}
Benjamin Bollen
committed
// DumpStorage returns the full storage for an account.
func (erisNodeClient *ErisNodeClient) DumpStorage(address []byte) (storage *core_types.Storage, err error) {
Benjamin Bollen
committed
client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC)
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",
erisNodeClient.broadcastRPC, address, err.Error())
return nil, err
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 (erisNodeClient *ErisNodeClient) GetName(name string) (owner []byte, data string, expirationBlock int, err error) {
client := rpcclient.NewClientURI(erisNodeClient.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)",
erisNodeClient.broadcastRPC, name)
Benjamin Bollen
committed
owner = make([]byte, len(entryResult.Owner))
copy(owner, entryResult.Owner)
expirationBlock = entryResult.Expires
return
}
//--------------------------------------------------------------------------------------------
Benjamin Bollen
committed
func (erisNodeClient *ErisNodeClient) ListValidators() (blockHeight int,
bondedValidators []consensus_types.Validator, unbondingValidators []consensus_types.Validator, err error) {
client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC)
validatorsResult, err := tendermint_client.ListValidators(client)
if err != nil {
err = fmt.Errorf("Error connecting to node (%s) to get validators",
erisNodeClient.broadcastRPC)
return 0, nil, nil, err
}
// unwrap return results
blockHeight = validatorsResult.BlockHeight
bondedValidators = validatorsResult.BondedValidators
unbondingValidators = validatorsResult.UnbondingValidators
return
}