From ece5dfd5bbb482b5f10b43c5aeb8a0c20bed3178 Mon Sep 17 00:00:00 2001 From: HuangYi Date: Tue, 14 Jun 2022 16:21:16 +0800 Subject: [PATCH 1/5] fix json-rpc failures for pruned nodes Closes: #1123 Solution: - try to parse base fee from events if grpc query failed - use a `nil` base fee if failed to parse base fee from events - use zero address if query validator address failed - optimize some json-rpc apis by the way. --- rpc/backend/backend.go | 3 +- rpc/backend/evm_backend.go | 151 +++++++++++------- rpc/backend/utils.go | 16 +- rpc/namespaces/ethereum/eth/api.go | 23 +-- rpc/namespaces/ethereum/eth/filters/api.go | 5 +- .../ethereum/eth/filters/filters.go | 35 ++-- 6 files changed, 149 insertions(+), 84 deletions(-) diff --git a/rpc/backend/backend.go b/rpc/backend/backend.go index 98fc55d96c..5b6e35307a 100644 --- a/rpc/backend/backend.go +++ b/rpc/backend/backend.go @@ -51,6 +51,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) @@ -67,7 +68,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 d5d8a84a86..d53eac6493 100644 --- a/rpc/backend/evm_backend.go +++ b/rpc/backend/evm_backend.go @@ -70,7 +70,12 @@ func (b *Backend) GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (map return nil, nil } - res, err := b.EthBlockFromTendermint(resBlock, fullTx) + blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &resBlock.Block.Height) + if err != nil { + 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 @@ -90,7 +95,12 @@ func (b *Backend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]inte return nil, nil } - return b.EthBlockFromTendermint(resBlock, fullTx) + blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &resBlock.Block.Height) + if err != nil { + return nil, nil + } + + return b.EthBlockFromTendermint(resBlock, blockRes, fullTx) } // BlockByNumber returns the block identified by number. @@ -104,7 +114,12 @@ func (b *Backend) BlockByNumber(blockNum types.BlockNumber) (*ethtypes.Block, er return nil, errors.Errorf("block not found for height %d", blockNum) } - return b.EthBlockFromTm(resBlock) + blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &resBlock.Block.Height) + if err != nil { + return nil, errors.Errorf("block result not found for height %s", resBlock.Block.Height) + } + + return b.EthBlockFromTm(resBlock, blockRes) } // BlockByHash returns the block identified by hash. @@ -118,21 +133,26 @@ func (b *Backend) BlockByHash(hash common.Hash) (*ethtypes.Block, error) { return nil, errors.Errorf("block not found for hash %s", hash) } - return b.EthBlockFromTm(resBlock) + blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &resBlock.Block.Height) + if err != nil { + return nil, errors.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 + // tolerate the error for pruned node. + b.logger.Error("fetch basefee failed, node is pruned?", "height", height, "error", err) } ethHeader := types.EthHeaderFromTendermint(block.Header, bloom, baseFee) @@ -179,6 +199,11 @@ func (b *Backend) GetTendermintBlockByNumber(blockNum types.BlockNumber) (*tmrpc return resBlock, nil } +// GetTendermintBlockResultByNumber returns a Tendermint format block 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 +221,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 +239,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 + // tolerate the error for pruned node. + b.logger.Error("fetch basefee failed, node is pruned?", "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 +274,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()) } @@ -267,6 +285,7 @@ func (b *Backend) EthBlockFromTendermint( ctx := types.ContextWithHeight(block.Height) res, err := b.queryClient.ValidatorAccount(ctx, req) + var addr sdk.AccAddress if err != nil { b.logger.Debug( "failed to query validator operator address", @@ -274,12 +293,12 @@ func (b *Backend) EthBlockFromTendermint( "cons-address", req.ConsAddress, "error", err.Error(), ) - return nil, err - } - - addr, err := sdk.AccAddressFromBech32(res.AccountAddress) - if err != nil { - return nil, err + addr, _ = sdk.AccAddressFromHex("0000000000000000000000000000000000000000") + } else { + addr, err = sdk.AccAddressFromBech32(res.AccountAddress) + if err != nil { + return nil, err + } } validatorAddr := common.BytesToAddress(addr) @@ -291,7 +310,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 +344,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.clientCtx.Client.BlockResults(b.ctx, &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("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 + // tolerate the error for pruned node. + b.logger.Error("fetch basefee failed, node is pruned?", "height", resBlock.Block.Height, "error", err) } ethHeader := types.EthHeaderFromTendermint(resBlock.Block.Header, bloom, baseFee) @@ -350,15 +374,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.clientCtx.Client.BlockResults(b.ctx, &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 + // tolerate the error for pruned node. + b.logger.Error("fetch basefee failed, node is pruned?", "height", resBlock.Block.Height, "error", err) } ethHeader := types.EthHeaderFromTendermint(resBlock.Block.Header, bloom, baseFee) @@ -393,17 +422,7 @@ func (b *Backend) GetLogsByHeight(height *int64) ([][]*ethtypes.Log, error) { 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 +539,13 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransactio return nil, err } + blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &block.Block.Height) + if err != nil { + 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 +558,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 + // tolerate the error for pruned node. + b.logger.Error("fetch basefee failed, node is pruned?", "height", block.Block.Height, "error", err) } return types.NewTransactionFromMsg( @@ -804,10 +825,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{}) - if err != nil { + 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 { + baseFee, err := strconv.ParseInt(string(evt.Attributes[0].Value), 10, 64) + if err == nil { + return big.NewInt(baseFee), nil + } + break + } + } return nil, err } diff --git a/rpc/backend/utils.go b/rpc/backend/utils.go index 019806f8af..473f2c80f2 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,17 @@ 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") } + +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 07add625ac..b229e1e83a 100644 --- a/rpc/namespaces/ethereum/eth/api.go +++ b/rpc/namespaces/ethereum/eth/api.go @@ -747,6 +747,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.clientCtx.Client.BlockResults(e.ctx, &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)) @@ -775,11 +780,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) { @@ -790,9 +790,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 + // tolerate the error for pruned node. + e.logger.Error("fetch basefee failed, node is pruned?", "height", block.Block.Height, "error", err) } return rpctypes.NewTransactionFromMsg( @@ -976,11 +977,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 745876f58c..fd4b9deebf 100644 --- a/rpc/namespaces/ethereum/eth/filters/api.go +++ b/rpc/namespaces/ethereum/eth/filters/api.go @@ -12,6 +12,7 @@ import ( "github.com/tendermint/tendermint/libs/log" coretypes "github.com/tendermint/tendermint/rpc/core/types" + tmrpctypes "github.com/tendermint/tendermint/rpc/core/types" rpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client" tmtypes "github.com/tendermint/tendermint/types" @@ -28,9 +29,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) (*tmrpctypes.ResultBlock, error) + GetTendermintBlockResultByNumber(height *int64) (*tmrpctypes.ResultBlockResults, error) GetLogs(blockHash common.Hash) ([][]*ethtypes.Log, error) GetLogsByHeight(*int64) ([][]*ethtypes.Log, error) - BlockBloom(height *int64) (ethtypes.Bloom, error) + BlockBloom(blockRes *tmrpctypes.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 80e5d36dc2..5283dd6a77 100644 --- a/rpc/namespaces/ethereum/eth/filters/filters.go +++ b/rpc/namespaces/ethereum/eth/filters/filters.go @@ -5,10 +5,12 @@ import ( "encoding/binary" "math/big" + "github.com/tharsis/ethermint/rpc/backend" "github.com/tharsis/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,22 +91,28 @@ 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") } - 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 { + return nil, nil + } + + bloom, err := f.backend.BlockBloom(blockRes) + if err != nil { + return nil, err } - return f.blockLogs(header.Number.Int64(), header.Bloom) + return f.blockLogs(blockRes, bloom) } // Figure out the limits of the filter range @@ -145,12 +153,17 @@ 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 { + 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) } @@ -165,16 +178,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) From cfdb2dd0ff460794500983e1c9c5330f72d837a4 Mon Sep 17 00:00:00 2001 From: HuangYi Date: Tue, 14 Jun 2022 17:06:43 +0800 Subject: [PATCH 2/5] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8d17fed12..17407dd7de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (evm) [tharsis#1124](https://github.com/tharsis/ethermint/pull/1124) Reject non-replay-protected tx in ante handler to prevent replay attack +### API Breaking + +- (json-rpc) [tharsis#1126](https://github.com/tharsis/ethermint/pull/1126) Make some json-rpc apis work for pruned nodes. + ### Bug Fixes * (evm) [tharsis#1118](https://github.com/tharsis/ethermint/pull/1118) Fix `Type()` `Account` method `EmptyCodeHash` comparison From c971b657b707e0382a8153e6b955dac860504e5a Mon Sep 17 00:00:00 2001 From: HuangYi Date: Tue, 14 Jun 2022 17:37:37 +0800 Subject: [PATCH 3/5] fix lint --- rpc/backend/evm_backend.go | 2 +- rpc/namespaces/ethereum/eth/filters/api.go | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/rpc/backend/evm_backend.go b/rpc/backend/evm_backend.go index d53eac6493..810151af3a 100644 --- a/rpc/backend/evm_backend.go +++ b/rpc/backend/evm_backend.go @@ -116,7 +116,7 @@ func (b *Backend) BlockByNumber(blockNum types.BlockNumber) (*ethtypes.Block, er blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &resBlock.Block.Height) if err != nil { - return nil, errors.Errorf("block result not found for height %s", resBlock.Block.Height) + return nil, errors.Errorf("block result not found for height %d", resBlock.Block.Height) } return b.EthBlockFromTm(resBlock, blockRes) diff --git a/rpc/namespaces/ethereum/eth/filters/api.go b/rpc/namespaces/ethereum/eth/filters/api.go index fd4b9deebf..5a22198db7 100644 --- a/rpc/namespaces/ethereum/eth/filters/api.go +++ b/rpc/namespaces/ethereum/eth/filters/api.go @@ -12,7 +12,6 @@ import ( "github.com/tendermint/tendermint/libs/log" coretypes "github.com/tendermint/tendermint/rpc/core/types" - tmrpctypes "github.com/tendermint/tendermint/rpc/core/types" rpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client" tmtypes "github.com/tendermint/tendermint/types" @@ -29,11 +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) (*tmrpctypes.ResultBlock, error) - GetTendermintBlockResultByNumber(height *int64) (*tmrpctypes.ResultBlockResults, 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(blockRes *tmrpctypes.ResultBlockResults) (ethtypes.Bloom, error) + BlockBloom(blockRes *coretypes.ResultBlockResults) (ethtypes.Bloom, error) BloomStatus() (uint64, uint64) From a81a93efdc8a47cd7a43e29a83f409b1c92614cf Mon Sep 17 00:00:00 2001 From: HuangYi Date: Tue, 14 Jun 2022 17:41:14 +0800 Subject: [PATCH 4/5] use GetTendermintBlockResultByNumber --- rpc/backend/evm_backend.go | 20 ++++++++++---------- rpc/namespaces/ethereum/eth/api.go | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/rpc/backend/evm_backend.go b/rpc/backend/evm_backend.go index 810151af3a..43555b8274 100644 --- a/rpc/backend/evm_backend.go +++ b/rpc/backend/evm_backend.go @@ -70,7 +70,7 @@ func (b *Backend) GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (map return nil, nil } - blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &resBlock.Block.Height) + blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) if err != nil { return nil, nil } @@ -95,7 +95,7 @@ func (b *Backend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]inte return nil, nil } - blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &resBlock.Block.Height) + blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) if err != nil { return nil, nil } @@ -114,7 +114,7 @@ func (b *Backend) BlockByNumber(blockNum types.BlockNumber) (*ethtypes.Block, er return nil, errors.Errorf("block not found for height %d", blockNum) } - blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &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) } @@ -133,7 +133,7 @@ func (b *Backend) BlockByHash(hash common.Hash) (*ethtypes.Block, error) { return nil, errors.Errorf("block not found for hash %s", hash) } - blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &resBlock.Block.Height) + blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) if err != nil { return nil, errors.Errorf("block result not found for hash %s", hash) } @@ -157,7 +157,7 @@ func (b *Backend) EthBlockFromTm(resBlock *tmrpctypes.ResultBlock, blockRes *tmr 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 } @@ -344,7 +344,7 @@ func (b *Backend) HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Header, return nil, errors.Errorf("block not found for height %d", blockNum) } - blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &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) } @@ -374,7 +374,7 @@ func (b *Backend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) return nil, errors.Errorf("block not found for hash %s", blockHash.Hex()) } - blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &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) } @@ -417,7 +417,7 @@ 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 } @@ -539,7 +539,7 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransactio return nil, err } - blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &block.Block.Height) + blockRes, err := b.GetTendermintBlockResultByNumber(&block.Block.Height) if err != nil { return nil, nil } @@ -917,7 +917,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/namespaces/ethereum/eth/api.go b/rpc/namespaces/ethereum/eth/api.go index b229e1e83a..eb05239dc1 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 } @@ -747,7 +747,7 @@ 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.clientCtx.Client.BlockResults(e.ctx, &block.Block.Height) + blockRes, err := e.backend.GetTendermintBlockResultByNumber(&block.Block.Height) if err != nil { return nil, nil } @@ -898,7 +898,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 From a9e5d3655a954ef0a2795687e8fb2fd05864bb2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Wed, 22 Jun 2022 13:23:14 +0200 Subject: [PATCH 5/5] refactor --- rpc/backend/evm_backend.go | 48 +++++++++++-------- rpc/backend/utils.go | 1 + rpc/namespaces/ethereum/eth/api.go | 4 +- .../ethereum/eth/filters/filters.go | 11 +++-- 4 files changed, 37 insertions(+), 27 deletions(-) diff --git a/rpc/backend/evm_backend.go b/rpc/backend/evm_backend.go index efbe318fac..a62f5a5d5e 100644 --- a/rpc/backend/evm_backend.go +++ b/rpc/backend/evm_backend.go @@ -72,6 +72,7 @@ func (b *Backend) GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (map 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 } @@ -90,6 +91,7 @@ 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 @@ -97,6 +99,7 @@ func (b *Backend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]inte 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 } @@ -111,12 +114,12 @@ 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) } blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) if err != nil { - return nil, errors.Errorf("block result not found for height %d", resBlock.Block.Height) + return nil, fmt.Errorf("block result not found for height %d", resBlock.Block.Height) } return b.EthBlockFromTm(resBlock, blockRes) @@ -130,12 +133,12 @@ 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) } blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) if err != nil { - return nil, errors.Errorf("block result not found for hash %s", hash) + return nil, fmt.Errorf("block result not found for hash %s", hash) } return b.EthBlockFromTm(resBlock, blockRes) @@ -151,8 +154,8 @@ func (b *Backend) EthBlockFromTm(resBlock *tmrpctypes.ResultBlock, blockRes *tmr baseFee, err := b.BaseFee(blockRes) if err != nil { - // tolerate the error for pruned node. - b.logger.Error("fetch basefee failed, node is pruned?", "height", height, "error", 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) @@ -199,7 +202,7 @@ func (b *Backend) GetTendermintBlockByNumber(blockNum types.BlockNumber) (*tmrpc return resBlock, nil } -// GetTendermintBlockResultByNumber returns a Tendermint format block by block number +// 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) } @@ -247,8 +250,8 @@ func (b *Backend) EthBlockFromTendermint( baseFee, err := b.BaseFee(blockRes) if err != nil { - // tolerate the error for pruned node. - b.logger.Error("fetch basefee failed, node is pruned?", "height", block.Height, "error", 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) } msgs := b.GetEthereumMsgsFromTendermintBlock(resBlock, blockRes) @@ -283,9 +286,10 @@ 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) - var addr sdk.AccAddress if err != nil { b.logger.Debug( "failed to query validator operator address", @@ -293,15 +297,16 @@ func (b *Backend) EthBlockFromTendermint( "cons-address", req.ConsAddress, "error", err.Error(), ) - addr, _ = sdk.AccAddressFromHex("0000000000000000000000000000000000000000") + // use zero address as the validator operator address + validatorAccAddr = sdk.AccAddress(common.Address{}.Bytes()) } else { - addr, err = sdk.AccAddressFromBech32(res.AccountAddress) + validatorAccAddr, 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 { @@ -346,7 +351,7 @@ func (b *Backend) HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Header, blockRes, err := b.GetTendermintBlockResultByNumber(&resBlock.Block.Height) if err != nil { - return nil, errors.Errorf("block result not found for height %d", resBlock.Block.Height) + return nil, fmt.Errorf("block result not found for height %d", resBlock.Block.Height) } bloom, err := b.BlockBloom(blockRes) @@ -356,8 +361,8 @@ func (b *Backend) HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Header, baseFee, err := b.BaseFee(blockRes) if err != nil { - // tolerate the error for pruned node. - b.logger.Error("fetch basefee failed, node is pruned?", "height", resBlock.Block.Height, "error", 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) @@ -386,8 +391,8 @@ func (b *Backend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) baseFee, err := b.BaseFee(blockRes) if err != nil { - // tolerate the error for pruned node. - b.logger.Error("fetch basefee failed, node is pruned?", "height", resBlock.Block.Height, "error", 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) @@ -541,6 +546,7 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransactio 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 } @@ -560,8 +566,8 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransactio baseFee, err := b.BaseFee(blockRes) if err != nil { - // tolerate the error for pruned node. - b.logger.Error("fetch basefee failed, node is pruned?", "height", block.Block.Height, "error", 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( @@ -841,7 +847,7 @@ func (b *Backend) BaseFee(blockRes *tmrpctypes.ResultBlockResults) (*big.Int, er // faster to iterate reversely for i := len(blockRes.BeginBlockEvents) - 1; i >= 0; i-- { evt := blockRes.BeginBlockEvents[i] - if evt.Type == feemarkettypes.EventTypeFeeMarket { + 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 diff --git a/rpc/backend/utils.go b/rpc/backend/utils.go index b1c56e4ec2..c9f878058c 100644 --- a/rpc/backend/utils.go +++ b/rpc/backend/utils.go @@ -391,6 +391,7 @@ 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 { diff --git a/rpc/namespaces/ethereum/eth/api.go b/rpc/namespaces/ethereum/eth/api.go index 456123e3f2..c2094d8111 100644 --- a/rpc/namespaces/ethereum/eth/api.go +++ b/rpc/namespaces/ethereum/eth/api.go @@ -798,8 +798,8 @@ func (e *PublicAPI) getTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, baseFee, err := e.backend.BaseFee(blockRes) if err != nil { - // tolerate the error for pruned node. - e.logger.Error("fetch basefee failed, node is pruned?", "height", block.Block.Height, "error", 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( diff --git a/rpc/namespaces/ethereum/eth/filters/filters.go b/rpc/namespaces/ethereum/eth/filters/filters.go index 36b6305dfd..63a6d82bfc 100644 --- a/rpc/namespaces/ethereum/eth/filters/filters.go +++ b/rpc/namespaces/ethereum/eth/filters/filters.go @@ -3,6 +3,7 @@ package filters import ( "context" "encoding/binary" + "fmt" "math/big" "github.com/evmos/ethermint/rpc/backend" @@ -99,11 +100,12 @@ func (f *Filter) Logs(ctx context.Context, logLimit int, blockLimit int64) ([]*e if f.criteria.BlockHash != nil && *f.criteria.BlockHash != (common.Hash{}) { 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) } 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 } @@ -118,7 +120,7 @@ func (f *Filter) Logs(ctx context.Context, logLimit int, blockLimit int64) ([]*e // 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 { @@ -139,7 +141,7 @@ func (f *Filter) Logs(ctx context.Context, logLimit int, blockLimit int64) ([]*e } 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 @@ -155,6 +157,7 @@ func (f *Filter) Logs(ctx context.Context, logLimit int, blockLimit int64) ([]*e for height := from; height <= to; 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 } @@ -170,7 +173,7 @@ func (f *Filter) Logs(ctx context.Context, logLimit int, blockLimit int64) ([]*e // 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...) }