diff --git a/CHANGELOG.md b/CHANGELOG.md index ceccca55..83d6292c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### API Breaking +* (rpc) [\#1126](https://github.com/evmos/ethermint/pull/1126) Make some JSON-RPC APIS work for pruned nodes. * (rpc) [\#1143](https://github.com/evmos/ethermint/pull/1143) Restrict unprotected txs on the node JSON-RPC configuration. * (all) [\#1137](https://github.com/evmos/ethermint/pull/1137) Rename go module to `evmos/ethermint` diff --git a/rpc/backend/backend.go b/rpc/backend/backend.go index b116fc1e..bb3fe58b 100644 --- a/rpc/backend/backend.go +++ b/rpc/backend/backend.go @@ -52,6 +52,7 @@ type EVMBackend interface { // Blockchain API BlockNumber() (hexutil.Uint64, error) GetTendermintBlockByNumber(blockNum types.BlockNumber) (*tmrpctypes.ResultBlock, error) + GetTendermintBlockResultByNumber(height *int64) (*tmrpctypes.ResultBlockResults, error) GetTendermintBlockByHash(blockHash common.Hash) (*tmrpctypes.ResultBlock, error) GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (map[string]interface{}, error) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) @@ -68,7 +69,7 @@ type EVMBackend interface { GetTxByEthHash(txHash common.Hash) (*tmrpctypes.ResultTx, error) GetTxByTxIndex(height int64, txIndex uint) (*tmrpctypes.ResultTx, error) EstimateGas(args evmtypes.TransactionArgs, blockNrOptional *types.BlockNumber) (hexutil.Uint64, error) - BaseFee(height int64) (*big.Int, error) + BaseFee(blockRes *tmrpctypes.ResultBlockResults) (*big.Int, error) GlobalMinGasPrice() (sdk.Dec, error) // Fee API diff --git a/rpc/backend/evm_backend.go b/rpc/backend/evm_backend.go index d06e35a2..a62f5a5d 100644 --- a/rpc/backend/evm_backend.go +++ b/rpc/backend/evm_backend.go @@ -70,7 +70,13 @@ func (b *Backend) GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (map return nil, nil } - res, err := b.EthBlockFromTendermint(resBlock, fullTx) + blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + b.logger.Debug("failed to fetch block result from Tendermint", "height", blockNum, "error", err.Error()) + return nil, nil + } + + res, err := b.EthBlockFromTendermint(resBlock, blockRes, fullTx) if err != nil { b.logger.Debug("EthBlockFromTendermint failed", "height", blockNum, "error", err.Error()) return nil, err @@ -85,12 +91,19 @@ func (b *Backend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]inte if err != nil { return nil, err } + if resBlock == nil { // block not found return nil, nil } - return b.EthBlockFromTendermint(resBlock, fullTx) + blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + b.logger.Debug("failed to fetch block result from Tendermint", "block-hash", hash.String(), "error", err.Error()) + return nil, nil + } + + return b.EthBlockFromTendermint(resBlock, blockRes, fullTx) } // BlockByNumber returns the block identified by number. @@ -101,10 +114,15 @@ func (b *Backend) BlockByNumber(blockNum types.BlockNumber) (*ethtypes.Block, er } if resBlock == nil { // block not found - return nil, errors.Errorf("block not found for height %d", blockNum) + return nil, fmt.Errorf("block not found for height %d", blockNum) } - return b.EthBlockFromTm(resBlock) + blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + return nil, fmt.Errorf("block result not found for height %d", resBlock.Block.Height) + } + + return b.EthBlockFromTm(resBlock, blockRes) } // BlockByHash returns the block identified by hash. @@ -115,29 +133,34 @@ func (b *Backend) BlockByHash(hash common.Hash) (*ethtypes.Block, error) { } if resBlock == nil || resBlock.Block == nil { - return nil, errors.Errorf("block not found for hash %s", hash) + return nil, fmt.Errorf("block not found for hash %s", hash) } - return b.EthBlockFromTm(resBlock) + blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + return nil, fmt.Errorf("block result not found for hash %s", hash) + } + + return b.EthBlockFromTm(resBlock, blockRes) } -func (b *Backend) EthBlockFromTm(resBlock *tmrpctypes.ResultBlock) (*ethtypes.Block, error) { +func (b *Backend) EthBlockFromTm(resBlock *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults) (*ethtypes.Block, error) { block := resBlock.Block height := block.Height - bloom, err := b.BlockBloom(&height) + bloom, err := b.BlockBloom(blockRes) if err != nil { b.logger.Debug("HeaderByNumber BlockBloom failed", "height", height) } - baseFee, err := b.BaseFee(height) + baseFee, err := b.BaseFee(blockRes) if err != nil { - b.logger.Debug("HeaderByNumber BaseFee failed", "height", height, "error", err.Error()) - return nil, err + // handle error for pruned node and log + b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", height, "error", err) } ethHeader := types.EthHeaderFromTendermint(block.Header, bloom, baseFee) - resBlockResult, err := b.clientCtx.Client.BlockResults(b.ctx, &block.Height) + resBlockResult, err := b.GetTendermintBlockResultByNumber(&block.Height) if err != nil { return nil, err } @@ -179,6 +202,11 @@ func (b *Backend) GetTendermintBlockByNumber(blockNum types.BlockNumber) (*tmrpc return resBlock, nil } +// GetTendermintBlockResultByNumber returns a Tendermint-formatted block result by block number +func (b *Backend) GetTendermintBlockResultByNumber(height *int64) (*tmrpctypes.ResultBlockResults, error) { + return b.clientCtx.Client.BlockResults(b.ctx, height) +} + // GetTendermintBlockByHash returns a Tendermint format block by block number func (b *Backend) GetTendermintBlockByHash(blockHash common.Hash) (*tmrpctypes.ResultBlock, error) { resBlock, err := b.clientCtx.Client.BlockByHash(b.ctx, blockHash.Bytes()) @@ -196,12 +224,8 @@ func (b *Backend) GetTendermintBlockByHash(blockHash common.Hash) (*tmrpctypes.R } // BlockBloom query block bloom filter from block results -func (b *Backend) BlockBloom(height *int64) (ethtypes.Bloom, error) { - result, err := b.clientCtx.Client.BlockResults(b.ctx, height) - if err != nil { - return ethtypes.Bloom{}, err - } - for _, event := range result.EndBlockEvents { +func (b *Backend) BlockBloom(blockRes *tmrpctypes.ResultBlockResults) (ethtypes.Bloom, error) { + for _, event := range blockRes.EndBlockEvents { if event.Type != evmtypes.EventTypeBlockBloom { continue } @@ -218,22 +242,19 @@ func (b *Backend) BlockBloom(height *int64) (ethtypes.Bloom, error) { // EthBlockFromTendermint returns a JSON-RPC compatible Ethereum block from a given Tendermint block and its block result. func (b *Backend) EthBlockFromTendermint( resBlock *tmrpctypes.ResultBlock, + blockRes *tmrpctypes.ResultBlockResults, fullTx bool, ) (map[string]interface{}, error) { ethRPCTxs := []interface{}{} block := resBlock.Block - baseFee, err := b.BaseFee(block.Height) + baseFee, err := b.BaseFee(blockRes) if err != nil { - return nil, err + // handle the error for pruned node. + b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", block.Height, "error", err) } - resBlockResult, err := b.clientCtx.Client.BlockResults(b.ctx, &block.Height) - if err != nil { - return nil, err - } - - msgs := b.GetEthereumMsgsFromTendermintBlock(resBlock, resBlockResult) + msgs := b.GetEthereumMsgsFromTendermintBlock(resBlock, blockRes) for txIndex, ethMsg := range msgs { if !fullTx { hash := common.HexToHash(ethMsg.Hash) @@ -256,7 +277,7 @@ func (b *Backend) EthBlockFromTendermint( ethRPCTxs = append(ethRPCTxs, rpcTx) } - bloom, err := b.BlockBloom(&block.Height) + bloom, err := b.BlockBloom(blockRes) if err != nil { b.logger.Debug("failed to query BlockBloom", "height", block.Height, "error", err.Error()) } @@ -265,6 +286,8 @@ func (b *Backend) EthBlockFromTendermint( ConsAddress: sdk.ConsAddress(block.Header.ProposerAddress).String(), } + var validatorAccAddr sdk.AccAddress + ctx := types.ContextWithHeight(block.Height) res, err := b.queryClient.ValidatorAccount(ctx, req) if err != nil { @@ -274,15 +297,16 @@ func (b *Backend) EthBlockFromTendermint( "cons-address", req.ConsAddress, "error", err.Error(), ) - return nil, err + // use zero address as the validator operator address + validatorAccAddr = sdk.AccAddress(common.Address{}.Bytes()) + } else { + validatorAccAddr, err = sdk.AccAddressFromBech32(res.AccountAddress) + if err != nil { + return nil, err + } } - addr, err := sdk.AccAddressFromBech32(res.AccountAddress) - if err != nil { - return nil, err - } - - validatorAddr := common.BytesToAddress(addr) + validatorAddr := common.BytesToAddress(validatorAccAddr) gasLimit, err := types.BlockMaxGasFromConsensusParams(ctx, b.clientCtx, block.Height) if err != nil { @@ -291,7 +315,7 @@ func (b *Backend) EthBlockFromTendermint( gasUsed := uint64(0) - for _, txsResult := range resBlockResult.TxsResults { + for _, txsResult := range blockRes.TxsResults { // workaround for cosmos-sdk bug. https://github.com/cosmos/cosmos-sdk/issues/10832 if ShouldIgnoreGasUsed(txsResult) { // block gas limit has exceeded, other txs must have failed with same reason. @@ -325,15 +349,20 @@ func (b *Backend) HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Header, return nil, errors.Errorf("block not found for height %d", blockNum) } - bloom, err := b.BlockBloom(&resBlock.Block.Height) + blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + return nil, fmt.Errorf("block result not found for height %d", resBlock.Block.Height) + } + + bloom, err := b.BlockBloom(blockRes) if err != nil { b.logger.Debug("HeaderByNumber BlockBloom failed", "height", resBlock.Block.Height) } - baseFee, err := b.BaseFee(resBlock.Block.Height) + baseFee, err := b.BaseFee(blockRes) if err != nil { - b.logger.Debug("HeaderByNumber BaseFee failed", "height", resBlock.Block.Height, "error", err.Error()) - return nil, err + // handle the error for pruned node. + b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", resBlock.Block.Height, "error", err) } ethHeader := types.EthHeaderFromTendermint(resBlock.Block.Header, bloom, baseFee) @@ -350,15 +379,20 @@ func (b *Backend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) return nil, errors.Errorf("block not found for hash %s", blockHash.Hex()) } - bloom, err := b.BlockBloom(&resBlock.Block.Height) + blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + return nil, errors.Errorf("block result not found for height %d", resBlock.Block.Height) + } + + bloom, err := b.BlockBloom(blockRes) if err != nil { b.logger.Debug("HeaderByHash BlockBloom failed", "height", resBlock.Block.Height) } - baseFee, err := b.BaseFee(resBlock.Block.Height) + baseFee, err := b.BaseFee(blockRes) if err != nil { - b.logger.Debug("HeaderByHash BaseFee failed", "height", resBlock.Block.Height, "error", err.Error()) - return nil, err + // handle the error for pruned node. + b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", resBlock.Block.Height, "error", err) } ethHeader := types.EthHeaderFromTendermint(resBlock.Block.Header, bloom, baseFee) @@ -388,22 +422,12 @@ func (b *Backend) PendingTransactions() ([]*sdk.Tx, error) { // GetLogsByHeight returns all the logs from all the ethereum transactions in a block. func (b *Backend) GetLogsByHeight(height *int64) ([][]*ethtypes.Log, error) { // NOTE: we query the state in case the tx result logs are not persisted after an upgrade. - blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, height) + blockRes, err := b.GetTendermintBlockResultByNumber(height) if err != nil { return nil, err } - blockLogs := [][]*ethtypes.Log{} - for _, txResult := range blockRes.TxsResults { - logs, err := AllTxLogsFromEvents(txResult.Events) - if err != nil { - return nil, err - } - - blockLogs = append(blockLogs, logs...) - } - - return blockLogs, nil + return GetLogsFromBlockResults(blockRes) } // GetLogs returns all the logs from all the ethereum transactions in a block. @@ -520,12 +544,14 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransactio return nil, err } + blockRes, err := b.GetTendermintBlockResultByNumber(&block.Block.Height) + if err != nil { + b.logger.Debug("block result not found", "height", block.Block.Height, "error", err.Error()) + return nil, nil + } + if parsedTx.EthTxIndex == -1 { // Fallback to find tx index by iterating all valid eth transactions - blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &block.Block.Height) - if err != nil { - return nil, nil - } msgs := b.GetEthereumMsgsFromTendermintBlock(block, blockRes) for i := range msgs { if msgs[i].Hash == hexTx { @@ -538,9 +564,10 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransactio return nil, errors.New("can't find index of ethereum tx") } - baseFee, err := b.BaseFee(block.Block.Height) + baseFee, err := b.BaseFee(blockRes) if err != nil { - return nil, err + // handle the error for pruned node. + b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", blockRes.Height, "error", err) } return types.NewTransactionFromMsg( @@ -812,10 +839,22 @@ func (b *Backend) SuggestGasTipCap(baseFee *big.Int) (*big.Int, error) { // If the base fee is not enabled globally, the query returns nil. // If the London hard fork is not activated at the current height, the query will // return nil. -func (b *Backend) BaseFee(height int64) (*big.Int, error) { +func (b *Backend) BaseFee(blockRes *tmrpctypes.ResultBlockResults) (*big.Int, error) { // return BaseFee if London hard fork is activated and feemarket is enabled - res, err := b.queryClient.BaseFee(types.ContextWithHeight(height), &evmtypes.QueryBaseFeeRequest{}) + res, err := b.queryClient.BaseFee(types.ContextWithHeight(blockRes.Height), &evmtypes.QueryBaseFeeRequest{}) if err != nil { + // fallback to parsing from begin blocker event, could happen on pruned nodes. + // faster to iterate reversely + for i := len(blockRes.BeginBlockEvents) - 1; i >= 0; i-- { + evt := blockRes.BeginBlockEvents[i] + if evt.Type == feemarkettypes.EventTypeFeeMarket && len(evt.Attributes) > 0 { + baseFee, err := strconv.ParseInt(string(evt.Attributes[0].Value), 10, 64) + if err == nil { + return big.NewInt(baseFee), nil + } + break + } + } return nil, err } @@ -892,7 +931,7 @@ func (b *Backend) FeeHistory( } // tendermint block result - tendermintBlockResult, err := b.clientCtx.Client.BlockResults(b.ctx, &tendermintblock.Block.Height) + tendermintBlockResult, err := b.GetTendermintBlockResultByNumber(&tendermintblock.Block.Height) if tendermintBlockResult == nil { b.logger.Debug("block result not found", "height", tendermintblock.Block.Height, "error", err.Error()) return nil, err diff --git a/rpc/backend/utils.go b/rpc/backend/utils.go index 5c4c9ec4..c9f87805 100644 --- a/rpc/backend/utils.go +++ b/rpc/backend/utils.go @@ -232,7 +232,7 @@ func (b *Backend) processBlock( targetOneFeeHistory *types.OneFeeHistory, ) error { blockHeight := tendermintBlock.Block.Height - blockBaseFee, err := b.BaseFee(blockHeight) + blockBaseFee, err := b.BaseFee(tendermintBlockResult) if err != nil { return err } @@ -390,3 +390,18 @@ func TxSuccessOrExceedsBlockGasLimit(res *abci.ResponseDeliverTx) bool { func ShouldIgnoreGasUsed(res *abci.ResponseDeliverTx) bool { return res.GetCode() == 11 && strings.Contains(res.GetLog(), "no block gas left to run tx: out of gas") } + +// GetLogsFromBlockResults returns the list of event logs from the tendermint block result response +func GetLogsFromBlockResults(blockRes *tmrpctypes.ResultBlockResults) ([][]*ethtypes.Log, error) { + blockLogs := [][]*ethtypes.Log{} + for _, txResult := range blockRes.TxsResults { + logs, err := AllTxLogsFromEvents(txResult.Events) + if err != nil { + return nil, err + } + + blockLogs = append(blockLogs, logs...) + } + + return blockLogs, nil +} diff --git a/rpc/namespaces/ethereum/eth/api.go b/rpc/namespaces/ethereum/eth/api.go index ba001494..c2094d81 100644 --- a/rpc/namespaces/ethereum/eth/api.go +++ b/rpc/namespaces/ethereum/eth/api.go @@ -339,7 +339,7 @@ func (e *PublicAPI) GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Ui return nil } - blockRes, err := e.clientCtx.Client.BlockResults(e.ctx, &block.Block.Height) + blockRes, err := e.backend.GetTendermintBlockResultByNumber(&block.Block.Height) if err != nil { return nil } @@ -363,7 +363,7 @@ func (e *PublicAPI) GetBlockTransactionCountByNumber(blockNum rpctypes.BlockNumb return nil } - blockRes, err := e.clientCtx.Client.BlockResults(e.ctx, &block.Block.Height) + blockRes, err := e.backend.GetTendermintBlockResultByNumber(&block.Block.Height) if err != nil { return nil } @@ -753,6 +753,11 @@ func (e *PublicAPI) GetTransactionByHash(hash common.Hash) (*rpctypes.RPCTransac // getTransactionByBlockAndIndex is the common code shared by `GetTransactionByBlockNumberAndIndex` and `GetTransactionByBlockHashAndIndex`. func (e *PublicAPI) getTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) { + blockRes, err := e.backend.GetTendermintBlockResultByNumber(&block.Block.Height) + if err != nil { + return nil, nil + } + var msg *evmtypes.MsgEthereumTx // try /tx_search first res, err := e.backend.GetTxByTxIndex(block.Block.Height, uint(idx)) @@ -781,11 +786,6 @@ func (e *PublicAPI) getTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, return nil, nil } } else { - blockRes, err := e.clientCtx.Client.BlockResults(e.ctx, &block.Block.Height) - if err != nil { - return nil, nil - } - i := int(idx) ethMsgs := e.backend.GetEthereumMsgsFromTendermintBlock(block, blockRes) if i >= len(ethMsgs) { @@ -796,9 +796,10 @@ func (e *PublicAPI) getTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, msg = ethMsgs[i] } - baseFee, err := e.backend.BaseFee(block.Block.Height) + baseFee, err := e.backend.BaseFee(blockRes) if err != nil { - return nil, err + // handle the error for pruned node. + e.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", block.Block.Height, "error", err) } return rpctypes.NewTransactionFromMsg( @@ -903,7 +904,7 @@ func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interfac } cumulativeGasUsed := uint64(0) - blockRes, err := e.clientCtx.Client.BlockResults(e.ctx, &res.Height) + blockRes, err := e.backend.GetTendermintBlockResultByNumber(&res.Height) if err != nil { e.logger.Debug("failed to retrieve block results", "height", res.Height, "error", err.Error()) return nil, nil @@ -982,11 +983,13 @@ func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interfac } if dynamicTx, ok := txData.(*evmtypes.DynamicFeeTx); ok { - baseFee, err := e.backend.BaseFee(res.Height) + baseFee, err := e.backend.BaseFee(blockRes) if err != nil { - return nil, err + // tolerate the error for pruned node. + e.logger.Error("fetch basefee failed, node is pruned?", "height", res.Height, "error", err) + } else { + receipt["effectiveGasPrice"] = hexutil.Big(*dynamicTx.GetEffectiveGasPrice(baseFee)) } - receipt["effectiveGasPrice"] = hexutil.Big(*dynamicTx.GetEffectiveGasPrice(baseFee)) } return receipt, nil diff --git a/rpc/namespaces/ethereum/eth/filters/api.go b/rpc/namespaces/ethereum/eth/filters/api.go index 00a13e96..10409fd8 100644 --- a/rpc/namespaces/ethereum/eth/filters/api.go +++ b/rpc/namespaces/ethereum/eth/filters/api.go @@ -28,9 +28,11 @@ type Backend interface { GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (map[string]interface{}, error) HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Header, error) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) + GetTendermintBlockByHash(hash common.Hash) (*coretypes.ResultBlock, error) + GetTendermintBlockResultByNumber(height *int64) (*coretypes.ResultBlockResults, error) GetLogs(blockHash common.Hash) ([][]*ethtypes.Log, error) GetLogsByHeight(*int64) ([][]*ethtypes.Log, error) - BlockBloom(height *int64) (ethtypes.Bloom, error) + BlockBloom(blockRes *coretypes.ResultBlockResults) (ethtypes.Bloom, error) BloomStatus() (uint64, uint64) diff --git a/rpc/namespaces/ethereum/eth/filters/filters.go b/rpc/namespaces/ethereum/eth/filters/filters.go index 531dff4d..63a6d82b 100644 --- a/rpc/namespaces/ethereum/eth/filters/filters.go +++ b/rpc/namespaces/ethereum/eth/filters/filters.go @@ -3,12 +3,15 @@ package filters import ( "context" "encoding/binary" + "fmt" "math/big" + "github.com/evmos/ethermint/rpc/backend" "github.com/evmos/ethermint/rpc/types" "github.com/pkg/errors" "github.com/tendermint/tendermint/libs/log" + tmrpctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -89,28 +92,35 @@ const ( // Logs searches the blockchain for matching log entries, returning all from the // first block that contains matches, updating the start of the filter accordingly. -func (f *Filter) Logs(_ context.Context, logLimit int, blockLimit int64) ([]*ethtypes.Log, error) { +func (f *Filter) Logs(ctx context.Context, logLimit int, blockLimit int64) ([]*ethtypes.Log, error) { logs := []*ethtypes.Log{} var err error // If we're doing singleton block filtering, execute and return if f.criteria.BlockHash != nil && *f.criteria.BlockHash != (common.Hash{}) { - header, err := f.backend.HeaderByHash(*f.criteria.BlockHash) + resBlock, err := f.backend.GetTendermintBlockByHash(*f.criteria.BlockHash) if err != nil { - return nil, errors.Wrap(err, "failed to fetch header by hash") + return nil, fmt.Errorf("failed to fetch header by hash %s: %w", f.criteria.BlockHash, err) } - if header == nil { - return nil, errors.Errorf("unknown block header %s", f.criteria.BlockHash.String()) + blockRes, err := f.backend.GetTendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + f.logger.Debug("failed to fetch block result from Tendermint", "height", resBlock.Block.Height, "error", err.Error()) + return nil, nil } - return f.blockLogs(header.Number.Int64(), header.Bloom) + bloom, err := f.backend.BlockBloom(blockRes) + if err != nil { + return nil, err + } + + return f.blockLogs(blockRes, bloom) } // Figure out the limits of the filter range header, err := f.backend.HeaderByNumber(types.EthLatestBlockNumber) if err != nil { - return nil, errors.Wrap(err, "failed to fetch header by number (latest)") + return nil, fmt.Errorf("failed to fetch header by number (latest): %w", err) } if header == nil || header.Number == nil { @@ -131,7 +141,7 @@ func (f *Filter) Logs(_ context.Context, logLimit int, blockLimit int64) ([]*eth } if f.criteria.ToBlock.Int64()-f.criteria.FromBlock.Int64() > blockLimit { - return nil, errors.Errorf("maximum [from, to] blocks distance: %d", blockLimit) + return nil, fmt.Errorf("maximum [from, to] blocks distance: %d", blockLimit) } // check bounds @@ -145,19 +155,25 @@ func (f *Filter) Logs(_ context.Context, logLimit int, blockLimit int64) ([]*eth to := f.criteria.ToBlock.Int64() for height := from; height <= to; height++ { - bloom, err := f.backend.BlockBloom(&height) + blockRes, err := f.backend.GetTendermintBlockResultByNumber(&height) + if err != nil { + f.logger.Debug("failed to fetch block result from Tendermint", "height", height, "error", err.Error()) + return nil, nil + } + + bloom, err := f.backend.BlockBloom(blockRes) if err != nil { return nil, err } - filtered, err := f.blockLogs(height, bloom) + filtered, err := f.blockLogs(blockRes, bloom) if err != nil { return nil, errors.Wrapf(err, "failed to fetch block by number %d", height) } // check logs limit if len(logs)+len(filtered) > logLimit { - return nil, errors.Errorf("query returned more than %d results", logLimit) + return nil, fmt.Errorf("query returned more than %d results", logLimit) } logs = append(logs, filtered...) } @@ -165,16 +181,14 @@ func (f *Filter) Logs(_ context.Context, logLimit int, blockLimit int64) ([]*eth } // blockLogs returns the logs matching the filter criteria within a single block. -func (f *Filter) blockLogs(height int64, bloom ethtypes.Bloom) ([]*ethtypes.Log, error) { +func (f *Filter) blockLogs(blockRes *tmrpctypes.ResultBlockResults, bloom ethtypes.Bloom) ([]*ethtypes.Log, error) { if !bloomFilter(bloom, f.criteria.Addresses, f.criteria.Topics) { return []*ethtypes.Log{}, nil } - // DANGER: do not call GetLogs(header.Hash()) - // eth header's hash doesn't match tm block hash - logsList, err := f.backend.GetLogsByHeight(&height) + logsList, err := backend.GetLogsFromBlockResults(blockRes) if err != nil { - return []*ethtypes.Log{}, errors.Wrapf(err, "failed to fetch logs block number %d", height) + return []*ethtypes.Log{}, errors.Wrapf(err, "failed to fetch logs block number %d", blockRes.Height) } unfiltered := make([]*ethtypes.Log, 0)