diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 2dbc593569..6734684675 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -504,7 +504,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter to = query.ToBlock.Int64() } // Construct the range filter - filter = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics) + filter = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics, false) } // Run the filter and return all the logs logs, err := filter.Logs(ctx) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 7ad300a112..249dd65efa 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -69,6 +69,7 @@ var ( utils.ExternalSignerFlag, utils.NoUSBFlag, utils.DirectBroadcastFlag, + utils.RangeLimitFlag, utils.SmartCardDaemonPathFlag, utils.OverrideIstanbulFlag, utils.OverrideMuirGlacierFlag, diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index e91be898c8..16e7537862 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -71,6 +71,7 @@ var AppHelpFlagGroups = []flagGroup{ utils.KeyStoreDirFlag, utils.NoUSBFlag, utils.DirectBroadcastFlag, + utils.RangeLimitFlag, utils.SmartCardDaemonPathFlag, utils.NetworkIdFlag, utils.GoerliFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c1b1ae94af..74d10e6f27 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -147,6 +147,10 @@ var ( Name: "directbroadcast", Usage: "Enable directly broadcast mined block to all peers", } + RangeLimitFlag = cli.BoolFlag{ + Name: "rangelimit", + Usage: "Enable 5000 blocks limit for range query", + } AncientFlag = DirectoryFlag{ Name: "datadir.ancient", Usage: "Data directory for ancient chain segments (default = inside chaindata)", @@ -1250,6 +1254,9 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(DirectBroadcastFlag.Name) { cfg.DirectBroadcast = ctx.GlobalBool(DirectBroadcastFlag.Name) } + if ctx.GlobalIsSet(RangeLimitFlag.Name) { + cfg.RangeLimit = ctx.GlobalBool(RangeLimitFlag.Name) + } if ctx.GlobalIsSet(InsecureUnlockAllowedFlag.Name) { cfg.InsecureUnlockAllowed = ctx.GlobalBool(InsecureUnlockAllowedFlag.Name) } @@ -1529,6 +1536,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { if ctx.GlobalIsSet(DirectBroadcastFlag.Name) { cfg.DirectBroadcast = ctx.GlobalBool(DirectBroadcastFlag.Name) } + if ctx.GlobalIsSet(RangeLimitFlag.Name) { + cfg.RangeLimit = ctx.GlobalBool(RangeLimitFlag.Name) + } if ctx.GlobalIsSet(CacheNoPrefetchFlag.Name) { cfg.NoPrefetch = ctx.GlobalBool(CacheNoPrefetchFlag.Name) } diff --git a/eth/backend.go b/eth/backend.go index 4bfc0d097c..8293cd3f12 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -328,7 +328,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.APIBackend, false), + Service: filters.NewPublicFilterAPI(s.APIBackend, false, s.config.RangeLimit), Public: true, }, { Namespace: "admin", diff --git a/eth/config.go b/eth/config.go index 6cf3e4db36..6c2a63225a 100644 --- a/eth/config.go +++ b/eth/config.go @@ -106,6 +106,7 @@ type Config struct { NoPruning bool // Whether to disable pruning and flush everything to disk NoPrefetch bool // Whether to disable prefetching and only load state on demand DirectBroadcast bool + RangeLimit bool // Whitelist of required block number -> hash values to accept Whitelist map[uint64]common.Hash `toml:"-"` diff --git a/eth/filters/api.go b/eth/filters/api.go index 83c1d3ba30..c299988dbd 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -52,22 +52,24 @@ 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 + backend Backend + mux *event.TypeMux + quit chan struct{} + chainDb ethdb.Database + events *EventSystem + filtersMu sync.Mutex + filters map[rpc.ID]*filter + rangeLimit bool } // NewPublicFilterAPI returns a new PublicFilterAPI instance. -func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { +func NewPublicFilterAPI(backend Backend, lightMode, rangeLimit bool) *PublicFilterAPI { api := &PublicFilterAPI{ - backend: backend, - chainDb: backend.ChainDb(), - events: NewEventSystem(backend, lightMode), - filters: make(map[rpc.ID]*filter), + backend: backend, + chainDb: backend.ChainDb(), + events: NewEventSystem(backend, lightMode), + filters: make(map[rpc.ID]*filter), + rangeLimit: rangeLimit, } go api.timeoutLoop() @@ -335,7 +337,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([ end = crit.ToBlock.Int64() } // Construct the range filter - filter = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics) + filter = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics, api.rangeLimit) } // Run the filter and return all the logs logs, err := filter.Logs(ctx) @@ -390,7 +392,7 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*ty end = f.crit.ToBlock.Int64() } // Construct the range filter - filter = NewRangeFilter(api.backend, begin, end, f.crit.Addresses, f.crit.Topics) + filter = NewRangeFilter(api.backend, begin, end, f.crit.Addresses, f.crit.Topics, api.rangeLimit) } // Run the filter and return all the logs logs, err := filter.Logs(ctx) diff --git a/eth/filters/bench_test.go b/eth/filters/bench_test.go index 4f70d6d04a..e55ff0ca13 100644 --- a/eth/filters/bench_test.go +++ b/eth/filters/bench_test.go @@ -132,7 +132,7 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) { var addr common.Address addr[0] = byte(i) addr[1] = byte(i / 256) - filter := NewRangeFilter(backend, 0, int64(cnt*sectionSize-1), []common.Address{addr}, nil) + filter := NewRangeFilter(backend, 0, int64(cnt*sectionSize-1), []common.Address{addr}, nil, false) if _, err := filter.Logs(context.Background()); err != nil { b.Error("filter.Find error:", err) } @@ -172,7 +172,7 @@ func BenchmarkNoBloomBits(b *testing.B) { b.Log("Running filter benchmarks...") start := time.Now() backend := &testBackend{db: db} - filter := NewRangeFilter(backend, 0, int64(*headNum), []common.Address{{}}, nil) + filter := NewRangeFilter(backend, 0, int64(*headNum), []common.Address{{}}, nil, false) filter.Logs(context.Background()) d := time.Since(start) b.Log("Finished running filter benchmarks") diff --git a/eth/filters/filter.go b/eth/filters/filter.go index 17635837af..1f281a4ee1 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" @@ -30,6 +31,8 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) +const maxFilterBlockRange = 5000 + type Backend interface { ChainDb() ethdb.Database HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) @@ -59,11 +62,13 @@ type Filter struct { begin, end int64 // Range interval if filtering multiple blocks matcher *bloombits.Matcher + + rangeLimit bool } // NewRangeFilter creates a new filter which uses a bloom filter on blocks to // figure out whether a particular block is interesting or not. -func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { +func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash, rangeLimit bool) *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. @@ -90,6 +95,7 @@ func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Addres filter.matcher = bloombits.NewMatcher(size, filters) filter.begin = begin filter.end = end + filter.rangeLimit = rangeLimit return filter } @@ -142,6 +148,9 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { if f.end == -1 { end = head } + if f.rangeLimit && (int64(end)-f.begin) > maxFilterBlockRange { + return nil, fmt.Errorf("exceed maximum block range: %d", maxFilterBlockRange) + } // Gather all indexed logs, and finish with non indexed ones var ( logs []*types.Log diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index c8d1d43abb..4aba9621cf 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -163,7 +163,7 @@ func TestBlockSubscription(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, false) genesis = new(core.Genesis).MustCommit(db) chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {}) chainEvents = []core.ChainEvent{} @@ -215,7 +215,7 @@ func TestPendingTxFilter(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, false) transactions = []*types.Transaction{ types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), @@ -270,7 +270,7 @@ func TestLogFilterCreation(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, false) testCases = []struct { crit FilterCriteria @@ -314,7 +314,7 @@ func TestInvalidLogFilterCreation(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, false) ) // different situations where log filter creation should fail. @@ -336,7 +336,7 @@ func TestInvalidGetLogsRequest(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, false) blockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") ) @@ -361,7 +361,7 @@ func TestLogFilter(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, false) firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") @@ -475,7 +475,7 @@ func TestPendingLogsSubscription(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, false) firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index f45720d5a9..b12efd6e29 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -85,7 +85,7 @@ func BenchmarkFilters(b *testing.B) { } b.ResetTimer() - filter := NewRangeFilter(backend, 0, -1, []common.Address{addr1, addr2, addr3, addr4}, nil) + filter := NewRangeFilter(backend, 0, -1, []common.Address{addr1, addr2, addr3, addr4}, nil, false) for i := 0; i < b.N; i++ { logs, _ := filter.Logs(context.Background()) @@ -168,14 +168,14 @@ func TestFilters(t *testing.T) { rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts[i]) } - filter := NewRangeFilter(backend, 0, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}}) + filter := NewRangeFilter(backend, 0, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}}, false) logs, _ := filter.Logs(context.Background()) if len(logs) != 4 { t.Error("expected 4 log, got", len(logs)) } - filter = NewRangeFilter(backend, 900, 999, []common.Address{addr}, [][]common.Hash{{hash3}}) + filter = NewRangeFilter(backend, 900, 999, []common.Address{addr}, [][]common.Hash{{hash3}}, false) logs, _ = filter.Logs(context.Background()) if len(logs) != 1 { t.Error("expected 1 log, got", len(logs)) @@ -184,7 +184,7 @@ func TestFilters(t *testing.T) { t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) } - filter = NewRangeFilter(backend, 990, -1, []common.Address{addr}, [][]common.Hash{{hash3}}) + filter = NewRangeFilter(backend, 990, -1, []common.Address{addr}, [][]common.Hash{{hash3}}, false) logs, _ = filter.Logs(context.Background()) if len(logs) != 1 { t.Error("expected 1 log, got", len(logs)) @@ -193,7 +193,7 @@ func TestFilters(t *testing.T) { t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) } - filter = NewRangeFilter(backend, 1, 10, nil, [][]common.Hash{{hash1, hash2}}) + filter = NewRangeFilter(backend, 1, 10, nil, [][]common.Hash{{hash1, hash2}}, false) logs, _ = filter.Logs(context.Background()) if len(logs) != 2 { @@ -201,7 +201,7 @@ func TestFilters(t *testing.T) { } failHash := common.BytesToHash([]byte("fail")) - filter = NewRangeFilter(backend, 0, -1, nil, [][]common.Hash{{failHash}}) + filter = NewRangeFilter(backend, 0, -1, nil, [][]common.Hash{{failHash}}, false) logs, _ = filter.Logs(context.Background()) if len(logs) != 0 { @@ -209,14 +209,14 @@ func TestFilters(t *testing.T) { } failAddr := common.BytesToAddress([]byte("failmenow")) - filter = NewRangeFilter(backend, 0, -1, []common.Address{failAddr}, nil) + filter = NewRangeFilter(backend, 0, -1, []common.Address{failAddr}, nil, false) logs, _ = filter.Logs(context.Background()) if len(logs) != 0 { t.Error("expected 0 log, got", len(logs)) } - filter = NewRangeFilter(backend, 0, -1, nil, [][]common.Hash{{failHash}, {hash1}}) + filter = NewRangeFilter(backend, 0, -1, nil, [][]common.Hash{{failHash}, {hash1}}, false) logs, _ = filter.Logs(context.Background()) if len(logs) != 0 { diff --git a/graphql/graphql.go b/graphql/graphql.go index 0f7bba8657..52db342634 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -1023,7 +1023,7 @@ func (r *Resolver) Logs(ctx context.Context, args struct{ Filter FilterCriteria topics = *args.Filter.Topics } // Construct the range filter - filter := filters.NewRangeFilter(filters.Backend(r.backend), begin, end, addresses, topics) + filter := filters.NewRangeFilter(filters.Backend(r.backend), begin, end, addresses, topics, false) return runFilter(ctx, r.backend, filter) } diff --git a/les/client.go b/les/client.go index 0b88d82355..091f89380f 100644 --- a/les/client.go +++ b/les/client.go @@ -221,7 +221,7 @@ func (s *LightEthereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend, true), + Service: filters.NewPublicFilterAPI(s.ApiBackend, true, s.config.RangeLimit), Public: true, }, { Namespace: "net", diff --git a/node/config.go b/node/config.go index e4c7af9675..730757e56d 100644 --- a/node/config.go +++ b/node/config.go @@ -98,6 +98,9 @@ type Config struct { // DirectBroadcast enable directly broadcast mined block to all peers DirectBroadcast bool `toml:",omitempty"` + // RangeLimit enable 5000 blocks limit when handle range query + RangeLimit bool `toml:",omitempty"` + // SmartCardDaemonPath is the path to the smartcard daemon's socket SmartCardDaemonPath string `toml:",omitempty"`