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