github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/abci/client/grpc_client.go (about)

     1  package abcicli
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"sync"
     7  	"time"
     8  
     9  	"context"
    10  
    11  	"google.golang.org/grpc"
    12  	"google.golang.org/grpc/credentials/insecure"
    13  
    14  	"github.com/badrootd/celestia-core/abci/types"
    15  	cmtnet "github.com/badrootd/celestia-core/libs/net"
    16  	"github.com/badrootd/celestia-core/libs/service"
    17  	cmtsync "github.com/badrootd/celestia-core/libs/sync"
    18  )
    19  
    20  var _ Client = (*grpcClient)(nil)
    21  
    22  // A stripped copy of the remoteClient that makes
    23  // synchronous calls using grpc
    24  type grpcClient struct {
    25  	service.BaseService
    26  	mustConnect bool
    27  
    28  	client   types.ABCIApplicationClient
    29  	conn     *grpc.ClientConn
    30  	chReqRes chan *ReqRes // dispatches "async" responses to callbacks *in order*, needed by mempool
    31  
    32  	mtx   cmtsync.Mutex
    33  	addr  string
    34  	err   error
    35  	resCb func(*types.Request, *types.Response) // listens to all callbacks
    36  }
    37  
    38  func NewGRPCClient(addr string, mustConnect bool) Client {
    39  	cli := &grpcClient{
    40  		addr:        addr,
    41  		mustConnect: mustConnect,
    42  		// Buffering the channel is needed to make calls appear asynchronous,
    43  		// which is required when the caller makes multiple async calls before
    44  		// processing callbacks (e.g. due to holding locks). 64 means that a
    45  		// caller can make up to 64 async calls before a callback must be
    46  		// processed (otherwise it deadlocks). It also means that we can make 64
    47  		// gRPC calls while processing a slow callback at the channel head.
    48  		chReqRes: make(chan *ReqRes, 64),
    49  	}
    50  	cli.BaseService = *service.NewBaseService(nil, "grpcClient", cli)
    51  	return cli
    52  }
    53  
    54  func dialerFunc(ctx context.Context, addr string) (net.Conn, error) {
    55  	return cmtnet.Connect(addr)
    56  }
    57  
    58  func (cli *grpcClient) OnStart() error {
    59  	if err := cli.BaseService.OnStart(); err != nil {
    60  		return err
    61  	}
    62  
    63  	// This processes asynchronous request/response messages and dispatches
    64  	// them to callbacks.
    65  	go func() {
    66  		// Use a separate function to use defer for mutex unlocks (this handles panics)
    67  		callCb := func(reqres *ReqRes) {
    68  			cli.mtx.Lock()
    69  			defer cli.mtx.Unlock()
    70  
    71  			reqres.Done()
    72  
    73  			// Notify client listener if set
    74  			if cli.resCb != nil {
    75  				cli.resCb(reqres.Request, reqres.Response)
    76  			}
    77  
    78  			// Notify reqRes listener if set
    79  			reqres.InvokeCallback()
    80  		}
    81  		for reqres := range cli.chReqRes {
    82  			if reqres != nil {
    83  				callCb(reqres)
    84  			} else {
    85  				cli.Logger.Error("Received nil reqres")
    86  			}
    87  		}
    88  	}()
    89  
    90  RETRY_LOOP:
    91  	for {
    92  		conn, err := grpc.Dial(cli.addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(dialerFunc))
    93  		if err != nil {
    94  			if cli.mustConnect {
    95  				return err
    96  			}
    97  			cli.Logger.Error(fmt.Sprintf("abci.grpcClient failed to connect to %v.  Retrying...\n", cli.addr), "err", err)
    98  			time.Sleep(time.Second * dialRetryIntervalSeconds)
    99  			continue RETRY_LOOP
   100  		}
   101  
   102  		cli.Logger.Info("Dialed server. Waiting for echo.", "addr", cli.addr)
   103  		client := types.NewABCIApplicationClient(conn)
   104  		cli.conn = conn
   105  
   106  	ENSURE_CONNECTED:
   107  		for {
   108  			_, err := client.Echo(context.Background(), &types.RequestEcho{Message: "hello"}, grpc.WaitForReady(true))
   109  			if err == nil {
   110  				break ENSURE_CONNECTED
   111  			}
   112  			cli.Logger.Error("Echo failed", "err", err)
   113  			time.Sleep(time.Second * echoRetryIntervalSeconds)
   114  		}
   115  
   116  		cli.client = client
   117  		return nil
   118  	}
   119  }
   120  
   121  func (cli *grpcClient) OnStop() {
   122  	cli.BaseService.OnStop()
   123  
   124  	if cli.conn != nil {
   125  		cli.conn.Close()
   126  	}
   127  	close(cli.chReqRes)
   128  }
   129  
   130  func (cli *grpcClient) StopForError(err error) {
   131  	if !cli.IsRunning() {
   132  		return
   133  	}
   134  
   135  	cli.mtx.Lock()
   136  	if cli.err == nil {
   137  		cli.err = err
   138  	}
   139  	cli.mtx.Unlock()
   140  
   141  	cli.Logger.Error(fmt.Sprintf("Stopping abci.grpcClient for error: %v", err.Error()))
   142  	if err := cli.Stop(); err != nil {
   143  		cli.Logger.Error("Error stopping abci.grpcClient", "err", err)
   144  	}
   145  }
   146  
   147  func (cli *grpcClient) Error() error {
   148  	cli.mtx.Lock()
   149  	defer cli.mtx.Unlock()
   150  	return cli.err
   151  }
   152  
   153  // Set listener for all responses
   154  // NOTE: callback may get internally generated flush responses.
   155  func (cli *grpcClient) SetResponseCallback(resCb Callback) {
   156  	cli.mtx.Lock()
   157  	cli.resCb = resCb
   158  	cli.mtx.Unlock()
   159  }
   160  
   161  //----------------------------------------
   162  // GRPC calls are synchronous, but some callbacks expect to be called asynchronously
   163  // (eg. the mempool expects to be able to lock to remove bad txs from cache).
   164  // To accommodate, we finish each call in its own go-routine,
   165  // which is expensive, but easy - if you want something better, use the socket protocol!
   166  // maybe one day, if people really want it, we use grpc streams,
   167  // but hopefully not :D
   168  
   169  func (cli *grpcClient) EchoAsync(msg string) *ReqRes {
   170  	req := types.ToRequestEcho(msg)
   171  	res, err := cli.client.Echo(context.Background(), req.GetEcho(), grpc.WaitForReady(true))
   172  	if err != nil {
   173  		cli.StopForError(err)
   174  	}
   175  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Echo{Echo: res}})
   176  }
   177  
   178  func (cli *grpcClient) FlushAsync() *ReqRes {
   179  	req := types.ToRequestFlush()
   180  	res, err := cli.client.Flush(context.Background(), req.GetFlush(), grpc.WaitForReady(true))
   181  	if err != nil {
   182  		cli.StopForError(err)
   183  	}
   184  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Flush{Flush: res}})
   185  }
   186  
   187  func (cli *grpcClient) InfoAsync(params types.RequestInfo) *ReqRes {
   188  	req := types.ToRequestInfo(params)
   189  	res, err := cli.client.Info(context.Background(), req.GetInfo(), grpc.WaitForReady(true))
   190  	if err != nil {
   191  		cli.StopForError(err)
   192  	}
   193  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Info{Info: res}})
   194  }
   195  
   196  func (cli *grpcClient) DeliverTxAsync(params types.RequestDeliverTx) *ReqRes {
   197  	req := types.ToRequestDeliverTx(params)
   198  	res, err := cli.client.DeliverTx(context.Background(), req.GetDeliverTx(), grpc.WaitForReady(true))
   199  	if err != nil {
   200  		cli.StopForError(err)
   201  	}
   202  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_DeliverTx{DeliverTx: res}})
   203  }
   204  
   205  func (cli *grpcClient) CheckTxAsync(params types.RequestCheckTx) *ReqRes {
   206  	req := types.ToRequestCheckTx(params)
   207  	res, err := cli.client.CheckTx(context.Background(), req.GetCheckTx(), grpc.WaitForReady(true))
   208  	if err != nil {
   209  		cli.StopForError(err)
   210  	}
   211  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_CheckTx{CheckTx: res}})
   212  }
   213  
   214  func (cli *grpcClient) QueryAsync(params types.RequestQuery) *ReqRes {
   215  	req := types.ToRequestQuery(params)
   216  	res, err := cli.client.Query(context.Background(), req.GetQuery(), grpc.WaitForReady(true))
   217  	if err != nil {
   218  		cli.StopForError(err)
   219  	}
   220  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Query{Query: res}})
   221  }
   222  
   223  func (cli *grpcClient) CommitAsync() *ReqRes {
   224  	req := types.ToRequestCommit()
   225  	res, err := cli.client.Commit(context.Background(), req.GetCommit(), grpc.WaitForReady(true))
   226  	if err != nil {
   227  		cli.StopForError(err)
   228  	}
   229  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Commit{Commit: res}})
   230  }
   231  
   232  func (cli *grpcClient) InitChainAsync(params types.RequestInitChain) *ReqRes {
   233  	req := types.ToRequestInitChain(params)
   234  	res, err := cli.client.InitChain(context.Background(), req.GetInitChain(), grpc.WaitForReady(true))
   235  	if err != nil {
   236  		cli.StopForError(err)
   237  	}
   238  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_InitChain{InitChain: res}})
   239  }
   240  
   241  func (cli *grpcClient) BeginBlockAsync(params types.RequestBeginBlock) *ReqRes {
   242  	req := types.ToRequestBeginBlock(params)
   243  	res, err := cli.client.BeginBlock(context.Background(), req.GetBeginBlock(), grpc.WaitForReady(true))
   244  	if err != nil {
   245  		cli.StopForError(err)
   246  	}
   247  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_BeginBlock{BeginBlock: res}})
   248  }
   249  
   250  func (cli *grpcClient) EndBlockAsync(params types.RequestEndBlock) *ReqRes {
   251  	req := types.ToRequestEndBlock(params)
   252  	res, err := cli.client.EndBlock(context.Background(), req.GetEndBlock(), grpc.WaitForReady(true))
   253  	if err != nil {
   254  		cli.StopForError(err)
   255  	}
   256  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_EndBlock{EndBlock: res}})
   257  }
   258  
   259  func (cli *grpcClient) ListSnapshotsAsync(params types.RequestListSnapshots) *ReqRes {
   260  	req := types.ToRequestListSnapshots(params)
   261  	res, err := cli.client.ListSnapshots(context.Background(), req.GetListSnapshots(), grpc.WaitForReady(true))
   262  	if err != nil {
   263  		cli.StopForError(err)
   264  	}
   265  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_ListSnapshots{ListSnapshots: res}})
   266  }
   267  
   268  func (cli *grpcClient) OfferSnapshotAsync(params types.RequestOfferSnapshot) *ReqRes {
   269  	req := types.ToRequestOfferSnapshot(params)
   270  	res, err := cli.client.OfferSnapshot(context.Background(), req.GetOfferSnapshot(), grpc.WaitForReady(true))
   271  	if err != nil {
   272  		cli.StopForError(err)
   273  	}
   274  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_OfferSnapshot{OfferSnapshot: res}})
   275  }
   276  
   277  func (cli *grpcClient) LoadSnapshotChunkAsync(params types.RequestLoadSnapshotChunk) *ReqRes {
   278  	req := types.ToRequestLoadSnapshotChunk(params)
   279  	res, err := cli.client.LoadSnapshotChunk(context.Background(), req.GetLoadSnapshotChunk(), grpc.WaitForReady(true))
   280  	if err != nil {
   281  		cli.StopForError(err)
   282  	}
   283  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_LoadSnapshotChunk{LoadSnapshotChunk: res}})
   284  }
   285  
   286  func (cli *grpcClient) ApplySnapshotChunkAsync(params types.RequestApplySnapshotChunk) *ReqRes {
   287  	req := types.ToRequestApplySnapshotChunk(params)
   288  	res, err := cli.client.ApplySnapshotChunk(context.Background(), req.GetApplySnapshotChunk(), grpc.WaitForReady(true))
   289  	if err != nil {
   290  		cli.StopForError(err)
   291  	}
   292  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_ApplySnapshotChunk{ApplySnapshotChunk: res}})
   293  }
   294  
   295  func (cli *grpcClient) PrepareProposalAsync(
   296  	params types.RequestPrepareProposal,
   297  ) *ReqRes {
   298  
   299  	req := types.ToRequestPrepareProposal(params)
   300  	res, err := cli.client.PrepareProposal(context.Background(), req.GetPrepareProposal(), grpc.WaitForReady(true))
   301  	if err != nil {
   302  		cli.StopForError(err)
   303  	}
   304  	return cli.finishAsyncCall(
   305  		req,
   306  		&types.Response{
   307  			Value: &types.Response_PrepareProposal{
   308  				PrepareProposal: res,
   309  			},
   310  		},
   311  	)
   312  }
   313  
   314  func (cli *grpcClient) ProcessProposalAsync(
   315  	params types.RequestProcessProposal,
   316  ) *ReqRes {
   317  
   318  	req := types.ToRequestProcessProposal(params)
   319  	res, err := cli.client.ProcessProposal(context.Background(), req.GetProcessProposal(), grpc.WaitForReady(true))
   320  	if err != nil {
   321  		return nil
   322  	}
   323  
   324  	return cli.finishAsyncCall(
   325  		req,
   326  		&types.Response{
   327  			Value: &types.Response_ProcessProposal{
   328  				ProcessProposal: res,
   329  			},
   330  		},
   331  	)
   332  }
   333  
   334  // finishAsyncCall creates a ReqRes for an async call, and immediately populates it
   335  // with the response. We don't complete it until it's been ordered via the channel.
   336  func (cli *grpcClient) finishAsyncCall(req *types.Request, res *types.Response) *ReqRes {
   337  	reqres := NewReqRes(req)
   338  	reqres.Response = res
   339  	cli.chReqRes <- reqres // use channel for async responses, since they must be ordered
   340  	return reqres
   341  }
   342  
   343  // finishSyncCall waits for an async call to complete. It is necessary to call all
   344  // sync calls asynchronously as well, to maintain call and response ordering via
   345  // the channel, and this method will wait until the async call completes.
   346  func (cli *grpcClient) finishSyncCall(reqres *ReqRes) *types.Response {
   347  	// It's possible that the callback is called twice, since the callback can
   348  	// be called immediately on SetCallback() in addition to after it has been
   349  	// set. This is because completing the ReqRes happens in a separate critical
   350  	// section from the one where the callback is called: there is a race where
   351  	// SetCallback() is called between completing the ReqRes and dispatching the
   352  	// callback.
   353  	//
   354  	// We also buffer the channel with 1 response, since SetCallback() will be
   355  	// called synchronously if the reqres is already completed, in which case
   356  	// it will block on sending to the channel since it hasn't gotten around to
   357  	// receiving from it yet.
   358  	//
   359  	// ReqRes should really handle callback dispatch internally, to guarantee
   360  	// that it's only called once and avoid the above race conditions.
   361  	var once sync.Once
   362  	ch := make(chan *types.Response, 1)
   363  	reqres.SetCallback(func(res *types.Response) {
   364  		once.Do(func() {
   365  			ch <- res
   366  		})
   367  	})
   368  	return <-ch
   369  }
   370  
   371  //----------------------------------------
   372  
   373  func (cli *grpcClient) FlushSync() error {
   374  	reqres := cli.FlushAsync()
   375  	cli.finishSyncCall(reqres).GetFlush()
   376  	return cli.Error()
   377  }
   378  
   379  func (cli *grpcClient) EchoSync(msg string) (*types.ResponseEcho, error) {
   380  	reqres := cli.EchoAsync(msg)
   381  	// StopForError should already have been called if error is set
   382  	return cli.finishSyncCall(reqres).GetEcho(), cli.Error()
   383  }
   384  
   385  func (cli *grpcClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) {
   386  	reqres := cli.InfoAsync(req)
   387  	return cli.finishSyncCall(reqres).GetInfo(), cli.Error()
   388  }
   389  
   390  func (cli *grpcClient) DeliverTxSync(params types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
   391  	reqres := cli.DeliverTxAsync(params)
   392  	return cli.finishSyncCall(reqres).GetDeliverTx(), cli.Error()
   393  }
   394  
   395  func (cli *grpcClient) CheckTxSync(params types.RequestCheckTx) (*types.ResponseCheckTx, error) {
   396  	reqres := cli.CheckTxAsync(params)
   397  	return cli.finishSyncCall(reqres).GetCheckTx(), cli.Error()
   398  }
   399  
   400  func (cli *grpcClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) {
   401  	reqres := cli.QueryAsync(req)
   402  	return cli.finishSyncCall(reqres).GetQuery(), cli.Error()
   403  }
   404  
   405  func (cli *grpcClient) CommitSync() (*types.ResponseCommit, error) {
   406  	reqres := cli.CommitAsync()
   407  	return cli.finishSyncCall(reqres).GetCommit(), cli.Error()
   408  }
   409  
   410  func (cli *grpcClient) InitChainSync(params types.RequestInitChain) (*types.ResponseInitChain, error) {
   411  	reqres := cli.InitChainAsync(params)
   412  	return cli.finishSyncCall(reqres).GetInitChain(), cli.Error()
   413  }
   414  
   415  func (cli *grpcClient) BeginBlockSync(params types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
   416  	reqres := cli.BeginBlockAsync(params)
   417  	return cli.finishSyncCall(reqres).GetBeginBlock(), cli.Error()
   418  }
   419  
   420  func (cli *grpcClient) EndBlockSync(params types.RequestEndBlock) (*types.ResponseEndBlock, error) {
   421  	reqres := cli.EndBlockAsync(params)
   422  	return cli.finishSyncCall(reqres).GetEndBlock(), cli.Error()
   423  }
   424  
   425  func (cli *grpcClient) ListSnapshotsSync(params types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
   426  	reqres := cli.ListSnapshotsAsync(params)
   427  	return cli.finishSyncCall(reqres).GetListSnapshots(), cli.Error()
   428  }
   429  
   430  func (cli *grpcClient) OfferSnapshotSync(params types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
   431  	reqres := cli.OfferSnapshotAsync(params)
   432  	return cli.finishSyncCall(reqres).GetOfferSnapshot(), cli.Error()
   433  }
   434  
   435  func (cli *grpcClient) LoadSnapshotChunkSync(
   436  	params types.RequestLoadSnapshotChunk,
   437  ) (*types.ResponseLoadSnapshotChunk, error) {
   438  	reqres := cli.LoadSnapshotChunkAsync(params)
   439  	return cli.finishSyncCall(reqres).GetLoadSnapshotChunk(), cli.Error()
   440  }
   441  
   442  func (cli *grpcClient) ApplySnapshotChunkSync(
   443  	params types.RequestApplySnapshotChunk,
   444  ) (*types.ResponseApplySnapshotChunk, error) {
   445  	reqres := cli.ApplySnapshotChunkAsync(params)
   446  	return cli.finishSyncCall(reqres).GetApplySnapshotChunk(), cli.Error()
   447  }
   448  
   449  func (cli *grpcClient) PrepareProposalSync(
   450  	params types.RequestPrepareProposal,
   451  ) (*types.ResponsePrepareProposal, error) {
   452  	reqres := cli.PrepareProposalAsync(params)
   453  	return cli.finishSyncCall(reqres).GetPrepareProposal(), cli.Error()
   454  }
   455  
   456  func (cli *grpcClient) ProcessProposalSync(
   457  	params types.RequestProcessProposal,
   458  ) (*types.ResponseProcessProposal, error) {
   459  	reqres := cli.ProcessProposalAsync(params)
   460  	return cli.finishSyncCall(reqres).GetProcessProposal(), cli.Error()
   461  }