github.com/okex/exchain@v1.8.0/libs/tendermint/rpc/client/local/local.go (about) 1 package local 2 3 import ( 4 "context" 5 "crypto/sha256" 6 "time" 7 8 "github.com/pkg/errors" 9 10 "github.com/okex/exchain/libs/tendermint/libs/bytes" 11 "github.com/okex/exchain/libs/tendermint/libs/log" 12 tmpubsub "github.com/okex/exchain/libs/tendermint/libs/pubsub" 13 tmquery "github.com/okex/exchain/libs/tendermint/libs/pubsub/query" 14 nm "github.com/okex/exchain/libs/tendermint/node" 15 rpcclient "github.com/okex/exchain/libs/tendermint/rpc/client" 16 "github.com/okex/exchain/libs/tendermint/rpc/core" 17 ctypes "github.com/okex/exchain/libs/tendermint/rpc/core/types" 18 rpctypes "github.com/okex/exchain/libs/tendermint/rpc/jsonrpc/types" 19 "github.com/okex/exchain/libs/tendermint/types" 20 ) 21 22 /* 23 Local is a Client implementation that directly executes the rpc 24 functions on a given node, without going through HTTP or GRPC. 25 26 This implementation is useful for: 27 28 * Running tests against a node in-process without the overhead 29 of going through an http server 30 * Communication between an ABCI app and Tendermint core when they 31 are compiled in process. 32 33 For real clients, you probably want to use client.HTTP. For more 34 powerful control during testing, you probably want the "client/mock" package. 35 36 You can subscribe for any event published by Tendermint using Subscribe method. 37 Note delivery is best-effort. If you don't read events fast enough, Tendermint 38 might cancel the subscription. The client will attempt to resubscribe (you 39 don't need to do anything). It will keep trying indefinitely with exponential 40 backoff (10ms -> 20ms -> 40ms) until successful. 41 */ 42 type Local struct { 43 *types.EventBus 44 Logger log.Logger 45 ctx *rpctypes.Context 46 } 47 48 func (l *Local) GetUnconfirmedTxByHash(hash [sha256.Size]byte) (types.Tx, error) { 49 return core.GetUnconfirmedTxByHash(hash) 50 } 51 52 // NewLocal configures a client that calls the Node directly. 53 // 54 // Note that given how rpc/core works with package singletons, that 55 // you can only have one node per process. So make sure test cases 56 // don't run in parallel, or try to simulate an entire network in 57 // one process... 58 func New(node *nm.Node) *Local { 59 node.ConfigureRPC() 60 return &Local{ 61 EventBus: node.EventBus(), 62 Logger: log.NewNopLogger(), 63 ctx: &rpctypes.Context{}, 64 } 65 } 66 67 var _ rpcclient.Client = (*Local)(nil) 68 69 // SetLogger allows to set a logger on the client. 70 func (c *Local) SetLogger(l log.Logger) { 71 c.Logger = l 72 } 73 74 func (c *Local) Status() (*ctypes.ResultStatus, error) { 75 return core.Status(c.ctx) 76 } 77 78 func (c *Local) ABCIInfo() (*ctypes.ResultABCIInfo, error) { 79 return core.ABCIInfo(c.ctx) 80 } 81 82 func (c *Local) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) { 83 return c.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions) 84 } 85 86 func (c *Local) ABCIQueryWithOptions( 87 path string, 88 data bytes.HexBytes, 89 opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { 90 return core.ABCIQuery(c.ctx, path, data, opts.Height, opts.Prove) 91 } 92 93 func (c *Local) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { 94 return core.BroadcastTxCommit(c.ctx, tx) 95 } 96 97 func (c *Local) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 98 return core.BroadcastTxAsync(c.ctx, tx) 99 } 100 101 func (c *Local) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 102 return core.BroadcastTxSync(c.ctx, tx) 103 } 104 105 func (c *Local) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { 106 return core.UnconfirmedTxs(c.ctx, limit) 107 } 108 109 func (c *Local) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { 110 return core.NumUnconfirmedTxs(c.ctx) 111 } 112 113 func (c *Local) UserUnconfirmedTxs(address string, limit int) (*ctypes.ResultUserUnconfirmedTxs, error) { 114 return core.UserUnconfirmedTxs(address, limit) 115 } 116 117 func (c *Local) UserNumUnconfirmedTxs(address string) (*ctypes.ResultUserUnconfirmedTxs, error) { 118 return core.UserNumUnconfirmedTxs(address) 119 } 120 121 func (c *Local) GetAddressList() (*ctypes.ResultUnconfirmedAddresses, error) { 122 return core.GetAddressList() 123 } 124 125 func (c *Local) GetPendingNonce(address string) (*ctypes.ResultPendingNonce, bool) { 126 return core.GetPendingNonce(address) 127 } 128 129 func (c *Local) NetInfo() (*ctypes.ResultNetInfo, error) { 130 return core.NetInfo(c.ctx) 131 } 132 133 func (c *Local) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { 134 return core.DumpConsensusState(c.ctx) 135 } 136 137 func (c *Local) ConsensusState() (*ctypes.ResultConsensusState, error) { 138 return core.ConsensusState(c.ctx) 139 } 140 141 func (c *Local) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) { 142 return core.ConsensusParams(c.ctx, height) 143 } 144 145 func (c *Local) Health() (*ctypes.ResultHealth, error) { 146 return core.Health(c.ctx) 147 } 148 149 func (c *Local) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { 150 return core.UnsafeDialSeeds(c.ctx, seeds) 151 } 152 153 func (c *Local) DialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, error) { 154 return core.UnsafeDialPeers(c.ctx, peers, persistent) 155 } 156 157 func (c *Local) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { 158 return core.BlockchainInfo(c.ctx, minHeight, maxHeight) 159 } 160 161 func (c *Local) LatestBlockNumber() (int64, error) { 162 return core.LatestBlockNumber() 163 } 164 165 func (c *Local) Genesis() (*ctypes.ResultGenesis, error) { 166 return core.Genesis(c.ctx) 167 } 168 169 func (c *Local) Block(height *int64) (*ctypes.ResultBlock, error) { 170 return core.Block(c.ctx, height) 171 } 172 173 func (c *Local) BlockInfo(height *int64) (*types.BlockMeta, error) { 174 return core.BlockInfo(c.ctx, height) 175 } 176 177 func (c *Local) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) { 178 return core.BlockResults(c.ctx, height) 179 } 180 181 func (c *Local) Commit(height *int64) (*ctypes.ResultCommit, error) { 182 return core.Commit(c.ctx, height) 183 } 184 185 func (c *Local) Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) { 186 return core.Validators(c.ctx, height, page, perPage) 187 } 188 189 func (c *Local) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { 190 return core.Tx(c.ctx, hash, prove) 191 } 192 193 func (c *Local) TxSearch(query string, prove bool, page, perPage int, orderBy string) ( 194 *ctypes.ResultTxSearch, error) { 195 return core.TxSearch(c.ctx, query, prove, page, perPage, orderBy) 196 } 197 198 func (c *Local) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) { 199 return core.BroadcastEvidence(c.ctx, ev) 200 } 201 202 func (c *Local) Subscribe( 203 ctx context.Context, 204 subscriber, 205 query string, 206 outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) { 207 q, err := tmquery.New(query) 208 if err != nil { 209 return nil, errors.Wrap(err, "failed to parse query") 210 } 211 212 outCap := 1 213 if len(outCapacity) > 0 { 214 outCap = outCapacity[0] 215 } 216 217 var sub types.Subscription 218 if outCap > 0 { 219 sub, err = c.EventBus.Subscribe(ctx, subscriber, q, outCap) 220 } else { 221 sub, err = c.EventBus.SubscribeUnbuffered(ctx, subscriber, q) 222 } 223 if err != nil { 224 return nil, errors.Wrap(err, "failed to subscribe") 225 } 226 227 outc := make(chan ctypes.ResultEvent, outCap) 228 go c.eventsRoutine(sub, subscriber, q, outc) 229 230 return outc, nil 231 } 232 233 func (c *Local) eventsRoutine( 234 sub types.Subscription, 235 subscriber string, 236 q tmpubsub.Query, 237 outc chan<- ctypes.ResultEvent) { 238 for { 239 select { 240 case msg := <-sub.Out(): 241 result := ctypes.ResultEvent{Query: q.String(), Data: msg.Data(), Events: msg.Events()} 242 if cap(outc) == 0 { 243 outc <- result 244 } else { 245 select { 246 case outc <- result: 247 default: 248 c.Logger.Error("wanted to publish ResultEvent, but out channel is full", "result", result, "query", result.Query) 249 } 250 } 251 case <-sub.Cancelled(): 252 if sub.Err() == tmpubsub.ErrUnsubscribed { 253 return 254 } 255 256 c.Logger.Error("subscription was cancelled, resubscribing...", "err", sub.Err(), "query", q.String()) 257 sub = c.resubscribe(subscriber, q) 258 if sub == nil { // client was stopped 259 return 260 } 261 case <-c.Quit(): 262 return 263 } 264 } 265 } 266 267 // Try to resubscribe with exponential backoff. 268 func (c *Local) resubscribe(subscriber string, q tmpubsub.Query) types.Subscription { 269 attempts := 0 270 for { 271 if !c.IsRunning() { 272 return nil 273 } 274 275 sub, err := c.EventBus.Subscribe(context.Background(), subscriber, q) 276 if err == nil { 277 return sub 278 } 279 280 attempts++ 281 time.Sleep((10 << uint(attempts)) * time.Millisecond) // 10ms -> 20ms -> 40ms 282 } 283 } 284 285 func (c *Local) Unsubscribe(ctx context.Context, subscriber, query string) error { 286 q, err := tmquery.New(query) 287 if err != nil { 288 return errors.Wrap(err, "failed to parse query") 289 } 290 return c.EventBus.Unsubscribe(ctx, subscriber, q) 291 } 292 293 func (c *Local) UnsubscribeAll(ctx context.Context, subscriber string) error { 294 return c.EventBus.UnsubscribeAll(ctx, subscriber) 295 }