Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add limitation to jsonrpc batch query and block range #145

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 24 additions & 19 deletions command/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,33 @@ import (
"strings"

"github.com/dogechain-lab/dogechain/command"
"github.com/dogechain-lab/dogechain/jsonrpc"
"github.com/dogechain-lab/dogechain/network"

"github.com/hashicorp/hcl"
)

// Config defines the server configuration params
type Config struct {
GenesisPath string `json:"chain_config"`
SecretsConfigPath string `json:"secrets_config"`
DataDir string `json:"data_dir"`
BlockGasTarget string `json:"block_gas_target"`
GRPCAddr string `json:"grpc_addr"`
JSONRPCAddr string `json:"jsonrpc_addr"`
Telemetry *Telemetry `json:"telemetry"`
Network *Network `json:"network"`
ShouldSeal bool `json:"seal"`
TxPool *TxPool `json:"tx_pool"`
LogLevel string `json:"log_level"`
RestoreFile string `json:"restore_file"`
BlockTime uint64 `json:"block_time_s"`
Headers *Headers `json:"headers"`
LogFilePath string `json:"log_to"`
EnableGraphQL bool `json:"enable_graphql"`
GraphQLAddr string `json:"graphql_addr"`
GenesisPath string `json:"chain_config"`
SecretsConfigPath string `json:"secrets_config"`
DataDir string `json:"data_dir"`
BlockGasTarget string `json:"block_gas_target"`
GRPCAddr string `json:"grpc_addr"`
JSONRPCAddr string `json:"jsonrpc_addr"`
Telemetry *Telemetry `json:"telemetry"`
Network *Network `json:"network"`
ShouldSeal bool `json:"seal"`
TxPool *TxPool `json:"tx_pool"`
LogLevel string `json:"log_level"`
RestoreFile string `json:"restore_file"`
BlockTime uint64 `json:"block_time_s"`
Headers *Headers `json:"headers"`
LogFilePath string `json:"log_to"`
EnableGraphQL bool `json:"enable_graphql"`
GraphQLAddr string `json:"graphql_addr"`
JSONRPCBatchRequestLimit uint64 `json:"json_rpc_batch_request_limit" yaml:"json_rpc_batch_request_limit"`
JSONRPCBlockRangeLimit uint64 `json:"json_rpc_block_range_limit" yaml:"json_rpc_block_range_limit"`
}

// Telemetry holds the config details for metric services.
Expand Down Expand Up @@ -95,8 +98,10 @@ func DefaultConfig() *Config {
Headers: &Headers{
AccessControlAllowOrigins: []string{"*"},
},
LogFilePath: "",
EnableGraphQL: false,
LogFilePath: "",
EnableGraphQL: false,
JSONRPCBatchRequestLimit: jsonrpc.DefaultJSONRPCBatchRequestLimit,
JSONRPCBlockRangeLimit: jsonrpc.DefaultJSONRPCBlockRangeLimit,
}
}

Expand Down
57 changes: 31 additions & 26 deletions command/server/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,34 @@ import (
)

const (
configFlag = "config"
genesisPathFlag = "chain"
dataDirFlag = "data-dir"
libp2pAddressFlag = "libp2p"
prometheusAddressFlag = "prometheus"
natFlag = "nat"
dnsFlag = "dns"
sealFlag = "seal"
maxPeersFlag = "max-peers"
maxInboundPeersFlag = "max-inbound-peers"
maxOutboundPeersFlag = "max-outbound-peers"
priceLimitFlag = "price-limit"
maxSlotsFlag = "max-slots"
maxAccountDemotionsFlag = "max-account-demotions"
pruneTickSecondsFlag = "prune-tick-seconds"
promoteOutdateSecondsFlag = "promote-outdate-seconds"
blockGasTargetFlag = "block-gas-target"
secretsConfigFlag = "secrets-config"
restoreFlag = "restore"
blockTimeFlag = "block-time"
devIntervalFlag = "dev-interval"
devFlag = "dev"
corsOriginFlag = "access-control-allow-origins"
daemonFlag = "daemon"
logFileLocationFlag = "log-to"
enableGraphQLFlag = "enable-graphql"
configFlag = "config"
genesisPathFlag = "chain"
dataDirFlag = "data-dir"
libp2pAddressFlag = "libp2p"
prometheusAddressFlag = "prometheus"
natFlag = "nat"
dnsFlag = "dns"
sealFlag = "seal"
maxPeersFlag = "max-peers"
maxInboundPeersFlag = "max-inbound-peers"
maxOutboundPeersFlag = "max-outbound-peers"
priceLimitFlag = "price-limit"
maxSlotsFlag = "max-slots"
maxAccountDemotionsFlag = "max-account-demotions"
pruneTickSecondsFlag = "prune-tick-seconds"
promoteOutdateSecondsFlag = "promote-outdate-seconds"
blockGasTargetFlag = "block-gas-target"
secretsConfigFlag = "secrets-config"
restoreFlag = "restore"
blockTimeFlag = "block-time"
devIntervalFlag = "dev-interval"
devFlag = "dev"
corsOriginFlag = "access-control-allow-origins"
daemonFlag = "daemon"
logFileLocationFlag = "log-to"
enableGraphQLFlag = "enable-graphql"
jsonRPCBatchRequestLimitFlag = "json-rpc-batch-request-limit"
jsonRPCBlockRangeLimitFlag = "json-rpc-block-range-limit"
)

const (
Expand Down Expand Up @@ -162,11 +164,14 @@ func (p *serverParams) generateConfig() *server.Config {
JSONRPC: &server.JSONRPC{
JSONRPCAddr: p.jsonRPCAddress,
AccessControlAllowOrigin: p.corsAllowedOrigins,
BatchLengthLimit: p.rawConfig.JSONRPCBatchRequestLimit,
BlockRangeLimit: p.rawConfig.JSONRPCBlockRangeLimit,
},
EnableGraphQL: p.rawConfig.EnableGraphQL,
GraphQL: &server.GraphQL{
GraphQLAddr: p.graphqlAddress,
AccessControlAllowOrigin: p.corsAllowedOrigins,
BlockRangeLimit: p.rawConfig.JSONRPCBlockRangeLimit,
},
GRPCAddr: p.grpcAddress,
LibP2PAddr: p.libp2pAddress,
Expand Down
15 changes: 15 additions & 0 deletions command/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,21 @@ func setFlags(cmd *cobra.Command) {
"the flag indicating that node enable graphql service",
)

cmd.Flags().Uint64Var(
&params.rawConfig.JSONRPCBatchRequestLimit,
jsonRPCBatchRequestLimitFlag,
defaultConfig.JSONRPCBatchRequestLimit,
"the max length to be considered when handling json-rpc batch requests",
)

cmd.Flags().Uint64Var(
&params.rawConfig.JSONRPCBlockRangeLimit,
jsonRPCBlockRangeLimitFlag,
defaultConfig.JSONRPCBlockRangeLimit,
"the max block range to be considered when executing json-rpc requests "+
"that consider fromBlock/toBlock values (e.g. eth_getLogs)",
)

setDevFlags(cmd)
}

Expand Down
3 changes: 2 additions & 1 deletion graphql/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Config struct {
Forks chain.Forks
ChainID uint64
AccessControlAllowOrigin []string
BlockRangeLimit uint64
}

// GraphQLStore defines all the methods required
Expand All @@ -41,7 +42,7 @@ func NewGraphQLService(logger hclog.Logger, config *Config) (*GraphQLService, er
q := Resolver{
backend: config.Store,
chainID: config.ChainID,
filterManager: rpc.NewFilterManager(hclog.NewNullLogger(), config.Store),
filterManager: rpc.NewFilterManager(hclog.NewNullLogger(), config.Store, config.BlockRangeLimit),
}

s, err := graphql.ParseSchema(schema, &q)
Expand Down
9 changes: 9 additions & 0 deletions jsonrpc/default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package jsonrpc

const (
// DefaultJSONRPCBatchRequestLimit maximum length allowed for json_rpc batch requests
DefaultJSONRPCBatchRequestLimit uint64 = 1
// DefaultJSONRPCBlockRangeLimit maximum block range allowed for json_rpc
// requests with fromBlock/toBlock values (e.g. eth_getLogs)
DefaultJSONRPCBlockRangeLimit uint64 = 100
)
38 changes: 29 additions & 9 deletions jsonrpc/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,29 @@ type endpoints struct {
// Dispatcher handles all json rpc requests by delegating
// the execution flow to the corresponding service
type Dispatcher struct {
logger hclog.Logger
serviceMap map[string]*serviceData
filterManager *FilterManager
endpoints endpoints
chainID uint64
logger hclog.Logger
serviceMap map[string]*serviceData
filterManager *FilterManager
endpoints endpoints
chainID uint64
jsonRPCBatchLengthLimit uint64
}

func newDispatcher(logger hclog.Logger, store JSONRPCStore, chainID uint64) *Dispatcher {
func newDispatcher(
logger hclog.Logger,
store JSONRPCStore,
chainID uint64,
jsonRPCBatchLengthLimit uint64,
blockRangeLimit uint64,
) *Dispatcher {
d := &Dispatcher{
logger: logger.Named("dispatcher"),
chainID: chainID,
logger: logger.Named("dispatcher"),
chainID: chainID,
jsonRPCBatchLengthLimit: jsonRPCBatchLengthLimit,
}

if store != nil {
d.filterManager = NewFilterManager(logger, store)
d.filterManager = NewFilterManager(logger, store, blockRangeLimit)
go d.filterManager.Run()
}

Expand Down Expand Up @@ -98,6 +106,8 @@ func (d *Dispatcher) getFnHandler(req Request) (*serviceData, *funcData, Error)

type wsConn interface {
WriteMessage(messageType int, data []byte) error
GetFilterID() string
SetFilterID(string)
}

// as per https://www.jsonrpc.org/specification, the `id` in JSON-RPC 2.0
Expand Down Expand Up @@ -168,6 +178,10 @@ func (d *Dispatcher) handleUnsubscribe(req Request) (bool, Error) {
return d.filterManager.Uninstall(filterID), nil
}

func (d *Dispatcher) RemoveFilterByWs(conn wsConn) {
d.filterManager.RemoveFilterByWs(conn)
}

func (d *Dispatcher) HandleWs(reqBody []byte, conn wsConn) ([]byte, error) {
var req Request
if err := json.Unmarshal(reqBody, &req); err != nil {
Expand Down Expand Up @@ -246,6 +260,12 @@ func (d *Dispatcher) Handle(reqBody []byte) ([]byte, error) {
return NewRPCResponse(nil, "2.0", nil, NewInvalidRequestError("Invalid json request")).Bytes()
}

// if not disabled, avoid handling long batch requests
if d.jsonRPCBatchLengthLimit > 0 &&
len(requests) > int(d.jsonRPCBatchLengthLimit) {
return NewRPCResponse(nil, "2.0", nil, NewInvalidRequestError("Batch request length too long")).Bytes()
}

responses := make([]Response, 0)

for _, req := range requests {
Expand Down
Loading