github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/abci/client/grpc_client.go (about) 1 package abcicli 2 3 import ( 4 "fmt" 5 "net" 6 "sync" 7 "time" 8 9 "golang.org/x/net/context" 10 "google.golang.org/grpc" 11 12 "github.com/tendermint/tendermint/abci/types" 13 14 ocabci "github.com/line/ostracon/abci/types" 15 tmnet "github.com/line/ostracon/libs/net" 16 "github.com/line/ostracon/libs/service" 17 tmsync "github.com/line/ostracon/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 ocabci.ABCIApplicationClient 29 conn *grpc.ClientConn 30 31 mtx tmsync.Mutex 32 addr string 33 err error 34 35 globalCbMtx sync.Mutex 36 globalCb func(*ocabci.Request, *ocabci.Response) // listens to all callbacks 37 } 38 39 func NewGRPCClient(addr string, mustConnect bool) Client { 40 cli := &grpcClient{ 41 addr: addr, 42 mustConnect: mustConnect, 43 } 44 cli.BaseService = *service.NewBaseService(nil, "grpcClient", cli) 45 return cli 46 } 47 48 func dialerFunc(ctx context.Context, addr string) (net.Conn, error) { 49 return tmnet.Connect(addr) 50 } 51 52 func (cli *grpcClient) OnStart() error { 53 if err := cli.BaseService.OnStart(); err != nil { 54 return err 55 } 56 57 RETRY_LOOP: 58 for { 59 //nolint:staticcheck // SA1019 Existing use of deprecated but supported dial option. 60 conn, err := grpc.Dial(cli.addr, grpc.WithInsecure(), grpc.WithContextDialer(dialerFunc)) 61 if err != nil { 62 if cli.mustConnect { 63 return err 64 } 65 cli.Logger.Error(fmt.Sprintf("abci.grpcClient failed to connect to %v. Retrying...\n", cli.addr), "err", err) 66 time.Sleep(time.Second * dialRetryIntervalSeconds) 67 continue RETRY_LOOP 68 } 69 70 cli.Logger.Info("Dialed server. Waiting for echo.", "addr", cli.addr) 71 client := ocabci.NewABCIApplicationClient(conn) 72 cli.conn = conn 73 74 ENSURE_CONNECTED: 75 for { 76 _, err := client.Echo(context.Background(), &types.RequestEcho{Message: "hello"}, grpc.WaitForReady(true)) 77 if err == nil { 78 break ENSURE_CONNECTED 79 } 80 cli.Logger.Error("Echo failed", "err", err) 81 time.Sleep(time.Second * echoRetryIntervalSeconds) 82 } 83 84 cli.client = client 85 return nil 86 } 87 } 88 89 func (cli *grpcClient) OnStop() { 90 cli.BaseService.OnStop() 91 92 if cli.conn != nil { 93 cli.conn.Close() 94 } 95 } 96 97 func (cli *grpcClient) StopForError(err error) { 98 if !cli.IsRunning() { 99 return 100 } 101 102 cli.mtx.Lock() 103 if cli.err == nil { 104 cli.err = err 105 } 106 cli.mtx.Unlock() 107 108 cli.Logger.Error(fmt.Sprintf("Stopping abci.grpcClient for error: %v", err.Error())) 109 if err := cli.Stop(); err != nil { 110 cli.Logger.Error("Error stopping abci.grpcClient", "err", err) 111 } 112 } 113 114 func (cli *grpcClient) Error() error { 115 cli.mtx.Lock() 116 defer cli.mtx.Unlock() 117 return cli.err 118 } 119 120 func (cli *grpcClient) SetGlobalCallback(globalCb GlobalCallback) { 121 cli.globalCbMtx.Lock() 122 defer cli.globalCbMtx.Unlock() 123 cli.globalCb = globalCb 124 } 125 126 func (cli *grpcClient) GetGlobalCallback() (cb GlobalCallback) { 127 cli.globalCbMtx.Lock() 128 defer cli.globalCbMtx.Unlock() 129 cb = cli.globalCb 130 return cb 131 } 132 133 //---------------------------------------- 134 // GRPC calls are synchronous, but some callbacks expect to be called asynchronously 135 // (eg. the mempool expects to be able to lock to remove bad txs from cache). 136 // To accommodate, we finish each call in its own go-routine, 137 // which is expensive, but easy - if you want something better, use the socket protocol! 138 // maybe one day, if people really want it, we use grpc streams, 139 // but hopefully not :D 140 141 func (cli *grpcClient) EchoAsync(msg string, cb ResponseCallback) *ReqRes { 142 req := ocabci.ToRequestEcho(msg) 143 res, err := cli.client.Echo(context.Background(), req.GetEcho(), grpc.WaitForReady(true)) 144 if err != nil { 145 cli.StopForError(err) 146 } 147 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_Echo{Echo: res}}, cb) 148 } 149 150 func (cli *grpcClient) FlushAsync(cb ResponseCallback) *ReqRes { 151 req := ocabci.ToRequestFlush() 152 res, err := cli.client.Flush(context.Background(), req.GetFlush(), grpc.WaitForReady(true)) 153 if err != nil { 154 cli.StopForError(err) 155 } 156 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_Flush{Flush: res}}, cb) 157 } 158 159 func (cli *grpcClient) InfoAsync(params types.RequestInfo, cb ResponseCallback) *ReqRes { 160 req := ocabci.ToRequestInfo(params) 161 res, err := cli.client.Info(context.Background(), req.GetInfo(), grpc.WaitForReady(true)) 162 if err != nil { 163 cli.StopForError(err) 164 } 165 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_Info{Info: res}}, cb) 166 } 167 168 func (cli *grpcClient) SetOptionAsync(params types.RequestSetOption, cb ResponseCallback) *ReqRes { 169 req := ocabci.ToRequestSetOption(params) 170 res, err := cli.client.SetOption(context.Background(), req.GetSetOption(), grpc.WaitForReady(true)) 171 if err != nil { 172 cli.StopForError(err) 173 } 174 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_SetOption{SetOption: res}}, cb) 175 } 176 177 func (cli *grpcClient) DeliverTxAsync(params types.RequestDeliverTx, cb ResponseCallback) *ReqRes { 178 req := ocabci.ToRequestDeliverTx(params) 179 res, err := cli.client.DeliverTx(context.Background(), req.GetDeliverTx(), grpc.WaitForReady(true)) 180 if err != nil { 181 cli.StopForError(err) 182 } 183 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_DeliverTx{DeliverTx: res}}, cb) 184 } 185 186 func (cli *grpcClient) CheckTxAsync(params types.RequestCheckTx, cb ResponseCallback) *ReqRes { 187 req := ocabci.ToRequestCheckTx(params) 188 res, err := cli.client.CheckTx(context.Background(), req.GetCheckTx(), grpc.WaitForReady(true)) 189 if err != nil { 190 cli.StopForError(err) 191 } 192 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_CheckTx{CheckTx: res}}, cb) 193 } 194 195 func (cli *grpcClient) QueryAsync(params types.RequestQuery, cb ResponseCallback) *ReqRes { 196 req := ocabci.ToRequestQuery(params) 197 res, err := cli.client.Query(context.Background(), req.GetQuery(), grpc.WaitForReady(true)) 198 if err != nil { 199 cli.StopForError(err) 200 } 201 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_Query{Query: res}}, cb) 202 } 203 204 func (cli *grpcClient) CommitAsync(cb ResponseCallback) *ReqRes { 205 req := ocabci.ToRequestCommit() 206 res, err := cli.client.Commit(context.Background(), req.GetCommit(), grpc.WaitForReady(true)) 207 if err != nil { 208 cli.StopForError(err) 209 } 210 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_Commit{Commit: res}}, cb) 211 } 212 213 func (cli *grpcClient) InitChainAsync(params types.RequestInitChain, cb ResponseCallback) *ReqRes { 214 req := ocabci.ToRequestInitChain(params) 215 res, err := cli.client.InitChain(context.Background(), req.GetInitChain(), grpc.WaitForReady(true)) 216 if err != nil { 217 cli.StopForError(err) 218 } 219 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_InitChain{InitChain: res}}, cb) 220 } 221 222 func (cli *grpcClient) BeginBlockAsync(params ocabci.RequestBeginBlock, cb ResponseCallback) *ReqRes { 223 req := ocabci.ToRequestBeginBlock(params) 224 res, err := cli.client.BeginBlock(context.Background(), req.GetBeginBlock(), grpc.WaitForReady(true)) 225 if err != nil { 226 cli.StopForError(err) 227 } 228 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_BeginBlock{BeginBlock: res}}, cb) 229 } 230 231 func (cli *grpcClient) EndBlockAsync(params types.RequestEndBlock, cb ResponseCallback) *ReqRes { 232 req := ocabci.ToRequestEndBlock(params) 233 res, err := cli.client.EndBlock(context.Background(), req.GetEndBlock(), grpc.WaitForReady(true)) 234 if err != nil { 235 cli.StopForError(err) 236 } 237 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_EndBlock{EndBlock: res}}, cb) 238 } 239 240 func (cli *grpcClient) BeginRecheckTxAsync(params ocabci.RequestBeginRecheckTx, cb ResponseCallback) *ReqRes { 241 req := ocabci.ToRequestBeginRecheckTx(params) 242 res, err := cli.client.BeginRecheckTx(context.Background(), req.GetBeginRecheckTx(), grpc.WaitForReady(true)) 243 if err != nil { 244 cli.StopForError(err) 245 } 246 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_BeginRecheckTx{BeginRecheckTx: res}}, cb) 247 } 248 249 func (cli *grpcClient) EndRecheckTxAsync(params ocabci.RequestEndRecheckTx, cb ResponseCallback) *ReqRes { 250 req := ocabci.ToRequestEndRecheckTx(params) 251 res, err := cli.client.EndRecheckTx(context.Background(), req.GetEndRecheckTx(), grpc.WaitForReady(true)) 252 if err != nil { 253 cli.StopForError(err) 254 } 255 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_EndRecheckTx{EndRecheckTx: res}}, cb) 256 } 257 258 func (cli *grpcClient) ListSnapshotsAsync(params types.RequestListSnapshots, cb ResponseCallback) *ReqRes { 259 req := ocabci.ToRequestListSnapshots(params) 260 res, err := cli.client.ListSnapshots(context.Background(), req.GetListSnapshots(), grpc.WaitForReady(true)) 261 if err != nil { 262 cli.StopForError(err) 263 } 264 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_ListSnapshots{ListSnapshots: res}}, cb) 265 } 266 267 func (cli *grpcClient) OfferSnapshotAsync(params types.RequestOfferSnapshot, cb ResponseCallback) *ReqRes { 268 req := ocabci.ToRequestOfferSnapshot(params) 269 res, err := cli.client.OfferSnapshot(context.Background(), req.GetOfferSnapshot(), grpc.WaitForReady(true)) 270 if err != nil { 271 cli.StopForError(err) 272 } 273 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_OfferSnapshot{OfferSnapshot: res}}, cb) 274 } 275 276 func (cli *grpcClient) LoadSnapshotChunkAsync(params types.RequestLoadSnapshotChunk, cb ResponseCallback) *ReqRes { 277 req := ocabci.ToRequestLoadSnapshotChunk(params) 278 res, err := cli.client.LoadSnapshotChunk(context.Background(), req.GetLoadSnapshotChunk(), grpc.WaitForReady(true)) 279 if err != nil { 280 cli.StopForError(err) 281 } 282 return cli.finishAsyncCall(req, &ocabci.Response{Value: &ocabci.Response_LoadSnapshotChunk{LoadSnapshotChunk: res}}, cb) 283 } 284 285 func (cli *grpcClient) ApplySnapshotChunkAsync(params types.RequestApplySnapshotChunk, cb ResponseCallback) *ReqRes { 286 req := ocabci.ToRequestApplySnapshotChunk(params) 287 res, err := cli.client.ApplySnapshotChunk(context.Background(), req.GetApplySnapshotChunk(), grpc.WaitForReady(true)) 288 if err != nil { 289 cli.StopForError(err) 290 } 291 return cli.finishAsyncCall(req, 292 &ocabci.Response{Value: &ocabci.Response_ApplySnapshotChunk{ApplySnapshotChunk: res}}, cb) 293 } 294 295 func (cli *grpcClient) finishAsyncCall(req *ocabci.Request, res *ocabci.Response, cb ResponseCallback) *ReqRes { 296 reqRes := NewReqRes(req, cb) 297 298 // goroutine for callbacks 299 go func() { 300 set := reqRes.SetDone(res) 301 if set { 302 // Notify client listener if set 303 if globalCb := cli.GetGlobalCallback(); globalCb != nil { 304 globalCb(req, res) 305 } 306 } 307 }() 308 309 return reqRes 310 } 311 312 // ---------------------------------------- 313 func (cli *grpcClient) FlushSync() (*types.ResponseFlush, error) { 314 reqres := cli.FlushAsync(nil) 315 reqres.Wait() 316 return reqres.Response.GetFlush(), cli.Error() 317 } 318 319 func (cli *grpcClient) EchoSync(msg string) (*types.ResponseEcho, error) { 320 reqres := cli.EchoAsync(msg, nil) 321 reqres.Wait() 322 // StopForError should already have been called if error is set 323 return reqres.Response.GetEcho(), cli.Error() 324 } 325 326 func (cli *grpcClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) { 327 reqres := cli.InfoAsync(req, nil) 328 reqres.Wait() 329 return reqres.Response.GetInfo(), cli.Error() 330 } 331 332 func (cli *grpcClient) SetOptionSync(req types.RequestSetOption) (*types.ResponseSetOption, error) { 333 reqres := cli.SetOptionAsync(req, nil) 334 reqres.Wait() 335 return reqres.Response.GetSetOption(), cli.Error() 336 } 337 338 func (cli *grpcClient) DeliverTxSync(params types.RequestDeliverTx) (*types.ResponseDeliverTx, error) { 339 reqres := cli.DeliverTxAsync(params, nil) 340 reqres.Wait() 341 return reqres.Response.GetDeliverTx(), cli.Error() 342 } 343 344 func (cli *grpcClient) CheckTxSync(params types.RequestCheckTx) (*ocabci.ResponseCheckTx, error) { 345 reqres := cli.CheckTxAsync(params, nil) 346 reqres.Wait() 347 return reqres.Response.GetCheckTx(), cli.Error() 348 } 349 350 func (cli *grpcClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) { 351 reqres := cli.QueryAsync(req, nil) 352 reqres.Wait() 353 return reqres.Response.GetQuery(), cli.Error() 354 } 355 356 func (cli *grpcClient) CommitSync() (*types.ResponseCommit, error) { 357 reqres := cli.CommitAsync(nil) 358 reqres.Wait() 359 return reqres.Response.GetCommit(), cli.Error() 360 } 361 362 func (cli *grpcClient) InitChainSync(params types.RequestInitChain) (*types.ResponseInitChain, error) { 363 reqres := cli.InitChainAsync(params, nil) 364 reqres.Wait() 365 return reqres.Response.GetInitChain(), cli.Error() 366 } 367 368 func (cli *grpcClient) BeginBlockSync(params ocabci.RequestBeginBlock) (*types.ResponseBeginBlock, error) { 369 reqres := cli.BeginBlockAsync(params, nil) 370 reqres.Wait() 371 return reqres.Response.GetBeginBlock(), cli.Error() 372 } 373 374 func (cli *grpcClient) EndBlockSync(params types.RequestEndBlock) (*types.ResponseEndBlock, error) { 375 reqres := cli.EndBlockAsync(params, nil) 376 reqres.Wait() 377 return reqres.Response.GetEndBlock(), cli.Error() 378 } 379 380 func (cli *grpcClient) BeginRecheckTxSync(params ocabci.RequestBeginRecheckTx) (*ocabci.ResponseBeginRecheckTx, error) { 381 reqres := cli.BeginRecheckTxAsync(params, nil) 382 reqres.Wait() 383 return reqres.Response.GetBeginRecheckTx(), cli.Error() 384 } 385 386 func (cli *grpcClient) EndRecheckTxSync(params ocabci.RequestEndRecheckTx) (*ocabci.ResponseEndRecheckTx, error) { 387 reqres := cli.EndRecheckTxAsync(params, nil) 388 reqres.Wait() 389 return reqres.Response.GetEndRecheckTx(), cli.Error() 390 } 391 392 func (cli *grpcClient) ListSnapshotsSync(params types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { 393 reqres := cli.ListSnapshotsAsync(params, nil) 394 reqres.Wait() 395 return reqres.Response.GetListSnapshots(), cli.Error() 396 } 397 398 func (cli *grpcClient) OfferSnapshotSync(params types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { 399 reqres := cli.OfferSnapshotAsync(params, nil) 400 reqres.Wait() 401 return reqres.Response.GetOfferSnapshot(), cli.Error() 402 } 403 404 func (cli *grpcClient) LoadSnapshotChunkSync( 405 params types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { 406 reqres := cli.LoadSnapshotChunkAsync(params, nil) 407 reqres.Wait() 408 return reqres.Response.GetLoadSnapshotChunk(), cli.Error() 409 } 410 411 func (cli *grpcClient) ApplySnapshotChunkSync( 412 params types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { 413 reqres := cli.ApplySnapshotChunkAsync(params, nil) 414 reqres.Wait() 415 return reqres.Response.GetApplySnapshotChunk(), cli.Error() 416 }