diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 33443eec5f5a..5150a99f476a 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -190,6 +190,7 @@ var ( utils.RPCGlobalEVMTimeoutFlag, utils.RPCGlobalTxFeeCapFlag, utils.AllowUnprotectedTxs, + utils.MaxBlockRangeFlag, } metricsFlags = []cli.Flag{ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 3555667b4655..6e15a2b551da 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -831,6 +831,12 @@ var ( Name: "ccc", Usage: "Enable circuit capacity check during block validation", } + + // Max block range for `eth_getLogs` method + MaxBlockRangeFlag = cli.Int64Flag{ + Name: "rpc.getlogs.maxrange", + Usage: "Limit max fetched block range for `eth_getLogs` method", + } ) // MakeDataDir retrieves the currently requested data directory, terminating @@ -1529,6 +1535,12 @@ func setCircuitCapacityCheck(ctx *cli.Context, cfg *ethconfig.Config) { } } +func setMaxBlockRange(ctx *cli.Context, cfg *ethconfig.Config) { + if ctx.GlobalIsSet(MaxBlockRangeFlag.Name) { + cfg.MaxBlockRange = ctx.GlobalInt64(MaxBlockRangeFlag.Name) + } +} + // 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. @@ -1595,6 +1607,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { setWhitelist(ctx, cfg) setLes(ctx, cfg) setCircuitCapacityCheck(ctx, cfg) + setMaxBlockRange(ctx, cfg) // Cap the cache allowance and tune the garbage collector mem, err := gopsutil.VirtualMemory() diff --git a/eth/backend.go b/eth/backend.go index a1fb5cbaf535..e5d4c33f0ba8 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -333,7 +333,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.APIBackend, false, 5*time.Minute), + Service: filters.NewPublicFilterAPI(s.APIBackend, false, 5*time.Minute, s.config.MaxBlockRange), Public: true, }, { Namespace: "admin", diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index e9f95b67baed..d9ad5527edcf 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -92,6 +92,7 @@ var Defaults = Config{ RPCEVMTimeout: 5 * time.Second, GPO: FullNodeGPO, RPCTxFeeCap: 1, // 1 ether + MaxBlockRange: 5000, } func init() { @@ -210,6 +211,9 @@ type Config struct { // Check circuit capacity in block validator CheckCircuitCapacity bool + + // Max block range for eth_getLogs api method + MaxBlockRange int64 } // CreateConsensusEngine creates a consensus engine for the given chain configuration. diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index b07f9c4f5664..24b755eb687a 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -60,6 +60,9 @@ func (c Config) MarshalTOML() (interface{}, error) { Checkpoint *params.TrustedCheckpoint `toml:",omitempty"` CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` OverrideArrowGlacier *big.Int `toml:",omitempty"` + MPTWitness int + CheckCircuitCapacity bool + MaxBlockRange int64 } var enc Config enc.Genesis = c.Genesis @@ -104,6 +107,9 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.Checkpoint = c.Checkpoint enc.CheckpointOracle = c.CheckpointOracle enc.OverrideArrowGlacier = c.OverrideArrowGlacier + enc.MPTWitness = c.MPTWitness + enc.CheckCircuitCapacity = c.CheckCircuitCapacity + enc.MaxBlockRange = c.MaxBlockRange return &enc, nil } @@ -152,6 +158,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { Checkpoint *params.TrustedCheckpoint `toml:",omitempty"` CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` OverrideArrowGlacier *big.Int `toml:",omitempty"` + MPTWitness *int + CheckCircuitCapacity *bool + MaxBlockRange *int64 } var dec Config if err := unmarshal(&dec); err != nil { @@ -283,5 +292,14 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.OverrideArrowGlacier != nil { c.OverrideArrowGlacier = dec.OverrideArrowGlacier } + if dec.MPTWitness != nil { + c.MPTWitness = *dec.MPTWitness + } + if dec.CheckCircuitCapacity != nil { + c.CheckCircuitCapacity = *dec.CheckCircuitCapacity + } + if dec.MaxBlockRange != nil { + c.MaxBlockRange = *dec.MaxBlockRange + } return nil } diff --git a/eth/filters/api.go b/eth/filters/api.go index 66d62a86de64..28c2ced8c36f 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -48,24 +48,26 @@ type filter struct { // PublicFilterAPI offers support to create and manage filters. This will allow external clients to retrieve various // information related to the Ethereum protocol such als blocks, transactions and logs. type PublicFilterAPI struct { - backend Backend - mux *event.TypeMux - quit chan struct{} - chainDb ethdb.Database - events *EventSystem - filtersMu sync.Mutex - filters map[rpc.ID]*filter - timeout time.Duration + backend Backend + mux *event.TypeMux + quit chan struct{} + chainDb ethdb.Database + events *EventSystem + filtersMu sync.Mutex + filters map[rpc.ID]*filter + timeout time.Duration + maxBlockRange int64 } // NewPublicFilterAPI returns a new PublicFilterAPI instance. -func NewPublicFilterAPI(backend Backend, lightMode bool, timeout time.Duration) *PublicFilterAPI { +func NewPublicFilterAPI(backend Backend, lightMode bool, timeout time.Duration, maxBlockRange int64) *PublicFilterAPI { api := &PublicFilterAPI{ - backend: backend, - chainDb: backend.ChainDb(), - events: NewEventSystem(backend, lightMode), - filters: make(map[rpc.ID]*filter), - timeout: timeout, + backend: backend, + chainDb: backend.ChainDb(), + events: NewEventSystem(backend, lightMode), + filters: make(map[rpc.ID]*filter), + timeout: timeout, + maxBlockRange: maxBlockRange, } go api.timeoutLoop(timeout) @@ -345,6 +347,20 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([ if crit.ToBlock != nil { end = crit.ToBlock.Int64() } + + beginBlock, err := api.backend.HeaderByNumber(ctx, rpc.BlockNumber(begin)) + if err != nil { + return nil, fmt.Errorf("couldn't find fromBlock, fromBlock: %d", begin) + } + endBlock, err := api.backend.HeaderByNumber(ctx, rpc.BlockNumber(end)) + if err != nil { + return nil, fmt.Errorf("couldn't find toBlock, toBlock: %d", end) + } + realBegin := beginBlock.Number.Int64() + realEnd := endBlock.Number.Int64() + if realEnd-realBegin+1 > api.maxBlockRange { + return nil, fmt.Errorf("block range is bigger than maxBlockRange, block range: %d, maxBlockRange: %d", realEnd-realBegin+1, api.maxBlockRange) + } // Construct the range filter filter = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics) } diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 8e1a4bcaeb4b..f59205e3cee4 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -33,6 +33,8 @@ import ( "github.com/scroll-tech/go-ethereum/core/bloombits" "github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/core/types" + "github.com/scroll-tech/go-ethereum/core/vm" + "github.com/scroll-tech/go-ethereum/eth/ethconfig" "github.com/scroll-tech/go-ethereum/ethdb" "github.com/scroll-tech/go-ethereum/event" "github.com/scroll-tech/go-ethereum/params" @@ -168,7 +170,7 @@ func TestBlockSubscription(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false, deadline) + api = NewPublicFilterAPI(backend, false, deadline, ethconfig.Defaults.MaxBlockRange) genesis = (&core.Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {}) chainEvents = []core.ChainEvent{} @@ -220,7 +222,7 @@ func TestPendingTxFilter(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false, deadline) + api = NewPublicFilterAPI(backend, false, deadline, ethconfig.Defaults.MaxBlockRange) transactions = []*types.Transaction{ types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), @@ -275,7 +277,7 @@ func TestLogFilterCreation(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false, deadline) + api = NewPublicFilterAPI(backend, false, deadline, ethconfig.Defaults.MaxBlockRange) testCases = []struct { crit FilterCriteria @@ -319,7 +321,7 @@ func TestInvalidLogFilterCreation(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false, deadline) + api = NewPublicFilterAPI(backend, false, deadline, ethconfig.Defaults.MaxBlockRange) ) // different situations where log filter creation should fail. @@ -341,7 +343,7 @@ func TestInvalidGetLogsRequest(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false, deadline) + api = NewPublicFilterAPI(backend, false, deadline, ethconfig.Defaults.MaxBlockRange) blockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") ) @@ -359,6 +361,50 @@ func TestInvalidGetLogsRequest(t *testing.T) { } } +func TestGetLogsRange(t *testing.T) { + var ( + db = rawdb.NewMemoryDatabase() + backend = &testBackend{db: db} + api = NewPublicFilterAPI(backend, false, deadline, 2) + ) + (&core.Genesis{ + Config: params.TestChainConfig, + }).MustCommit(db) + chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, false) + bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, 10, nil) + if _, err := chain.InsertChain(bs); err != nil { + panic(err) + } + // those test cases should fail because block range is greater then limit + failTestCases := []FilterCriteria{ + // from 0 to 2 block + 0: {FromBlock: big.NewInt(0), ToBlock: big.NewInt(2)}, + // from 8 to latest block (10) + 1: {FromBlock: big.NewInt(8)}, + // from 0 to latest block (10) + 2: {FromBlock: big.NewInt(0)}, + } + for i, test := range failTestCases { + if _, err := api.GetLogs(context.Background(), test); err == nil { + t.Errorf("Expected Logs for failing case #%d to fail", i) + } + } + + okTestCases := []FilterCriteria{ + // from latest to latest block + 0: {}, + // from 9 to last block (10) + 1: {FromBlock: big.NewInt(9)}, + // from 3 to 4 block + 2: {FromBlock: big.NewInt(3), ToBlock: big.NewInt(4)}, + } + for i, test := range okTestCases { + if _, err := api.GetLogs(context.Background(), test); err != nil { + t.Errorf("Expected Logs for ok case #%d not to fail", i) + } + } +} + // TestLogFilter tests whether log filters match the correct logs that are posted to the event feed. func TestLogFilter(t *testing.T) { t.Parallel() @@ -366,7 +412,7 @@ func TestLogFilter(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false, deadline) + api = NewPublicFilterAPI(backend, false, deadline, ethconfig.Defaults.MaxBlockRange) firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") @@ -480,7 +526,7 @@ func TestPendingLogsSubscription(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false, deadline) + api = NewPublicFilterAPI(backend, false, deadline, ethconfig.Defaults.MaxBlockRange) firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") @@ -664,7 +710,7 @@ func TestPendingTxFilterDeadlock(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false, timeout) + api = NewPublicFilterAPI(backend, false, timeout, ethconfig.Defaults.MaxBlockRange) done = make(chan struct{}) ) diff --git a/les/client.go b/les/client.go index d9e20d765855..311f78a658a5 100644 --- a/les/client.go +++ b/les/client.go @@ -301,7 +301,7 @@ func (s *LightEthereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend, true, 5*time.Minute), + Service: filters.NewPublicFilterAPI(s.ApiBackend, true, 5*time.Minute, s.config.MaxBlockRange), Public: true, }, { Namespace: "net", diff --git a/params/version.go b/params/version.go index cc6547c08514..cbf2097013c1 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 4 // Major version component of the current release VersionMinor = 4 // Minor version component of the current release - VersionPatch = 15 // Patch version component of the current release + VersionPatch = 16 // Patch version component of the current release VersionMeta = "sepolia" // Version metadata to append to the version string )