Skip to content

Commit 97541a1

Browse files
authored
Add relay rpc (#208)
* add configure * add relay call * rename config * rename default config * fix config * delete sub call * expose configure * add log * add log for relay * add toString * add debug and fix call * add data * add data * add reply for trace_transaction * use hash string * add inner tx * format docs * fix lint * add rpc unlimited * relay in front of the function call * support wss add metrics * rename JSONRPCCallEx to JSONRPCRelay * rm useless handler file
1 parent cc20479 commit 97541a1

15 files changed

+238
-18
lines changed

config/apollo/apollo.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (c *Client) LoadConfig() (loaded bool) {
5959
switch namespace {
6060
case L2GasPricer:
6161
c.loadL2GasPricer(value)
62-
case JsonRPCRO, JsonRPCExplorer, JsonRPCSubgraph, JsonRPCLight:
62+
case JsonRPCRO, JsonRPCExplorer, JsonRPCSubgraph, JsonRPCLight, JsonRPCBridge, JsonRPCWO, JsonRPCUnlimited:
6363
c.loadJsonRPC(value)
6464
case Sequencer:
6565
c.loadSequencer(value)
@@ -82,13 +82,13 @@ func (c *CustomChangeListener) OnChange(changeEvent *storage.ChangeEvent) {
8282
for key, value := range changeEvent.Changes {
8383
if value.ChangeType == storage.MODIFIED {
8484
switch changeEvent.Namespace {
85-
case L2GasPricerHalt, SequencerHalt, JsonRPCROHalt, JsonRPCExplorerHalt, JsonRPCSubgraphHalt, JsonRPCLightHalt, JsonRPCBridgeHalt, JsonRPCWOHalt:
85+
case L2GasPricerHalt, SequencerHalt, JsonRPCROHalt, JsonRPCExplorerHalt, JsonRPCSubgraphHalt, JsonRPCLightHalt, JsonRPCBridgeHalt, JsonRPCWOHalt, JsonRPCUnlimitedHalt:
8686
c.fireHalt(key, value)
8787
case L2GasPricer:
8888
c.fireL2GasPricer(key, value)
8989
case Sequencer:
9090
c.fireSequencer(key, value)
91-
case JsonRPCRO, JsonRPCExplorer, JsonRPCSubgraph, JsonRPCLight, JsonRPCBridge, JsonRPCWO:
91+
case JsonRPCRO, JsonRPCExplorer, JsonRPCSubgraph, JsonRPCLight, JsonRPCBridge, JsonRPCWO, JsonRPCUnlimited:
9292
c.fireJsonRPC(key, value)
9393
case Pool:
9494
c.firePool(key, value)

config/apollo/namespace.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ const (
3535
JsonRPCWO = "jsonrpc-wo.txt"
3636
// JsonRPCWOHalt is the json-rpc-wo halt namespace,change the Halt to different value will halt json-rpc-wo
3737
JsonRPCWOHalt = "jsonrpc-wo-halt"
38+
// JsonRPCUnlimited is the json-rpc-unlimited namespace, the content of the namespace is the config for json-rpc-unlimited with toml format
39+
JsonRPCUnlimited = "jsonrpc-unlimited.txt"
40+
// JsonRPCUnlimitedHalt is the json-rpc-unlimited halt namespace,change the Halt to different value will halt json-rpc-unlimited
41+
JsonRPCUnlimitedHalt = "jsonrpc-unlimited-halt"
3842

3943
// Pool is the pool namespace, the content of the namespace is the config for pool with toml format
4044
Pool = "pool.txt"

config/default.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ DisableAPIs = []
138138
[RPC.ApiAuthentication]
139139
Enabled = false
140140
ApiKeys = []
141+
[RPC.ApiRelay]
142+
Enabled = false
143+
DestURI = ""
144+
RPCs = []
141145
142146
[Synchronizer]
143147
SyncInterval = "1s"

docs/config-file/node-config-doc.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

docs/config-file/node-config-doc.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,7 @@ FreeClaimGasLimit=150000
12821282
| - [EnableInnerTxCacheDB](#RPC_EnableInnerTxCacheDB ) | No | boolean | No | - | EnableInnerTxCacheDB enables the inner tx cache db |
12831283
| - [BridgeAddress](#RPC_BridgeAddress ) | No | array of integer | No | - | BridgeAddress is the address of the bridge contract |
12841284
| - [ApiAuthentication](#RPC_ApiAuthentication ) | No | object | No | - | ApiAuthentication defines the authentication configuration for the API |
1285+
| - [ApiRelay](#RPC_ApiRelay ) | No | object | No | - | ApiRelay defines the relay configuration for the API |
12851286

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

@@ -2241,6 +2242,53 @@ ApiKeys=[]
22412242
**Type:** : `string`
22422243
**Description:** Timeout defines the timeout
22432244

2245+
### <a name="RPC_ApiRelay"></a>8.28. `[RPC.ApiRelay]`
2246+
2247+
**Type:** : `object`
2248+
**Description:** ApiRelay defines the relay configuration for the API
2249+
2250+
| Property | Pattern | Type | Deprecated | Definition | Title/Description |
2251+
| ----------------------------------- | ------- | --------------- | ---------- | ---------- | ----------------- |
2252+
| - [Enabled](#RPC_ApiRelay_Enabled ) | No | boolean | No | - | - |
2253+
| - [DestURI](#RPC_ApiRelay_DestURI ) | No | string | No | - | - |
2254+
| - [RPCs](#RPC_ApiRelay_RPCs ) | No | array of string | No | - | - |
2255+
2256+
#### <a name="RPC_ApiRelay_Enabled"></a>8.28.1. `RPC.ApiRelay.Enabled`
2257+
2258+
**Type:** : `boolean`
2259+
2260+
**Default:** `false`
2261+
2262+
**Example setting the default value** (false):
2263+
```
2264+
[RPC.ApiRelay]
2265+
Enabled=false
2266+
```
2267+
2268+
#### <a name="RPC_ApiRelay_DestURI"></a>8.28.2. `RPC.ApiRelay.DestURI`
2269+
2270+
**Type:** : `string`
2271+
2272+
**Default:** `""`
2273+
2274+
**Example setting the default value** (""):
2275+
```
2276+
[RPC.ApiRelay]
2277+
DestURI=""
2278+
```
2279+
2280+
#### <a name="RPC_ApiRelay_RPCs"></a>8.28.3. `RPC.ApiRelay.RPCs`
2281+
2282+
**Type:** : `array of string`
2283+
2284+
**Default:** `[]`
2285+
2286+
**Example setting the default value** ([]):
2287+
```
2288+
[RPC.ApiRelay]
2289+
RPCs=[]
2290+
```
2291+
22442292
## <a name="Synchronizer"></a>9. `[Synchronizer]`
22452293

22462294
**Type:** : `object`

docs/config-file/node-config-schema.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,28 @@
861861
"additionalProperties": false,
862862
"type": "object",
863863
"description": "ApiAuthentication defines the authentication configuration for the API"
864+
},
865+
"ApiRelay": {
866+
"properties": {
867+
"Enabled": {
868+
"type": "boolean",
869+
"default": false
870+
},
871+
"DestURI": {
872+
"type": "string",
873+
"default": ""
874+
},
875+
"RPCs": {
876+
"items": {
877+
"type": "string"
878+
},
879+
"type": "array",
880+
"default": []
881+
}
882+
},
883+
"additionalProperties": false,
884+
"type": "object",
885+
"description": "ApiRelay defines the relay configuration for the API"
864886
}
865887
},
866888
"additionalProperties": false,

jsonrpc/api_relay_xlayer.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package jsonrpc
2+
3+
import (
4+
"github.com/0xPolygonHermez/zkevm-node/jsonrpc/client"
5+
"github.com/0xPolygonHermez/zkevm-node/jsonrpc/metrics"
6+
"github.com/0xPolygonHermez/zkevm-node/jsonrpc/types"
7+
"github.com/0xPolygonHermez/zkevm-node/log"
8+
)
9+
10+
// ApiRelayConfig is the api relay config
11+
type ApiRelayConfig struct {
12+
Enabled bool `mapstructure:"Enabled"`
13+
DestURI string `mapstructure:"DestURI"`
14+
RPCs []string `mapstructure:"RPCs"`
15+
}
16+
17+
func shouldRelay(localCfg ApiRelayConfig, name string) bool {
18+
enable := localCfg.Enabled && localCfg.DestURI != ""
19+
contained := types.Contains(localCfg.RPCs, name)
20+
if getApolloConfig().Enable() {
21+
getApolloConfig().RLock()
22+
defer getApolloConfig().RUnlock()
23+
enable = getApolloConfig().ApiRelay.Enabled && getApolloConfig().ApiRelay.DestURI != ""
24+
contained = types.Contains(getApolloConfig().ApiRelay.RPCs, name)
25+
}
26+
27+
return enable && contained
28+
}
29+
30+
func getRelayDestURI(localDestURI string) string {
31+
ret := localDestURI
32+
if getApolloConfig().Enable() {
33+
getApolloConfig().RLock()
34+
defer getApolloConfig().RUnlock()
35+
36+
ret = getApolloConfig().ApiRelay.DestURI
37+
}
38+
39+
return ret
40+
}
41+
42+
func tryRelay(localCfg ApiRelayConfig, request types.Request) (types.Response, bool) {
43+
if shouldRelay(localCfg, request.Method) {
44+
destURI := getRelayDestURI(localCfg.DestURI)
45+
res, err := client.JSONRPCRelay(destURI, request)
46+
if err != nil {
47+
log.Errorf("failed to relay %v to %s: %v", request.Method, destURI, err)
48+
metrics.RequestRelayFailCount(request.Method)
49+
return types.Response{}, false
50+
}
51+
52+
return res, true
53+
}
54+
55+
return types.Response{}, false
56+
}

jsonrpc/apollo_xlayer.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import (
88

99
// ApolloConfig is the apollo RPC dynamic config
1010
type ApolloConfig struct {
11-
EnableApollo bool `json:"enable"`
12-
BatchRequestsEnabled bool `json:"batchRequestsEnabled"`
13-
BatchRequestsLimit uint `json:"batchRequestsLimit"`
14-
GasLimitFactor float64 `json:"gasLimitFactor"`
15-
DisableAPIs []string `json:"disableAPIs"`
16-
RateLimit RateLimitConfig `json:"rateLimit"`
17-
DynamicGP DynamicGPConfig `json:"dynamicGP"`
18-
ApiAuthentication ApiAuthConfig `json:"apiAuthentication"`
11+
EnableApollo bool
12+
BatchRequestsEnabled bool
13+
BatchRequestsLimit uint
14+
GasLimitFactor float64
15+
DisableAPIs []string
16+
RateLimit RateLimitConfig
17+
DynamicGP DynamicGPConfig
18+
ApiAuthentication ApiAuthConfig
19+
ApiRelay ApiRelayConfig
1920

2021
sync.RWMutex
2122
}
@@ -45,6 +46,16 @@ func (c *ApolloConfig) setDisableAPIs(disableAPIs []string) {
4546
copy(c.DisableAPIs, disableAPIs)
4647
}
4748

49+
func (c *ApolloConfig) setApiRelayCfg(apiRelayCfg ApiRelayConfig) {
50+
if c == nil || !c.EnableApollo {
51+
return
52+
}
53+
c.ApiRelay.Enabled = apiRelayCfg.Enabled
54+
c.ApiRelay.DestURI = apiRelayCfg.DestURI
55+
c.ApiRelay.RPCs = make([]string, len(apiRelayCfg.RPCs))
56+
copy(c.ApiRelay.RPCs, apiRelayCfg.RPCs)
57+
}
58+
4859
// UpdateConfig updates the apollo config
4960
func UpdateConfig(apolloConfig Config) {
5061
getApolloConfig().Lock()
@@ -56,6 +67,7 @@ func UpdateConfig(apolloConfig Config) {
5667
setRateLimit(apolloConfig.RateLimit)
5768
setApiAuth(apolloConfig.ApiAuthentication)
5869
getApolloConfig().DynamicGP = apolloConfig.DynamicGP
70+
getApolloConfig().setApiRelayCfg(apolloConfig.ApiRelay)
5971
getApolloConfig().Unlock()
6072
}
6173

jsonrpc/client/client_xlayer.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package client
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
9+
"github.com/0xPolygonHermez/zkevm-node/jsonrpc/types"
10+
)
11+
12+
// JSONRPCRelay executes a 2.0 JSON RPC HTTP Post Request to the provided URL with
13+
// types.Request, which is compatible with the Ethereum
14+
// JSON RPC Server.
15+
func JSONRPCRelay(url string, request types.Request) (types.Response, error) {
16+
httpRes, err := sendJSONRPC_HTTPRequest(url, request)
17+
if err != nil {
18+
return types.Response{}, err
19+
}
20+
21+
resBody, err := io.ReadAll(httpRes.Body)
22+
if err != nil {
23+
return types.Response{}, err
24+
}
25+
defer httpRes.Body.Close()
26+
27+
if httpRes.StatusCode != http.StatusOK {
28+
return types.Response{}, fmt.Errorf("%v - %v", httpRes.StatusCode, string(resBody))
29+
}
30+
31+
var res types.Response
32+
err = json.Unmarshal(resBody, &res)
33+
if err != nil {
34+
return types.Response{}, err
35+
}
36+
return res, nil
37+
}

jsonrpc/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ type Config struct {
106106

107107
// ApiAuthentication defines the authentication configuration for the API
108108
ApiAuthentication ApiAuthConfig `mapstructure:"ApiAuthentication"`
109+
110+
// ApiRelay defines the relay configuration for the API
111+
ApiRelay ApiRelayConfig `mapstructure:"ApiRelay"`
109112
}
110113

111114
// ZKCountersLimits defines the ZK Counter limits

jsonrpc/handler.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ type handleRequest struct {
6464
// check the `eth.go` file for more example on how the methods are implemented
6565
type Handler struct {
6666
serviceMap map[string]*serviceData
67+
cfg Config
6768
}
6869

6970
func newJSONRpcHandler() *Handler {
@@ -164,7 +165,12 @@ func (h *Handler) HandleWs(reqBody []byte, wsConn *concurrentWsConn, httpReq *ht
164165
return types.NewResponse(req, nil, types.NewRPCError(types.InvalidParamsErrorCode, "server is too busy")).Bytes()
165166
}
166167

167-
return h.Handle(handleReq).Bytes()
168+
response, relayed := tryRelay(h.cfg.ApiRelay, req)
169+
if !relayed {
170+
response = h.Handle(handleReq)
171+
}
172+
173+
return response.Bytes()
168174
}
169175

170176
// RemoveFilterByWsConn uninstalls the filter attached to this websocket connection

jsonrpc/handler_xlayer.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package jsonrpc
2+
3+
func (h *Handler) setCfg(cfg Config) {
4+
h.cfg = cfg
5+
}

jsonrpc/metrics/metrics_xlayer.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ var (
1515
requestInnerTxAddErrorCount = requestPrefix + "inner_tx_error_count"
1616
requestAuthCountName = requestPrefix + "auth_count"
1717
requestAuthErrorCountName = requestPrefix + "auth_error_count"
18+
requestRelayFailCountName = requestPrefix + "relay_fail_count"
1819

1920
wsRequestPrefix = prefix + "ws_request_"
2021
requestWsMethodName = wsRequestPrefix + "method"
@@ -108,6 +109,13 @@ var (
108109
},
109110
Labels: []string{"type"},
110111
},
112+
{
113+
CounterOpts: prometheus.CounterOpts{
114+
Name: requestRelayFailCountName,
115+
Help: "[JSONRPC] number of relay fail requests",
116+
},
117+
Labels: []string{"type"},
118+
},
111119
}
112120
)
113121

@@ -179,3 +187,8 @@ func RequestAuthCount(project string) {
179187
func RequestAuthErrorCount(tp RequestAuthErrorType) {
180188
metrics.CounterVecInc(requestAuthErrorCountName, string(tp))
181189
}
190+
191+
// RequestRelayFailCount increments the requests handled counter vector by one for the given project.
192+
func RequestRelayFailCount(method string) {
193+
metrics.CounterVecInc(requestRelayFailCountName, method)
194+
}

jsonrpc/server.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ func NewServer(
7979
}
8080

8181
handler := newJSONRpcHandler()
82+
handler.setCfg(cfg)
8283

8384
for _, service := range services {
8485
handler.registerService(service)
@@ -322,8 +323,11 @@ func (s *Server) handleSingleRequest(httpRequest *http.Request, w http.ResponseW
322323
defer metrics.RequestMethodCount(request.Method)
323324
defer metrics.RequestMethodDuration(request.Method, st)
324325

325-
req := handleRequest{Request: request, HttpRequest: httpRequest}
326-
response := s.handler.Handle(req)
326+
response, relayed := tryRelay(s.config.ApiRelay, request)
327+
if !relayed {
328+
req := handleRequest{Request: request, HttpRequest: httpRequest}
329+
response = s.handler.Handle(req)
330+
}
327331

328332
respBytes, err := json.Marshal(response)
329333
if err != nil {
@@ -373,8 +377,11 @@ func (s *Server) handleBatchRequest(httpRequest *http.Request, w http.ResponseWr
373377
}
374378
st := time.Now()
375379
metrics.RequestMethodCount(request.Method)
376-
req := handleRequest{Request: request, HttpRequest: httpRequest}
377-
response := s.handler.Handle(req)
380+
response, relayed := tryRelay(s.config.ApiRelay, request)
381+
if !relayed {
382+
req := handleRequest{Request: request, HttpRequest: httpRequest}
383+
response = s.handler.Handle(req)
384+
}
378385
responses = append(responses, response)
379386
metrics.RequestMethodDuration(request.Method, st)
380387
}

jsonrpc/server_xlayer.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ func (s *Server) handleWsBatch(httpRequest *http.Request, wsConn *concurrentWsCo
8484
st := time.Now()
8585
metrics.RequestMethodCount(request.Method)
8686
req := handleRequest{Request: request, wsConn: wsConn, HttpRequest: httpRequest}
87-
response := s.handler.Handle(req)
87+
response, relayed := tryRelay(s.config.ApiRelay, request)
88+
if !relayed {
89+
response = s.handler.Handle(req)
90+
}
8891
responses = append(responses, response)
8992
metrics.RequestMethodDuration(request.Method, st)
9093
}

0 commit comments

Comments
 (0)