Skip to content

Commit

Permalink
Add limitation to jsonrpc batch query and block range (#145)
Browse files Browse the repository at this point in the history
* Add debug logs

* Remove websocket connection when closed

* Add batch and block limit to jsonrpc and graphql

* Refresh jsonrpc filter when it is reachable

* Fix lint error

* Disable rpc batch and block range limit when set to 0

* Fix lll nolint warning
  • Loading branch information
DarianShawn authored Aug 25, 2022
1 parent 1928a2a commit 5fff9a8
Show file tree
Hide file tree
Showing 14 changed files with 550 additions and 218 deletions.
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

0 comments on commit 5fff9a8

Please sign in to comment.