github.com/evdatsion/aphelion-dpos-bft@v0.32.1/rpc/client/httpclient.go (about) 1 package client 2 3 import ( 4 "context" 5 "strings" 6 "sync" 7 "time" 8 9 "github.com/pkg/errors" 10 11 amino "github.com/evdatsion/go-amino" 12 13 cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common" 14 tmpubsub "github.com/evdatsion/aphelion-dpos-bft/libs/pubsub" 15 ctypes "github.com/evdatsion/aphelion-dpos-bft/rpc/core/types" 16 rpcclient "github.com/evdatsion/aphelion-dpos-bft/rpc/lib/client" 17 "github.com/evdatsion/aphelion-dpos-bft/types" 18 ) 19 20 /* 21 HTTP is a Client implementation that communicates with a Tendermint node over 22 JSON RPC and WebSockets. 23 24 This is the main implementation you probably want to use in production code. 25 There are other implementations when calling the Tendermint node in-process 26 (Local), or when you want to mock out the server for test code (mock). 27 28 You can subscribe for any event published by Tendermint using Subscribe method. 29 Note delivery is best-effort. If you don't read events fast enough or network is 30 slow, Tendermint might cancel the subscription. The client will attempt to 31 resubscribe (you don't need to do anything). It will keep trying every second 32 indefinitely until successful. 33 34 Request batching is available for JSON RPC requests over HTTP, which conforms to 35 the JSON RPC specification (https://www.jsonrpc.org/specification#batch). See 36 the example for more details. 37 */ 38 type HTTP struct { 39 remote string 40 rpc *rpcclient.JSONRPCClient 41 42 *baseRPCClient 43 *WSEvents 44 } 45 46 // BatchHTTP provides the same interface as `HTTP`, but allows for batching of 47 // requests (as per https://www.jsonrpc.org/specification#batch). Do not 48 // instantiate directly - rather use the HTTP.NewBatch() method to create an 49 // instance of this struct. 50 // 51 // Batching of HTTP requests is thread-safe in the sense that multiple 52 // goroutines can each create their own batches and send them using the same 53 // HTTP client. Multiple goroutines could also enqueue transactions in a single 54 // batch, but ordering of transactions in the batch cannot be guaranteed in such 55 // an example. 56 type BatchHTTP struct { 57 rpcBatch *rpcclient.JSONRPCRequestBatch 58 *baseRPCClient 59 } 60 61 // rpcClient is an internal interface to which our RPC clients (batch and 62 // non-batch) must conform. Acts as an additional code-level sanity check to 63 // make sure the implementations stay coherent. 64 type rpcClient interface { 65 ABCIClient 66 HistoryClient 67 NetworkClient 68 SignClient 69 StatusClient 70 } 71 72 // baseRPCClient implements the basic RPC method logic without the actual 73 // underlying RPC call functionality, which is provided by `caller`. 74 type baseRPCClient struct { 75 caller rpcclient.JSONRPCCaller 76 } 77 78 var _ rpcClient = (*HTTP)(nil) 79 var _ rpcClient = (*BatchHTTP)(nil) 80 var _ rpcClient = (*baseRPCClient)(nil) 81 82 //----------------------------------------------------------------------------- 83 // HTTP 84 85 // NewHTTP takes a remote endpoint in the form <protocol>://<host>:<port> and 86 // the websocket path (which always seems to be "/websocket") 87 func NewHTTP(remote, wsEndpoint string) *HTTP { 88 rc := rpcclient.NewJSONRPCClient(remote) 89 cdc := rc.Codec() 90 ctypes.RegisterAmino(cdc) 91 rc.SetCodec(cdc) 92 93 return &HTTP{ 94 rpc: rc, 95 remote: remote, 96 baseRPCClient: &baseRPCClient{caller: rc}, 97 WSEvents: newWSEvents(cdc, remote, wsEndpoint), 98 } 99 } 100 101 var _ Client = (*HTTP)(nil) 102 103 // NewBatch creates a new batch client for this HTTP client. 104 func (c *HTTP) NewBatch() *BatchHTTP { 105 rpcBatch := c.rpc.NewRequestBatch() 106 return &BatchHTTP{ 107 rpcBatch: rpcBatch, 108 baseRPCClient: &baseRPCClient{ 109 caller: rpcBatch, 110 }, 111 } 112 } 113 114 //----------------------------------------------------------------------------- 115 // BatchHTTP 116 117 // Send is a convenience function for an HTTP batch that will trigger the 118 // compilation of the batched requests and send them off using the client as a 119 // single request. On success, this returns a list of the deserialized results 120 // from each request in the sent batch. 121 func (b *BatchHTTP) Send() ([]interface{}, error) { 122 return b.rpcBatch.Send() 123 } 124 125 // Clear will empty out this batch of requests and return the number of requests 126 // that were cleared out. 127 func (b *BatchHTTP) Clear() int { 128 return b.rpcBatch.Clear() 129 } 130 131 // Count returns the number of enqueued requests waiting to be sent. 132 func (b *BatchHTTP) Count() int { 133 return b.rpcBatch.Count() 134 } 135 136 //----------------------------------------------------------------------------- 137 // baseRPCClient 138 139 func (c *baseRPCClient) Status() (*ctypes.ResultStatus, error) { 140 result := new(ctypes.ResultStatus) 141 _, err := c.caller.Call("status", map[string]interface{}{}, result) 142 if err != nil { 143 return nil, errors.Wrap(err, "Status") 144 } 145 return result, nil 146 } 147 148 func (c *baseRPCClient) ABCIInfo() (*ctypes.ResultABCIInfo, error) { 149 result := new(ctypes.ResultABCIInfo) 150 _, err := c.caller.Call("abci_info", map[string]interface{}{}, result) 151 if err != nil { 152 return nil, errors.Wrap(err, "ABCIInfo") 153 } 154 return result, nil 155 } 156 157 func (c *baseRPCClient) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) { 158 return c.ABCIQueryWithOptions(path, data, DefaultABCIQueryOptions) 159 } 160 161 func (c *baseRPCClient) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { 162 result := new(ctypes.ResultABCIQuery) 163 _, err := c.caller.Call("abci_query", 164 map[string]interface{}{"path": path, "data": data, "height": opts.Height, "prove": opts.Prove}, 165 result) 166 if err != nil { 167 return nil, errors.Wrap(err, "ABCIQuery") 168 } 169 return result, nil 170 } 171 172 func (c *baseRPCClient) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { 173 result := new(ctypes.ResultBroadcastTxCommit) 174 _, err := c.caller.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result) 175 if err != nil { 176 return nil, errors.Wrap(err, "broadcast_tx_commit") 177 } 178 return result, nil 179 } 180 181 func (c *baseRPCClient) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 182 return c.broadcastTX("broadcast_tx_async", tx) 183 } 184 185 func (c *baseRPCClient) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 186 return c.broadcastTX("broadcast_tx_sync", tx) 187 } 188 189 func (c *baseRPCClient) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 190 result := new(ctypes.ResultBroadcastTx) 191 _, err := c.caller.Call(route, map[string]interface{}{"tx": tx}, result) 192 if err != nil { 193 return nil, errors.Wrap(err, route) 194 } 195 return result, nil 196 } 197 198 func (c *baseRPCClient) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { 199 result := new(ctypes.ResultUnconfirmedTxs) 200 _, err := c.caller.Call("unconfirmed_txs", map[string]interface{}{"limit": limit}, result) 201 if err != nil { 202 return nil, errors.Wrap(err, "unconfirmed_txs") 203 } 204 return result, nil 205 } 206 207 func (c *baseRPCClient) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { 208 result := new(ctypes.ResultUnconfirmedTxs) 209 _, err := c.caller.Call("num_unconfirmed_txs", map[string]interface{}{}, result) 210 if err != nil { 211 return nil, errors.Wrap(err, "num_unconfirmed_txs") 212 } 213 return result, nil 214 } 215 216 func (c *baseRPCClient) NetInfo() (*ctypes.ResultNetInfo, error) { 217 result := new(ctypes.ResultNetInfo) 218 _, err := c.caller.Call("net_info", map[string]interface{}{}, result) 219 if err != nil { 220 return nil, errors.Wrap(err, "NetInfo") 221 } 222 return result, nil 223 } 224 225 func (c *baseRPCClient) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { 226 result := new(ctypes.ResultDumpConsensusState) 227 _, err := c.caller.Call("dump_consensus_state", map[string]interface{}{}, result) 228 if err != nil { 229 return nil, errors.Wrap(err, "DumpConsensusState") 230 } 231 return result, nil 232 } 233 234 func (c *baseRPCClient) ConsensusState() (*ctypes.ResultConsensusState, error) { 235 result := new(ctypes.ResultConsensusState) 236 _, err := c.caller.Call("consensus_state", map[string]interface{}{}, result) 237 if err != nil { 238 return nil, errors.Wrap(err, "ConsensusState") 239 } 240 return result, nil 241 } 242 243 func (c *baseRPCClient) Health() (*ctypes.ResultHealth, error) { 244 result := new(ctypes.ResultHealth) 245 _, err := c.caller.Call("health", map[string]interface{}{}, result) 246 if err != nil { 247 return nil, errors.Wrap(err, "Health") 248 } 249 return result, nil 250 } 251 252 func (c *baseRPCClient) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { 253 result := new(ctypes.ResultBlockchainInfo) 254 _, err := c.caller.Call("blockchain", 255 map[string]interface{}{"minHeight": minHeight, "maxHeight": maxHeight}, 256 result) 257 if err != nil { 258 return nil, errors.Wrap(err, "BlockchainInfo") 259 } 260 return result, nil 261 } 262 263 func (c *baseRPCClient) Genesis() (*ctypes.ResultGenesis, error) { 264 result := new(ctypes.ResultGenesis) 265 _, err := c.caller.Call("genesis", map[string]interface{}{}, result) 266 if err != nil { 267 return nil, errors.Wrap(err, "Genesis") 268 } 269 return result, nil 270 } 271 272 func (c *baseRPCClient) Block(height *int64) (*ctypes.ResultBlock, error) { 273 result := new(ctypes.ResultBlock) 274 _, err := c.caller.Call("block", map[string]interface{}{"height": height}, result) 275 if err != nil { 276 return nil, errors.Wrap(err, "Block") 277 } 278 return result, nil 279 } 280 281 func (c *baseRPCClient) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) { 282 result := new(ctypes.ResultBlockResults) 283 _, err := c.caller.Call("block_results", map[string]interface{}{"height": height}, result) 284 if err != nil { 285 return nil, errors.Wrap(err, "Block Result") 286 } 287 return result, nil 288 } 289 290 func (c *baseRPCClient) Commit(height *int64) (*ctypes.ResultCommit, error) { 291 result := new(ctypes.ResultCommit) 292 _, err := c.caller.Call("commit", map[string]interface{}{"height": height}, result) 293 if err != nil { 294 return nil, errors.Wrap(err, "Commit") 295 } 296 return result, nil 297 } 298 299 func (c *baseRPCClient) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { 300 result := new(ctypes.ResultTx) 301 params := map[string]interface{}{ 302 "hash": hash, 303 "prove": prove, 304 } 305 _, err := c.caller.Call("tx", params, result) 306 if err != nil { 307 return nil, errors.Wrap(err, "Tx") 308 } 309 return result, nil 310 } 311 312 func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { 313 result := new(ctypes.ResultTxSearch) 314 params := map[string]interface{}{ 315 "query": query, 316 "prove": prove, 317 "page": page, 318 "per_page": perPage, 319 } 320 _, err := c.caller.Call("tx_search", params, result) 321 if err != nil { 322 return nil, errors.Wrap(err, "TxSearch") 323 } 324 return result, nil 325 } 326 327 func (c *baseRPCClient) Validators(height *int64) (*ctypes.ResultValidators, error) { 328 result := new(ctypes.ResultValidators) 329 _, err := c.caller.Call("validators", map[string]interface{}{"height": height}, result) 330 if err != nil { 331 return nil, errors.Wrap(err, "Validators") 332 } 333 return result, nil 334 } 335 336 //----------------------------------------------------------------------------- 337 // WSEvents 338 339 type WSEvents struct { 340 cmn.BaseService 341 cdc *amino.Codec 342 remote string 343 endpoint string 344 ws *rpcclient.WSClient 345 346 mtx sync.RWMutex 347 // query -> chan 348 subscriptions map[string]chan ctypes.ResultEvent 349 } 350 351 func newWSEvents(cdc *amino.Codec, remote, endpoint string) *WSEvents { 352 wsEvents := &WSEvents{ 353 cdc: cdc, 354 endpoint: endpoint, 355 remote: remote, 356 subscriptions: make(map[string]chan ctypes.ResultEvent), 357 } 358 359 wsEvents.BaseService = *cmn.NewBaseService(nil, "WSEvents", wsEvents) 360 return wsEvents 361 } 362 363 // OnStart implements cmn.Service by starting WSClient and event loop. 364 func (w *WSEvents) OnStart() error { 365 w.ws = rpcclient.NewWSClient(w.remote, w.endpoint, rpcclient.OnReconnect(func() { 366 // resubscribe immediately 367 w.redoSubscriptionsAfter(0 * time.Second) 368 })) 369 w.ws.SetCodec(w.cdc) 370 371 err := w.ws.Start() 372 if err != nil { 373 return err 374 } 375 376 go w.eventListener() 377 return nil 378 } 379 380 // OnStop implements cmn.Service by stopping WSClient. 381 func (w *WSEvents) OnStop() { 382 _ = w.ws.Stop() 383 } 384 385 // Subscribe implements EventsClient by using WSClient to subscribe given 386 // subscriber to query. By default, returns a channel with cap=1. Error is 387 // returned if it fails to subscribe. 388 // Channel is never closed to prevent clients from seeing an erroneus event. 389 func (w *WSEvents) Subscribe(ctx context.Context, subscriber, query string, 390 outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) { 391 392 if err := w.ws.Subscribe(ctx, query); err != nil { 393 return nil, err 394 } 395 396 outCap := 1 397 if len(outCapacity) > 0 { 398 outCap = outCapacity[0] 399 } 400 401 outc := make(chan ctypes.ResultEvent, outCap) 402 w.mtx.Lock() 403 // subscriber param is ignored because Tendermint will override it with 404 // remote IP anyway. 405 w.subscriptions[query] = outc 406 w.mtx.Unlock() 407 408 return outc, nil 409 } 410 411 // Unsubscribe implements EventsClient by using WSClient to unsubscribe given 412 // subscriber from query. 413 func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber, query string) error { 414 if err := w.ws.Unsubscribe(ctx, query); err != nil { 415 return err 416 } 417 418 w.mtx.Lock() 419 _, ok := w.subscriptions[query] 420 if ok { 421 delete(w.subscriptions, query) 422 } 423 w.mtx.Unlock() 424 425 return nil 426 } 427 428 // UnsubscribeAll implements EventsClient by using WSClient to unsubscribe 429 // given subscriber from all the queries. 430 func (w *WSEvents) UnsubscribeAll(ctx context.Context, subscriber string) error { 431 if err := w.ws.UnsubscribeAll(ctx); err != nil { 432 return err 433 } 434 435 w.mtx.Lock() 436 w.subscriptions = make(map[string]chan ctypes.ResultEvent) 437 w.mtx.Unlock() 438 439 return nil 440 } 441 442 // After being reconnected, it is necessary to redo subscription to server 443 // otherwise no data will be automatically received. 444 func (w *WSEvents) redoSubscriptionsAfter(d time.Duration) { 445 time.Sleep(d) 446 447 for q := range w.subscriptions { 448 err := w.ws.Subscribe(context.Background(), q) 449 if err != nil { 450 w.Logger.Error("Failed to resubscribe", "err", err) 451 } 452 } 453 } 454 455 func isErrAlreadySubscribed(err error) bool { 456 return strings.Contains(err.Error(), tmpubsub.ErrAlreadySubscribed.Error()) 457 } 458 459 func (w *WSEvents) eventListener() { 460 for { 461 select { 462 case resp, ok := <-w.ws.ResponsesCh: 463 if !ok { 464 return 465 } 466 467 if resp.Error != nil { 468 w.Logger.Error("WS error", "err", resp.Error.Error()) 469 // Error can be ErrAlreadySubscribed or max client (subscriptions per 470 // client) reached or Tendermint exited. 471 // We can ignore ErrAlreadySubscribed, but need to retry in other 472 // cases. 473 if !isErrAlreadySubscribed(resp.Error) { 474 // Resubscribe after 1 second to give Tendermint time to restart (if 475 // crashed). 476 w.redoSubscriptionsAfter(1 * time.Second) 477 } 478 continue 479 } 480 481 result := new(ctypes.ResultEvent) 482 err := w.cdc.UnmarshalJSON(resp.Result, result) 483 if err != nil { 484 w.Logger.Error("failed to unmarshal response", "err", err) 485 continue 486 } 487 488 w.mtx.RLock() 489 if out, ok := w.subscriptions[result.Query]; ok { 490 if cap(out) == 0 { 491 out <- *result 492 } else { 493 select { 494 case out <- *result: 495 default: 496 w.Logger.Error("wanted to publish ResultEvent, but out channel is full", "result", result, "query", result.Query) 497 } 498 } 499 } 500 w.mtx.RUnlock() 501 case <-w.Quit(): 502 return 503 } 504 } 505 }