github.com/franono/tendermint@v0.32.2-0.20200527150959-749313264ce9/rpc/client/local/local.go (about) 1 package local 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/franono/tendermint/libs/bytes" 9 "github.com/franono/tendermint/libs/log" 10 tmpubsub "github.com/franono/tendermint/libs/pubsub" 11 tmquery "github.com/franono/tendermint/libs/pubsub/query" 12 nm "github.com/franono/tendermint/node" 13 rpcclient "github.com/franono/tendermint/rpc/client" 14 "github.com/franono/tendermint/rpc/core" 15 ctypes "github.com/franono/tendermint/rpc/core/types" 16 rpctypes "github.com/franono/tendermint/rpc/jsonrpc/types" 17 "github.com/franono/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) NetInfo() (*ctypes.ResultNetInfo, error) { 108 return core.NetInfo(c.ctx) 109 } 110 111 func (c *Local) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { 112 return core.DumpConsensusState(c.ctx) 113 } 114 115 func (c *Local) ConsensusState() (*ctypes.ResultConsensusState, error) { 116 return core.ConsensusState(c.ctx) 117 } 118 119 func (c *Local) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) { 120 return core.ConsensusParams(c.ctx, height) 121 } 122 123 func (c *Local) Health() (*ctypes.ResultHealth, error) { 124 return core.Health(c.ctx) 125 } 126 127 func (c *Local) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { 128 return core.UnsafeDialSeeds(c.ctx, seeds) 129 } 130 131 func (c *Local) DialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, error) { 132 return core.UnsafeDialPeers(c.ctx, peers, persistent) 133 } 134 135 func (c *Local) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { 136 return core.BlockchainInfo(c.ctx, minHeight, maxHeight) 137 } 138 139 func (c *Local) Genesis() (*ctypes.ResultGenesis, error) { 140 return core.Genesis(c.ctx) 141 } 142 143 func (c *Local) Block(height *int64) (*ctypes.ResultBlock, error) { 144 return core.Block(c.ctx, height) 145 } 146 147 func (c *Local) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) { 148 return core.BlockResults(c.ctx, height) 149 } 150 151 func (c *Local) Commit(height *int64) (*ctypes.ResultCommit, error) { 152 return core.Commit(c.ctx, height) 153 } 154 155 func (c *Local) Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) { 156 return core.Validators(c.ctx, height, page, perPage) 157 } 158 159 func (c *Local) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { 160 return core.Tx(c.ctx, hash, prove) 161 } 162 163 func (c *Local) TxSearch(query string, prove bool, page, perPage int, orderBy string) ( 164 *ctypes.ResultTxSearch, error) { 165 return core.TxSearch(c.ctx, query, prove, page, perPage, orderBy) 166 } 167 168 func (c *Local) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) { 169 return core.BroadcastEvidence(c.ctx, ev) 170 } 171 172 func (c *Local) Subscribe( 173 ctx context.Context, 174 subscriber, 175 query string, 176 outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) { 177 q, err := tmquery.New(query) 178 if err != nil { 179 return nil, fmt.Errorf("failed to parse query: %w", err) 180 } 181 182 outCap := 1 183 if len(outCapacity) > 0 { 184 outCap = outCapacity[0] 185 } 186 187 var sub types.Subscription 188 if outCap > 0 { 189 sub, err = c.EventBus.Subscribe(ctx, subscriber, q, outCap) 190 } else { 191 sub, err = c.EventBus.SubscribeUnbuffered(ctx, subscriber, q) 192 } 193 if err != nil { 194 return nil, fmt.Errorf("failed to subscribe: %w", err) 195 } 196 197 outc := make(chan ctypes.ResultEvent, outCap) 198 go c.eventsRoutine(sub, subscriber, q, outc) 199 200 return outc, nil 201 } 202 203 func (c *Local) eventsRoutine( 204 sub types.Subscription, 205 subscriber string, 206 q tmpubsub.Query, 207 outc chan<- ctypes.ResultEvent) { 208 for { 209 select { 210 case msg := <-sub.Out(): 211 result := ctypes.ResultEvent{Query: q.String(), Data: msg.Data(), Events: msg.Events()} 212 if cap(outc) == 0 { 213 outc <- result 214 } else { 215 select { 216 case outc <- result: 217 default: 218 c.Logger.Error("wanted to publish ResultEvent, but out channel is full", "result", result, "query", result.Query) 219 } 220 } 221 case <-sub.Cancelled(): 222 if sub.Err() == tmpubsub.ErrUnsubscribed { 223 return 224 } 225 226 c.Logger.Error("subscription was cancelled, resubscribing...", "err", sub.Err(), "query", q.String()) 227 sub = c.resubscribe(subscriber, q) 228 if sub == nil { // client was stopped 229 return 230 } 231 case <-c.Quit(): 232 return 233 } 234 } 235 } 236 237 // Try to resubscribe with exponential backoff. 238 func (c *Local) resubscribe(subscriber string, q tmpubsub.Query) types.Subscription { 239 attempts := 0 240 for { 241 if !c.IsRunning() { 242 return nil 243 } 244 245 sub, err := c.EventBus.Subscribe(context.Background(), subscriber, q) 246 if err == nil { 247 return sub 248 } 249 250 attempts++ 251 time.Sleep((10 << uint(attempts)) * time.Millisecond) // 10ms -> 20ms -> 40ms 252 } 253 } 254 255 func (c *Local) Unsubscribe(ctx context.Context, subscriber, query string) error { 256 q, err := tmquery.New(query) 257 if err != nil { 258 return fmt.Errorf("failed to parse query: %w", err) 259 } 260 return c.EventBus.Unsubscribe(ctx, subscriber, q) 261 } 262 263 func (c *Local) UnsubscribeAll(ctx context.Context, subscriber string) error { 264 return c.EventBus.UnsubscribeAll(ctx, subscriber) 265 }