github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/app/rpc/backend/backend.go (about)

     1  package backend
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/server"
    10  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/types"
    11  
    12  	"github.com/fibonacci-chain/fbc/libs/tendermint/global"
    13  
    14  	coretypes "github.com/fibonacci-chain/fbc/libs/tendermint/rpc/core/types"
    15  	lru "github.com/hashicorp/golang-lru"
    16  
    17  	"github.com/spf13/viper"
    18  
    19  	"github.com/fibonacci-chain/fbc/libs/tendermint/libs/log"
    20  	"github.com/fibonacci-chain/fbc/x/evm/watcher"
    21  	"golang.org/x/time/rate"
    22  
    23  	rpctypes "github.com/fibonacci-chain/fbc/app/rpc/types"
    24  	evmtypes "github.com/fibonacci-chain/fbc/x/evm/types"
    25  
    26  	clientcontext "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context"
    27  
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/common/bitutil"
    30  	"github.com/ethereum/go-ethereum/common/hexutil"
    31  	"github.com/ethereum/go-ethereum/core/bloombits"
    32  	ethtypes "github.com/ethereum/go-ethereum/core/types"
    33  	tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types"
    34  	dbm "github.com/fibonacci-chain/fbc/libs/tm-db"
    35  )
    36  
    37  const (
    38  	FlagLogsLimit   = "rpc.logs-limit"
    39  	FlagLogsTimeout = "rpc.logs-timeout"
    40  	blockCacheSize  = 1024
    41  )
    42  
    43  var ErrTimeout = errors.New("query timeout exceeded")
    44  
    45  // Backend implements the functionality needed to filter changes.
    46  // Implemented by EthermintBackend.
    47  type Backend interface {
    48  	// Used by block filter; also used for polling
    49  	BlockNumber() (hexutil.Uint64, error)
    50  	LatestBlockNumber() (int64, error)
    51  	HeaderByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Header, error)
    52  	HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error)
    53  	GetBlockByNumber(blockNum rpctypes.BlockNumber, fullTx bool) (*watcher.Block, error)
    54  	GetBlockByHash(hash common.Hash, fullTx bool) (*watcher.Block, error)
    55  
    56  	GetTransactionByHash(hash common.Hash) (*watcher.Transaction, error)
    57  
    58  	// returns the logs of a given block
    59  	GetLogs(height int64) ([][]*ethtypes.Log, error)
    60  
    61  	// Used by pending transaction filter
    62  	PendingTransactions() ([]*watcher.Transaction, error)
    63  	PendingTransactionCnt() (int, error)
    64  	PendingTransactionsByHash(target common.Hash) (*watcher.Transaction, error)
    65  	UserPendingTransactionsCnt(address string) (int, error)
    66  	UserPendingTransactions(address string, limit int) ([]*watcher.Transaction, error)
    67  	PendingAddressList() ([]string, error)
    68  	GetPendingNonce(address string) (uint64, bool)
    69  
    70  	// Used by log filter
    71  	GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error)
    72  	BloomStatus() (uint64, uint64)
    73  	ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
    74  
    75  	// Used by eip-1898
    76  	ConvertToBlockNumber(rpctypes.BlockNumberOrHash) (rpctypes.BlockNumber, error)
    77  	// Block returns the block at the given block number, block data is readonly
    78  	Block(height *int64) (*coretypes.ResultBlock, error)
    79  	PruneEverything() bool
    80  }
    81  
    82  var _ Backend = (*EthermintBackend)(nil)
    83  
    84  // EthermintBackend implements the Backend interface
    85  type EthermintBackend struct {
    86  	ctx               context.Context
    87  	clientCtx         clientcontext.CLIContext
    88  	logger            log.Logger
    89  	gasLimit          int64
    90  	bloomRequests     chan chan *bloombits.Retrieval
    91  	closeBloomHandler chan struct{}
    92  	wrappedBackend    *watcher.Querier
    93  	rateLimiters      map[string]*rate.Limiter
    94  	disableAPI        map[string]bool
    95  	backendCache      Cache
    96  	logsLimit         int
    97  	logsTimeout       int // timeout second
    98  	blockCache        *lru.Cache
    99  	pruneEverything   bool
   100  }
   101  
   102  // New creates a new EthermintBackend instance
   103  func New(clientCtx clientcontext.CLIContext, log log.Logger, rateLimiters map[string]*rate.Limiter, disableAPI map[string]bool) *EthermintBackend {
   104  	b := &EthermintBackend{
   105  		ctx:               context.Background(),
   106  		clientCtx:         clientCtx,
   107  		logger:            log.With("module", "json-rpc"),
   108  		gasLimit:          int64(^uint32(0)),
   109  		bloomRequests:     make(chan chan *bloombits.Retrieval),
   110  		closeBloomHandler: make(chan struct{}),
   111  		wrappedBackend:    watcher.NewQuerier(),
   112  		rateLimiters:      rateLimiters,
   113  		disableAPI:        disableAPI,
   114  		backendCache:      NewLruCache(),
   115  		logsLimit:         viper.GetInt(FlagLogsLimit),
   116  		logsTimeout:       viper.GetInt(FlagLogsTimeout),
   117  		pruneEverything:   viper.GetString(server.FlagPruning) == types.PruningOptionEverything,
   118  	}
   119  	b.blockCache, _ = lru.New(blockCacheSize)
   120  	return b
   121  }
   122  
   123  func (b *EthermintBackend) PruneEverything() bool {
   124  	return b.pruneEverything
   125  }
   126  
   127  func (b *EthermintBackend) LogsLimit() int {
   128  	return b.logsLimit
   129  }
   130  
   131  func (b *EthermintBackend) LogsTimeout() time.Duration {
   132  	return time.Duration(b.logsTimeout) * time.Second
   133  }
   134  
   135  // BlockNumber returns the current block number.
   136  func (b *EthermintBackend) BlockNumber() (hexutil.Uint64, error) {
   137  	committedHeight := global.GetGlobalHeight()
   138  	return hexutil.Uint64(committedHeight), nil
   139  }
   140  
   141  // GetBlockByNumber returns the block identified by number.
   142  func (b *EthermintBackend) GetBlockByNumber(blockNum rpctypes.BlockNumber, fullTx bool) (*watcher.Block, error) {
   143  	//query block in cache first
   144  	block, err := b.backendCache.GetBlockByNumber(uint64(blockNum), fullTx)
   145  	if err == nil {
   146  		return block, nil
   147  	}
   148  	//query block from watch db
   149  	block, err = b.wrappedBackend.GetBlockByNumber(uint64(blockNum), fullTx)
   150  	if err == nil {
   151  		b.backendCache.AddOrUpdateBlock(block.Hash, block, fullTx)
   152  		return block, nil
   153  	}
   154  	//query block from db
   155  	height := blockNum.Int64()
   156  	if height <= 0 {
   157  		// get latest block height
   158  		num, err := b.BlockNumber()
   159  		if err != nil {
   160  			return nil, err
   161  		}
   162  		height = int64(num)
   163  	}
   164  
   165  	resBlock, err := b.Block(&height)
   166  	if err != nil {
   167  		return nil, nil
   168  	}
   169  
   170  	block, err = rpctypes.RpcBlockFromTendermint(b.clientCtx, resBlock.Block, fullTx)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	b.backendCache.AddOrUpdateBlock(block.Hash, block, fullTx)
   175  	return block, nil
   176  }
   177  
   178  // GetBlockByHash returns the block identified by hash.
   179  func (b *EthermintBackend) GetBlockByHash(hash common.Hash, fullTx bool) (*watcher.Block, error) {
   180  	//query block in cache first
   181  	block, err := b.backendCache.GetBlockByHash(hash, fullTx)
   182  	if err == nil {
   183  		return block, err
   184  	}
   185  	//query block from watch db
   186  	block, err = b.wrappedBackend.GetBlockByHash(hash, fullTx)
   187  	if err == nil {
   188  		b.backendCache.AddOrUpdateBlock(hash, block, fullTx)
   189  		return block, nil
   190  	}
   191  	//query block from tendermint
   192  	res, _, err := b.clientCtx.Query(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryHashToHeight, hash.Hex()))
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	var out evmtypes.QueryResBlockNumber
   198  	if err := b.clientCtx.Codec.UnmarshalJSON(res, &out); err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	resBlock, err := b.Block(&out.Number)
   203  	if err != nil {
   204  		return nil, nil
   205  	}
   206  
   207  	block, err = rpctypes.RpcBlockFromTendermint(b.clientCtx, resBlock.Block, fullTx)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  	b.backendCache.AddOrUpdateBlock(hash, block, fullTx)
   212  	return block, nil
   213  }
   214  
   215  // HeaderByNumber returns the block header identified by height.
   216  func (b *EthermintBackend) HeaderByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Header, error) {
   217  	height := blockNum.Int64()
   218  	if height <= 0 {
   219  		// get latest block height
   220  		num, err := b.BlockNumber()
   221  		if err != nil {
   222  			return nil, err
   223  		}
   224  
   225  		height = int64(num)
   226  	}
   227  
   228  	resBlock, err := b.Block(&height)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  
   233  	res, _, err := b.clientCtx.Query(fmt.Sprintf("custom/%s/%s/%d", evmtypes.ModuleName, evmtypes.QueryBloom, resBlock.Block.Height))
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  
   238  	var bloomRes evmtypes.QueryBloomFilter
   239  	b.clientCtx.Codec.MustUnmarshalJSON(res, &bloomRes)
   240  
   241  	ethHeader := rpctypes.EthHeaderFromTendermint(resBlock.Block.Header)
   242  	ethHeader.Bloom = bloomRes.Bloom
   243  	return ethHeader, nil
   244  }
   245  
   246  // HeaderByHash returns the block header identified by hash.
   247  func (b *EthermintBackend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) {
   248  	res, _, err := b.clientCtx.Query(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryHashToHeight, blockHash.Hex()))
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  
   253  	var out evmtypes.QueryResBlockNumber
   254  	if err := b.clientCtx.Codec.UnmarshalJSON(res, &out); err != nil {
   255  		return nil, err
   256  	}
   257  
   258  	resBlock, err := b.Block(&out.Number)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  
   263  	res, _, err = b.clientCtx.Query(fmt.Sprintf("custom/%s/%s/%d", evmtypes.ModuleName, evmtypes.QueryBloom, resBlock.Block.Height))
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	var bloomRes evmtypes.QueryBloomFilter
   269  	b.clientCtx.Codec.MustUnmarshalJSON(res, &bloomRes)
   270  
   271  	ethHeader := rpctypes.EthHeaderFromTendermint(resBlock.Block.Header)
   272  	ethHeader.Bloom = bloomRes.Bloom
   273  	return ethHeader, nil
   274  }
   275  
   276  // GetTransactionLogs returns the logs given a transaction hash.
   277  // It returns an error if there's an encoding error.
   278  // If no logs are found for the tx hash, the error is nil.
   279  func (b *EthermintBackend) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error) {
   280  	txRes, err := b.clientCtx.Client.Tx(txHash.Bytes(), !b.clientCtx.TrustNode)
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  
   285  	execRes, err := evmtypes.DecodeResultData(txRes.TxResult.Data)
   286  	if err != nil {
   287  		return nil, err
   288  	}
   289  
   290  	// Sometimes failed txs leave Logs which need to be cleared
   291  	if !txRes.TxResult.IsOK() && execRes.Logs != nil {
   292  		return []*ethtypes.Log{}, nil
   293  	}
   294  
   295  	return execRes.Logs, nil
   296  }
   297  
   298  // PendingTransactions returns the transactions that are in the transaction pool
   299  // and have a from address that is one of the accounts this node manages.
   300  func (b *EthermintBackend) PendingTransactions() ([]*watcher.Transaction, error) {
   301  	lastHeight, err := b.clientCtx.Client.LatestBlockNumber()
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  	pendingTxs, err := b.clientCtx.Client.UnconfirmedTxs(-1)
   306  	if err != nil {
   307  		return nil, err
   308  	}
   309  
   310  	transactions := make([]*watcher.Transaction, 0, len(pendingTxs.Txs))
   311  	for _, tx := range pendingTxs.Txs {
   312  		ethTx, err := rpctypes.RawTxToEthTx(b.clientCtx, tx, lastHeight)
   313  		if err != nil {
   314  			// ignore non Ethermint EVM transactions
   315  			continue
   316  		}
   317  
   318  		// TODO: check signer and reference against accounts the node manages
   319  		rpcTx, err := watcher.NewTransaction(ethTx, common.BytesToHash(ethTx.Hash), common.Hash{}, 0, 0)
   320  		if err != nil {
   321  			return nil, err
   322  		}
   323  
   324  		transactions = append(transactions, rpcTx)
   325  	}
   326  
   327  	return transactions, nil
   328  }
   329  
   330  func (b *EthermintBackend) PendingTransactionCnt() (int, error) {
   331  	result, err := b.clientCtx.Client.NumUnconfirmedTxs()
   332  	if err != nil {
   333  		return 0, err
   334  	}
   335  	return result.Count, nil
   336  }
   337  
   338  func (b *EthermintBackend) UserPendingTransactionsCnt(address string) (int, error) {
   339  	result, err := b.clientCtx.Client.UserNumUnconfirmedTxs(address)
   340  	if err != nil {
   341  		return 0, err
   342  	}
   343  	return result.Count, nil
   344  }
   345  
   346  func (b *EthermintBackend) GetPendingNonce(address string) (uint64, bool) {
   347  	result, ok := b.clientCtx.Client.GetPendingNonce(address)
   348  	if !ok {
   349  		return 0, false
   350  	}
   351  	return result.Nonce, true
   352  }
   353  
   354  func (b *EthermintBackend) UserPendingTransactions(address string, limit int) ([]*watcher.Transaction, error) {
   355  	lastHeight, err := b.clientCtx.Client.LatestBlockNumber()
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  	result, err := b.clientCtx.Client.UserUnconfirmedTxs(address, limit)
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  	transactions := make([]*watcher.Transaction, 0, len(result.Txs))
   364  	for _, tx := range result.Txs {
   365  		ethTx, err := rpctypes.RawTxToEthTx(b.clientCtx, tx, lastHeight)
   366  		if err != nil {
   367  			// ignore non Ethermint EVM transactions
   368  			continue
   369  		}
   370  
   371  		// TODO: check signer and reference against accounts the node manages
   372  		rpcTx, err := watcher.NewTransaction(ethTx, common.BytesToHash(ethTx.Hash), common.Hash{}, 0, 0)
   373  		if err != nil {
   374  			return nil, err
   375  		}
   376  
   377  		transactions = append(transactions, rpcTx)
   378  	}
   379  
   380  	return transactions, nil
   381  }
   382  
   383  func (b *EthermintBackend) PendingAddressList() ([]string, error) {
   384  	res, err := b.clientCtx.Client.GetAddressList()
   385  	if err != nil {
   386  		return nil, err
   387  	}
   388  	return res.Addresses, nil
   389  }
   390  
   391  // PendingTransactions returns the transaction that is in the transaction pool
   392  // and have a from address that is one of the accounts this node manages.
   393  func (b *EthermintBackend) PendingTransactionsByHash(target common.Hash) (*watcher.Transaction, error) {
   394  	lastHeight, err := b.clientCtx.Client.LatestBlockNumber()
   395  	if err != nil {
   396  		return nil, err
   397  	}
   398  	pendingTx, err := b.clientCtx.Client.GetUnconfirmedTxByHash(target)
   399  	if err != nil {
   400  		return nil, err
   401  	}
   402  	ethTx, err := rpctypes.RawTxToEthTx(b.clientCtx, pendingTx, lastHeight)
   403  	if err != nil {
   404  		// ignore non Ethermint EVM transactions
   405  		return nil, err
   406  	}
   407  	rpcTx, err := watcher.NewTransaction(ethTx, common.BytesToHash(ethTx.Hash), common.Hash{}, 0, 0)
   408  	if err != nil {
   409  		return nil, err
   410  	}
   411  	return rpcTx, nil
   412  }
   413  
   414  func (b *EthermintBackend) GetTransactionByHash(hash common.Hash) (tx *watcher.Transaction, err error) {
   415  	// query tx in cache first
   416  	tx, err = b.backendCache.GetTransaction(hash)
   417  	if err == nil {
   418  		return tx, err
   419  	}
   420  	// query tx in watch db
   421  	tx, err = b.wrappedBackend.GetTransactionByHash(hash)
   422  	if err == nil {
   423  		b.backendCache.AddOrUpdateTransaction(hash, tx)
   424  		return tx, nil
   425  	}
   426  	// query tx in tendermint
   427  	txRes, err := b.clientCtx.Client.Tx(hash.Bytes(), false)
   428  	if err != nil {
   429  		return nil, err
   430  	}
   431  
   432  	// Can either cache or just leave this out if not necessary
   433  	block, err := b.Block(&txRes.Height)
   434  	if err != nil {
   435  		return nil, err
   436  	}
   437  
   438  	blockHash := common.BytesToHash(block.Block.Hash())
   439  
   440  	ethTx, err := rpctypes.RawTxToEthTx(b.clientCtx, txRes.Tx, txRes.Height)
   441  	if err != nil {
   442  		return nil, err
   443  	}
   444  
   445  	tx, err = watcher.NewTransaction(ethTx, common.BytesToHash(ethTx.Hash), blockHash, uint64(txRes.Height), uint64(txRes.Index))
   446  	if err != nil {
   447  		return nil, err
   448  	}
   449  	b.backendCache.AddOrUpdateTransaction(hash, tx)
   450  	return tx, nil
   451  }
   452  
   453  // GetLogs returns all the logs from all the ethereum transactions in a block.
   454  func (b *EthermintBackend) GetLogs(height int64) ([][]*ethtypes.Log, error) {
   455  	block, err := b.Block(&height)
   456  	if err != nil {
   457  		return nil, err
   458  	}
   459  	// return empty directly when block was produced during stress testing.
   460  	var blockLogs = [][]*ethtypes.Log{}
   461  	if b.logsLimit > 0 && len(block.Block.Txs) > b.logsLimit {
   462  		return blockLogs, nil
   463  	}
   464  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(b.logsTimeout)*time.Second)
   465  	defer cancel()
   466  	for _, tx := range block.Block.Txs {
   467  		select {
   468  		case <-ctx.Done():
   469  			return nil, ErrTimeout
   470  		default:
   471  			// NOTE: we query the state in case the tx result logs are not persisted after an upgrade.
   472  			txRes, err := b.clientCtx.Client.Tx(tx.Hash(block.Block.Height), !b.clientCtx.TrustNode)
   473  			if err != nil {
   474  				continue
   475  			}
   476  			execRes, err := evmtypes.DecodeResultData(txRes.TxResult.Data)
   477  			if err != nil {
   478  				continue
   479  			}
   480  			var validLogs []*ethtypes.Log
   481  			for _, log := range execRes.Logs {
   482  				if int64(log.BlockNumber) == block.Block.Height {
   483  					validLogs = append(validLogs, log)
   484  				}
   485  			}
   486  			blockLogs = append(blockLogs, validLogs)
   487  		}
   488  	}
   489  
   490  	return blockLogs, nil
   491  }
   492  
   493  // BloomStatus returns the BloomBitsBlocks and the number of processed sections maintained
   494  // by the chain indexer.
   495  func (b *EthermintBackend) BloomStatus() (uint64, uint64) {
   496  	sections := evmtypes.GetIndexer().StoredSection()
   497  	return evmtypes.BloomBitsBlocks, sections
   498  }
   499  
   500  // LatestBlockNumber gets the latest block height in int64 format.
   501  func (b *EthermintBackend) LatestBlockNumber() (int64, error) {
   502  	return b.clientCtx.Client.LatestBlockNumber()
   503  }
   504  
   505  func (b *EthermintBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {
   506  	for i := 0; i < evmtypes.BloomFilterThreads; i++ {
   507  		go session.Multiplex(evmtypes.BloomRetrievalBatch, evmtypes.BloomRetrievalWait, b.bloomRequests)
   508  	}
   509  }
   510  
   511  // startBloomHandlers starts a batch of goroutines to accept bloom bit database
   512  // retrievals from possibly a range of filters and serving the data to satisfy.
   513  func (b *EthermintBackend) StartBloomHandlers(sectionSize uint64, db dbm.DB) {
   514  	for i := 0; i < evmtypes.BloomServiceThreads; i++ {
   515  		go func() {
   516  			for {
   517  				select {
   518  				case <-b.closeBloomHandler:
   519  					return
   520  
   521  				case request := <-b.bloomRequests:
   522  					task := <-request
   523  					task.Bitsets = make([][]byte, len(task.Sections))
   524  					for i, section := range task.Sections {
   525  						height := int64((section+1)*sectionSize-1) + tmtypes.GetStartBlockHeight()
   526  						hash, err := b.GetBlockHashByHeight(rpctypes.BlockNumber(height))
   527  						if err != nil {
   528  							task.Error = err
   529  						}
   530  						if compVector, err := evmtypes.ReadBloomBits(db, task.Bit, section, hash); err == nil {
   531  							if blob, err := bitutil.DecompressBytes(compVector, int(sectionSize/8)); err == nil {
   532  								task.Bitsets[i] = blob
   533  							} else {
   534  								task.Error = err
   535  							}
   536  						} else {
   537  							task.Error = err
   538  						}
   539  					}
   540  					request <- task
   541  				}
   542  			}
   543  		}()
   544  	}
   545  }
   546  
   547  // GetBlockHashByHeight returns the block hash by height.
   548  func (b *EthermintBackend) GetBlockHashByHeight(height rpctypes.BlockNumber) (common.Hash, error) {
   549  	res, _, err := b.clientCtx.Query(fmt.Sprintf("custom/%s/%s/%d", evmtypes.ModuleName, evmtypes.QueryHeightToHash, height))
   550  	if err != nil {
   551  		return common.Hash{}, err
   552  	}
   553  
   554  	hash := common.BytesToHash(res)
   555  	return hash, nil
   556  }
   557  
   558  // Close
   559  func (b *EthermintBackend) Close() {
   560  	close(b.closeBloomHandler)
   561  }
   562  
   563  func (b *EthermintBackend) GetRateLimiter(apiName string) *rate.Limiter {
   564  	if b.rateLimiters == nil {
   565  		return nil
   566  	}
   567  	return b.rateLimiters[apiName]
   568  }
   569  
   570  func (b *EthermintBackend) IsDisabled(apiName string) bool {
   571  	if b.disableAPI == nil {
   572  		return false
   573  	}
   574  	return b.disableAPI[apiName]
   575  }
   576  
   577  func (b *EthermintBackend) ConvertToBlockNumber(blockNumberOrHash rpctypes.BlockNumberOrHash) (rpctypes.BlockNumber, error) {
   578  	if blockNumber, ok := blockNumberOrHash.Number(); ok {
   579  		return blockNumber, nil
   580  	}
   581  	hash, ok := blockNumberOrHash.Hash()
   582  	if !ok {
   583  		return rpctypes.LatestBlockNumber, nil
   584  	}
   585  	ethBlock, err := b.wrappedBackend.GetBlockByHash(hash, false)
   586  	if err == nil {
   587  		return rpctypes.BlockNumber(ethBlock.Number), nil
   588  	}
   589  
   590  	res, _, err := b.clientCtx.Query(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryHashToHeight, hash.Hex()))
   591  	if err != nil {
   592  		return rpctypes.LatestBlockNumber, rpctypes.ErrResourceNotFound
   593  	}
   594  
   595  	var out evmtypes.QueryResBlockNumber
   596  	if err := b.clientCtx.Codec.UnmarshalJSON(res, &out); err != nil {
   597  		return rpctypes.LatestBlockNumber, rpctypes.ErrResourceNotFound
   598  	}
   599  	return rpctypes.BlockNumber(out.Number), nil
   600  }
   601  
   602  func (b *EthermintBackend) cacheBlock(block *coretypes.ResultBlock) {
   603  	if b.blockCache != nil {
   604  		b.blockCache.Add(block.Block.Height, block)
   605  	}
   606  }
   607  
   608  func (b *EthermintBackend) getBlockFromCache(height int64) *coretypes.ResultBlock {
   609  	if b.blockCache != nil {
   610  		if v, ok := b.blockCache.Get(height); ok {
   611  			return v.(*coretypes.ResultBlock)
   612  		}
   613  	}
   614  	return nil
   615  }
   616  
   617  func (b *EthermintBackend) Block(height *int64) (block *coretypes.ResultBlock, err error) {
   618  	if height != nil {
   619  		block = b.getBlockFromCache(*height)
   620  	}
   621  	if block == nil {
   622  		block, err = b.clientCtx.Client.Block(height)
   623  		if err != nil {
   624  			return nil, err
   625  		}
   626  		b.cacheBlock(block)
   627  	}
   628  	return block, nil
   629  }