Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Problem: No way to limit total number of filters that can be created #661

Merged
merged 10 commits into from
Oct 13, 2021
5 changes: 5 additions & 0 deletions rpc/ethereum/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,11 @@ func (e *EVMBackend) RPCGasCap() uint64 {
return e.cfg.JSONRPC.GasCap
}

// RPCFilterCap is the limit for total number of filters that can be created
func (e *EVMBackend) RPCFilterCap() uint64 {
return e.cfg.JSONRPC.FilterCap
}

// RPCMinGasPrice returns the minimum gas price for a transaction obtained from
// the node config. If set value is 0, it will default to 20.

Expand Down
29 changes: 26 additions & 3 deletions rpc/ethereum/namespaces/eth/filters/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ type Backend interface {
BloomStatus() (uint64, uint64)

GetFilteredBlocks(from int64, to int64, bloomIndexes [][]BloomIV, filterAddresses bool) ([]int64, error)

RPCFilterCap() uint64
}

// consider a filter inactive if it has not been polled for within deadline
Expand Down Expand Up @@ -107,13 +109,20 @@ func (api *PublicFilterAPI) timeoutLoop() {
//
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newPendingTransactionFilter
func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID {
api.filtersMu.Lock()

if len(api.filters) >= int(api.backend.RPCFilterCap()) {
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
api.filtersMu.Unlock()
devashishdxt marked this conversation as resolved.
Show resolved Hide resolved
return rpc.ID("error creating pending tx filter: max limit reached")
}

pendingTxSub, cancelSubs, err := api.events.SubscribePendingTxs()
if err != nil {
api.filtersMu.Unlock()
// wrap error on the ID
return rpc.ID(fmt.Sprintf("error creating pending tx filter: %s", err.Error()))
}

api.filtersMu.Lock()
api.filters[pendingTxSub.ID()] = &filter{typ: filters.PendingTransactionsSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: pendingTxSub}
api.filtersMu.Unlock()

Expand Down Expand Up @@ -219,16 +228,23 @@ func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Su
//
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter
func (api *PublicFilterAPI) NewBlockFilter() rpc.ID {
api.filtersMu.Lock()

if len(api.filters) >= int(api.backend.RPCFilterCap()) {
api.filtersMu.Unlock()
return rpc.ID("error creating block filter: max limit reached")
}

headerSub, cancelSubs, err := api.events.SubscribeNewHeads()
if err != nil {
api.filtersMu.Unlock()
// wrap error on the ID
return rpc.ID(fmt.Sprintf("error creating block filter: %s", err.Error()))
}

// TODO: use events to get the base fee amount
baseFee := big.NewInt(params.InitialBaseFee)

api.filtersMu.Lock()
api.filters[headerSub.ID()] = &filter{typ: filters.BlocksSubscription, deadline: time.NewTimer(deadline), hashes: []common.Hash{}, s: headerSub}
api.filtersMu.Unlock()

Expand Down Expand Up @@ -404,19 +420,26 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit filters.FilterCriteri
//
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter
func (api *PublicFilterAPI) NewFilter(criteria filters.FilterCriteria) (rpc.ID, error) {
api.filtersMu.Lock()

if len(api.filters) >= int(api.backend.RPCFilterCap()) {
api.filtersMu.Unlock()
return rpc.ID(""), fmt.Errorf("error creating filter: max limit reached")
}

var (
filterID = rpc.ID("")
err error
)

logsSub, cancelSubs, err := api.events.SubscribeLogs(criteria)
if err != nil {
api.filtersMu.Unlock()
return rpc.ID(""), err
}

filterID = logsSub.ID()

api.filtersMu.Lock()
api.filters[filterID] = &filter{typ: filters.LogsSubscription, deadline: time.NewTimer(deadline), hashes: []common.Hash{}, s: logsSub}
api.filtersMu.Unlock()

Expand Down
6 changes: 6 additions & 0 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const (
DefaultEVMTracer = "json"

DefaultGasCap uint64 = 25000000

DefaultFilterCap uint64 = 2000000
devashishdxt marked this conversation as resolved.
Show resolved Hide resolved
)

var evmTracers = []string{DefaultEVMTracer, "markdown", "struct", "access_list"}
Expand Down Expand Up @@ -61,6 +63,8 @@ type JSONRPCConfig struct {
Enable bool `mapstructure:"enable"`
// GasCap is the global gas cap for eth-call variants.
GasCap uint64 `mapstructure:"gas-cap"`
// FilterCap is the global cap for total number of filters that can be created.
FilterCap uint64 `mapstructure:"filter-cap"`
}

// TLSConfig defines the certificate and matching private key for the server.
Expand Down Expand Up @@ -145,6 +149,7 @@ func DefaultJSONRPCConfig() *JSONRPCConfig {
Address: DefaultJSONRPCAddress,
WsAddress: DefaultJSONRPCWsAddress,
GasCap: DefaultGasCap,
FilterCap: DefaultFilterCap,
}
}

Expand Down Expand Up @@ -207,6 +212,7 @@ func GetConfig(v *viper.Viper) Config {
Address: v.GetString("json-rpc.address"),
WsAddress: v.GetString("json-rpc.ws-address"),
GasCap: v.GetUint64("json-rpc.gas-cap"),
FilterCap: v.GetUint64("json-rpc.filter-cap"),
},
TLS: TLSConfig{
CertificatePath: v.GetString("tls.certificate-path"),
Expand Down
3 changes: 3 additions & 0 deletions server/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ api = "{{range $index, $elmt := .JSONRPC.API}}{{if $index}},{{$elmt}}{{else}}{{$
# GasCap sets a cap on gas that can be used in eth_call/estimateGas (0=infinite). Default: 25,000,000.
gas-cap = {{ .JSONRPC.GasCap }}

# FilterCap sets the global cap for total number of filters that can be created
filter-cap = {{ .JSONRPC.FilterCap }}

###############################################################################
### TLS Configuration ###
###############################################################################
Expand Down
11 changes: 6 additions & 5 deletions server/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ const (

// JSON-RPC flags
const (
JSONRPCEnable = "json-rpc.enable"
JSONRPCAPI = "json-rpc.api"
JSONRPCAddress = "json-rpc.address"
JSONWsAddress = "json-rpc.ws-address"
JSONRPCGasCap = "json-rpc.gas-cap"
JSONRPCEnable = "json-rpc.enable"
JSONRPCAPI = "json-rpc.api"
JSONRPCAddress = "json-rpc.address"
JSONWsAddress = "json-rpc.ws-address"
JSONRPCGasCap = "json-rpc.gas-cap"
JSONRPCFilterCap = "json-rpc.filter-cap"
)

// EVM flags
Expand Down
1 change: 1 addition & 0 deletions server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ which accepts a path for the resulting pprof file.
cmd.Flags().String(srvflags.JSONRPCAddress, config.DefaultJSONRPCAddress, "the JSON-RPC server address to listen on")
cmd.Flags().String(srvflags.JSONWsAddress, config.DefaultJSONRPCWsAddress, "the JSON-RPC WS server address to listen on")
cmd.Flags().Uint64(srvflags.JSONRPCGasCap, config.DefaultGasCap, "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)")
cmd.Flags().Uint64(srvflags.JSONRPCFilterCap, config.DefaultFilterCap, "Sets the global cap for total number of filters that can be created")

cmd.Flags().String(srvflags.EVMTracer, config.DefaultEVMTracer, "the EVM tracer type to collect execution traces from the EVM transaction execution (json|struct|access_list|markdown)")

Expand Down