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  }