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