github.com/evdatsion/aphelion-dpos-bft@v0.32.1/rpc/client/httpclient.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/pkg/errors"
    10  
    11  	amino "github.com/evdatsion/go-amino"
    12  
    13  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    14  	tmpubsub "github.com/evdatsion/aphelion-dpos-bft/libs/pubsub"
    15  	ctypes "github.com/evdatsion/aphelion-dpos-bft/rpc/core/types"
    16  	rpcclient "github.com/evdatsion/aphelion-dpos-bft/rpc/lib/client"
    17  	"github.com/evdatsion/aphelion-dpos-bft/types"
    18  )
    19  
    20  /*
    21  HTTP is a Client implementation that communicates with a Tendermint node over
    22  JSON RPC and WebSockets.
    23  
    24  This is the main implementation you probably want to use in production code.
    25  There are other implementations when calling the Tendermint node in-process
    26  (Local), or when you want to mock out the server for test code (mock).
    27  
    28  You can subscribe for any event published by Tendermint using Subscribe method.
    29  Note delivery is best-effort. If you don't read events fast enough or network is
    30  slow, Tendermint might cancel the subscription. The client will attempt to
    31  resubscribe (you don't need to do anything). It will keep trying every second
    32  indefinitely until successful.
    33  
    34  Request batching is available for JSON RPC requests over HTTP, which conforms to
    35  the JSON RPC specification (https://www.jsonrpc.org/specification#batch). See
    36  the example for more details.
    37  */
    38  type HTTP struct {
    39  	remote string
    40  	rpc    *rpcclient.JSONRPCClient
    41  
    42  	*baseRPCClient
    43  	*WSEvents
    44  }
    45  
    46  // BatchHTTP provides the same interface as `HTTP`, but allows for batching of
    47  // requests (as per https://www.jsonrpc.org/specification#batch). Do not
    48  // instantiate directly - rather use the HTTP.NewBatch() method to create an
    49  // instance of this struct.
    50  //
    51  // Batching of HTTP requests is thread-safe in the sense that multiple
    52  // goroutines can each create their own batches and send them using the same
    53  // HTTP client. Multiple goroutines could also enqueue transactions in a single
    54  // batch, but ordering of transactions in the batch cannot be guaranteed in such
    55  // an example.
    56  type BatchHTTP struct {
    57  	rpcBatch *rpcclient.JSONRPCRequestBatch
    58  	*baseRPCClient
    59  }
    60  
    61  // rpcClient is an internal interface to which our RPC clients (batch and
    62  // non-batch) must conform. Acts as an additional code-level sanity check to
    63  // make sure the implementations stay coherent.
    64  type rpcClient interface {
    65  	ABCIClient
    66  	HistoryClient
    67  	NetworkClient
    68  	SignClient
    69  	StatusClient
    70  }
    71  
    72  // baseRPCClient implements the basic RPC method logic without the actual
    73  // underlying RPC call functionality, which is provided by `caller`.
    74  type baseRPCClient struct {
    75  	caller rpcclient.JSONRPCCaller
    76  }
    77  
    78  var _ rpcClient = (*HTTP)(nil)
    79  var _ rpcClient = (*BatchHTTP)(nil)
    80  var _ rpcClient = (*baseRPCClient)(nil)
    81  
    82  //-----------------------------------------------------------------------------
    83  // HTTP
    84  
    85  // NewHTTP takes a remote endpoint in the form <protocol>://<host>:<port> and
    86  // the websocket path (which always seems to be "/websocket")
    87  func NewHTTP(remote, wsEndpoint string) *HTTP {
    88  	rc := rpcclient.NewJSONRPCClient(remote)
    89  	cdc := rc.Codec()
    90  	ctypes.RegisterAmino(cdc)
    91  	rc.SetCodec(cdc)
    92  
    93  	return &HTTP{
    94  		rpc:           rc,
    95  		remote:        remote,
    96  		baseRPCClient: &baseRPCClient{caller: rc},
    97  		WSEvents:      newWSEvents(cdc, remote, wsEndpoint),
    98  	}
    99  }
   100  
   101  var _ Client = (*HTTP)(nil)
   102  
   103  // NewBatch creates a new batch client for this HTTP client.
   104  func (c *HTTP) NewBatch() *BatchHTTP {
   105  	rpcBatch := c.rpc.NewRequestBatch()
   106  	return &BatchHTTP{
   107  		rpcBatch: rpcBatch,
   108  		baseRPCClient: &baseRPCClient{
   109  			caller: rpcBatch,
   110  		},
   111  	}
   112  }
   113  
   114  //-----------------------------------------------------------------------------
   115  // BatchHTTP
   116  
   117  // Send is a convenience function for an HTTP batch that will trigger the
   118  // compilation of the batched requests and send them off using the client as a
   119  // single request. On success, this returns a list of the deserialized results
   120  // from each request in the sent batch.
   121  func (b *BatchHTTP) Send() ([]interface{}, error) {
   122  	return b.rpcBatch.Send()
   123  }
   124  
   125  // Clear will empty out this batch of requests and return the number of requests
   126  // that were cleared out.
   127  func (b *BatchHTTP) Clear() int {
   128  	return b.rpcBatch.Clear()
   129  }
   130  
   131  // Count returns the number of enqueued requests waiting to be sent.
   132  func (b *BatchHTTP) Count() int {
   133  	return b.rpcBatch.Count()
   134  }
   135  
   136  //-----------------------------------------------------------------------------
   137  // baseRPCClient
   138  
   139  func (c *baseRPCClient) Status() (*ctypes.ResultStatus, error) {
   140  	result := new(ctypes.ResultStatus)
   141  	_, err := c.caller.Call("status", map[string]interface{}{}, result)
   142  	if err != nil {
   143  		return nil, errors.Wrap(err, "Status")
   144  	}
   145  	return result, nil
   146  }
   147  
   148  func (c *baseRPCClient) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
   149  	result := new(ctypes.ResultABCIInfo)
   150  	_, err := c.caller.Call("abci_info", map[string]interface{}{}, result)
   151  	if err != nil {
   152  		return nil, errors.Wrap(err, "ABCIInfo")
   153  	}
   154  	return result, nil
   155  }
   156  
   157  func (c *baseRPCClient) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) {
   158  	return c.ABCIQueryWithOptions(path, data, DefaultABCIQueryOptions)
   159  }
   160  
   161  func (c *baseRPCClient) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
   162  	result := new(ctypes.ResultABCIQuery)
   163  	_, err := c.caller.Call("abci_query",
   164  		map[string]interface{}{"path": path, "data": data, "height": opts.Height, "prove": opts.Prove},
   165  		result)
   166  	if err != nil {
   167  		return nil, errors.Wrap(err, "ABCIQuery")
   168  	}
   169  	return result, nil
   170  }
   171  
   172  func (c *baseRPCClient) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
   173  	result := new(ctypes.ResultBroadcastTxCommit)
   174  	_, err := c.caller.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result)
   175  	if err != nil {
   176  		return nil, errors.Wrap(err, "broadcast_tx_commit")
   177  	}
   178  	return result, nil
   179  }
   180  
   181  func (c *baseRPCClient) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
   182  	return c.broadcastTX("broadcast_tx_async", tx)
   183  }
   184  
   185  func (c *baseRPCClient) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
   186  	return c.broadcastTX("broadcast_tx_sync", tx)
   187  }
   188  
   189  func (c *baseRPCClient) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
   190  	result := new(ctypes.ResultBroadcastTx)
   191  	_, err := c.caller.Call(route, map[string]interface{}{"tx": tx}, result)
   192  	if err != nil {
   193  		return nil, errors.Wrap(err, route)
   194  	}
   195  	return result, nil
   196  }
   197  
   198  func (c *baseRPCClient) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) {
   199  	result := new(ctypes.ResultUnconfirmedTxs)
   200  	_, err := c.caller.Call("unconfirmed_txs", map[string]interface{}{"limit": limit}, result)
   201  	if err != nil {
   202  		return nil, errors.Wrap(err, "unconfirmed_txs")
   203  	}
   204  	return result, nil
   205  }
   206  
   207  func (c *baseRPCClient) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
   208  	result := new(ctypes.ResultUnconfirmedTxs)
   209  	_, err := c.caller.Call("num_unconfirmed_txs", map[string]interface{}{}, result)
   210  	if err != nil {
   211  		return nil, errors.Wrap(err, "num_unconfirmed_txs")
   212  	}
   213  	return result, nil
   214  }
   215  
   216  func (c *baseRPCClient) NetInfo() (*ctypes.ResultNetInfo, error) {
   217  	result := new(ctypes.ResultNetInfo)
   218  	_, err := c.caller.Call("net_info", map[string]interface{}{}, result)
   219  	if err != nil {
   220  		return nil, errors.Wrap(err, "NetInfo")
   221  	}
   222  	return result, nil
   223  }
   224  
   225  func (c *baseRPCClient) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
   226  	result := new(ctypes.ResultDumpConsensusState)
   227  	_, err := c.caller.Call("dump_consensus_state", map[string]interface{}{}, result)
   228  	if err != nil {
   229  		return nil, errors.Wrap(err, "DumpConsensusState")
   230  	}
   231  	return result, nil
   232  }
   233  
   234  func (c *baseRPCClient) ConsensusState() (*ctypes.ResultConsensusState, error) {
   235  	result := new(ctypes.ResultConsensusState)
   236  	_, err := c.caller.Call("consensus_state", map[string]interface{}{}, result)
   237  	if err != nil {
   238  		return nil, errors.Wrap(err, "ConsensusState")
   239  	}
   240  	return result, nil
   241  }
   242  
   243  func (c *baseRPCClient) Health() (*ctypes.ResultHealth, error) {
   244  	result := new(ctypes.ResultHealth)
   245  	_, err := c.caller.Call("health", map[string]interface{}{}, result)
   246  	if err != nil {
   247  		return nil, errors.Wrap(err, "Health")
   248  	}
   249  	return result, nil
   250  }
   251  
   252  func (c *baseRPCClient) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
   253  	result := new(ctypes.ResultBlockchainInfo)
   254  	_, err := c.caller.Call("blockchain",
   255  		map[string]interface{}{"minHeight": minHeight, "maxHeight": maxHeight},
   256  		result)
   257  	if err != nil {
   258  		return nil, errors.Wrap(err, "BlockchainInfo")
   259  	}
   260  	return result, nil
   261  }
   262  
   263  func (c *baseRPCClient) Genesis() (*ctypes.ResultGenesis, error) {
   264  	result := new(ctypes.ResultGenesis)
   265  	_, err := c.caller.Call("genesis", map[string]interface{}{}, result)
   266  	if err != nil {
   267  		return nil, errors.Wrap(err, "Genesis")
   268  	}
   269  	return result, nil
   270  }
   271  
   272  func (c *baseRPCClient) Block(height *int64) (*ctypes.ResultBlock, error) {
   273  	result := new(ctypes.ResultBlock)
   274  	_, err := c.caller.Call("block", map[string]interface{}{"height": height}, result)
   275  	if err != nil {
   276  		return nil, errors.Wrap(err, "Block")
   277  	}
   278  	return result, nil
   279  }
   280  
   281  func (c *baseRPCClient) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
   282  	result := new(ctypes.ResultBlockResults)
   283  	_, err := c.caller.Call("block_results", map[string]interface{}{"height": height}, result)
   284  	if err != nil {
   285  		return nil, errors.Wrap(err, "Block Result")
   286  	}
   287  	return result, nil
   288  }
   289  
   290  func (c *baseRPCClient) Commit(height *int64) (*ctypes.ResultCommit, error) {
   291  	result := new(ctypes.ResultCommit)
   292  	_, err := c.caller.Call("commit", map[string]interface{}{"height": height}, result)
   293  	if err != nil {
   294  		return nil, errors.Wrap(err, "Commit")
   295  	}
   296  	return result, nil
   297  }
   298  
   299  func (c *baseRPCClient) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
   300  	result := new(ctypes.ResultTx)
   301  	params := map[string]interface{}{
   302  		"hash":  hash,
   303  		"prove": prove,
   304  	}
   305  	_, err := c.caller.Call("tx", params, result)
   306  	if err != nil {
   307  		return nil, errors.Wrap(err, "Tx")
   308  	}
   309  	return result, nil
   310  }
   311  
   312  func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) {
   313  	result := new(ctypes.ResultTxSearch)
   314  	params := map[string]interface{}{
   315  		"query":    query,
   316  		"prove":    prove,
   317  		"page":     page,
   318  		"per_page": perPage,
   319  	}
   320  	_, err := c.caller.Call("tx_search", params, result)
   321  	if err != nil {
   322  		return nil, errors.Wrap(err, "TxSearch")
   323  	}
   324  	return result, nil
   325  }
   326  
   327  func (c *baseRPCClient) Validators(height *int64) (*ctypes.ResultValidators, error) {
   328  	result := new(ctypes.ResultValidators)
   329  	_, err := c.caller.Call("validators", map[string]interface{}{"height": height}, result)
   330  	if err != nil {
   331  		return nil, errors.Wrap(err, "Validators")
   332  	}
   333  	return result, nil
   334  }
   335  
   336  //-----------------------------------------------------------------------------
   337  // WSEvents
   338  
   339  type WSEvents struct {
   340  	cmn.BaseService
   341  	cdc      *amino.Codec
   342  	remote   string
   343  	endpoint string
   344  	ws       *rpcclient.WSClient
   345  
   346  	mtx sync.RWMutex
   347  	// query -> chan
   348  	subscriptions map[string]chan ctypes.ResultEvent
   349  }
   350  
   351  func newWSEvents(cdc *amino.Codec, remote, endpoint string) *WSEvents {
   352  	wsEvents := &WSEvents{
   353  		cdc:           cdc,
   354  		endpoint:      endpoint,
   355  		remote:        remote,
   356  		subscriptions: make(map[string]chan ctypes.ResultEvent),
   357  	}
   358  
   359  	wsEvents.BaseService = *cmn.NewBaseService(nil, "WSEvents", wsEvents)
   360  	return wsEvents
   361  }
   362  
   363  // OnStart implements cmn.Service by starting WSClient and event loop.
   364  func (w *WSEvents) OnStart() error {
   365  	w.ws = rpcclient.NewWSClient(w.remote, w.endpoint, rpcclient.OnReconnect(func() {
   366  		// resubscribe immediately
   367  		w.redoSubscriptionsAfter(0 * time.Second)
   368  	}))
   369  	w.ws.SetCodec(w.cdc)
   370  
   371  	err := w.ws.Start()
   372  	if err != nil {
   373  		return err
   374  	}
   375  
   376  	go w.eventListener()
   377  	return nil
   378  }
   379  
   380  // OnStop implements cmn.Service by stopping WSClient.
   381  func (w *WSEvents) OnStop() {
   382  	_ = w.ws.Stop()
   383  }
   384  
   385  // Subscribe implements EventsClient by using WSClient to subscribe given
   386  // subscriber to query. By default, returns a channel with cap=1. Error is
   387  // returned if it fails to subscribe.
   388  // Channel is never closed to prevent clients from seeing an erroneus event.
   389  func (w *WSEvents) Subscribe(ctx context.Context, subscriber, query string,
   390  	outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) {
   391  
   392  	if err := w.ws.Subscribe(ctx, query); err != nil {
   393  		return nil, err
   394  	}
   395  
   396  	outCap := 1
   397  	if len(outCapacity) > 0 {
   398  		outCap = outCapacity[0]
   399  	}
   400  
   401  	outc := make(chan ctypes.ResultEvent, outCap)
   402  	w.mtx.Lock()
   403  	// subscriber param is ignored because Tendermint will override it with
   404  	// remote IP anyway.
   405  	w.subscriptions[query] = outc
   406  	w.mtx.Unlock()
   407  
   408  	return outc, nil
   409  }
   410  
   411  // Unsubscribe implements EventsClient by using WSClient to unsubscribe given
   412  // subscriber from query.
   413  func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber, query string) error {
   414  	if err := w.ws.Unsubscribe(ctx, query); err != nil {
   415  		return err
   416  	}
   417  
   418  	w.mtx.Lock()
   419  	_, ok := w.subscriptions[query]
   420  	if ok {
   421  		delete(w.subscriptions, query)
   422  	}
   423  	w.mtx.Unlock()
   424  
   425  	return nil
   426  }
   427  
   428  // UnsubscribeAll implements EventsClient by using WSClient to unsubscribe
   429  // given subscriber from all the queries.
   430  func (w *WSEvents) UnsubscribeAll(ctx context.Context, subscriber string) error {
   431  	if err := w.ws.UnsubscribeAll(ctx); err != nil {
   432  		return err
   433  	}
   434  
   435  	w.mtx.Lock()
   436  	w.subscriptions = make(map[string]chan ctypes.ResultEvent)
   437  	w.mtx.Unlock()
   438  
   439  	return nil
   440  }
   441  
   442  // After being reconnected, it is necessary to redo subscription to server
   443  // otherwise no data will be automatically received.
   444  func (w *WSEvents) redoSubscriptionsAfter(d time.Duration) {
   445  	time.Sleep(d)
   446  
   447  	for q := range w.subscriptions {
   448  		err := w.ws.Subscribe(context.Background(), q)
   449  		if err != nil {
   450  			w.Logger.Error("Failed to resubscribe", "err", err)
   451  		}
   452  	}
   453  }
   454  
   455  func isErrAlreadySubscribed(err error) bool {
   456  	return strings.Contains(err.Error(), tmpubsub.ErrAlreadySubscribed.Error())
   457  }
   458  
   459  func (w *WSEvents) eventListener() {
   460  	for {
   461  		select {
   462  		case resp, ok := <-w.ws.ResponsesCh:
   463  			if !ok {
   464  				return
   465  			}
   466  
   467  			if resp.Error != nil {
   468  				w.Logger.Error("WS error", "err", resp.Error.Error())
   469  				// Error can be ErrAlreadySubscribed or max client (subscriptions per
   470  				// client) reached or Tendermint exited.
   471  				// We can ignore ErrAlreadySubscribed, but need to retry in other
   472  				// cases.
   473  				if !isErrAlreadySubscribed(resp.Error) {
   474  					// Resubscribe after 1 second to give Tendermint time to restart (if
   475  					// crashed).
   476  					w.redoSubscriptionsAfter(1 * time.Second)
   477  				}
   478  				continue
   479  			}
   480  
   481  			result := new(ctypes.ResultEvent)
   482  			err := w.cdc.UnmarshalJSON(resp.Result, result)
   483  			if err != nil {
   484  				w.Logger.Error("failed to unmarshal response", "err", err)
   485  				continue
   486  			}
   487  
   488  			w.mtx.RLock()
   489  			if out, ok := w.subscriptions[result.Query]; ok {
   490  				if cap(out) == 0 {
   491  					out <- *result
   492  				} else {
   493  					select {
   494  					case out <- *result:
   495  					default:
   496  						w.Logger.Error("wanted to publish ResultEvent, but out channel is full", "result", result, "query", result.Query)
   497  					}
   498  				}
   499  			}
   500  			w.mtx.RUnlock()
   501  		case <-w.Quit():
   502  			return
   503  		}
   504  	}
   505  }