From e38b9f183065b374110918660114e93006f102dc Mon Sep 17 00:00:00 2001 From: Delweng Date: Fri, 10 Nov 2023 15:10:03 +0800 Subject: [PATCH] eth/filters: exit early if topics-filter has more than 4 topics (#28494) Currently, geth's will return `[]` for any `len(topics) > 4` log filter. The EVM only supports up to four logs, via LOG4 opcode, so larger criterias fail. This change makes the filter query exit early in those cases. --- eth/filters/api.go | 7 +++++++ eth/filters/filter_system.go | 3 +++ eth/filters/filter_system_test.go | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/eth/filters/api.go b/eth/filters/api.go index 22dff6c59e..a4eaa9cec8 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -37,8 +37,12 @@ var ( errInvalidTopic = errors.New("invalid topic(s)") errFilterNotFound = errors.New("filter not found") errInvalidBlockRange = errors.New("invalid block range params") + errExceedMaxTopics = errors.New("exceed max topics") ) +// The maximum number of topic criteria allowed, vm.LOG4 - vm.LOG0 +const maxTopics = 4 + // filter is a helper struct that holds meta information over the filter type // and associated subscription in the event system. type filter struct { @@ -334,6 +338,9 @@ func (api *FilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) { // GetLogs returns logs matching the given argument that are stored within the state. func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) { + if len(crit.Topics) > maxTopics { + return nil, errExceedMaxTopics + } var filter *Filter if crit.BlockHash != nil { // Block filter requested, construct a single-shot filter diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index a9b5f2e079..f98a1f84ce 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -299,6 +299,9 @@ func (es *EventSystem) subscribe(sub *subscription) *Subscription { // given criteria to the given logs channel. Default value for the from and to // block is "latest". If the fromBlock > toBlock an error is returned. func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*types.Log) (*Subscription, error) { + if len(crit.Topics) > maxTopics { + return nil, errExceedMaxTopics + } var from, to rpc.BlockNumber if crit.FromBlock == nil { from = rpc.LatestBlockNumber diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 93cbf01830..27cad8826a 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -386,6 +386,8 @@ func TestLogFilterCreation(t *testing.T) { {FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false}, // from block "higher" than to block {FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, false}, + // topics more then 4 + {FilterCriteria{Topics: [][]common.Hash{{}, {}, {}, {}, {}}}, false}, } ) @@ -420,6 +422,7 @@ func TestInvalidLogFilterCreation(t *testing.T) { 0: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, 1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, 2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, + 3: {Topics: [][]common.Hash{{}, {}, {}, {}, {}}}, } for i, test := range testCases { @@ -445,6 +448,7 @@ func TestInvalidGetLogsRequest(t *testing.T) { 0: {BlockHash: &blockHash, FromBlock: big.NewInt(100)}, 1: {BlockHash: &blockHash, ToBlock: big.NewInt(500)}, 2: {BlockHash: &blockHash, FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, + 3: {BlockHash: &blockHash, Topics: [][]common.Hash{{}, {}, {}, {}, {}}}, } for i, test := range testCases {