From a5e62bc7b2e643d4f51e6e95ef0065ff31643198 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Wed, 21 Jul 2021 13:50:41 -0700 Subject: [PATCH] [rpc] add limitation on GetBlocks and GetLogs. Also some other small changes (#3830) --- rpc/blockchain.go | 95 +++++++++++++++++++++++-------------------- rpc/common/types.go | 3 +- rpc/filters/api.go | 8 ++++ rpc/filters/filter.go | 5 +++ rpc/v2/types.go | 6 +-- 5 files changed, 69 insertions(+), 48 deletions(-) diff --git a/rpc/blockchain.go b/rpc/blockchain.go index 097d36e45..bc0d52096 100644 --- a/rpc/blockchain.go +++ b/rpc/blockchain.go @@ -42,7 +42,8 @@ type PublicBlockchainService struct { const ( DefaultRateLimiterWaitTimeout = 5 * time.Second - blockCacheLimit = 256 + blockCacheLimit = 2048 + rpcGetBlocksLimit = 1024 ) // NewPublicBlockchainAPI creates a new API for the RPC interface @@ -68,7 +69,6 @@ func NewPublicBlockchainAPI(hmy *hmy.Harmony, version Version, limiterEnable boo Public: true, } } - } // ChainId returns the chain id of the chain - required by MetaMask @@ -188,7 +188,6 @@ func (s *PublicBlockchainService) GetBlockByNumber( return nil, err } } - blockArgs.InclTx = true blockNum := blockNumber.EthBlockNumber() if blockNum != rpc.PendingBlockNumber { @@ -218,53 +217,52 @@ func (s *PublicBlockchainService) GetBlockByNumber( } blk, err := s.hmy.BlockByNumber(ctx, blockNum) - if blk != nil && err == nil { - if blockArgs.WithSigners { - blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNumber) - if err != nil { - DoMetricRPCQueryInfo(GetBlockByNumber, FailedNumber) - return nil, err - } - } - - // Format the response according to version - leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) - var rpcBlock interface{} - switch s.version { - case V1: - rpcBlock, err = v1.NewBlock(blk, blockArgs, leader) - case V2: - rpcBlock, err = v2.NewBlock(blk, blockArgs, leader) - case Eth: - rpcBlock, err = eth.NewBlock(blk, blockArgs, leader) - default: - return nil, ErrUnknownRPCVersion - } + if blk == nil || err != nil { + return nil, err + } + if blockArgs.WithSigners { + blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNumber) if err != nil { DoMetricRPCQueryInfo(GetBlockByNumber, FailedNumber) return nil, err } + } - response, err = NewStructuredResponse(rpcBlock) - if err != nil { - return nil, err - } + // Format the response according to version + leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) + var rpcBlock interface{} + switch s.version { + case V1: + rpcBlock, err = v1.NewBlock(blk, blockArgs, leader) + case V2: + rpcBlock, err = v2.NewBlock(blk, blockArgs, leader) + case Eth: + rpcBlock, err = eth.NewBlock(blk, blockArgs, leader) + default: + return nil, ErrUnknownRPCVersion + } + if err != nil { + DoMetricRPCQueryInfo(GetBlockByNumber, FailedNumber) + return nil, err + } - // Pending blocks need to nil out a few fields - if blockNum == rpc.PendingBlockNumber { - for _, field := range []string{"hash", "nonce", "miner"} { - response[field] = nil - } - } + response, err = NewStructuredResponse(rpcBlock) + if err != nil { + return nil, err + } - if blockNum != rpc.PendingBlockNumber { - cacheKey := combineCacheKey(blk.NumberU64(), s.version, blockArgs) - s.blockCache.Add(cacheKey, response) + // Pending blocks need to nil out a few fields + if blockNum == rpc.PendingBlockNumber { + for _, field := range []string{"hash", "nonce", "miner"} { + response[field] = nil } - return response, err } - DoMetricRPCQueryInfo(GetBlockByNumber, FailedNumber) - return nil, err + + if blockNum != rpc.PendingBlockNumber { + cacheKey := combineCacheKey(blk.NumberU64(), s.version, blockArgs) + s.blockCache.Add(cacheKey, response) + } + return response, err } // GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full @@ -292,7 +290,6 @@ func (s *PublicBlockchainService) GetBlockByHash( return nil, err } } - blockArgs.InclTx = true // Fetch the block blk, err := s.hmy.GetBlock(ctx, blockHash) @@ -366,10 +363,22 @@ func (s *PublicBlockchainService) GetBlocks( blockStart := blockNumberStart.Int64() blockEnd := blockNumberEnd.Int64() + if blockNumberEnd.EthBlockNumber() == rpc.LatestBlockNumber { + blockEnd = s.hmy.BlockChain.CurrentHeader().Number().Int64() + } + if blockEnd >= blockStart && blockEnd-blockStart > rpcGetBlocksLimit { + return nil, fmt.Errorf("GetBlocks query must be smaller than size %v", rpcGetBlocksLimit) + } // Fetch blocks within given range result := []StructuredResponse{} for i := blockStart; i <= blockEnd; i++ { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + blockNum := BlockNumber(i) if blockNum.Int64() > s.hmy.CurrentBlock().Number().Int64() { break @@ -916,5 +925,5 @@ func (s *PublicBlockchainService) SetNodeToBackupMode(ctx context.Context, isBac func combineCacheKey(number uint64, version Version, blockArgs *rpc_common.BlockArgs) string { // no need format blockArgs.Signers[] as a part of cache key // because it's not input from rpc caller, it's caculate with blockArgs.WithSigners - return strconv.FormatUint(number, 10) + strconv.FormatInt(int64(version), 10) + strconv.FormatBool(blockArgs.WithSigners) + strconv.FormatBool(blockArgs.InclTx) + strconv.FormatBool(blockArgs.FullTx) + strconv.FormatBool(blockArgs.InclStaking) + return strconv.FormatUint(number, 10) + strconv.FormatInt(int64(version), 10) + strconv.FormatBool(blockArgs.WithSigners) + strconv.FormatBool(blockArgs.FullTx) + strconv.FormatBool(blockArgs.InclStaking) } diff --git a/rpc/common/types.go b/rpc/common/types.go index fa2aea4ab..e987d6cea 100644 --- a/rpc/common/types.go +++ b/rpc/common/types.go @@ -13,9 +13,8 @@ import ( // BlockArgs is struct to include optional block formatting params. type BlockArgs struct { WithSigners bool `json:"withSigners"` - InclTx bool `json:"inclTx"` FullTx bool `json:"fullTx"` - Signers []string `json:"signers"` + Signers []string `json:"-"` InclStaking bool `json:"inclStaking"` } diff --git a/rpc/filters/api.go b/rpc/filters/api.go index 2e2a2b8a2..6051f64aa 100644 --- a/rpc/filters/api.go +++ b/rpc/filters/api.go @@ -18,6 +18,10 @@ var ( deadline = 5 * time.Minute // consider a filter inactive if it has not been polled for within deadline ) +const ( + rpcGetLogsLimit = 1024 +) + // filter is a helper struct that holds meta information over the filter type // and associated subscription in the event system. type filter struct { @@ -378,6 +382,10 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([ if crit.ToBlock != nil { end = crit.ToBlock.Int64() } + if end >= begin && end-begin > rpcGetLogsLimit { + return nil, fmt.Errorf("GetLogs query must be smaller than size %v", rpcGetLogsLimit) + } + // Construct the range filter filter = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics, api.isEth()) } diff --git a/rpc/filters/filter.go b/rpc/filters/filter.go index d6ad24245..70ea64405 100644 --- a/rpc/filters/filter.go +++ b/rpc/filters/filter.go @@ -220,6 +220,11 @@ func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, e var logs []*types.Log for ; f.begin <= int64(end); f.begin++ { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin)) if header == nil || err != nil { return logs, err diff --git a/rpc/v2/types.go b/rpc/v2/types.go index a9eb31633..aa882a293 100644 --- a/rpc/v2/types.go +++ b/rpc/v2/types.go @@ -704,7 +704,7 @@ func NewBlockWithFullTx( } for _, tx := range b.Transactions() { - fmtTx, err := NewTransactionFromBlockHash(b, tx.Hash()) + fmtTx, err := NewTransactionFromHash(b, tx.Hash()) if err != nil { return nil, err } @@ -727,8 +727,8 @@ func NewBlockWithFullTx( return blk, nil } -// NewTransactionFromBlockHash returns a transaction that will serialize to the RPC representation. -func NewTransactionFromBlockHash(b *types.Block, hash common.Hash) (*Transaction, error) { +// NewTransactionFromHash returns a transaction that will serialize to the RPC representation. +func NewTransactionFromHash(b *types.Block, hash common.Hash) (*Transaction, error) { for idx, tx := range b.Transactions() { if tx.Hash() == hash { return NewTransactionFromBlockIndex(b, uint64(idx))