Skip to content

Commit

Permalink
Merge pull request 0xPolygonHermez#2431 from 0xPolygonHermez/improve/…
Browse files Browse the repository at this point in the history
…rpc-limit-batch-requests

Introduce Config Parameters to Control RPC Batch Requests
  • Loading branch information
Psykepro authored Aug 22, 2023
2 parents 4fecb20 + 9132e55 commit 659b082
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 16 deletions.
8 changes: 8 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,14 @@ func Test_Defaults(t *testing.T) {
path: "RPC.EnableL2SuggestedGasPricePolling",
expectedValue: true,
},
{
path: "RPC.BatchRequestsEnabled",
expectedValue: false,
},
{
path: "RPC.BatchRequestsLimit",
expectedValue: uint(20),
},
{
path: "RPC.WebSockets.Enabled",
expectedValue: true,
Expand Down
2 changes: 2 additions & 0 deletions config/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ MaxRequestsPerIPAndSecond = 500
SequencerNodeURI = ""
EnableL2SuggestedGasPricePolling = true
TraceBatchUseHTTPS = true
BatchRequestsEnabled = false
BatchRequestsLimit = 20
[RPC.WebSockets]
Enabled = true
Host = "0.0.0.0"
Expand Down
2 changes: 1 addition & 1 deletion docs/config-file/node-config-doc.html

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions docs/config-file/node-config-doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,8 @@ GlobalQueue=1024
| - [WebSockets](#RPC_WebSockets ) | No | object | No | - | WebSockets configuration |
| - [EnableL2SuggestedGasPricePolling](#RPC_EnableL2SuggestedGasPricePolling ) | No | boolean | No | - | EnableL2SuggestedGasPricePolling enables polling of the L2 gas price to block tx in the RPC with lower gas price. |
| - [TraceBatchUseHTTPS](#RPC_TraceBatchUseHTTPS ) | No | boolean | No | - | TraceBatchUseHTTPS enables, in the debug_traceBatchByNum endpoint, the use of the HTTPS protocol (instead of HTTP)<br />to do the parallel requests to RPC.debug_traceTransaction endpoint |
| - [BatchRequestsEnabled](#RPC_BatchRequestsEnabled ) | No | boolean | No | - | BatchRequestsEnabled defines if the Batch requests are enabled or disabled |
| - [BatchRequestsLimit](#RPC_BatchRequestsLimit ) | No | integer | No | - | BatchRequestsLimit defines the limit of requests that can be incorporated into each batch request |

### <a name="RPC_Host"></a>8.1. `RPC.Host`

Expand Down Expand Up @@ -939,6 +941,34 @@ to do the parallel requests to RPC.debug_traceTransaction endpoint
TraceBatchUseHTTPS=true
```

### <a name="RPC_BatchRequestsEnabled"></a>8.11. `RPC.BatchRequestsEnabled`

**Type:** : `boolean`

**Default:** `false`

**Description:** BatchRequestsEnabled defines if the Batch requests are enabled or disabled

**Example setting the default value** (false):
```
[RPC]
BatchRequestsEnabled=false
```

### <a name="RPC_BatchRequestsLimit"></a>8.12. `RPC.BatchRequestsLimit`

**Type:** : `integer`

**Default:** `20`

**Description:** BatchRequestsLimit defines the limit of requests that can be incorporated into each batch request

**Example setting the default value** (20):
```
[RPC]
BatchRequestsLimit=20
```

## <a name="Synchronizer"></a>9. `[Synchronizer]`

**Type:** : `object`
Expand Down
10 changes: 10 additions & 0 deletions docs/config-file/node-config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,16 @@
"type": "boolean",
"description": "TraceBatchUseHTTPS enables, in the debug_traceBatchByNum endpoint, the use of the HTTPS protocol (instead of HTTP)\nto do the parallel requests to RPC.debug_traceTransaction endpoint",
"default": true
},
"BatchRequestsEnabled": {
"type": "boolean",
"description": "BatchRequestsEnabled defines if the Batch requests are enabled or disabled",
"default": false
},
"BatchRequestsLimit": {
"type": "integer",
"description": "BatchRequestsLimit defines the limit of requests that can be incorporated into each batch request",
"default": 20
}
},
"additionalProperties": false,
Expand Down
85 changes: 71 additions & 14 deletions jsonrpc/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/0xPolygonHermez/zkevm-node/jsonrpc/types"
)

const jsonRPCVersion = "2.0"

// Client defines typed wrappers for the zkEVM RPC API.
type Client struct {
url string
Expand All @@ -26,53 +28,108 @@ func NewClient(url string) *Client {
// the provided method and parameters, which is compatible with the Ethereum
// JSON RPC Server.
func JSONRPCCall(url, method string, parameters ...interface{}) (types.Response, error) {
const jsonRPCVersion = "2.0"

params, err := json.Marshal(parameters)
if err != nil {
return types.Response{}, err
}

req := types.Request{
request := types.Request{
JSONRPC: jsonRPCVersion,
ID: float64(1),
Method: method,
Params: params,
}

reqBody, err := json.Marshal(req)
httpRes, err := sendJSONRPC_HTTPRequest(url, request)
if err != nil {
return types.Response{}, err
}

reqBodyReader := bytes.NewReader(reqBody)
httpReq, err := http.NewRequest(http.MethodPost, url, reqBodyReader)
resBody, err := io.ReadAll(httpRes.Body)
if err != nil {
return types.Response{}, err
}
defer httpRes.Body.Close()

httpReq.Header.Add("Content-type", "application/json")

httpRes, err := http.DefaultClient.Do(httpReq)
var res types.Response
err = json.Unmarshal(resBody, &res)
if err != nil {
return types.Response{}, err
}

if httpRes.StatusCode != http.StatusOK {
return types.Response{}, fmt.Errorf("Invalid status code, expected: %v, found: %v", http.StatusOK, httpRes.StatusCode)
return res, nil
}

// BatchCall used in batch requests to send multiple methods and parameters at once
type BatchCall struct {
Method string
Parameters []interface{}
}

// JSONRPCBatchCall executes a 2.0 JSON RPC HTTP Post Batch Request to the provided URL with
// the provided method and parameters groups, which is compatible with the Ethereum
// JSON RPC Server.
func JSONRPCBatchCall(url string, calls ...BatchCall) ([]types.Response, error) {
requests := []types.Request{}

for i, call := range calls {
params, err := json.Marshal(call.Parameters)
if err != nil {
return nil, err
}

req := types.Request{
JSONRPC: jsonRPCVersion,
ID: float64(i),
Method: call.Method,
Params: params,
}

requests = append(requests, req)
}

httpRes, err := sendJSONRPC_HTTPRequest(url, requests)
if err != nil {
return nil, err
}

resBody, err := io.ReadAll(httpRes.Body)
if err != nil {
return types.Response{}, err
return nil, err
}
defer httpRes.Body.Close()

var res types.Response
var res []types.Response
err = json.Unmarshal(resBody, &res)
if err != nil {
return types.Response{}, err
errorMessage := string(resBody)
return nil, fmt.Errorf(errorMessage)
}

return res, nil
}

func sendJSONRPC_HTTPRequest(url string, payload interface{}) (*http.Response, error) {
reqBody, err := json.Marshal(payload)
if err != nil {
return nil, err
}

reqBodyReader := bytes.NewReader(reqBody)
httpReq, err := http.NewRequest(http.MethodPost, url, reqBodyReader)
if err != nil {
return nil, err
}

httpReq.Header.Add("Content-type", "application/json")

httpRes, err := http.DefaultClient.Do(httpReq)
if err != nil {
return nil, err
}

if httpRes.StatusCode != http.StatusOK {
return nil, fmt.Errorf("invalid status code, expected: %v, found: %v", http.StatusOK, httpRes.StatusCode)
}
return httpRes, nil
}
6 changes: 6 additions & 0 deletions jsonrpc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ type Config struct {
// TraceBatchUseHTTPS enables, in the debug_traceBatchByNum endpoint, the use of the HTTPS protocol (instead of HTTP)
// to do the parallel requests to RPC.debug_traceTransaction endpoint
TraceBatchUseHTTPS bool `mapstructure:"TraceBatchUseHTTPS"`

// BatchRequestsEnabled defines if the Batch requests are enabled or disabled
BatchRequestsEnabled bool `mapstructure:"BatchRequestsEnabled"`

// BatchRequestsLimit defines the limit of requests that can be incorporated into each batch request
BatchRequestsLimit uint `mapstructure:"BatchRequestsLimit"`
}

// WebSocketsConfig has parameters to config the rpc websocket support
Expand Down
2 changes: 2 additions & 0 deletions jsonrpc/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type RequestHandledLabel string
const (
// RequestHandledLabelInvalid represents an request of type invalid
RequestHandledLabelInvalid RequestHandledLabel = "invalid"
// RequestHandledLabelError represents an request of type error
RequestHandledLabelError RequestHandledLabel = "error"
// RequestHandledLabelSingle represents an request of type single
RequestHandledLabelSingle RequestHandledLabel = "single"
// RequestHandledLabelBatch represents an request of type batch
Expand Down
21 changes: 20 additions & 1 deletion jsonrpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,13 +276,27 @@ func (s *Server) handleSingleRequest(httpRequest *http.Request, w http.ResponseW
}

func (s *Server) handleBatchRequest(httpRequest *http.Request, w http.ResponseWriter, data []byte) int {
// Checking if batch requests are enabled
if !s.config.BatchRequestsEnabled {
s.handleInvalidRequest(w, types.ErrBatchRequestsDisabled)
return 0
}

defer metrics.RequestHandled(metrics.RequestHandledLabelBatch)
requests, err := s.parseRequests(data)
if err != nil {
handleError(w, err)
return 0
}

// Checking if batch requests limit is exceeded
if s.config.BatchRequestsLimit > 0 {
if len(requests) > int(s.config.BatchRequestsLimit) {
s.handleInvalidRequest(w, types.ErrBatchRequestsLimitExceeded)
return 0
}
}

responses := make([]types.Response, 0, len(requests))

for _, request := range requests {
Expand Down Expand Up @@ -322,7 +336,11 @@ func (s *Server) parseRequests(data []byte) ([]types.Request, error) {

func (s *Server) handleInvalidRequest(w http.ResponseWriter, err error) {
defer metrics.RequestHandled(metrics.RequestHandledLabelInvalid)
handleError(w, err)
log.Info(err)
_, err = w.Write([]byte(err.Error()))
if err != nil {
log.Error(err)
}
}

func (s *Server) handleWs(w http.ResponseWriter, req *http.Request) {
Expand Down Expand Up @@ -384,6 +402,7 @@ func (s *Server) handleWs(w http.ResponseWriter, req *http.Request) {
}

func handleError(w http.ResponseWriter, err error) {
defer metrics.RequestHandled(metrics.RequestHandledLabelError)
log.Error(err)
_, err = w.Write([]byte(err.Error()))
if err != nil {
Expand Down
Loading

0 comments on commit 659b082

Please sign in to comment.