github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/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/celestia-core/libs/bytes"
    11  	cmtjson "github.com/badrootd/celestia-core/libs/json"
    12  	"github.com/badrootd/celestia-core/libs/log"
    13  	cmtpubsub "github.com/badrootd/celestia-core/libs/pubsub"
    14  	"github.com/badrootd/celestia-core/libs/service"
    15  	cmtsync "github.com/badrootd/celestia-core/libs/sync"
    16  	rpcclient "github.com/badrootd/celestia-core/rpc/client"
    17  	ctypes "github.com/badrootd/celestia-core/rpc/core/types"
    18  	jsonrpcclient "github.com/badrootd/celestia-core/rpc/jsonrpc/client"
    19  	"github.com/badrootd/celestia-core/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) SignedBlock(ctx context.Context, height *int64) (*ctypes.ResultSignedBlock, error) {
   422  	result := new(ctypes.ResultSignedBlock)
   423  	params := make(map[string]interface{})
   424  	if height != nil {
   425  		params["height"] = height
   426  	}
   427  	_, err := c.caller.Call(ctx, "signed_block", params, result)
   428  	if err != nil {
   429  		return nil, err
   430  	}
   431  	return result, nil
   432  }
   433  
   434  func (c *baseRPCClient) BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error) {
   435  	result := new(ctypes.ResultBlock)
   436  	params := map[string]interface{}{
   437  		"hash": hash,
   438  	}
   439  	_, err := c.caller.Call(ctx, "block_by_hash", params, result)
   440  	if err != nil {
   441  		return nil, err
   442  	}
   443  	return result, nil
   444  }
   445  
   446  func (c *baseRPCClient) BlockResults(
   447  	ctx context.Context,
   448  	height *int64,
   449  ) (*ctypes.ResultBlockResults, error) {
   450  	result := new(ctypes.ResultBlockResults)
   451  	params := make(map[string]interface{})
   452  	if height != nil {
   453  		params["height"] = height
   454  	}
   455  	_, err := c.caller.Call(ctx, "block_results", params, result)
   456  	if err != nil {
   457  		return nil, err
   458  	}
   459  	return result, nil
   460  }
   461  
   462  func (c *baseRPCClient) Header(ctx context.Context, height *int64) (*ctypes.ResultHeader, error) {
   463  	result := new(ctypes.ResultHeader)
   464  	params := make(map[string]interface{})
   465  	if height != nil {
   466  		params["height"] = height
   467  	}
   468  	_, err := c.caller.Call(ctx, "header", params, result)
   469  	if err != nil {
   470  		return nil, err
   471  	}
   472  	return result, nil
   473  }
   474  
   475  func (c *baseRPCClient) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*ctypes.ResultHeader, error) {
   476  	result := new(ctypes.ResultHeader)
   477  	params := map[string]interface{}{
   478  		"hash": hash,
   479  	}
   480  	_, err := c.caller.Call(ctx, "header_by_hash", params, result)
   481  	if err != nil {
   482  		return nil, err
   483  	}
   484  	return result, nil
   485  }
   486  
   487  func (c *baseRPCClient) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) {
   488  	result := new(ctypes.ResultCommit)
   489  	params := make(map[string]interface{})
   490  	if height != nil {
   491  		params["height"] = height
   492  	}
   493  	_, err := c.caller.Call(ctx, "commit", params, result)
   494  	if err != nil {
   495  		return nil, err
   496  	}
   497  	return result, nil
   498  }
   499  
   500  func (c *baseRPCClient) DataCommitment(
   501  	ctx context.Context,
   502  	start uint64,
   503  	end uint64,
   504  ) (*ctypes.ResultDataCommitment, error) {
   505  	result := new(ctypes.ResultDataCommitment)
   506  	params := map[string]interface{}{
   507  		"start": start,
   508  		"end":   end,
   509  	}
   510  
   511  	_, err := c.caller.Call(ctx, "data_commitment", params, result)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  
   516  	return result, nil
   517  }
   518  
   519  func (c *baseRPCClient) DataRootInclusionProof(
   520  	ctx context.Context,
   521  	height uint64,
   522  	start uint64,
   523  	end uint64,
   524  ) (*ctypes.ResultDataRootInclusionProof, error) {
   525  	result := new(ctypes.ResultDataRootInclusionProof)
   526  	params := map[string]interface{}{
   527  		"height": height,
   528  		"start":  start,
   529  		"end":    end,
   530  	}
   531  
   532  	_, err := c.caller.Call(ctx, "data_root_inclusion_proof", params, result)
   533  	if err != nil {
   534  		return nil, err
   535  	}
   536  
   537  	return result, nil
   538  }
   539  
   540  func (c *baseRPCClient) Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) {
   541  	result := new(ctypes.ResultTx)
   542  	params := map[string]interface{}{
   543  		"hash":  hash,
   544  		"prove": prove,
   545  	}
   546  	_, err := c.caller.Call(ctx, "tx", params, result)
   547  	if err != nil {
   548  		return nil, err
   549  	}
   550  	return result, nil
   551  }
   552  
   553  func (c *baseRPCClient) ProveShares(
   554  	ctx context.Context,
   555  	height uint64,
   556  	startShare uint64,
   557  	endShare uint64,
   558  ) (types.ShareProof, error) {
   559  	result := new(types.ShareProof)
   560  	params := map[string]interface{}{
   561  		"height":     height,
   562  		"startShare": startShare,
   563  		"endShare":   endShare,
   564  	}
   565  	_, err := c.caller.Call(ctx, "prove_shares", params, result)
   566  	if err != nil {
   567  		return types.ShareProof{}, err
   568  	}
   569  	return *result, nil
   570  }
   571  
   572  func (c *baseRPCClient) TxSearch(
   573  	ctx context.Context,
   574  	query string,
   575  	prove bool,
   576  	page,
   577  	perPage *int,
   578  	orderBy string,
   579  ) (*ctypes.ResultTxSearch, error) {
   580  
   581  	result := new(ctypes.ResultTxSearch)
   582  	params := map[string]interface{}{
   583  		"query":    query,
   584  		"prove":    prove,
   585  		"order_by": orderBy,
   586  	}
   587  
   588  	if page != nil {
   589  		params["page"] = page
   590  	}
   591  	if perPage != nil {
   592  		params["per_page"] = perPage
   593  	}
   594  
   595  	_, err := c.caller.Call(ctx, "tx_search", params, result)
   596  	if err != nil {
   597  		return nil, err
   598  	}
   599  
   600  	return result, nil
   601  }
   602  
   603  func (c *baseRPCClient) BlockSearch(
   604  	ctx context.Context,
   605  	query string,
   606  	page, perPage *int,
   607  	orderBy string,
   608  ) (*ctypes.ResultBlockSearch, error) {
   609  
   610  	result := new(ctypes.ResultBlockSearch)
   611  	params := map[string]interface{}{
   612  		"query":    query,
   613  		"order_by": orderBy,
   614  	}
   615  
   616  	if page != nil {
   617  		params["page"] = page
   618  	}
   619  	if perPage != nil {
   620  		params["per_page"] = perPage
   621  	}
   622  
   623  	_, err := c.caller.Call(ctx, "block_search", params, result)
   624  	if err != nil {
   625  		return nil, err
   626  	}
   627  
   628  	return result, nil
   629  }
   630  
   631  func (c *baseRPCClient) Validators(
   632  	ctx context.Context,
   633  	height *int64,
   634  	page,
   635  	perPage *int,
   636  ) (*ctypes.ResultValidators, error) {
   637  	result := new(ctypes.ResultValidators)
   638  	params := make(map[string]interface{})
   639  	if page != nil {
   640  		params["page"] = page
   641  	}
   642  	if perPage != nil {
   643  		params["per_page"] = perPage
   644  	}
   645  	if height != nil {
   646  		params["height"] = height
   647  	}
   648  	_, err := c.caller.Call(ctx, "validators", params, result)
   649  	if err != nil {
   650  		return nil, err
   651  	}
   652  	return result, nil
   653  }
   654  
   655  func (c *baseRPCClient) BroadcastEvidence(
   656  	ctx context.Context,
   657  	ev types.Evidence,
   658  ) (*ctypes.ResultBroadcastEvidence, error) {
   659  	result := new(ctypes.ResultBroadcastEvidence)
   660  	_, err := c.caller.Call(ctx, "broadcast_evidence", map[string]interface{}{"evidence": ev}, result)
   661  	if err != nil {
   662  		return nil, err
   663  	}
   664  	return result, nil
   665  }
   666  
   667  //-----------------------------------------------------------------------------
   668  // WSEvents
   669  
   670  var errNotRunning = errors.New("client is not running. Use .Start() method to start")
   671  
   672  // WSEvents is a wrapper around WSClient, which implements EventsClient.
   673  type WSEvents struct {
   674  	service.BaseService
   675  	remote   string
   676  	endpoint string
   677  	ws       *jsonrpcclient.WSClient
   678  
   679  	mtx           cmtsync.RWMutex
   680  	subscriptions map[string]chan ctypes.ResultEvent // query -> chan
   681  }
   682  
   683  func newWSEvents(remote, endpoint string) (*WSEvents, error) {
   684  	w := &WSEvents{
   685  		endpoint:      endpoint,
   686  		remote:        remote,
   687  		subscriptions: make(map[string]chan ctypes.ResultEvent),
   688  	}
   689  	w.BaseService = *service.NewBaseService(nil, "WSEvents", w)
   690  
   691  	var err error
   692  	w.ws, err = jsonrpcclient.NewWS(w.remote, w.endpoint, jsonrpcclient.OnReconnect(func() {
   693  		// resubscribe immediately
   694  		w.redoSubscriptionsAfter(0 * time.Second)
   695  	}))
   696  	if err != nil {
   697  		return nil, err
   698  	}
   699  	w.ws.SetLogger(w.Logger)
   700  
   701  	return w, nil
   702  }
   703  
   704  // OnStart implements service.Service by starting WSClient and event loop.
   705  func (w *WSEvents) OnStart() error {
   706  	if err := w.ws.Start(); err != nil {
   707  		return err
   708  	}
   709  
   710  	go w.eventListener()
   711  
   712  	return nil
   713  }
   714  
   715  // OnStop implements service.Service by stopping WSClient.
   716  func (w *WSEvents) OnStop() {
   717  	if err := w.ws.Stop(); err != nil {
   718  		w.Logger.Error("Can't stop ws client", "err", err)
   719  	}
   720  }
   721  
   722  // Subscribe implements EventsClient by using WSClient to subscribe given
   723  // subscriber to query. By default, returns a channel with cap=1. Error is
   724  // returned if it fails to subscribe.
   725  //
   726  // Channel is never closed to prevent clients from seeing an erroneous event.
   727  //
   728  // It returns an error if WSEvents is not running.
   729  func (w *WSEvents) Subscribe(ctx context.Context, subscriber, query string,
   730  	outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) {
   731  
   732  	if !w.IsRunning() {
   733  		return nil, errNotRunning
   734  	}
   735  
   736  	if err := w.ws.Subscribe(ctx, query); err != nil {
   737  		return nil, err
   738  	}
   739  
   740  	outCap := 1
   741  	if len(outCapacity) > 0 {
   742  		outCap = outCapacity[0]
   743  	}
   744  
   745  	outc := make(chan ctypes.ResultEvent, outCap)
   746  	w.mtx.Lock()
   747  	// subscriber param is ignored because CometBFT will override it with
   748  	// remote IP anyway.
   749  	w.subscriptions[query] = outc
   750  	w.mtx.Unlock()
   751  
   752  	return outc, nil
   753  }
   754  
   755  // Unsubscribe implements EventsClient by using WSClient to unsubscribe given
   756  // subscriber from query.
   757  //
   758  // It returns an error if WSEvents is not running.
   759  func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber, query string) error {
   760  	if !w.IsRunning() {
   761  		return errNotRunning
   762  	}
   763  
   764  	if err := w.ws.Unsubscribe(ctx, query); err != nil {
   765  		return err
   766  	}
   767  
   768  	w.mtx.Lock()
   769  	_, ok := w.subscriptions[query]
   770  	if ok {
   771  		delete(w.subscriptions, query)
   772  	}
   773  	w.mtx.Unlock()
   774  
   775  	return nil
   776  }
   777  
   778  // UnsubscribeAll implements EventsClient by using WSClient to unsubscribe
   779  // given subscriber from all the queries.
   780  //
   781  // It returns an error if WSEvents is not running.
   782  func (w *WSEvents) UnsubscribeAll(ctx context.Context, subscriber string) error {
   783  	if !w.IsRunning() {
   784  		return errNotRunning
   785  	}
   786  
   787  	if err := w.ws.UnsubscribeAll(ctx); err != nil {
   788  		return err
   789  	}
   790  
   791  	w.mtx.Lock()
   792  	w.subscriptions = make(map[string]chan ctypes.ResultEvent)
   793  	w.mtx.Unlock()
   794  
   795  	return nil
   796  }
   797  
   798  // After being reconnected, it is necessary to redo subscription to server
   799  // otherwise no data will be automatically received.
   800  func (w *WSEvents) redoSubscriptionsAfter(d time.Duration) {
   801  	time.Sleep(d)
   802  
   803  	w.mtx.RLock()
   804  	defer w.mtx.RUnlock()
   805  	for q := range w.subscriptions {
   806  		err := w.ws.Subscribe(context.Background(), q)
   807  		if err != nil {
   808  			w.Logger.Error("Failed to resubscribe", "err", err)
   809  		}
   810  	}
   811  }
   812  
   813  func isErrAlreadySubscribed(err error) bool {
   814  	return strings.Contains(err.Error(), cmtpubsub.ErrAlreadySubscribed.Error())
   815  }
   816  
   817  func (w *WSEvents) eventListener() {
   818  	for {
   819  		select {
   820  		case resp, ok := <-w.ws.ResponsesCh:
   821  			if !ok {
   822  				return
   823  			}
   824  
   825  			if resp.Error != nil {
   826  				w.Logger.Error("WS error", "err", resp.Error.Error())
   827  				// Error can be ErrAlreadySubscribed or max client (subscriptions per
   828  				// client) reached or CometBFT exited.
   829  				// We can ignore ErrAlreadySubscribed, but need to retry in other
   830  				// cases.
   831  				if !isErrAlreadySubscribed(resp.Error) {
   832  					// Resubscribe after 1 second to give CometBFT time to restart (if
   833  					// crashed).
   834  					w.redoSubscriptionsAfter(1 * time.Second)
   835  				}
   836  				continue
   837  			}
   838  
   839  			result := new(ctypes.ResultEvent)
   840  			err := cmtjson.Unmarshal(resp.Result, result)
   841  			if err != nil {
   842  				w.Logger.Error("failed to unmarshal response", "err", err)
   843  				continue
   844  			}
   845  
   846  			w.mtx.RLock()
   847  			if out, ok := w.subscriptions[result.Query]; ok {
   848  				if cap(out) == 0 {
   849  					out <- *result
   850  				} else {
   851  					select {
   852  					case out <- *result:
   853  					default:
   854  						w.Logger.Error("wanted to publish ResultEvent, but out channel is full", "result", result, "query", result.Query)
   855  					}
   856  				}
   857  			}
   858  			w.mtx.RUnlock()
   859  		case <-w.Quit():
   860  			return
   861  		}
   862  	}
   863  }