github.com/consideritdone/landslidecore@v0.0.0-20230718131026-a8b21c5cf8a7/vm/service.go (about)

     1  package vm
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"sort"
     8  	"time"
     9  
    10  	abci "github.com/consideritdone/landslidecore/abci/types"
    11  	tmbytes "github.com/consideritdone/landslidecore/libs/bytes"
    12  	tmmath "github.com/consideritdone/landslidecore/libs/math"
    13  	tmpubsub "github.com/consideritdone/landslidecore/libs/pubsub"
    14  	tmquery "github.com/consideritdone/landslidecore/libs/pubsub/query"
    15  	mempl "github.com/consideritdone/landslidecore/mempool"
    16  	"github.com/consideritdone/landslidecore/p2p"
    17  	"github.com/consideritdone/landslidecore/proxy"
    18  	"github.com/consideritdone/landslidecore/rpc/core"
    19  	ctypes "github.com/consideritdone/landslidecore/rpc/core/types"
    20  	rpcserver "github.com/consideritdone/landslidecore/rpc/jsonrpc/server"
    21  	rpctypes "github.com/consideritdone/landslidecore/rpc/jsonrpc/types"
    22  	blockidxnull "github.com/consideritdone/landslidecore/state/indexer/block/null"
    23  	"github.com/consideritdone/landslidecore/state/txindex/null"
    24  	"github.com/consideritdone/landslidecore/types"
    25  )
    26  
    27  var (
    28  	SubscribeTimeout         = 5 * time.Second
    29  	_                Service = (*LocalService)(nil)
    30  )
    31  
    32  type (
    33  	LocalService struct {
    34  		vm *VM
    35  	}
    36  
    37  	Service interface {
    38  		ABCIService
    39  		EventsService
    40  		HistoryClient
    41  		NetworkClient
    42  		SignClient
    43  		StatusClient
    44  		MempoolClient
    45  	}
    46  
    47  	ABCIService interface {
    48  		// Reading from abci app
    49  		ABCIInfo(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error)
    50  		ABCIQuery(ctx *rpctypes.Context, path string, data tmbytes.HexBytes, height int64, prove bool) (*ctypes.ResultABCIQuery, error)
    51  
    52  		// Writing to abci app
    53  		BroadcastTxCommit(*rpctypes.Context, types.Tx) (*ctypes.ResultBroadcastTxCommit, error)
    54  		BroadcastTxAsync(*rpctypes.Context, types.Tx) (*ctypes.ResultBroadcastTx, error)
    55  		BroadcastTxSync(*rpctypes.Context, types.Tx) (*ctypes.ResultBroadcastTx, error)
    56  	}
    57  
    58  	EventsService interface {
    59  		Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error)
    60  		Unsubscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error)
    61  		UnsubscribeAll(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error)
    62  	}
    63  
    64  	HistoryClient interface {
    65  		Genesis(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error)
    66  		GenesisChunked(*rpctypes.Context, uint) (*ctypes.ResultGenesisChunk, error)
    67  		BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error)
    68  	}
    69  
    70  	MempoolClient interface {
    71  		UnconfirmedTxs(ctx *rpctypes.Context, limit *int) (*ctypes.ResultUnconfirmedTxs, error)
    72  		NumUnconfirmedTxs(ctx *rpctypes.Context) (*ctypes.ResultUnconfirmedTxs, error)
    73  		CheckTx(*rpctypes.Context, types.Tx) (*ctypes.ResultCheckTx, error)
    74  	}
    75  
    76  	NetworkClient interface {
    77  		NetInfo(ctx *rpctypes.Context) (*ctypes.ResultNetInfo, error)
    78  		DumpConsensusState(ctx *rpctypes.Context) (*ctypes.ResultDumpConsensusState, error)
    79  		ConsensusState(ctx *rpctypes.Context) (*ctypes.ResultConsensusState, error)
    80  		ConsensusParams(ctx *rpctypes.Context, height *int64) (*ctypes.ResultConsensusParams, error)
    81  		Health(ctx *rpctypes.Context) (*ctypes.ResultHealth, error)
    82  	}
    83  
    84  	SignClient interface {
    85  		Block(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlock, error)
    86  		BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error)
    87  		BlockResults(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlockResults, error)
    88  		Commit(ctx *rpctypes.Context, height *int64) (*ctypes.ResultCommit, error)
    89  		Validators(ctx *rpctypes.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error)
    90  		Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error)
    91  
    92  		TxSearch(ctx *rpctypes.Context, query string, prove bool,
    93  			page, perPage *int, orderBy string) (*ctypes.ResultTxSearch, error)
    94  
    95  		BlockSearch(ctx *rpctypes.Context, query string,
    96  			page, perPage *int, orderBy string) (*ctypes.ResultBlockSearch, error)
    97  	}
    98  
    99  	StatusClient interface {
   100  		Status(*rpctypes.Context) (*ctypes.ResultStatus, error)
   101  	}
   102  )
   103  
   104  func NewService(vm *VM) *LocalService {
   105  	return &LocalService{vm}
   106  }
   107  
   108  func NewServiceAsRPCRoutes(vm *VM) map[string]*rpcserver.RPCFunc {
   109  	s := NewService(vm)
   110  	return map[string]*rpcserver.RPCFunc{
   111  		// subscribe/unsubscribe are reserved for websocket events.
   112  		"subscribe":       rpcserver.NewWSRPCFunc(s.Subscribe, "query"),
   113  		"unsubscribe":     rpcserver.NewWSRPCFunc(s.Unsubscribe, "query"),
   114  		"unsubscribe_all": rpcserver.NewWSRPCFunc(s.UnsubscribeAll, ""),
   115  
   116  		// info API
   117  		"health":               rpcserver.NewRPCFunc(s.Health, ""),
   118  		"status":               rpcserver.NewRPCFunc(s.Status, ""),
   119  		"net_info":             rpcserver.NewRPCFunc(s.NetInfo, ""),
   120  		"blockchain":           rpcserver.NewRPCFunc(s.BlockchainInfo, "minHeight,maxHeight"),
   121  		"genesis":              rpcserver.NewRPCFunc(s.Genesis, ""),
   122  		"genesis_chunked":      rpcserver.NewRPCFunc(s.GenesisChunked, "chunk"),
   123  		"block":                rpcserver.NewRPCFunc(s.Block, "height"),
   124  		"block_by_hash":        rpcserver.NewRPCFunc(s.BlockByHash, "hash"),
   125  		"block_results":        rpcserver.NewRPCFunc(s.BlockResults, "height"),
   126  		"commit":               rpcserver.NewRPCFunc(s.Commit, "height"),
   127  		"check_tx":             rpcserver.NewRPCFunc(s.CheckTx, "tx"),
   128  		"tx":                   rpcserver.NewRPCFunc(s.Tx, "hash,prove"),
   129  		"tx_search":            rpcserver.NewRPCFunc(s.TxSearch, "query,prove,page,per_page,order_by"),
   130  		"block_search":         rpcserver.NewRPCFunc(s.BlockSearch, "query,page,per_page,order_by"),
   131  		"validators":           rpcserver.NewRPCFunc(s.Validators, "height,page,per_page"),
   132  		"dump_consensus_state": rpcserver.NewRPCFunc(s.DumpConsensusState, ""),
   133  		"consensus_state":      rpcserver.NewRPCFunc(s.ConsensusState, ""),
   134  		"consensus_params":     rpcserver.NewRPCFunc(s.ConsensusParams, "height"),
   135  		"unconfirmed_txs":      rpcserver.NewRPCFunc(s.UnconfirmedTxs, "limit"),
   136  		"num_unconfirmed_txs":  rpcserver.NewRPCFunc(s.NumUnconfirmedTxs, ""),
   137  
   138  		// tx broadcast API
   139  		"broadcast_tx_commit": rpcserver.NewRPCFunc(s.BroadcastTxCommit, "tx"),
   140  		"broadcast_tx_sync":   rpcserver.NewRPCFunc(s.BroadcastTxSync, "tx"),
   141  		"broadcast_tx_async":  rpcserver.NewRPCFunc(s.BroadcastTxAsync, "tx"),
   142  
   143  		// abci API
   144  		"abci_query": rpcserver.NewRPCFunc(s.ABCIQuery, "path,data,height,prove"),
   145  		"abci_info":  rpcserver.NewRPCFunc(s.ABCIInfo, ""),
   146  	}
   147  }
   148  
   149  func (s *LocalService) Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) {
   150  	addr := ctx.RemoteAddr()
   151  
   152  	if s.vm.eventBus.NumClients() >= s.vm.rpcConfig.MaxSubscriptionClients {
   153  		return nil, fmt.Errorf("max_subscription_clients %d reached", s.vm.rpcConfig.MaxSubscriptionClients)
   154  	} else if s.vm.eventBus.NumClientSubscriptions(addr) >= s.vm.rpcConfig.MaxSubscriptionsPerClient {
   155  		return nil, fmt.Errorf("max_subscriptions_per_client %d reached", s.vm.rpcConfig.MaxSubscriptionsPerClient)
   156  	}
   157  
   158  	s.vm.log.Info("Subscribe to query", "remote", addr, "query", query)
   159  
   160  	q, err := tmquery.New(query)
   161  	if err != nil {
   162  		return nil, fmt.Errorf("failed to parse query: %w", err)
   163  	}
   164  
   165  	subCtx, cancel := context.WithTimeout(ctx.Context(), SubscribeTimeout)
   166  	defer cancel()
   167  
   168  	sub, err := s.vm.eventBus.Subscribe(subCtx, addr, q, s.vm.rpcConfig.SubscriptionBufferSize)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	closeIfSlow := s.vm.rpcConfig.CloseOnSlowClient
   174  
   175  	// TODO: inspired by Ilnur: usage of ctx.JSONReq.ID may cause situation when user or server try to create multiple subscriptions with the same id.
   176  	// Solution: return error code with the error sescription when this situation happens
   177  	// Capture the current ID, since it can change in the future.
   178  	subscriptionID := ctx.JSONReq.ID
   179  	go func() {
   180  		for {
   181  			select {
   182  			case msg := <-sub.Out():
   183  				var (
   184  					resultEvent = &ctypes.ResultEvent{Query: query, Data: msg.Data(), Events: msg.Events()}
   185  					resp        = rpctypes.NewRPCSuccessResponse(subscriptionID, resultEvent)
   186  				)
   187  				writeCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   188  				defer cancel()
   189  				if err = ctx.WSConn.WriteRPCResponse(writeCtx, resp); err != nil {
   190  					s.vm.log.Info("Can't write response (slow client)",
   191  						"to", addr, "subscriptionID", subscriptionID, "err", err)
   192  
   193  					if closeIfSlow {
   194  						var (
   195  							err  = errors.New("subscription was cancelled (reason: slow client)")
   196  							resp = rpctypes.RPCServerError(subscriptionID, err)
   197  						)
   198  						if !ctx.WSConn.TryWriteRPCResponse(resp) {
   199  							s.vm.log.Info("Can't write response (slow client)",
   200  								"to", addr, "subscriptionID", subscriptionID, "err", err)
   201  						}
   202  						return
   203  					}
   204  				}
   205  			case <-sub.Cancelled():
   206  				if sub.Err() != tmpubsub.ErrUnsubscribed {
   207  					var reason string
   208  					if sub.Err() == nil {
   209  						reason = "Tendermint exited"
   210  					} else {
   211  						reason = sub.Err().Error()
   212  					}
   213  					resp := rpctypes.RPCServerError(subscriptionID, err)
   214  					if !ctx.WSConn.TryWriteRPCResponse(resp) {
   215  						s.vm.log.Info("Can't write response (slow client)",
   216  							"to", addr, "subscriptionID", subscriptionID, "err",
   217  							fmt.Errorf("subscription was cancelled (reason: %s)", reason))
   218  					}
   219  				}
   220  				return
   221  			}
   222  		}
   223  	}()
   224  
   225  	return &ctypes.ResultSubscribe{}, nil
   226  }
   227  
   228  func (s *LocalService) Unsubscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) {
   229  	addr := ctx.RemoteAddr()
   230  	s.vm.log.Info("Unsubscribe from query", "remote", addr, "query", query)
   231  	q, err := tmquery.New(query)
   232  	if err != nil {
   233  		return nil, fmt.Errorf("failed to parse query: %w", err)
   234  	}
   235  	err = s.vm.eventBus.Unsubscribe(context.Background(), addr, q)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  	return &ctypes.ResultUnsubscribe{}, nil
   240  }
   241  
   242  func (s *LocalService) UnsubscribeAll(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) {
   243  	addr := ctx.RemoteAddr()
   244  	s.vm.log.Info("Unsubscribe from all", "remote", addr)
   245  	err := s.vm.eventBus.UnsubscribeAll(context.Background(), addr)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  	return &ctypes.ResultUnsubscribe{}, nil
   250  }
   251  
   252  func (s *LocalService) ABCIInfo(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) {
   253  	resInfo, err := s.vm.app.Query().InfoSync(proxy.RequestInfo)
   254  	if err != nil || resInfo == nil {
   255  		return nil, err
   256  	}
   257  	return &ctypes.ResultABCIInfo{Response: *resInfo}, nil
   258  }
   259  
   260  // TODO: attention! Different signatures in RPC interfaces
   261  func (s *LocalService) ABCIQuery(
   262  	ctx *rpctypes.Context,
   263  	path string,
   264  	data tmbytes.HexBytes,
   265  	height int64,
   266  	prove bool,
   267  ) (*ctypes.ResultABCIQuery, error) {
   268  	resQuery, err := s.vm.app.Query().QuerySync(abci.RequestQuery{
   269  		Path:   path,
   270  		Data:   data,
   271  		Height: height,
   272  		Prove:  prove,
   273  	})
   274  	if err != nil || resQuery == nil {
   275  		return nil, err
   276  	}
   277  
   278  	return &ctypes.ResultABCIQuery{Response: *resQuery}, nil
   279  }
   280  
   281  func (s *LocalService) BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
   282  	subscriber := ""
   283  
   284  	// Subscribe to tx being committed in block.
   285  	subCtx, cancel := context.WithTimeout(ctx.Context(), core.SubscribeTimeout)
   286  	defer cancel()
   287  
   288  	q := types.EventQueryTxFor(tx)
   289  	deliverTxSub, err := s.vm.eventBus.Subscribe(subCtx, subscriber, q)
   290  	if err != nil {
   291  		err = fmt.Errorf("failed to subscribe to tx: %w", err)
   292  		s.vm.log.Error("Error on broadcast_tx_commit", "err", err)
   293  		return nil, err
   294  	}
   295  
   296  	defer func() {
   297  		if err := s.vm.eventBus.Unsubscribe(context.Background(), subscriber, q); err != nil {
   298  			s.vm.log.Error("Error unsubscribing from eventBus", "err", err)
   299  		}
   300  	}()
   301  
   302  	// Broadcast tx and wait for CheckTx result
   303  	checkTxResCh := make(chan *abci.Response, 1)
   304  	err = s.vm.mempool.CheckTx(tx, func(res *abci.Response) {
   305  		checkTxResCh <- res
   306  	}, mempl.TxInfo{})
   307  	if err != nil {
   308  		s.vm.log.Error("Error on broadcastTxCommit", "err", err)
   309  		return nil, fmt.Errorf("error on broadcastTxCommit: %v", err)
   310  	}
   311  	checkTxResMsg := <-checkTxResCh
   312  	checkTxRes := checkTxResMsg.GetCheckTx()
   313  	if checkTxRes.Code != abci.CodeTypeOK {
   314  		return &ctypes.ResultBroadcastTxCommit{
   315  			CheckTx:   *checkTxRes,
   316  			DeliverTx: abci.ResponseDeliverTx{},
   317  			Hash:      tx.Hash(),
   318  		}, nil
   319  	}
   320  
   321  	// Wait for the tx to be included in a block or timeout.
   322  	select {
   323  	case msg := <-deliverTxSub.Out(): // The tx was included in a block.
   324  		deliverTxRes := msg.Data().(types.EventDataTx)
   325  		return &ctypes.ResultBroadcastTxCommit{
   326  			CheckTx:   *checkTxRes,
   327  			DeliverTx: deliverTxRes.Result,
   328  			Hash:      tx.Hash(),
   329  			Height:    deliverTxRes.Height,
   330  		}, nil
   331  	case <-deliverTxSub.Cancelled():
   332  		var reason string
   333  		if deliverTxSub.Err() == nil {
   334  			reason = "Tendermint exited"
   335  		} else {
   336  			reason = deliverTxSub.Err().Error()
   337  		}
   338  		err = fmt.Errorf("deliverTxSub was cancelled (reason: %s)", reason)
   339  		s.vm.log.Error("Error on broadcastTxCommit", "err", err)
   340  		return &ctypes.ResultBroadcastTxCommit{
   341  			CheckTx:   *checkTxRes,
   342  			DeliverTx: abci.ResponseDeliverTx{},
   343  			Hash:      tx.Hash(),
   344  		}, err
   345  	// TODO: use config for timeout
   346  	case <-time.After(10 * time.Second):
   347  		err = errors.New("timed out waiting for tx to be included in a block")
   348  		s.vm.log.Error("Error on broadcastTxCommit", "err", err)
   349  		return &ctypes.ResultBroadcastTxCommit{
   350  			CheckTx:   *checkTxRes,
   351  			DeliverTx: abci.ResponseDeliverTx{},
   352  			Hash:      tx.Hash(),
   353  		}, err
   354  	}
   355  }
   356  
   357  func (s *LocalService) BroadcastTxAsync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
   358  	err := s.vm.mempool.CheckTx(tx, nil, mempl.TxInfo{})
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  	return &ctypes.ResultBroadcastTx{Hash: tx.Hash()}, nil
   363  }
   364  
   365  func (s *LocalService) BroadcastTxSync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
   366  	resCh := make(chan *abci.Response, 1)
   367  	err := s.vm.mempool.CheckTx(tx, func(res *abci.Response) {
   368  		s.vm.log.With("module", "service").Debug("handled response from checkTx")
   369  		resCh <- res
   370  	}, mempl.TxInfo{})
   371  	if err != nil {
   372  		return nil, err
   373  	}
   374  	res := <-resCh
   375  	r := res.GetCheckTx()
   376  	return &ctypes.ResultBroadcastTx{
   377  		Code:      r.Code,
   378  		Data:      r.Data,
   379  		Log:       r.Log,
   380  		Codespace: r.Codespace,
   381  		Hash:      tx.Hash(),
   382  	}, nil
   383  }
   384  
   385  func (s *LocalService) Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) {
   386  	height, err := getHeight(s.vm.blockStore, heightPtr)
   387  	if err != nil {
   388  		return nil, err
   389  	}
   390  
   391  	block := s.vm.blockStore.LoadBlock(height)
   392  	blockMeta := s.vm.blockStore.LoadBlockMeta(height)
   393  	if blockMeta == nil {
   394  		return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: block}, nil
   395  	}
   396  	return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
   397  }
   398  
   399  func (s *LocalService) BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
   400  	block := s.vm.blockStore.LoadBlockByHash(hash)
   401  	if block == nil {
   402  		return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil
   403  	}
   404  	// If block is not nil, then blockMeta can't be nil.
   405  	blockMeta := s.vm.blockStore.LoadBlockMeta(block.Height)
   406  	return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
   407  }
   408  
   409  func (s *LocalService) BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) {
   410  	height, err := getHeight(s.vm.blockStore, heightPtr)
   411  	if err != nil {
   412  		return nil, err
   413  	}
   414  
   415  	results, err := s.vm.stateStore.LoadABCIResponses(height)
   416  	if err != nil {
   417  		return nil, err
   418  	}
   419  
   420  	return &ctypes.ResultBlockResults{
   421  		Height:                height,
   422  		TxsResults:            results.DeliverTxs,
   423  		BeginBlockEvents:      results.BeginBlock.Events,
   424  		EndBlockEvents:        results.EndBlock.Events,
   425  		ValidatorUpdates:      results.EndBlock.ValidatorUpdates,
   426  		ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
   427  	}, nil
   428  }
   429  
   430  func (s *LocalService) Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) {
   431  	height, err := getHeight(s.vm.blockStore, heightPtr)
   432  	if err != nil {
   433  		return nil, err
   434  	}
   435  
   436  	blockMeta := s.vm.blockStore.LoadBlockMeta(height)
   437  	if blockMeta == nil {
   438  		return nil, nil
   439  	}
   440  	header := blockMeta.Header
   441  
   442  	// Return the canonical commit (comes from the block at height+1)
   443  	commit := s.vm.blockStore.LoadBlockCommit(height)
   444  	return ctypes.NewResultCommit(&header, commit, true), nil
   445  }
   446  
   447  func (s *LocalService) Validators(ctx *rpctypes.Context, heightPtr *int64, pagePtr, perPagePtr *int) (*ctypes.ResultValidators, error) {
   448  	height, err := getHeight(s.vm.blockStore, heightPtr)
   449  	if err != nil {
   450  		return nil, err
   451  	}
   452  
   453  	validators, err := s.vm.stateStore.LoadValidators(height)
   454  	if err != nil {
   455  		return nil, err
   456  	}
   457  
   458  	totalCount := len(validators.Validators)
   459  	perPage := validatePerPage(perPagePtr)
   460  	page, err := validatePage(pagePtr, perPage, totalCount)
   461  	if err != nil {
   462  		return nil, err
   463  	}
   464  
   465  	skipCount := validateSkipCount(page, perPage)
   466  
   467  	v := validators.Validators[skipCount : skipCount+tmmath.MinInt(perPage, totalCount-skipCount)]
   468  
   469  	return &ctypes.ResultValidators{
   470  		BlockHeight: height,
   471  		Validators:  v,
   472  		Count:       len(v),
   473  		Total:       totalCount}, nil
   474  }
   475  
   476  func (s *LocalService) Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) {
   477  	if _, ok := s.vm.txIndexer.(*null.TxIndex); ok {
   478  		return nil, fmt.Errorf("transaction indexing is disabled")
   479  	}
   480  
   481  	r, err := s.vm.txIndexer.Get(hash)
   482  	if err != nil {
   483  		return nil, err
   484  	}
   485  
   486  	if r == nil {
   487  		return nil, fmt.Errorf("tx (%X) not found", hash)
   488  	}
   489  
   490  	height := r.Height
   491  	index := r.Index
   492  
   493  	var proof types.TxProof
   494  	if prove {
   495  		block := s.vm.blockStore.LoadBlock(height)
   496  		proof = block.Data.Txs.Proof(int(index)) // XXX: overflow on 32-bit machines
   497  	}
   498  
   499  	return &ctypes.ResultTx{
   500  		Hash:     hash,
   501  		Height:   height,
   502  		Index:    index,
   503  		TxResult: r.Result,
   504  		Tx:       r.Tx,
   505  		Proof:    proof,
   506  	}, nil
   507  }
   508  
   509  func (s *LocalService) TxSearch(ctx *rpctypes.Context, query string, prove bool, pagePtr, perPagePtr *int, orderBy string) (*ctypes.ResultTxSearch, error) {
   510  	// if index is disabled, return error
   511  	q, err := tmquery.New(query)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  
   516  	results, err := s.vm.txIndexer.Search(ctx.Context(), q)
   517  	if err != nil {
   518  		return nil, err
   519  	}
   520  
   521  	// sort results (must be done before pagination)
   522  	switch orderBy {
   523  	case "desc":
   524  		sort.Slice(results, func(i, j int) bool {
   525  			if results[i].Height == results[j].Height {
   526  				return results[i].Index > results[j].Index
   527  			}
   528  			return results[i].Height > results[j].Height
   529  		})
   530  	case "asc", "":
   531  		sort.Slice(results, func(i, j int) bool {
   532  			if results[i].Height == results[j].Height {
   533  				return results[i].Index < results[j].Index
   534  			}
   535  			return results[i].Height < results[j].Height
   536  		})
   537  	default:
   538  		return nil, errors.New("expected order_by to be either `asc` or `desc` or empty")
   539  	}
   540  
   541  	// paginate results
   542  	totalCount := len(results)
   543  	perPage := validatePerPage(perPagePtr)
   544  
   545  	page, err := validatePage(pagePtr, perPage, totalCount)
   546  	if err != nil {
   547  		return nil, err
   548  	}
   549  
   550  	skipCount := validateSkipCount(page, perPage)
   551  	pageSize := tmmath.MinInt(perPage, totalCount-skipCount)
   552  
   553  	apiResults := make([]*ctypes.ResultTx, 0, pageSize)
   554  	for i := skipCount; i < skipCount+pageSize; i++ {
   555  		r := results[i]
   556  
   557  		var proof types.TxProof
   558  		if prove {
   559  			block := s.vm.blockStore.LoadBlock(r.Height)
   560  			proof = block.Data.Txs.Proof(int(r.Index)) // XXX: overflow on 32-bit machines
   561  		}
   562  
   563  		apiResults = append(apiResults, &ctypes.ResultTx{
   564  			Hash:     types.Tx(r.Tx).Hash(),
   565  			Height:   r.Height,
   566  			Index:    r.Index,
   567  			TxResult: r.Result,
   568  			Tx:       r.Tx,
   569  			Proof:    proof,
   570  		})
   571  	}
   572  
   573  	return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil
   574  }
   575  
   576  // BlockSearch searches for a paginated set of blocks matching BeginBlock and
   577  // EndBlock event search criteria.
   578  func (s *LocalService) BlockSearch(
   579  	ctx *rpctypes.Context,
   580  	query string,
   581  	pagePtr, perPagePtr *int,
   582  	orderBy string,
   583  ) (*ctypes.ResultBlockSearch, error) {
   584  
   585  	// skip if block indexing is disabled
   586  	if _, ok := s.vm.blockIndexer.(*blockidxnull.BlockerIndexer); ok {
   587  		return nil, errors.New("block indexing is disabled")
   588  	}
   589  
   590  	q, err := tmquery.New(query)
   591  	if err != nil {
   592  		return nil, err
   593  	}
   594  
   595  	results, err := s.vm.blockIndexer.Search(ctx.Context(), q)
   596  	if err != nil {
   597  		return nil, err
   598  	}
   599  
   600  	// sort results (must be done before pagination)
   601  	switch orderBy {
   602  	case "desc", "":
   603  		sort.Slice(results, func(i, j int) bool { return results[i] > results[j] })
   604  
   605  	case "asc":
   606  		sort.Slice(results, func(i, j int) bool { return results[i] < results[j] })
   607  
   608  	default:
   609  		return nil, errors.New("expected order_by to be either `asc` or `desc` or empty")
   610  	}
   611  
   612  	// paginate results
   613  	totalCount := len(results)
   614  	perPage := validatePerPage(perPagePtr)
   615  
   616  	page, err := validatePage(pagePtr, perPage, totalCount)
   617  	if err != nil {
   618  		return nil, err
   619  	}
   620  
   621  	skipCount := validateSkipCount(page, perPage)
   622  	pageSize := tmmath.MinInt(perPage, totalCount-skipCount)
   623  
   624  	apiResults := make([]*ctypes.ResultBlock, 0, pageSize)
   625  	for i := skipCount; i < skipCount+pageSize; i++ {
   626  		block := s.vm.blockStore.LoadBlock(results[i])
   627  		if block != nil {
   628  			blockMeta := s.vm.blockStore.LoadBlockMeta(block.Height)
   629  			if blockMeta != nil {
   630  				apiResults = append(apiResults, &ctypes.ResultBlock{
   631  					Block:   block,
   632  					BlockID: blockMeta.BlockID,
   633  				})
   634  			}
   635  		}
   636  	}
   637  
   638  	return &ctypes.ResultBlockSearch{Blocks: apiResults, TotalCount: totalCount}, nil
   639  }
   640  
   641  func (s *LocalService) BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
   642  	// maximum 20 block metas
   643  	const limit int64 = 20
   644  	var err error
   645  	minHeight, maxHeight, err = filterMinMax(
   646  		s.vm.blockStore.Base(),
   647  		s.vm.blockStore.Height(),
   648  		minHeight,
   649  		maxHeight,
   650  		limit)
   651  	if err != nil {
   652  		return nil, err
   653  	}
   654  	s.vm.log.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight)
   655  
   656  	var blockMetas []*types.BlockMeta
   657  	for height := maxHeight; height >= minHeight; height-- {
   658  		blockMeta := s.vm.blockStore.LoadBlockMeta(height)
   659  		blockMetas = append(blockMetas, blockMeta)
   660  	}
   661  
   662  	return &ctypes.ResultBlockchainInfo{
   663  		LastHeight: s.vm.blockStore.Height(),
   664  		BlockMetas: blockMetas}, nil
   665  }
   666  
   667  func (s *LocalService) Genesis(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) {
   668  	//if len(s.vm.genChunks) > 1 {
   669  	//	return nil, errors.New("genesis response is large, please use the genesis_chunked API instead")
   670  	//}
   671  
   672  	return &ctypes.ResultGenesis{Genesis: s.vm.genesis}, nil
   673  }
   674  
   675  func (s *LocalService) GenesisChunked(ctx *rpctypes.Context, chunk uint) (*ctypes.ResultGenesisChunk, error) {
   676  	if s.vm.genChunks == nil {
   677  		return nil, fmt.Errorf("service configuration error, genesis chunks are not initialized")
   678  	}
   679  
   680  	if len(s.vm.genChunks) == 0 {
   681  		return nil, fmt.Errorf("service configuration error, there are no chunks")
   682  	}
   683  
   684  	id := int(chunk)
   685  
   686  	if id > len(s.vm.genChunks)-1 {
   687  		return nil, fmt.Errorf("there are %d chunks, %d is invalid", len(s.vm.genChunks)-1, id)
   688  	}
   689  
   690  	return &ctypes.ResultGenesisChunk{
   691  		TotalChunks: len(s.vm.genChunks),
   692  		ChunkNumber: id,
   693  		Data:        s.vm.genChunks[id],
   694  	}, nil
   695  }
   696  
   697  func (s *LocalService) Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) {
   698  	var (
   699  		earliestBlockHeight   int64
   700  		earliestBlockHash     tmbytes.HexBytes
   701  		earliestAppHash       tmbytes.HexBytes
   702  		earliestBlockTimeNano int64
   703  	)
   704  
   705  	if earliestBlockMeta := s.vm.blockStore.LoadBaseMeta(); earliestBlockMeta != nil {
   706  		earliestBlockHeight = earliestBlockMeta.Header.Height
   707  		earliestAppHash = earliestBlockMeta.Header.AppHash
   708  		earliestBlockHash = earliestBlockMeta.BlockID.Hash
   709  		earliestBlockTimeNano = earliestBlockMeta.Header.Time.UnixNano()
   710  	}
   711  
   712  	var (
   713  		latestBlockHash     tmbytes.HexBytes
   714  		latestAppHash       tmbytes.HexBytes
   715  		latestBlockTimeNano int64
   716  
   717  		latestHeight = s.vm.blockStore.Height()
   718  	)
   719  
   720  	if latestHeight != 0 {
   721  		if latestBlockMeta := s.vm.blockStore.LoadBlockMeta(latestHeight); latestBlockMeta != nil {
   722  			latestBlockHash = latestBlockMeta.BlockID.Hash
   723  			latestAppHash = latestBlockMeta.Header.AppHash
   724  			latestBlockTimeNano = latestBlockMeta.Header.Time.UnixNano()
   725  		}
   726  	}
   727  
   728  	result := &ctypes.ResultStatus{
   729  		NodeInfo: p2p.DefaultNodeInfo{
   730  			DefaultNodeID: p2p.ID(s.vm.chainCtx.NodeID.String()),
   731  			Network:       fmt.Sprintf("%d", s.vm.chainCtx.NetworkID),
   732  		},
   733  		SyncInfo: ctypes.SyncInfo{
   734  			LatestBlockHash:     latestBlockHash,
   735  			LatestAppHash:       latestAppHash,
   736  			LatestBlockHeight:   latestHeight,
   737  			LatestBlockTime:     time.Unix(0, latestBlockTimeNano),
   738  			EarliestBlockHash:   earliestBlockHash,
   739  			EarliestAppHash:     earliestAppHash,
   740  			EarliestBlockHeight: earliestBlockHeight,
   741  			EarliestBlockTime:   time.Unix(0, earliestBlockTimeNano),
   742  			CatchingUp:          false,
   743  		},
   744  		ValidatorInfo: ctypes.ValidatorInfo{
   745  			Address:     proposerPubKey.Address(),
   746  			PubKey:      proposerPubKey,
   747  			VotingPower: 0,
   748  		},
   749  	}
   750  
   751  	return result, nil
   752  }
   753  
   754  // ToDo: no peers, no network from tendermint side
   755  func (s *LocalService) NetInfo(ctx *rpctypes.Context) (*ctypes.ResultNetInfo, error) {
   756  	return &ctypes.ResultNetInfo{}, nil
   757  }
   758  
   759  // ToDo: we doesn't have consensusState
   760  func (s *LocalService) DumpConsensusState(ctx *rpctypes.Context) (*ctypes.ResultDumpConsensusState, error) {
   761  	return &ctypes.ResultDumpConsensusState{}, nil
   762  }
   763  
   764  // ToDo: we doesn't have consensusState
   765  func (s *LocalService) ConsensusState(ctx *rpctypes.Context) (*ctypes.ResultConsensusState, error) {
   766  	return &ctypes.ResultConsensusState{}, nil
   767  }
   768  
   769  func (s *LocalService) ConsensusParams(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultConsensusParams, error) {
   770  	return &ctypes.ResultConsensusParams{
   771  		BlockHeight:     s.vm.blockStore.Height(),
   772  		ConsensusParams: *s.vm.genesis.ConsensusParams,
   773  	}, nil
   774  }
   775  
   776  func (s *LocalService) Health(ctx *rpctypes.Context) (*ctypes.ResultHealth, error) {
   777  	return &ctypes.ResultHealth{}, nil
   778  }
   779  
   780  func (s *LocalService) UnconfirmedTxs(ctx *rpctypes.Context, limitPtr *int) (*ctypes.ResultUnconfirmedTxs, error) {
   781  	limit := validatePerPage(limitPtr)
   782  	txs := s.vm.mempool.ReapMaxTxs(limit)
   783  	return &ctypes.ResultUnconfirmedTxs{
   784  		Count: len(txs),
   785  		Total: s.vm.mempool.Size(),
   786  		Txs:   txs,
   787  	}, nil
   788  }
   789  
   790  func (s *LocalService) NumUnconfirmedTxs(ctx *rpctypes.Context) (*ctypes.ResultUnconfirmedTxs, error) {
   791  	return &ctypes.ResultUnconfirmedTxs{
   792  		Count:      s.vm.mempool.Size(),
   793  		Total:      s.vm.mempool.Size(),
   794  		TotalBytes: s.vm.mempool.TxsBytes()}, nil
   795  }
   796  
   797  func (s *LocalService) CheckTx(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) {
   798  	res, err := s.vm.app.Mempool().CheckTxSync(abci.RequestCheckTx{Tx: tx})
   799  	if err != nil {
   800  		return nil, err
   801  	}
   802  	return &ctypes.ResultCheckTx{ResponseCheckTx: *res}, nil
   803  }