diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 4b26de05a9..cb8e5167c3 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -176,6 +176,7 @@ var ( utils.AllowUnprotectedTxs, utils.BatchRequestLimit, utils.BatchResponseMaxSize, + utils.MaxBlockRangeFlag, } metricsFlags = []cli.Flag{ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index db226c73d8..b2132d50eb 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -944,6 +944,13 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server. Value: metrics.DefaultConfig.InfluxDBOrganization, Category: flags.MetricsCategory, } + + + // Max block range for `eth_getLogs` method + MaxBlockRangeFlag = &cli.Int64Flag{ + Name: "rpc.getlogs.maxrange", + Usage: "Limit max fetched block range for `eth_getLogs` method", + } ) var ( @@ -1605,6 +1612,14 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) { } } +func setMaxBlockRange(ctx *cli.Context, cfg *ethconfig.Config) { + if ctx.IsSet(MaxBlockRangeFlag.Name) { + cfg.MaxBlockRange = ctx.Int64(MaxBlockRangeFlag.Name) + } else { + cfg.MaxBlockRange = -1 + } +} + // CheckExclusive verifies that only a single instance of the provided flags was // set by the user. Each flag might optionally be followed by a string type to // specialize it further. @@ -1660,6 +1675,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { setMiner(ctx, &cfg.Miner) setRequiredBlocks(ctx, cfg) setLes(ctx, cfg) + setMaxBlockRange(ctx, cfg) // Cap the cache allowance and tune the garbage collector mem, err := gopsutil.VirtualMemory() @@ -1956,7 +1972,7 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf }) stack.RegisterAPIs([]rpc.API{{ Namespace: "eth", - Service: filters.NewFilterAPI(filterSystem, isLightClient), + Service: filters.NewFilterAPI(filterSystem, isLightClient, ethcfg.MaxBlockRange), }}) return filterSystem } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index bfb1df3fb1..76aa27ccb3 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -76,7 +76,8 @@ var Defaults = Config{ RPCGasCap: 50000000, RPCEVMTimeout: 5 * time.Second, GPO: FullNodeGPO, - RPCTxFeeCap: 1, // 1 ether + RPCTxFeeCap: 1, // 1 ether + MaxBlockRange: -1, // Default unconfigured value: no block range limit for backward compatibility } //go:generate go run github.com/fjl/gencodec -type Config -formats toml -out gen_config.go @@ -168,6 +169,9 @@ type Config struct { // OverrideVerkle (TODO: remove after the fork) OverrideVerkle *uint64 `toml:",omitempty"` + + // Max block range for eth_getLogs api method + MaxBlockRange int64 } // CreateConsensusEngine creates a consensus engine for the given chain config. diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 2abddc9e0d..d8f4694927 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -56,6 +56,7 @@ func (c Config) MarshalTOML() (interface{}, error) { RPCTxFeeCap float64 OverrideCancun *uint64 `toml:",omitempty"` OverrideVerkle *uint64 `toml:",omitempty"` + MaxBlockRange int64 } var enc Config enc.Genesis = c.Genesis @@ -97,6 +98,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.RPCTxFeeCap = c.RPCTxFeeCap enc.OverrideCancun = c.OverrideCancun enc.OverrideVerkle = c.OverrideVerkle + enc.MaxBlockRange = c.MaxBlockRange return &enc, nil } @@ -142,6 +144,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { RPCTxFeeCap *float64 OverrideCancun *uint64 `toml:",omitempty"` OverrideVerkle *uint64 `toml:",omitempty"` + MaxBlockRange *int64 } var dec Config if err := unmarshal(&dec); err != nil { @@ -264,5 +267,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.OverrideVerkle != nil { c.OverrideVerkle = dec.OverrideVerkle } + if dec.MaxBlockRange != nil { + c.MaxBlockRange = *dec.MaxBlockRange + } return nil } diff --git a/eth/filters/api.go b/eth/filters/api.go index cc08b442e8..0e42cd4d8c 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -54,20 +54,22 @@ type filter struct { // FilterAPI offers support to create and manage filters. This will allow external clients to retrieve various // information related to the Ethereum protocol such as blocks, transactions and logs. type FilterAPI struct { - sys *FilterSystem - events *EventSystem - filtersMu sync.Mutex - filters map[rpc.ID]*filter - timeout time.Duration + sys *FilterSystem + events *EventSystem + filtersMu sync.Mutex + filters map[rpc.ID]*filter + timeout time.Duration + maxBlockRange int64 } // NewFilterAPI returns a new FilterAPI instance. -func NewFilterAPI(system *FilterSystem, lightMode bool) *FilterAPI { +func NewFilterAPI(system *FilterSystem, lightMode bool, maxBlockRange int64) *FilterAPI { api := &FilterAPI{ - sys: system, - events: NewEventSystem(system, lightMode), - filters: make(map[rpc.ID]*filter), - timeout: system.cfg.Timeout, + sys: system, + events: NewEventSystem(system, lightMode), + filters: make(map[rpc.ID]*filter), + timeout: system.cfg.Timeout, + maxBlockRange: maxBlockRange, } go api.timeoutLoop(system.cfg.Timeout) @@ -348,7 +350,7 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type end = crit.ToBlock.Int64() } // Construct the range filter - filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics) + filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics, api.maxBlockRange) } // Run the filter and return all the logs logs, err := filter.Logs(ctx) diff --git a/eth/filters/filter.go b/eth/filters/filter.go index a5750c1934..1970551865 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -19,6 +19,7 @@ package filters import ( "context" "errors" + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -38,11 +39,13 @@ type Filter struct { begin, end int64 // Range interval if filtering multiple blocks matcher *bloombits.Matcher + + maxBlockRange int64 } // NewRangeFilter creates a new filter which uses a bloom filter on blocks to // figure out whether a particular block is interesting or not. -func (sys *FilterSystem) NewRangeFilter(begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { +func (sys *FilterSystem) NewRangeFilter(begin, end int64, addresses []common.Address, topics [][]common.Hash, maxBlockRange ...int64) *Filter { // Flatten the address and topic filter clauses into a single bloombits filter // system. Since the bloombits are not positional, nil topics are permitted, // which get flattened into a nil byte slice. @@ -70,6 +73,12 @@ func (sys *FilterSystem) NewRangeFilter(begin, end int64, addresses []common.Add filter.begin = begin filter.end = end + if len(maxBlockRange) > 0 { + filter.maxBlockRange = maxBlockRange[0] + } else { + filter.maxBlockRange = -1 + } + return filter } @@ -157,6 +166,11 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { return nil, err } + // if maxBlockRange configured then check for it + if f.maxBlockRange != -1 && f.end-f.begin+1 > f.maxBlockRange { + return nil, fmt.Errorf("block range is larger than max block range, block range = %d, max block range = %d", f.end-f.begin+1, f.maxBlockRange) + } + logChan, errChan := f.rangeLogsAsync(ctx) var logs []*types.Log for {