mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
Added checks to RPC requests and introduced new flags to customise the parameters (#657)
* added a check to reject rpc requests with batch size > the one set using a newly added flag (rpcbatchlimit) * added a check to reject rpc requests whose result size > the one set using a newly added flag (rpcreturndatalimit) * updated the config files and docs
This commit is contained in:
parent
66c23d1eeb
commit
e4dd2ee1ac
24 changed files with 121 additions and 24 deletions
|
|
@ -8,6 +8,8 @@ chain = "mainnet"
|
||||||
datadir = "/var/lib/bor/data"
|
datadir = "/var/lib/bor/data"
|
||||||
# ancient = ""
|
# ancient = ""
|
||||||
# keystore = "/var/lib/bor/keystore"
|
# keystore = "/var/lib/bor/keystore"
|
||||||
|
# "rpc.batchlimit" = 100
|
||||||
|
# "rpc.returndatalimit" = 100000
|
||||||
syncmode = "full"
|
syncmode = "full"
|
||||||
# gcmode = "full"
|
# gcmode = "full"
|
||||||
# snapshot = true
|
# snapshot = true
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ log-level = "INFO" # Set log level for the server
|
||||||
datadir = "var/lib/bor" # Path of the data directory to store information
|
datadir = "var/lib/bor" # Path of the data directory to store information
|
||||||
ancient = "" # Data directory for ancient chain segments (default = inside chaindata)
|
ancient = "" # Data directory for ancient chain segments (default = inside chaindata)
|
||||||
keystore = "" # Path of the directory where keystores are located
|
keystore = "" # Path of the directory where keystores are located
|
||||||
|
"rpc.batchlimit" = 100 # Maximum number of messages in a batch (default=100, use 0 for no limits)
|
||||||
|
"rpc.returndatalimit" = 100000 # Maximum size (in bytes) a result of an rpc request could have (default=100000, use 0 for no limits)
|
||||||
syncmode = "full" # Blockchain sync mode (only "full" sync supported)
|
syncmode = "full" # Blockchain sync mode (only "full" sync supported)
|
||||||
gcmode = "full" # Blockchain garbage collection mode ("full", "archive")
|
gcmode = "full" # Blockchain garbage collection mode ("full", "archive")
|
||||||
snapshot = true # Enables the snapshot-database mode
|
snapshot = true # Enables the snapshot-database mode
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ The ```bor server``` command runs the Bor client.
|
||||||
|
|
||||||
- ```keystore```: Path of the directory where keystores are located
|
- ```keystore```: Path of the directory where keystores are located
|
||||||
|
|
||||||
|
- ```rpc.batchlimit```: Maximum number of messages in a batch (default=100, use 0 for no limits) (default: 100)
|
||||||
|
|
||||||
|
- ```rpc.returndatalimit```: Maximum size (in bytes) a result of an rpc request could have (default=100000, use 0 for no limits) (default: 100000)
|
||||||
|
|
||||||
- ```config```: File for the config file
|
- ```config```: File for the config file
|
||||||
|
|
||||||
- ```syncmode```: Blockchain sync mode (only "full" sync supported) (default: full)
|
- ```syncmode```: Blockchain sync mode (only "full" sync supported) (default: full)
|
||||||
|
|
|
||||||
|
|
@ -317,6 +317,10 @@ func (b *EthAPIBackend) RPCGasCap() uint64 {
|
||||||
return b.eth.config.RPCGasCap
|
return b.eth.config.RPCGasCap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *EthAPIBackend) RPCRpcReturnDataLimit() uint64 {
|
||||||
|
return b.eth.config.RPCReturnDataLimit
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) RPCEVMTimeout() time.Duration {
|
func (b *EthAPIBackend) RPCEVMTimeout() time.Duration {
|
||||||
return b.eth.config.RPCEVMTimeout
|
return b.eth.config.RPCEVMTimeout
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ var Defaults = Config{
|
||||||
},
|
},
|
||||||
TxPool: core.DefaultTxPoolConfig,
|
TxPool: core.DefaultTxPoolConfig,
|
||||||
RPCGasCap: 50000000,
|
RPCGasCap: 50000000,
|
||||||
|
RPCReturnDataLimit: 100000,
|
||||||
RPCEVMTimeout: 5 * time.Second,
|
RPCEVMTimeout: 5 * time.Second,
|
||||||
GPO: FullNodeGPO,
|
GPO: FullNodeGPO,
|
||||||
RPCTxFeeCap: 5, // 5 matic
|
RPCTxFeeCap: 5, // 5 matic
|
||||||
|
|
@ -199,6 +200,9 @@ type Config struct {
|
||||||
// RPCGasCap is the global gas cap for eth-call variants.
|
// RPCGasCap is the global gas cap for eth-call variants.
|
||||||
RPCGasCap uint64
|
RPCGasCap uint64
|
||||||
|
|
||||||
|
// Maximum size (in bytes) a result of an rpc request could have
|
||||||
|
RPCReturnDataLimit uint64
|
||||||
|
|
||||||
// RPCEVMTimeout is the global timeout for eth-call.
|
// RPCEVMTimeout is the global timeout for eth-call.
|
||||||
RPCEVMTimeout time.Duration
|
RPCEVMTimeout time.Duration
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,10 @@ func (b *testBackend) RPCGasCap() uint64 {
|
||||||
return 25000000
|
return 25000000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *testBackend) RPCRpcReturnDataLimit() uint64 {
|
||||||
|
return 100000
|
||||||
|
}
|
||||||
|
|
||||||
func (b *testBackend) ChainConfig() *params.ChainConfig {
|
func (b *testBackend) ChainConfig() *params.ChainConfig {
|
||||||
return b.chainConfig
|
return b.chainConfig
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,12 @@ type Config struct {
|
||||||
// KeyStoreDir is the directory to store keystores
|
// KeyStoreDir is the directory to store keystores
|
||||||
KeyStoreDir string `hcl:"keystore,optional" toml:"keystore,optional"`
|
KeyStoreDir string `hcl:"keystore,optional" toml:"keystore,optional"`
|
||||||
|
|
||||||
|
// Maximum number of messages in a batch (default=100, use 0 for no limits)
|
||||||
|
RPCBatchLimit uint64 `hcl:"rpc.batchlimit,optional" toml:"rpc.batchlimit,optional"`
|
||||||
|
|
||||||
|
// Maximum size (in bytes) a result of an rpc request could have (default=100000, use 0 for no limits)
|
||||||
|
RPCReturnDataLimit uint64 `hcl:"rpc.returndatalimit,optional" toml:"rpc.returndatalimit,optional"`
|
||||||
|
|
||||||
// SyncMode selects the sync protocol
|
// SyncMode selects the sync protocol
|
||||||
SyncMode string `hcl:"syncmode,optional" toml:"syncmode,optional"`
|
SyncMode string `hcl:"syncmode,optional" toml:"syncmode,optional"`
|
||||||
|
|
||||||
|
|
@ -441,6 +447,8 @@ func DefaultConfig() *Config {
|
||||||
LogLevel: "INFO",
|
LogLevel: "INFO",
|
||||||
DataDir: DefaultDataDir(),
|
DataDir: DefaultDataDir(),
|
||||||
Ancient: "",
|
Ancient: "",
|
||||||
|
RPCBatchLimit: 100,
|
||||||
|
RPCReturnDataLimit: 100000,
|
||||||
P2P: &P2PConfig{
|
P2P: &P2PConfig{
|
||||||
MaxPeers: 50,
|
MaxPeers: 50,
|
||||||
MaxPendPeers: 50,
|
MaxPendPeers: 50,
|
||||||
|
|
@ -936,6 +944,8 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
|
||||||
n.BorLogs = c.BorLogs
|
n.BorLogs = c.BorLogs
|
||||||
n.DatabaseHandles = dbHandles
|
n.DatabaseHandles = dbHandles
|
||||||
|
|
||||||
|
n.RPCReturnDataLimit = c.RPCReturnDataLimit
|
||||||
|
|
||||||
if c.Ancient != "" {
|
if c.Ancient != "" {
|
||||||
n.DatabaseFreezer = c.Ancient
|
n.DatabaseFreezer = c.Ancient
|
||||||
}
|
}
|
||||||
|
|
@ -986,6 +996,7 @@ func (c *Config) buildNode() (*node.Config, error) {
|
||||||
WriteTimeout: c.JsonRPC.HttpTimeout.WriteTimeout,
|
WriteTimeout: c.JsonRPC.HttpTimeout.WriteTimeout,
|
||||||
IdleTimeout: c.JsonRPC.HttpTimeout.IdleTimeout,
|
IdleTimeout: c.JsonRPC.HttpTimeout.IdleTimeout,
|
||||||
},
|
},
|
||||||
|
RPCBatchLimit: c.RPCBatchLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
// dev mode
|
// dev mode
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,18 @@ func (c *Command) Flags() *flagset.Flagset {
|
||||||
Usage: "Path of the directory where keystores are located",
|
Usage: "Path of the directory where keystores are located",
|
||||||
Value: &c.cliConfig.KeyStoreDir,
|
Value: &c.cliConfig.KeyStoreDir,
|
||||||
})
|
})
|
||||||
|
f.Uint64Flag(&flagset.Uint64Flag{
|
||||||
|
Name: "rpc.batchlimit",
|
||||||
|
Usage: "Maximum number of messages in a batch (default=100, use 0 for no limits)",
|
||||||
|
Value: &c.cliConfig.RPCBatchLimit,
|
||||||
|
Default: c.cliConfig.RPCBatchLimit,
|
||||||
|
})
|
||||||
|
f.Uint64Flag(&flagset.Uint64Flag{
|
||||||
|
Name: "rpc.returndatalimit",
|
||||||
|
Usage: "Maximum size (in bytes) a result of an rpc request could have (default=100000, use 0 for no limits)",
|
||||||
|
Value: &c.cliConfig.RPCReturnDataLimit,
|
||||||
|
Default: c.cliConfig.RPCReturnDataLimit,
|
||||||
|
})
|
||||||
f.StringFlag(&flagset.StringFlag{
|
f.StringFlag(&flagset.StringFlag{
|
||||||
Name: "config",
|
Name: "config",
|
||||||
Usage: "File for the config file",
|
Usage: "File for the config file",
|
||||||
|
|
|
||||||
|
|
@ -1078,6 +1078,11 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if int(s.b.RPCRpcReturnDataLimit()) > 0 && len(result.ReturnData) > int(s.b.RPCRpcReturnDataLimit()) {
|
||||||
|
return nil, fmt.Errorf("call returned result of length %d exceeding limit %d", len(result.ReturnData), int(s.b.RPCRpcReturnDataLimit()))
|
||||||
|
}
|
||||||
|
|
||||||
// If the result contains a revert reason, try to unpack and return it.
|
// If the result contains a revert reason, try to unpack and return it.
|
||||||
if len(result.Revert()) > 0 {
|
if len(result.Revert()) > 0 {
|
||||||
return nil, newRevertError(result)
|
return nil, newRevertError(result)
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ type Backend interface {
|
||||||
AccountManager() *accounts.Manager
|
AccountManager() *accounts.Manager
|
||||||
ExtRPCEnabled() bool
|
ExtRPCEnabled() bool
|
||||||
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
|
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
|
||||||
|
RPCRpcReturnDataLimit() uint64 // Maximum size (in bytes) a result of an rpc request could have
|
||||||
RPCEVMTimeout() time.Duration // global timeout for eth_call over rpc: DoS protection
|
RPCEVMTimeout() time.Duration // global timeout for eth_call over rpc: DoS protection
|
||||||
RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs
|
RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs
|
||||||
UnprotectedAllowed() bool // allows only for EIP155 transactions.
|
UnprotectedAllowed() bool // allows only for EIP155 transactions.
|
||||||
|
|
|
||||||
|
|
@ -294,6 +294,10 @@ func (b *LesApiBackend) RPCGasCap() uint64 {
|
||||||
return b.eth.config.RPCGasCap
|
return b.eth.config.RPCGasCap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *LesApiBackend) RPCRpcReturnDataLimit() uint64 {
|
||||||
|
return b.eth.config.RPCReturnDataLimit
|
||||||
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) RPCEVMTimeout() time.Duration {
|
func (b *LesApiBackend) RPCEVMTimeout() time.Duration {
|
||||||
return b.eth.config.RPCEVMTimeout
|
return b.eth.config.RPCEVMTimeout
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,9 @@ type Config struct {
|
||||||
|
|
||||||
// JWTSecret is the hex-encoded jwt secret.
|
// JWTSecret is the hex-encoded jwt secret.
|
||||||
JWTSecret string `toml:",omitempty"`
|
JWTSecret string `toml:",omitempty"`
|
||||||
|
|
||||||
|
// Maximum number of messages in a batch
|
||||||
|
RPCBatchLimit uint64 `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into
|
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into
|
||||||
|
|
|
||||||
11
node/node.go
11
node/node.go
|
|
@ -113,6 +113,9 @@ func New(conf *Config) (*Node, error) {
|
||||||
databases: make(map[*closeTrackingDB]struct{}),
|
databases: make(map[*closeTrackingDB]struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set RPC batch limit
|
||||||
|
node.inprocHandler.SetRPCBatchLimit(conf.RPCBatchLimit)
|
||||||
|
|
||||||
// Register built-in APIs.
|
// Register built-in APIs.
|
||||||
node.rpcAPIs = append(node.rpcAPIs, node.apis()...)
|
node.rpcAPIs = append(node.rpcAPIs, node.apis()...)
|
||||||
|
|
||||||
|
|
@ -153,10 +156,10 @@ func New(conf *Config) (*Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure RPC servers.
|
// Configure RPC servers.
|
||||||
node.http = newHTTPServer(node.log, conf.HTTPTimeouts)
|
node.http = newHTTPServer(node.log, conf.HTTPTimeouts, conf.RPCBatchLimit)
|
||||||
node.httpAuth = newHTTPServer(node.log, conf.HTTPTimeouts)
|
node.httpAuth = newHTTPServer(node.log, conf.HTTPTimeouts, conf.RPCBatchLimit)
|
||||||
node.ws = newHTTPServer(node.log, rpc.DefaultHTTPTimeouts)
|
node.ws = newHTTPServer(node.log, rpc.DefaultHTTPTimeouts, conf.RPCBatchLimit)
|
||||||
node.wsAuth = newHTTPServer(node.log, rpc.DefaultHTTPTimeouts)
|
node.wsAuth = newHTTPServer(node.log, rpc.DefaultHTTPTimeouts, conf.RPCBatchLimit)
|
||||||
node.ipc = newIPCServer(node.log, conf.IPCEndpoint())
|
node.ipc = newIPCServer(node.log, conf.IPCEndpoint())
|
||||||
|
|
||||||
return node, nil
|
return node, nil
|
||||||
|
|
|
||||||
|
|
@ -81,10 +81,12 @@ type httpServer struct {
|
||||||
port int
|
port int
|
||||||
|
|
||||||
handlerNames map[string]string
|
handlerNames map[string]string
|
||||||
|
|
||||||
|
RPCBatchLimit uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHTTPServer(log log.Logger, timeouts rpc.HTTPTimeouts) *httpServer {
|
func newHTTPServer(log log.Logger, timeouts rpc.HTTPTimeouts, rpcBatchLimit uint64) *httpServer {
|
||||||
h := &httpServer{log: log, timeouts: timeouts, handlerNames: make(map[string]string)}
|
h := &httpServer{log: log, timeouts: timeouts, handlerNames: make(map[string]string), RPCBatchLimit: rpcBatchLimit}
|
||||||
|
|
||||||
h.httpHandler.Store((*rpcHandler)(nil))
|
h.httpHandler.Store((*rpcHandler)(nil))
|
||||||
h.wsHandler.Store((*rpcHandler)(nil))
|
h.wsHandler.Store((*rpcHandler)(nil))
|
||||||
|
|
@ -283,6 +285,7 @@ func (h *httpServer) enableRPC(apis []rpc.API, config httpConfig) error {
|
||||||
|
|
||||||
// Create RPC server and handler.
|
// Create RPC server and handler.
|
||||||
srv := rpc.NewServer()
|
srv := rpc.NewServer()
|
||||||
|
srv.SetRPCBatchLimit(h.RPCBatchLimit)
|
||||||
if err := RegisterApis(apis, config.Modules, srv, false); err != nil {
|
if err := RegisterApis(apis, config.Modules, srv, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -314,6 +317,7 @@ func (h *httpServer) enableWS(apis []rpc.API, config wsConfig) error {
|
||||||
}
|
}
|
||||||
// Create RPC server and handler.
|
// Create RPC server and handler.
|
||||||
srv := rpc.NewServer()
|
srv := rpc.NewServer()
|
||||||
|
srv.SetRPCBatchLimit(h.RPCBatchLimit)
|
||||||
if err := RegisterApis(apis, config.Modules, srv, false); err != nil {
|
if err := RegisterApis(apis, config.Modules, srv, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ func Test_checkPath(t *testing.T) {
|
||||||
func createAndStartServer(t *testing.T, conf *httpConfig, ws bool, wsConf *wsConfig) *httpServer {
|
func createAndStartServer(t *testing.T, conf *httpConfig, ws bool, wsConf *wsConfig) *httpServer {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
srv := newHTTPServer(testlog.Logger(t, log.LvlDebug), rpc.DefaultHTTPTimeouts)
|
srv := newHTTPServer(testlog.Logger(t, log.LvlDebug), rpc.DefaultHTTPTimeouts, 100)
|
||||||
assert.NoError(t, srv.enableRPC(nil, *conf))
|
assert.NoError(t, srv.enableRPC(nil, *conf))
|
||||||
if ws {
|
if ws {
|
||||||
assert.NoError(t, srv.enableWS(nil, *wsConf))
|
assert.NoError(t, srv.enableWS(nil, *wsConf))
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ chain = "mainnet"
|
||||||
datadir = "/var/lib/bor/data"
|
datadir = "/var/lib/bor/data"
|
||||||
# ancient = ""
|
# ancient = ""
|
||||||
# keystore = ""
|
# keystore = ""
|
||||||
|
# "rpc.batchlimit" = 100
|
||||||
|
# "rpc.returndatalimit" = 100000
|
||||||
syncmode = "full"
|
syncmode = "full"
|
||||||
gcmode = "archive"
|
gcmode = "archive"
|
||||||
# snapshot = true
|
# snapshot = true
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ chain = "mainnet"
|
||||||
datadir = "/var/lib/bor/data"
|
datadir = "/var/lib/bor/data"
|
||||||
# ancient = ""
|
# ancient = ""
|
||||||
# keystore = ""
|
# keystore = ""
|
||||||
|
# "rpc.batchlimit" = 100
|
||||||
|
# "rpc.returndatalimit" = 100000
|
||||||
syncmode = "full"
|
syncmode = "full"
|
||||||
# gcmode = "full"
|
# gcmode = "full"
|
||||||
# snapshot = true
|
# snapshot = true
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ chain = "mainnet"
|
||||||
datadir = "/var/lib/bor/data"
|
datadir = "/var/lib/bor/data"
|
||||||
# ancient = ""
|
# ancient = ""
|
||||||
# keystore = "$BOR_DIR/keystore"
|
# keystore = "$BOR_DIR/keystore"
|
||||||
|
# "rpc.batchlimit" = 100
|
||||||
|
# "rpc.returndatalimit" = 100000
|
||||||
syncmode = "full"
|
syncmode = "full"
|
||||||
# gcmode = "full"
|
# gcmode = "full"
|
||||||
# snapshot = true
|
# snapshot = true
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ chain = "mainnet"
|
||||||
datadir = "/var/lib/bor/data"
|
datadir = "/var/lib/bor/data"
|
||||||
# ancient = ""
|
# ancient = ""
|
||||||
# keystore = "$BOR_DIR/keystore"
|
# keystore = "$BOR_DIR/keystore"
|
||||||
|
# "rpc.batchlimit" = 100
|
||||||
|
# "rpc.returndatalimit" = 100000
|
||||||
syncmode = "full"
|
syncmode = "full"
|
||||||
# gcmode = "full"
|
# gcmode = "full"
|
||||||
# snapshot = true
|
# snapshot = true
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ chain = "mumbai"
|
||||||
datadir = "/var/lib/bor/data"
|
datadir = "/var/lib/bor/data"
|
||||||
# ancient = ""
|
# ancient = ""
|
||||||
# keystore = ""
|
# keystore = ""
|
||||||
|
# "rpc.batchlimit" = 100
|
||||||
|
# "rpc.returndatalimit" = 100000
|
||||||
syncmode = "full"
|
syncmode = "full"
|
||||||
gcmode = "archive"
|
gcmode = "archive"
|
||||||
# snapshot = true
|
# snapshot = true
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ chain = "mumbai"
|
||||||
datadir = "/var/lib/bor/data"
|
datadir = "/var/lib/bor/data"
|
||||||
# ancient = ""
|
# ancient = ""
|
||||||
# keystore = ""
|
# keystore = ""
|
||||||
|
# "rpc.batchlimit" = 100
|
||||||
|
# "rpc.returndatalimit" = 100000
|
||||||
syncmode = "full"
|
syncmode = "full"
|
||||||
# gcmode = "full"
|
# gcmode = "full"
|
||||||
# snapshot = true
|
# snapshot = true
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ chain = "mumbai"
|
||||||
datadir = "/var/lib/bor/data"
|
datadir = "/var/lib/bor/data"
|
||||||
# ancient = ""
|
# ancient = ""
|
||||||
# keystore = "$BOR_DIR/keystore"
|
# keystore = "$BOR_DIR/keystore"
|
||||||
|
# "rpc.batchlimit" = 100
|
||||||
|
# "rpc.returndatalimit" = 100000
|
||||||
syncmode = "full"
|
syncmode = "full"
|
||||||
# gcmode = "full"
|
# gcmode = "full"
|
||||||
# snapshot = true
|
# snapshot = true
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ chain = "mumbai"
|
||||||
datadir = "/var/lib/bor/data"
|
datadir = "/var/lib/bor/data"
|
||||||
# ancient = ""
|
# ancient = ""
|
||||||
# keystore = "$BOR_DIR/keystore"
|
# keystore = "$BOR_DIR/keystore"
|
||||||
|
# "rpc.batchlimit" = 100
|
||||||
|
# "rpc.returndatalimit" = 100000
|
||||||
syncmode = "full"
|
syncmode = "full"
|
||||||
# gcmode = "full"
|
# gcmode = "full"
|
||||||
# snapshot = true
|
# snapshot = true
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
|
@ -47,6 +48,8 @@ type Server struct {
|
||||||
idgen func() ID
|
idgen func() ID
|
||||||
run int32
|
run int32
|
||||||
codecs mapset.Set
|
codecs mapset.Set
|
||||||
|
|
||||||
|
BatchLimit uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new server instance with no registered handlers.
|
// NewServer creates a new server instance with no registered handlers.
|
||||||
|
|
@ -59,6 +62,10 @@ func NewServer() *Server {
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) SetRPCBatchLimit(batchLimit uint64) {
|
||||||
|
s.BatchLimit = batchLimit
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterName creates a service for the given receiver type under the given name. When no
|
// RegisterName creates a service for the given receiver type under the given name. When no
|
||||||
// methods on the given receiver match the criteria to be either a RPC method or a
|
// methods on the given receiver match the criteria to be either a RPC method or a
|
||||||
// subscription an error is returned. Otherwise a new service is created and added to the
|
// subscription an error is returned. Otherwise a new service is created and added to the
|
||||||
|
|
@ -105,12 +112,23 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) {
|
||||||
reqs, batch, err := codec.readBatch()
|
reqs, batch, err := codec.readBatch()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
codec.writeJSON(ctx, errorMessage(&invalidMessageError{"parse error"}))
|
if err1 := codec.writeJSON(ctx, err); err1 != nil {
|
||||||
|
log.Warn("WARNING - error in reading batch", "err", err1)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if batch {
|
if batch {
|
||||||
|
if s.BatchLimit > 0 && len(reqs) > int(s.BatchLimit) {
|
||||||
|
if err1 := codec.writeJSON(ctx, errorMessage(fmt.Errorf("batch limit %d exceeded: %d requests given", s.BatchLimit, len(reqs)))); err1 != nil {
|
||||||
|
log.Warn("WARNING - requests given exceeds the batch limit", "err", err1)
|
||||||
|
log.Debug("batch limit %d exceeded: %d requests given", s.BatchLimit, len(reqs))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
h.handleBatch(reqs)
|
h.handleBatch(reqs)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
h.handleMsg(reqs[0])
|
h.handleMsg(reqs[0])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue