mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-16 04:56:36 +00:00
eth/filters: add address limit to filters (#31876)
The address filter was never checked against a maximum limit, which can be somewhat abusive for API nodes. This PR adds a limit similar to topics ## Description (AI generated) This pull request introduces a new validation to enforce a maximum limit on the number of addresses allowed in filter criteria for Ethereum logs. It includes updates to the `FilterAPI` and `EventSystem` logic, as well as corresponding test cases to ensure the new constraint is properly enforced. ### Core functionality changes: * **Validation for maximum addresses in filter criteria**: - Added a new constant, `maxAddresses`, set to 100, to define the maximum allowable addresses in a filter. - Introduced a new error, `errExceedMaxAddresses`, to handle cases where the number of addresses exceeds the limit. - Updated the `GetLogs` method in `FilterAPI` to validate the number of addresses against `maxAddresses`. - Modified the `UnmarshalJSON` method to return an error if the number of addresses in the input JSON exceeds `maxAddresses`. - Added similar validation to the `SubscribeLogs` method in `EventSystem`. ### Test updates: * **New test cases for address limit validation**: - Added a test in `TestUnmarshalJSONNewFilterArgs` to verify that exceeding the maximum number of addresses triggers the `errExceedMaxAddresses` error. - Updated `TestInvalidLogFilterCreation` to include a test case for an invalid filter with more than `maxAddresses` addresses. - Updated `TestInvalidGetLogsRequest` to test for invalid log requests with excessive addresses. These changes ensure that the system enforces a reasonable limit on the number of addresses in filter criteria, improving robustness and preventing potential performance issues. --------- Co-authored-by: zsfelfoldi <zsfelfoldi@gmail.com>
This commit is contained in:
parent
c59c647ed7
commit
1f4ea4d162
4 changed files with 32 additions and 5 deletions
|
|
@ -40,13 +40,17 @@ var (
|
||||||
errInvalidBlockRange = errors.New("invalid block range params")
|
errInvalidBlockRange = errors.New("invalid block range params")
|
||||||
errPendingLogsUnsupported = errors.New("pending logs are not supported")
|
errPendingLogsUnsupported = errors.New("pending logs are not supported")
|
||||||
errExceedMaxTopics = errors.New("exceed max topics")
|
errExceedMaxTopics = errors.New("exceed max topics")
|
||||||
|
errExceedMaxAddresses = errors.New("exceed max addresses")
|
||||||
)
|
)
|
||||||
|
|
||||||
// The maximum number of topic criteria allowed, vm.LOG4 - vm.LOG0
|
const (
|
||||||
const maxTopics = 4
|
// The maximum number of addresses allowed in a filter criteria
|
||||||
|
maxAddresses = 1000
|
||||||
// The maximum number of allowed topics within a topic criteria
|
// The maximum number of topic criteria allowed, vm.LOG4 - vm.LOG0
|
||||||
const maxSubTopics = 1000
|
maxTopics = 4
|
||||||
|
// The maximum number of allowed topics within a topic criteria
|
||||||
|
maxSubTopics = 1000
|
||||||
|
)
|
||||||
|
|
||||||
// filter is a helper struct that holds meta information over the filter type
|
// filter is a helper struct that holds meta information over the filter type
|
||||||
// and associated subscription in the event system.
|
// and associated subscription in the event system.
|
||||||
|
|
@ -341,6 +345,9 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type
|
||||||
if len(crit.Topics) > maxTopics {
|
if len(crit.Topics) > maxTopics {
|
||||||
return nil, errExceedMaxTopics
|
return nil, errExceedMaxTopics
|
||||||
}
|
}
|
||||||
|
if len(crit.Addresses) > maxAddresses {
|
||||||
|
return nil, errExceedMaxAddresses
|
||||||
|
}
|
||||||
var filter *Filter
|
var filter *Filter
|
||||||
if crit.BlockHash != nil {
|
if crit.BlockHash != nil {
|
||||||
// Block filter requested, construct a single-shot filter
|
// Block filter requested, construct a single-shot filter
|
||||||
|
|
@ -530,6 +537,9 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||||
// raw.Address can contain a single address or an array of addresses
|
// raw.Address can contain a single address or an array of addresses
|
||||||
switch rawAddr := raw.Addresses.(type) {
|
switch rawAddr := raw.Addresses.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
|
if len(rawAddr) > maxAddresses {
|
||||||
|
return errExceedMaxAddresses
|
||||||
|
}
|
||||||
for i, addr := range rawAddr {
|
for i, addr := range rawAddr {
|
||||||
if strAddr, ok := addr.(string); ok {
|
if strAddr, ok := addr.(string); ok {
|
||||||
addr, err := decodeAddress(strAddr)
|
addr, err := decodeAddress(strAddr)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package filters
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -182,4 +183,15 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) {
|
||||||
if len(test7.Topics[2]) != 0 {
|
if len(test7.Topics[2]) != 0 {
|
||||||
t.Fatalf("expected 0 topics, got %d topics", len(test7.Topics[2]))
|
t.Fatalf("expected 0 topics, got %d topics", len(test7.Topics[2]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// multiple address exceeding max
|
||||||
|
var test8 FilterCriteria
|
||||||
|
addresses := make([]string, maxAddresses+1)
|
||||||
|
for i := 0; i < maxAddresses+1; i++ {
|
||||||
|
addresses[i] = fmt.Sprintf(`"%s"`, common.HexToAddress(fmt.Sprintf("0x%x", i)).Hex())
|
||||||
|
}
|
||||||
|
vector = fmt.Sprintf(`{"address": [%s]}`, strings.Join(addresses, ", "))
|
||||||
|
if err := json.Unmarshal([]byte(vector), &test8); err != errExceedMaxAddresses {
|
||||||
|
t.Fatal("expected errExceedMaxAddresses, got", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -291,6 +291,9 @@ func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*typ
|
||||||
if len(crit.Topics) > maxTopics {
|
if len(crit.Topics) > maxTopics {
|
||||||
return nil, errExceedMaxTopics
|
return nil, errExceedMaxTopics
|
||||||
}
|
}
|
||||||
|
if len(crit.Addresses) > maxAddresses {
|
||||||
|
return nil, errExceedMaxAddresses
|
||||||
|
}
|
||||||
var from, to rpc.BlockNumber
|
var from, to rpc.BlockNumber
|
||||||
if crit.FromBlock == nil {
|
if crit.FromBlock == nil {
|
||||||
from = rpc.LatestBlockNumber
|
from = rpc.LatestBlockNumber
|
||||||
|
|
|
||||||
|
|
@ -435,6 +435,7 @@ func TestInvalidLogFilterCreation(t *testing.T) {
|
||||||
1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)},
|
1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)},
|
||||||
2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)},
|
2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)},
|
||||||
3: {Topics: [][]common.Hash{{}, {}, {}, {}, {}}},
|
3: {Topics: [][]common.Hash{{}, {}, {}, {}, {}}},
|
||||||
|
4: {Addresses: make([]common.Address, maxAddresses+1)},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range testCases {
|
for i, test := range testCases {
|
||||||
|
|
@ -461,6 +462,7 @@ func TestInvalidGetLogsRequest(t *testing.T) {
|
||||||
1: {BlockHash: &blockHash, ToBlock: big.NewInt(500)},
|
1: {BlockHash: &blockHash, ToBlock: big.NewInt(500)},
|
||||||
2: {BlockHash: &blockHash, FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64())},
|
2: {BlockHash: &blockHash, FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64())},
|
||||||
3: {BlockHash: &blockHash, Topics: [][]common.Hash{{}, {}, {}, {}, {}}},
|
3: {BlockHash: &blockHash, Topics: [][]common.Hash{{}, {}, {}, {}, {}}},
|
||||||
|
4: {BlockHash: &blockHash, Addresses: make([]common.Address, maxAddresses+1)},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range testCases {
|
for i, test := range testCases {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue