Skip to content

Commit

Permalink
Merge pull request #15 from wangdayong228/dev-retry-client
Browse files Browse the repository at this point in the history
Support client with retry and add example for client
  • Loading branch information
resodo authored May 12, 2020
2 parents 26223df + 865275d commit 6bf2802
Show file tree
Hide file tree
Showing 7 changed files with 372 additions and 34 deletions.
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,22 @@ func main() {
fmt.Printf("decoded transfer event: {From: 0x%x, To: 0x%x, Value: %v} ", Transfer.From, Transfer.To, Transfer.Value)
}

```
```
## Appendix
### Mapping of solidity types to go types
This is a mapping table for map solidity types to go types when using contract methods GetData/Call/SendTransaction/DecodeEvent
| solidity types | go types |
|----------------------------------------------|-----------------------------------------------------------------------------------|
| address | common.Address |
| uint8,uint16,uint32,uint64 | uint8,uint16,uint32,uint64 |
| uint24,uint40,uint48,uint56,uint72...uint256 | *big.Int |
| int8,int16,int32,int64 | int8,int16,int32,int64 |
| int24,int40,int48,int56,int72...int256 | *big.Int |
| fixed bytes (bytes1,bytes2...bytes32) | [length]byte |
| fixed type T array (T[length]) | [length]TG (TG is go type matched with solidty type T) |
| bytes | []byte |
| dynamic type T array T[] | []TG ((TG is go type matched with solidty type T)) |
| function | [24]byte |
| string | string |
| bool | bool |
| tuple | struct eg:[{"name": "balance","type": "uint256"}] => struct {Balance *big.Int} |
34 changes: 30 additions & 4 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,16 @@ func NewClient(nodeURL string) (*Client, error)
```
NewClient creates a new instance of Client with specified conflux node url.

#### func NewClientWithRetry

```go
func NewClientWithRetry(nodeURL string, retryCount int, retryInterval time.Duration) (*Client, error)
```
NewClientWithRetry creates a retryable new instance of Client with specified
conflux node url and retry options.

the retryInterval will be set to 1 second if pass 0

#### func (*Client) ApplyUnsignedTransactionDefault

```go
Expand Down Expand Up @@ -433,24 +443,36 @@ Call calls to the contract method with args and fills the excuted result to the

the resultPtr should be a pointer of the method output struct type.

please refer
https://github.com/Conflux-Chain/go-conflux-sdk/blob/master/README.md to get the
mappings of solidity types to go types

#### func (*Contract) DecodeEvent

```go
func (contract *Contract) DecodeEvent(out interface{}, event string, log types.LogEntry) error
```
DecodeEvent unpacks a retrieved log into the provided output structure.

please refer
https://github.com/Conflux-Chain/go-conflux-sdk/blob/master/README.md to get the
mappings of solidity types to go types

#### func (*Contract) GetData

```go
func (contract *Contract) GetData(method string, args ...interface{}) ([]byte, error)
```
GetData packs the given method name to conform the ABI of the contract "c".
Method call's data will consist of method_id, args0, arg1, ... argN. Method id
consists of 4 bytes and arguments are all 32 bytes. Method ids are created from
the first 4 bytes of the hash of the methods string signature. (signature =
GetData packs the given method name to conform the ABI of the contract. Method
call's data will consist of method_id, args0, arg1, ... argN. Method id consists
of 4 bytes and arguments are all 32 bytes. Method ids are created from the first
4 bytes of the hash of the methods string signature. (signature =
baz(uint32,string32))

please refer
https://github.com/Conflux-Chain/go-conflux-sdk/blob/master/README.md to get the
mappings of solidity types to go types

#### func (*Contract) SendTransaction

```go
Expand All @@ -459,6 +481,10 @@ func (contract *Contract) SendTransaction(option *types.ContractMethodSendOption
SendTransaction sends a transaction to the contract method with args and returns
its transaction hash

please refer
https://github.com/Conflux-Chain/go-conflux-sdk/blob/master/README.md to get the
mappings of solidity types to go types

### type ContractDeployResult

```go
Expand Down
119 changes: 92 additions & 27 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,85 @@ import (

// Client represents a client to interact with Conflux blockchain.
type Client struct {
rpcClient *rpc.Client
rpcRequester rpcRequester
accountManager AccountManagerOperator
}

// NewClient creates a new instance of Client with specified conflux node url.
func NewClient(nodeURL string) (*Client, error) {
client, err := rpc.Dial(nodeURL)
client, err := NewClientWithRetry(nodeURL, 0, 0)
return client, err
}

// NewClientWithRetry creates a retryable new instance of Client with specified conflux node url and retry options.
//
// the retryInterval will be set to 1 second if pass 0
func NewClientWithRetry(nodeURL string, retryCount int, retryInterval time.Duration) (*Client, error) {

var client Client

rpcClient, err := rpc.Dial(nodeURL)
if err != nil {
return nil, types.WrapError(err, "dail failed")
}

return &Client{
rpcClient: client,
}, nil
if retryCount == 0 {
client.rpcRequester = rpcClient
} else {
// Interval 0 is meaningless and may lead full node busy, so default sets it to 1 second
if retryInterval == 0 {
retryInterval = time.Second
}

client.rpcRequester = &rpcClientWithRetry{
inner: rpcClient,
retryCount: retryCount,
interval: retryInterval,
}
}

return &client, nil
}

type rpcClientWithRetry struct {
inner *rpc.Client
retryCount int
interval time.Duration
}

func (r *rpcClientWithRetry) Call(resultPtr interface{}, method string, args ...interface{}) error {
err := r.inner.Call(resultPtr, method, args...)
if err == nil {
return nil
}

if r.retryCount <= 0 {
return err
}

remain := r.retryCount
for {
err = r.inner.Call(resultPtr, method, args...)
if err == nil {
return nil
}

remain--
if remain == 0 {
msg := fmt.Sprintf("timeout when call %v with args %v", method, args)
return types.WrapError(err, msg)
}

if r.interval > 0 {
time.Sleep(r.interval)
}
}
}

func (r *rpcClientWithRetry) Close() {
if r != nil && r.inner != nil {
r.inner.Close()
}
}

// CallRPC performs a JSON-RPC call with the given arguments and unmarshals into
Expand All @@ -42,7 +107,7 @@ func NewClient(nodeURL string) (*Client, error) {
// The result must be a pointer so that package json can unmarshal into it. You
// can also pass nil, in which case the result is ignored.
func (client *Client) CallRPC(result interface{}, method string, args ...interface{}) error {
return client.rpcClient.Call(result, method, args...)
return client.rpcRequester.Call(result, method, args...)
}

// SetAccountManager sets account manager for sign transaction
Expand All @@ -54,7 +119,7 @@ func (client *Client) SetAccountManager(accountManager AccountManagerOperator) {
func (client *Client) GetGasPrice() (*big.Int, error) {
var result interface{}

if err := client.rpcClient.Call(&result, "cfx_gasPrice"); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_gasPrice"); err != nil {
msg := "rpc request cfx_gasPrice error"
return nil, types.WrapError(err, msg)
}
Expand All @@ -70,7 +135,7 @@ func (client *Client) GetNextNonce(address types.Address, epoch *types.Epoch) (*
args = append(args, epoch)
}

if err := client.rpcClient.Call(&result, "cfx_getNextNonce", args...); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getNextNonce", args...); err != nil {
msg := fmt.Sprintf("rpc request cfx_getNextNonce %+v error", address)
return nil, types.WrapErrorf(err, msg)
}
Expand All @@ -96,7 +161,7 @@ func (client *Client) GetEpochNumber(epoch ...*types.Epoch) (*big.Int, error) {
args = append(args, epoch[0])
}

if err := client.rpcClient.Call(&result, "cfx_epochNumber", args...); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_epochNumber", args...); err != nil {
msg := fmt.Sprintf("rpc cfx_epochNumber %+v error", args)
return nil, types.WrapError(err, msg)
}
Expand All @@ -113,7 +178,7 @@ func (client *Client) GetBalance(address types.Address, epoch ...*types.Epoch) (
args = append(args, epoch[0])
}

if err := client.rpcClient.Call(&result, "cfx_getBalance", args...); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getBalance", args...); err != nil {
msg := fmt.Sprintf("rpc cfx_getBalance %+v error", args)
return nil, types.WrapError(err, msg)
}
Expand All @@ -130,7 +195,7 @@ func (client *Client) GetCode(address types.Address, epoch ...*types.Epoch) (str
args = append(args, epoch[0])
}

if err := client.rpcClient.Call(&result, "cfx_getCode", args...); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getCode", args...); err != nil {
msg := fmt.Sprintf("rpc cfx_getCode %+v error", args)
return "", types.WrapError(err, msg)
}
Expand All @@ -143,7 +208,7 @@ func (client *Client) GetCode(address types.Address, epoch ...*types.Epoch) (str
func (client *Client) GetBlockSummaryByHash(blockHash types.Hash) (*types.BlockSummary, error) {
var result interface{}

if err := client.rpcClient.Call(&result, "cfx_getBlockByHash", blockHash, false); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getBlockByHash", blockHash, false); err != nil {
msg := fmt.Sprintf("rpc cfx_getBlockByHash %+v error", blockHash)
return nil, types.WrapError(err, msg)
}
Expand All @@ -166,7 +231,7 @@ func (client *Client) GetBlockSummaryByHash(blockHash types.Hash) (*types.BlockS
func (client *Client) GetBlockByHash(blockHash types.Hash) (*types.Block, error) {
var result interface{}

if err := client.rpcClient.Call(&result, "cfx_getBlockByHash", blockHash, true); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getBlockByHash", blockHash, true); err != nil {
msg := fmt.Sprintf("rpc cfx_getBlockByHash %+v error", blockHash)
return nil, types.WrapError(err, msg)
}
Expand All @@ -189,7 +254,7 @@ func (client *Client) GetBlockByHash(blockHash types.Hash) (*types.Block, error)
func (client *Client) GetBlockSummaryByEpoch(epoch *types.Epoch) (*types.BlockSummary, error) {
var result interface{}

if err := client.rpcClient.Call(&result, "cfx_getBlockByEpochNumber", epoch, false); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getBlockByEpochNumber", epoch, false); err != nil {
msg := fmt.Sprintf("rpc cfx_getBlockByEpochNumber %+v error", epoch)
return nil, types.WrapError(err, msg)
}
Expand All @@ -208,7 +273,7 @@ func (client *Client) GetBlockSummaryByEpoch(epoch *types.Epoch) (*types.BlockSu
func (client *Client) GetBlockByEpoch(epoch *types.Epoch) (*types.Block, error) {
var result interface{}

if err := client.rpcClient.Call(&result, "cfx_getBlockByEpochNumber", epoch, true); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getBlockByEpochNumber", epoch, true); err != nil {
msg := fmt.Sprintf("rpc cfx_getBlockByEpochNumber %+v error", epoch)
return nil, types.WrapError(err, msg)
}
Expand All @@ -226,7 +291,7 @@ func (client *Client) GetBlockByEpoch(epoch *types.Epoch) (*types.Block, error)
func (client *Client) GetBestBlockHash() (types.Hash, error) {
var result interface{}

if err := client.rpcClient.Call(&result, "cfx_getBestBlockHash"); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getBestBlockHash"); err != nil {
msg := "rpc cfx_getBestBlockHash error"
return "", types.WrapError(err, msg)
}
Expand All @@ -241,7 +306,7 @@ func (client *Client) GetBlockConfirmRiskByHash(blockhash types.Hash) (*big.Int,

args := []interface{}{blockhash}

if err := client.rpcClient.Call(&result, "cfx_getConfirmationRiskByHash", args...); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getConfirmationRiskByHash", args...); err != nil {
msg := fmt.Sprintf("rpc cfx_getConfirmationRiskByHash %+v error", args)
return nil, types.WrapError(err, msg)
}
Expand Down Expand Up @@ -343,7 +408,7 @@ func (client *Client) SendTransaction(tx *types.UnsignedTransaction) (types.Hash
func (client *Client) SendRawTransaction(rawData []byte) (types.Hash, error) {
var result interface{}
// fmt.Printf("send raw transaction %x\n", rawData)
if err := client.rpcClient.Call(&result, "cfx_sendRawTransaction", hexutil.Encode(rawData)); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_sendRawTransaction", hexutil.Encode(rawData)); err != nil {
msg := fmt.Sprintf("rpc cfx_sendRawTransaction 0x%+x error", rawData)
return "", types.WrapError(err, msg)
}
Expand Down Expand Up @@ -405,7 +470,7 @@ func (client *Client) Call(request types.CallRequest, epoch *types.Epoch) (*stri
args = append(args, epoch)
}

if err := client.rpcClient.Call(&rpcResult, "cfx_call", args...); err != nil {
if err := client.rpcRequester.Call(&rpcResult, "cfx_call", args...); err != nil {
msg := fmt.Sprintf("rpc cfx_call {%+v} error", args)
return nil, types.WrapError(err, msg)
}
Expand All @@ -422,7 +487,7 @@ func (client *Client) Call(request types.CallRequest, epoch *types.Epoch) (*stri
func (client *Client) GetLogs(filter types.LogFilter) ([]types.Log, error) {
var result interface{}

if err := client.rpcClient.Call(&result, "cfx_getLogs", filter); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getLogs", filter); err != nil {
msg := fmt.Sprintf("rpc cfx_getLogs of {%+v} error", filter)
return nil, types.WrapError(err, msg)
}
Expand All @@ -441,7 +506,7 @@ func (client *Client) GetLogs(filter types.LogFilter) ([]types.Log, error) {
func (client *Client) GetTransactionByHash(txHash types.Hash) (*types.Transaction, error) {
var result interface{}

if err := client.rpcClient.Call(&result, "cfx_getTransactionByHash", txHash); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getTransactionByHash", txHash); err != nil {
msg := fmt.Sprintf("rpc cfx_getTransactionByHash {%+v} error", txHash)
return nil, types.WrapError(err, msg)
}
Expand All @@ -466,7 +531,7 @@ func (client *Client) EstimateGasAndCollateral(request types.CallRequest) (*type

args := []interface{}{request}

if err := client.rpcClient.Call(&result, "cfx_estimateGasAndCollateral", args...); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_estimateGasAndCollateral", args...); err != nil {
msg := fmt.Sprintf("rpc cfx_estimateGasAndCollateral of {%+v} error", args)
return nil, types.WrapError(err, msg)
}
Expand All @@ -483,7 +548,7 @@ func (client *Client) EstimateGasAndCollateral(request types.CallRequest) (*type
func (client *Client) GetBlocksByEpoch(epoch *types.Epoch) ([]types.Hash, error) {
var result interface{}

if err := client.rpcClient.Call(&result, "cfx_getBlocksByEpoch", epoch); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getBlocksByEpoch", epoch); err != nil {
msg := fmt.Sprintf("rpc cfx_getBlocksByEpoch {%+v} error", epoch)
return nil, types.WrapError(err, msg)
}
Expand All @@ -502,7 +567,7 @@ func (client *Client) GetBlocksByEpoch(epoch *types.Epoch) ([]types.Hash, error)
func (client *Client) GetTransactionReceipt(txHash types.Hash) (*types.TransactionReceipt, error) {
var result interface{}

if err := client.rpcClient.Call(&result, "cfx_getTransactionReceipt", txHash); err != nil {
if err := client.rpcRequester.Call(&result, "cfx_getTransactionReceipt", txHash); err != nil {
msg := fmt.Sprintf("rpc cfx_getTransactionReceipt of {%+v} error", txHash)
return nil, types.WrapError(err, msg)
}
Expand Down Expand Up @@ -620,7 +685,7 @@ func (client *Client) ApplyUnsignedTransactionDefault(tx *types.UnsignedTransact
func (client *Client) Debug(method string, args ...interface{}) (interface{}, error) {
var result interface{}

if err := client.rpcClient.Call(&result, method, args...); err != nil {
if err := client.rpcRequester.Call(&result, method, args...); err != nil {
msg := fmt.Sprintf("rpc call method {%+v} with args {%+v} error", method, args)
return nil, types.WrapError(err, msg)
}
Expand Down Expand Up @@ -694,7 +759,7 @@ func (client *Client) DeployContract(option *types.ContractDeployOption, abiJSON
msg := fmt.Sprintf("deploy contract time out after %v, txhash is %+v", t, txhash)
result.Error = errors.New(msg)
return
// Got a tick, we should check on checkSomething()
// Got a tick
case <-ticker:
transaction, err := client.GetTransactionByHash(txhash)
if err != nil {
Expand Down Expand Up @@ -734,7 +799,7 @@ func (client *Client) GetContract(abiJSON []byte, deployedAt *types.Address) (*C

// Close closes the client, aborting any in-flight requests.
func (client *Client) Close() {
client.rpcClient.Close()
client.rpcRequester.Close()
}

func unmarshalRPCResult(result interface{}, v interface{}) error {
Expand Down
Loading

0 comments on commit 6bf2802

Please sign in to comment.