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 }