github.com/badrootd/nibiru-cometbft@v0.37.5-0.20240307173500-2a75559eee9b/rpc/client/http/http.go (about)

     1  package http
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/http"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/badrootd/nibiru-cometbft/libs/bytes"
    11  	cmtjson "github.com/badrootd/nibiru-cometbft/libs/json"
    12  	"github.com/badrootd/nibiru-cometbft/libs/log"
    13  	cmtpubsub "github.com/badrootd/nibiru-cometbft/libs/pubsub"
    14  	"github.com/badrootd/nibiru-cometbft/libs/service"
    15  	cmtsync "github.com/badrootd/nibiru-cometbft/libs/sync"
    16  	rpcclient "github.com/badrootd/nibiru-cometbft/rpc/client"
    17  	ctypes "github.com/badrootd/nibiru-cometbft/rpc/core/types"
    18  	jsonrpcclient "github.com/badrootd/nibiru-cometbft/rpc/jsonrpc/client"
    19  	"github.com/badrootd/nibiru-cometbft/types"
    20  )
    21  
    22  /*
    23  HTTP is a Client implementation that communicates with a CometBFT node over
    24  JSON RPC and WebSockets.
    25  
    26  This is the main implementation you probably want to use in production code.
    27  There are other implementations when calling the CometBFT node in-process
    28  (Local), or when you want to mock out the server for test code (mock).
    29  
    30  You can subscribe for any event published by CometBFT using Subscribe method.
    31  Note delivery is best-effort. If you don't read events fast enough or network is
    32  slow, CometBFT might cancel the subscription. The client will attempt to
    33  resubscribe (you don't need to do anything). It will keep trying every second
    34  indefinitely until successful.
    35  
    36  Request batching is available for JSON RPC requests over HTTP, which conforms to
    37  the JSON RPC specification (https://www.jsonrpc.org/specification#batch). See
    38  the example for more details.
    39  
    40  Example:
    41  
    42  	c, err := New("http://192.168.1.10:26657", "/websocket")
    43  	if err != nil {
    44  		// handle error
    45  	}
    46  
    47  	// call Start/Stop if you're subscribing to events
    48  	err = c.Start()
    49  	if err != nil {
    50  		// handle error
    51  	}
    52  	defer c.Stop()
    53  
    54  	res, err := c.Status()
    55  	if err != nil {
    56  		// handle error
    57  	}
    58  
    59  	// handle result
    60  */
    61  type HTTP struct {
    62  	remote string
    63  	rpc    *jsonrpcclient.Client
    64  
    65  	*baseRPCClient
    66  	*WSEvents
    67  }
    68  
    69  // BatchHTTP provides the same interface as `HTTP`, but allows for batching of
    70  // requests (as per https://www.jsonrpc.org/specification#batch). Do not
    71  // instantiate directly - rather use the HTTP.NewBatch() method to create an
    72  // instance of this struct.
    73  //
    74  // Batching of HTTP requests is thread-safe in the sense that multiple
    75  // goroutines can each create their own batches and send them using the same
    76  // HTTP client. Multiple goroutines could also enqueue transactions in a single
    77  // batch, but ordering of transactions in the batch cannot be guaranteed in such
    78  // an example.
    79  type BatchHTTP struct {
    80  	rpcBatch *jsonrpcclient.RequestBatch
    81  	*baseRPCClient
    82  }
    83  
    84  // rpcClient is an internal interface to which our RPC clients (batch and
    85  // non-batch) must conform. Acts as an additional code-level sanity check to
    86  // make sure the implementations stay coherent.
    87  type rpcClient interface {
    88  	rpcclient.ABCIClient
    89  	rpcclient.HistoryClient
    90  	rpcclient.NetworkClient
    91  	rpcclient.SignClient
    92  	rpcclient.StatusClient
    93  }
    94  
    95  // baseRPCClient implements the basic RPC method logic without the actual
    96  // underlying RPC call functionality, which is provided by `caller`.
    97  type baseRPCClient struct {
    98  	caller jsonrpcclient.Caller
    99  }
   100  
   101  var (
   102  	_ rpcClient = (*HTTP)(nil)
   103  	_ rpcClient = (*BatchHTTP)(nil)
   104  	_ rpcClient = (*baseRPCClient)(nil)
   105  )
   106  
   107  //-----------------------------------------------------------------------------
   108  // HTTP
   109  
   110  // New takes a remote endpoint in the form <protocol>://<host>:<port> and
   111  // the websocket path (which always seems to be "/websocket")
   112  // An error is returned on invalid remote. The function panics when remote is nil.
   113  func New(remote, wsEndpoint string) (*HTTP, error) {
   114  	httpClient, err := jsonrpcclient.DefaultHTTPClient(remote)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	return NewWithClient(remote, wsEndpoint, httpClient)
   119  }
   120  
   121  // Create timeout enabled http client
   122  func NewWithTimeout(remote, wsEndpoint string, timeout uint) (*HTTP, error) {
   123  	httpClient, err := jsonrpcclient.DefaultHTTPClient(remote)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	httpClient.Timeout = time.Duration(timeout) * time.Second
   128  	return NewWithClient(remote, wsEndpoint, httpClient)
   129  }
   130  
   131  // NewWithClient allows for setting a custom http client (See New).
   132  // An error is returned on invalid remote. The function panics when remote is nil.
   133  func NewWithClient(remote, wsEndpoint string, client *http.Client) (*HTTP, error) {
   134  	if client == nil {
   135  		panic("nil http.Client provided")
   136  	}
   137  
   138  	rc, err := jsonrpcclient.NewWithHTTPClient(remote, client)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	wsEvents, err := newWSEvents(remote, wsEndpoint)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	httpClient := &HTTP{
   149  		rpc:           rc,
   150  		remote:        remote,
   151  		baseRPCClient: &baseRPCClient{caller: rc},
   152  		WSEvents:      wsEvents,
   153  	}
   154  
   155  	return httpClient, nil
   156  }
   157  
   158  var _ rpcclient.Client = (*HTTP)(nil)
   159  
   160  // SetLogger sets a logger.
   161  func (c *HTTP) SetLogger(l log.Logger) {
   162  	c.WSEvents.SetLogger(l)
   163  }
   164  
   165  // Remote returns the remote network address in a string form.
   166  func (c *HTTP) Remote() string {
   167  	return c.remote
   168  }
   169  
   170  // NewBatch creates a new batch client for this HTTP client.
   171  func (c *HTTP) NewBatch() *BatchHTTP {
   172  	rpcBatch := c.rpc.NewRequestBatch()
   173  	return &BatchHTTP{
   174  		rpcBatch: rpcBatch,
   175  		baseRPCClient: &baseRPCClient{
   176  			caller: rpcBatch,
   177  		},
   178  	}
   179  }
   180  
   181  //-----------------------------------------------------------------------------
   182  // BatchHTTP
   183  
   184  // Send is a convenience function for an HTTP batch that will trigger the
   185  // compilation of the batched requests and send them off using the client as a
   186  // single request. On success, this returns a list of the deserialized results
   187  // from each request in the sent batch.
   188  func (b *BatchHTTP) Send(ctx context.Context) ([]interface{}, error) {
   189  	return b.rpcBatch.Send(ctx)
   190  }
   191  
   192  // Clear will empty out this batch of requests and return the number of requests
   193  // that were cleared out.
   194  func (b *BatchHTTP) Clear() int {
   195  	return b.rpcBatch.Clear()
   196  }
   197  
   198  // Count returns the number of enqueued requests waiting to be sent.
   199  func (b *BatchHTTP) Count() int {
   200  	return b.rpcBatch.Count()
   201  }
   202  
   203  //-----------------------------------------------------------------------------
   204  // baseRPCClient
   205  
   206  func (c *baseRPCClient) Status(ctx context.Context) (*ctypes.ResultStatus, error) {
   207  	result := new(ctypes.ResultStatus)
   208  	_, err := c.caller.Call(ctx, "status", map[string]interface{}{}, result)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	return result, nil
   214  }
   215  
   216  func (c *baseRPCClient) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) {
   217  	result := new(ctypes.ResultABCIInfo)
   218  	_, err := c.caller.Call(ctx, "abci_info", map[string]interface{}{}, result)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	return result, nil
   224  }
   225  
   226  func (c *baseRPCClient) ABCIQuery(
   227  	ctx context.Context,
   228  	path string,
   229  	data bytes.HexBytes,
   230  ) (*ctypes.ResultABCIQuery, error) {
   231  	return c.ABCIQueryWithOptions(ctx, path, data, rpcclient.DefaultABCIQueryOptions)
   232  }
   233  
   234  func (c *baseRPCClient) ABCIQueryWithOptions(
   235  	ctx context.Context,
   236  	path string,
   237  	data bytes.HexBytes,
   238  	opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
   239  	result := new(ctypes.ResultABCIQuery)
   240  	_, err := c.caller.Call(ctx, "abci_query",
   241  		map[string]interface{}{"path": path, "data": data, "height": opts.Height, "prove": opts.Prove},
   242  		result)
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  
   247  	return result, nil
   248  }
   249  
   250  func (c *baseRPCClient) BroadcastTxCommit(
   251  	ctx context.Context,
   252  	tx types.Tx,
   253  ) (*ctypes.ResultBroadcastTxCommit, error) {
   254  	result := new(ctypes.ResultBroadcastTxCommit)
   255  	_, err := c.caller.Call(ctx, "broadcast_tx_commit", map[string]interface{}{"tx": tx}, result)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  	return result, nil
   260  }
   261  
   262  func (c *baseRPCClient) BroadcastTxAsync(
   263  	ctx context.Context,
   264  	tx types.Tx,
   265  ) (*ctypes.ResultBroadcastTx, error) {
   266  	return c.broadcastTX(ctx, "broadcast_tx_async", tx)
   267  }
   268  
   269  func (c *baseRPCClient) BroadcastTxSync(
   270  	ctx context.Context,
   271  	tx types.Tx,
   272  ) (*ctypes.ResultBroadcastTx, error) {
   273  	return c.broadcastTX(ctx, "broadcast_tx_sync", tx)
   274  }
   275  
   276  func (c *baseRPCClient) broadcastTX(
   277  	ctx context.Context,
   278  	route string,
   279  	tx types.Tx,
   280  ) (*ctypes.ResultBroadcastTx, error) {
   281  	result := new(ctypes.ResultBroadcastTx)
   282  	_, err := c.caller.Call(ctx, route, map[string]interface{}{"tx": tx}, result)
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  	return result, nil
   287  }
   288  
   289  func (c *baseRPCClient) UnconfirmedTxs(
   290  	ctx context.Context,
   291  	limit *int,
   292  ) (*ctypes.ResultUnconfirmedTxs, error) {
   293  	result := new(ctypes.ResultUnconfirmedTxs)
   294  	params := make(map[string]interface{})
   295  	if limit != nil {
   296  		params["limit"] = limit
   297  	}
   298  	_, err := c.caller.Call(ctx, "unconfirmed_txs", params, result)
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  	return result, nil
   303  }
   304  
   305  func (c *baseRPCClient) NumUnconfirmedTxs(ctx context.Context) (*ctypes.ResultUnconfirmedTxs, error) {
   306  	result := new(ctypes.ResultUnconfirmedTxs)
   307  	_, err := c.caller.Call(ctx, "num_unconfirmed_txs", map[string]interface{}{}, result)
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  	return result, nil
   312  }
   313  
   314  func (c *baseRPCClient) CheckTx(ctx context.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) {
   315  	result := new(ctypes.ResultCheckTx)
   316  	_, err := c.caller.Call(ctx, "check_tx", map[string]interface{}{"tx": tx}, result)
   317  	if err != nil {
   318  		return nil, err
   319  	}
   320  	return result, nil
   321  }
   322  
   323  func (c *baseRPCClient) NetInfo(ctx context.Context) (*ctypes.ResultNetInfo, error) {
   324  	result := new(ctypes.ResultNetInfo)
   325  	_, err := c.caller.Call(ctx, "net_info", map[string]interface{}{}, result)
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	return result, nil
   330  }
   331  
   332  func (c *baseRPCClient) DumpConsensusState(ctx context.Context) (*ctypes.ResultDumpConsensusState, error) {
   333  	result := new(ctypes.ResultDumpConsensusState)
   334  	_, err := c.caller.Call(ctx, "dump_consensus_state", map[string]interface{}{}, result)
   335  	if err != nil {
   336  		return nil, err
   337  	}
   338  	return result, nil
   339  }
   340  
   341  func (c *baseRPCClient) ConsensusState(ctx context.Context) (*ctypes.ResultConsensusState, error) {
   342  	result := new(ctypes.ResultConsensusState)
   343  	_, err := c.caller.Call(ctx, "consensus_state", map[string]interface{}{}, result)
   344  	if err != nil {
   345  		return nil, err
   346  	}
   347  	return result, nil
   348  }
   349  
   350  func (c *baseRPCClient) ConsensusParams(
   351  	ctx context.Context,
   352  	height *int64,
   353  ) (*ctypes.ResultConsensusParams, error) {
   354  	result := new(ctypes.ResultConsensusParams)
   355  	params := make(map[string]interface{})
   356  	if height != nil {
   357  		params["height"] = height
   358  	}
   359  	_, err := c.caller.Call(ctx, "consensus_params", params, result)
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  	return result, nil
   364  }
   365  
   366  func (c *baseRPCClient) Health(ctx context.Context) (*ctypes.ResultHealth, error) {
   367  	result := new(ctypes.ResultHealth)
   368  	_, err := c.caller.Call(ctx, "health", map[string]interface{}{}, result)
   369  	if err != nil {
   370  		return nil, err
   371  	}
   372  	return result, nil
   373  }
   374  
   375  func (c *baseRPCClient) BlockchainInfo(
   376  	ctx context.Context,
   377  	minHeight,
   378  	maxHeight int64,
   379  ) (*ctypes.ResultBlockchainInfo, error) {
   380  	result := new(ctypes.ResultBlockchainInfo)
   381  	_, err := c.caller.Call(ctx, "blockchain",
   382  		map[string]interface{}{"minHeight": minHeight, "maxHeight": maxHeight},
   383  		result)
   384  	if err != nil {
   385  		return nil, err
   386  	}
   387  	return result, nil
   388  }
   389  
   390  func (c *baseRPCClient) Genesis(ctx context.Context) (*ctypes.ResultGenesis, error) {
   391  	result := new(ctypes.ResultGenesis)
   392  	_, err := c.caller.Call(ctx, "genesis", map[string]interface{}{}, result)
   393  	if err != nil {
   394  		return nil, err
   395  	}
   396  	return result, nil
   397  }
   398  
   399  func (c *baseRPCClient) GenesisChunked(ctx context.Context, id uint) (*ctypes.ResultGenesisChunk, error) {
   400  	result := new(ctypes.ResultGenesisChunk)
   401  	_, err := c.caller.Call(ctx, "genesis_chunked", map[string]interface{}{"chunk": id}, result)
   402  	if err != nil {
   403  		return nil, err
   404  	}
   405  	return result, nil
   406  }
   407  
   408  func (c *baseRPCClient) Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error) {
   409  	result := new(ctypes.ResultBlock)
   410  	params := make(map[string]interface{})
   411  	if height != nil {
   412  		params["height"] = height
   413  	}
   414  	_, err := c.caller.Call(ctx, "block", params, result)
   415  	if err != nil {
   416  		return nil, err
   417  	}
   418  	return result, nil
   419  }
   420  
   421  func (c *baseRPCClient) BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error) {
   422  	result := new(ctypes.ResultBlock)
   423  	params := map[string]interface{}{
   424  		"hash": hash,
   425  	}
   426  	_, err := c.caller.Call(ctx, "block_by_hash", params, result)
   427  	if err != nil {
   428  		return nil, err
   429  	}
   430  	return result, nil
   431  }
   432  
   433  func (c *baseRPCClient) BlockResults(
   434  	ctx context.Context,
   435  	height *int64,
   436  ) (*ctypes.ResultBlockResults, error) {
   437  	result := new(ctypes.ResultBlockResults)
   438  	params := make(map[string]interface{})
   439  	if height != nil {
   440  		params["height"] = height
   441  	}
   442  	_, err := c.caller.Call(ctx, "block_results", params, result)
   443  	if err != nil {
   444  		return nil, err
   445  	}
   446  	return result, nil
   447  }
   448  
   449  func (c *baseRPCClient) Header(ctx context.Context, height *int64) (*ctypes.ResultHeader, error) {
   450  	result := new(ctypes.ResultHeader)
   451  	params := make(map[string]interface{})
   452  	if height != nil {
   453  		params["height"] = height
   454  	}
   455  	_, err := c.caller.Call(ctx, "header", params, result)
   456  	if err != nil {
   457  		return nil, err
   458  	}
   459  	return result, nil
   460  }
   461  
   462  func (c *baseRPCClient) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*ctypes.ResultHeader, error) {
   463  	result := new(ctypes.ResultHeader)
   464  	params := map[string]interface{}{
   465  		"hash": hash,
   466  	}
   467  	_, err := c.caller.Call(ctx, "header_by_hash", params, result)
   468  	if err != nil {
   469  		return nil, err
   470  	}
   471  	return result, nil
   472  }
   473  
   474  func (c *baseRPCClient) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) {
   475  	result := new(ctypes.ResultCommit)
   476  	params := make(map[string]interface{})
   477  	if height != nil {
   478  		params["height"] = height
   479  	}
   480  	_, err := c.caller.Call(ctx, "commit", params, result)
   481  	if err != nil {
   482  		return nil, err
   483  	}
   484  	return result, nil
   485  }
   486  
   487  func (c *baseRPCClient) Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) {
   488  	result := new(ctypes.ResultTx)
   489  	params := map[string]interface{}{
   490  		"hash":  hash,
   491  		"prove": prove,
   492  	}
   493  	_, err := c.caller.Call(ctx, "tx", params, result)
   494  	if err != nil {
   495  		return nil, err
   496  	}
   497  	return result, nil
   498  }
   499  
   500  func (c *baseRPCClient) TxSearch(
   501  	ctx context.Context,
   502  	query string,
   503  	prove bool,
   504  	page,
   505  	perPage *int,
   506  	orderBy string,
   507  ) (*ctypes.ResultTxSearch, error) {
   508  
   509  	result := new(ctypes.ResultTxSearch)
   510  	params := map[string]interface{}{
   511  		"query":    query,
   512  		"prove":    prove,
   513  		"order_by": orderBy,
   514  	}
   515  
   516  	if page != nil {
   517  		params["page"] = page
   518  	}
   519  	if perPage != nil {
   520  		params["per_page"] = perPage
   521  	}
   522  
   523  	_, err := c.caller.Call(ctx, "tx_search", params, result)
   524  	if err != nil {
   525  		return nil, err
   526  	}
   527  
   528  	return result, nil
   529  }
   530  
   531  func (c *baseRPCClient) BlockSearch(
   532  	ctx context.Context,
   533  	query string,
   534  	page, perPage *int,
   535  	orderBy string,
   536  ) (*ctypes.ResultBlockSearch, error) {
   537  
   538  	result := new(ctypes.ResultBlockSearch)
   539  	params := map[string]interface{}{
   540  		"query":    query,
   541  		"order_by": orderBy,
   542  	}
   543  
   544  	if page != nil {
   545  		params["page"] = page
   546  	}
   547  	if perPage != nil {
   548  		params["per_page"] = perPage
   549  	}
   550  
   551  	_, err := c.caller.Call(ctx, "block_search", params, result)
   552  	if err != nil {
   553  		return nil, err
   554  	}
   555  
   556  	return result, nil
   557  }
   558  
   559  func (c *baseRPCClient) Validators(
   560  	ctx context.Context,
   561  	height *int64,
   562  	page,
   563  	perPage *int,
   564  ) (*ctypes.ResultValidators, error) {
   565  	result := new(ctypes.ResultValidators)
   566  	params := make(map[string]interface{})
   567  	if page != nil {
   568  		params["page"] = page
   569  	}
   570  	if perPage != nil {
   571  		params["per_page"] = perPage
   572  	}
   573  	if height != nil {
   574  		params["height"] = height
   575  	}
   576  	_, err := c.caller.Call(ctx, "validators", params, result)
   577  	if err != nil {
   578  		return nil, err
   579  	}
   580  	return result, nil
   581  }
   582  
   583  func (c *baseRPCClient) BroadcastEvidence(
   584  	ctx context.Context,
   585  	ev types.Evidence,
   586  ) (*ctypes.ResultBroadcastEvidence, error) {
   587  	result := new(ctypes.ResultBroadcastEvidence)
   588  	_, err := c.caller.Call(ctx, "broadcast_evidence", map[string]interface{}{"evidence": ev}, result)
   589  	if err != nil {
   590  		return nil, err
   591  	}
   592  	return result, nil
   593  }
   594  
   595  //-----------------------------------------------------------------------------
   596  // WSEvents
   597  
   598  var errNotRunning = errors.New("client is not running. Use .Start() method to start")
   599  
   600  // WSEvents is a wrapper around WSClient, which implements EventsClient.
   601  type WSEvents struct {
   602  	service.BaseService
   603  	remote   string
   604  	endpoint string
   605  	ws       *jsonrpcclient.WSClient
   606  
   607  	mtx           cmtsync.RWMutex
   608  	subscriptions map[string]chan ctypes.ResultEvent // query -> chan
   609  }
   610  
   611  func newWSEvents(remote, endpoint string) (*WSEvents, error) {
   612  	w := &WSEvents{
   613  		endpoint:      endpoint,
   614  		remote:        remote,
   615  		subscriptions: make(map[string]chan ctypes.ResultEvent),
   616  	}
   617  	w.BaseService = *service.NewBaseService(nil, "WSEvents", w)
   618  
   619  	var err error
   620  	w.ws, err = jsonrpcclient.NewWS(w.remote, w.endpoint, jsonrpcclient.OnReconnect(func() {
   621  		// resubscribe immediately
   622  		w.redoSubscriptionsAfter(0 * time.Second)
   623  	}))
   624  	if err != nil {
   625  		return nil, err
   626  	}
   627  	w.ws.SetLogger(w.Logger)
   628  
   629  	return w, nil
   630  }
   631  
   632  // OnStart implements service.Service by starting WSClient and event loop.
   633  func (w *WSEvents) OnStart() error {
   634  	if err := w.ws.Start(); err != nil {
   635  		return err
   636  	}
   637  
   638  	go w.eventListener()
   639  
   640  	return nil
   641  }
   642  
   643  // OnStop implements service.Service by stopping WSClient.
   644  func (w *WSEvents) OnStop() {
   645  	if err := w.ws.Stop(); err != nil {
   646  		w.Logger.Error("Can't stop ws client", "err", err)
   647  	}
   648  }
   649  
   650  // Subscribe implements EventsClient by using WSClient to subscribe given
   651  // subscriber to query. By default, returns a channel with cap=1. Error is
   652  // returned if it fails to subscribe.
   653  //
   654  // Channel is never closed to prevent clients from seeing an erroneous event.
   655  //
   656  // It returns an error if WSEvents is not running.
   657  func (w *WSEvents) Subscribe(ctx context.Context, subscriber, query string,
   658  	outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) {
   659  
   660  	if !w.IsRunning() {
   661  		return nil, errNotRunning
   662  	}
   663  
   664  	if err := w.ws.Subscribe(ctx, query); err != nil {
   665  		return nil, err
   666  	}
   667  
   668  	outCap := 1
   669  	if len(outCapacity) > 0 {
   670  		outCap = outCapacity[0]
   671  	}
   672  
   673  	outc := make(chan ctypes.ResultEvent, outCap)
   674  	w.mtx.Lock()
   675  	// subscriber param is ignored because CometBFT will override it with
   676  	// remote IP anyway.
   677  	w.subscriptions[query] = outc
   678  	w.mtx.Unlock()
   679  
   680  	return outc, nil
   681  }
   682  
   683  // Unsubscribe implements EventsClient by using WSClient to unsubscribe given
   684  // subscriber from query.
   685  //
   686  // It returns an error if WSEvents is not running.
   687  func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber, query string) error {
   688  	if !w.IsRunning() {
   689  		return errNotRunning
   690  	}
   691  
   692  	if err := w.ws.Unsubscribe(ctx, query); err != nil {
   693  		return err
   694  	}
   695  
   696  	w.mtx.Lock()
   697  	_, ok := w.subscriptions[query]
   698  	if ok {
   699  		delete(w.subscriptions, query)
   700  	}
   701  	w.mtx.Unlock()
   702  
   703  	return nil
   704  }
   705  
   706  // UnsubscribeAll implements EventsClient by using WSClient to unsubscribe
   707  // given subscriber from all the queries.
   708  //
   709  // It returns an error if WSEvents is not running.
   710  func (w *WSEvents) UnsubscribeAll(ctx context.Context, subscriber string) error {
   711  	if !w.IsRunning() {
   712  		return errNotRunning
   713  	}
   714  
   715  	if err := w.ws.UnsubscribeAll(ctx); err != nil {
   716  		return err
   717  	}
   718  
   719  	w.mtx.Lock()
   720  	w.subscriptions = make(map[string]chan ctypes.ResultEvent)
   721  	w.mtx.Unlock()
   722  
   723  	return nil
   724  }
   725  
   726  // After being reconnected, it is necessary to redo subscription to server
   727  // otherwise no data will be automatically received.
   728  func (w *WSEvents) redoSubscriptionsAfter(d time.Duration) {
   729  	time.Sleep(d)
   730  
   731  	w.mtx.RLock()
   732  	defer w.mtx.RUnlock()
   733  	for q := range w.subscriptions {
   734  		err := w.ws.Subscribe(context.Background(), q)
   735  		if err != nil {
   736  			w.Logger.Error("Failed to resubscribe", "err", err)
   737  		}
   738  	}
   739  }
   740  
   741  func isErrAlreadySubscribed(err error) bool {
   742  	return strings.Contains(err.Error(), cmtpubsub.ErrAlreadySubscribed.Error())
   743  }
   744  
   745  func (w *WSEvents) eventListener() {
   746  	for {
   747  		select {
   748  		case resp, ok := <-w.ws.ResponsesCh:
   749  			if !ok {
   750  				return
   751  			}
   752  
   753  			if resp.Error != nil {
   754  				w.Logger.Error("WS error", "err", resp.Error.Error())
   755  				// Error can be ErrAlreadySubscribed or max client (subscriptions per
   756  				// client) reached or CometBFT exited.
   757  				// We can ignore ErrAlreadySubscribed, but need to retry in other
   758  				// cases.
   759  				if !isErrAlreadySubscribed(resp.Error) {
   760  					// Resubscribe after 1 second to give CometBFT time to restart (if
   761  					// crashed).
   762  					w.redoSubscriptionsAfter(1 * time.Second)
   763  				}
   764  				continue
   765  			}
   766  
   767  			result := new(ctypes.ResultEvent)
   768  			err := cmtjson.Unmarshal(resp.Result, result)
   769  			if err != nil {
   770  				w.Logger.Error("failed to unmarshal response", "err", err)
   771  				continue
   772  			}
   773  
   774  			w.mtx.RLock()
   775  			if out, ok := w.subscriptions[result.Query]; ok {
   776  				if cap(out) == 0 {
   777  					out <- *result
   778  				} else {
   779  					select {
   780  					case out <- *result:
   781  					default:
   782  						w.Logger.Error("wanted to publish ResultEvent, but out channel is full", "result", result, "query", result.Query)
   783  					}
   784  				}
   785  			}
   786  			w.mtx.RUnlock()
   787  		case <-w.Quit():
   788  			return
   789  		}
   790  	}
   791  }