11package gateway
22
33import (
4- "context"
54 "bytes"
5+ "context"
66 "encoding/json"
77 "fmt"
88 "io"
99 "log"
10+ "math"
1011 "net/http"
1112 "os"
1213 "os/signal"
14+ "strconv"
15+ "strings"
1316 "sync/atomic"
1417 "syscall"
1518 "time"
1619
17- // "github.com/cometbft/cometbft/rpc/jsonrpc/types"
1820 "github.com/decentrio/gateway/config"
21+ "github.com/decentrio/gateway/utils"
1922)
2023
24+ // Error type
25+ type JSONRPCError struct {
26+ Code int `json:"code"`
27+ Message string `json:"message"`
28+ }
29+
2130// JSON-RPC request format
2231type JSONRPCRequest struct {
23- JSONRPC string `json:"jsonrpc"`
24- Method string `json:"method "`
25- Params interface {} `json:"params "`
26- ID int `json:"id "`
32+ JSONRPC string `json:"jsonrpc"`
33+ ID int `json:"id "`
34+ Method string `json:"method "`
35+ Params json. RawMessage `json:"params "`
2736}
2837
2938// JSON-RPC response format
3039type JSONRPCResponse struct {
3140 JSONRPC string `json:"jsonrpc"`
32- Result interface {} `json:"result"`
33- Error interface {} `json:"error,omitempty"`
3441 ID int `json:"id"`
42+ Result any `json:"result,omitempty"`
43+ Error * JSONRPCError `json:"error,omitempty"`
3544}
3645
3746var (
@@ -117,74 +126,213 @@ func trackRequestsMiddleware(next http.HandlerFunc) http.HandlerFunc {
117126}
118127
119128func handleJSONRPC (w http.ResponseWriter , r * http.Request ) {
120- var node * config.Node
129+ var req JSONRPCRequest
130+ var res JSONRPCResponse
121131 if r .Method != http .MethodPost {
122- http .Error (w , "Invalid request method" , http .StatusMethodNotAllowed )
132+ res = JSONRPCResponse {
133+ JSONRPC : "2.0" ,
134+ Error : & JSONRPCError {Code : - 32600 , Message : "Invalid request" },
135+ ID : 1 ,
136+ }
137+ json .NewEncoder (w ).Encode (res )
123138 return
124139 }
125140
126141 body , err := io .ReadAll (r .Body )
127142 if err != nil {
128- http .Error (w , "Failed to read request body" , http .StatusInternalServerError )
143+ res = JSONRPCResponse {
144+ JSONRPC : "2.0" ,
145+ Error : & JSONRPCError {Code : - 32600 , Message : "Parse error. Invalid JSON: " + err .Error ()},
146+ ID : 1 ,
147+ }
148+ json .NewEncoder (w ).Encode (res )
129149 return
130150 }
131- r .Body .Close ()
151+ r .Body = io .NopCloser (bytes .NewReader (body ))
152+ r .Header .Set ("Content-Type" , "application/json" )
153+ r .ContentLength = int64 (len (body ))
132154
133- var req JSONRPCRequest
134155 err = json .Unmarshal (body , & req )
135156 if err != nil {
136- http .Error (w , "Invalid JSON-RPC request" , http .StatusBadRequest )
157+ res = JSONRPCResponse {
158+ JSONRPC : "2.0" ,
159+ Error : & JSONRPCError {Code : - 32600 , Message : "Invalid JSON-RPC request: " + err .Error ()},
160+ ID : - 32700 ,
161+ }
162+ json .NewEncoder (w ).Encode (res )
137163 return
138164 }
139165
140- fmt .Printf ("Received JSON-RPC request: Method=%s, Params=%v, ID=%d\n " , req .Method , req .Params , req .ID )
166+ fmt .Printf ("Received JSON-RPC request: Method=%s, Params=%v\n " , req .Method , req .Params )
167+ paramsMap := make ([]any , len (req .Params ))
168+ json .Unmarshal (req .Params , & paramsMap )
169+ var height uint64 = math .MaxUint64
170+
171+ switch req .Method {
172+ case "eth_getTransactionByHash" , // tx hash in params
173+ "eth_getTransactionReceipt" ,
174+ "eth_getBlockByHash" , // block hash in params
175+ "eth_getBlockTransactionCountByHash" ,
176+ "eth_getTransactionByBlockHashAndIndex" ,
177+ "eth_getUncleByBlockHashAndIndex" :
178+ checkRequestManually (w , r )
179+ return
180+ case "eth_newFilter" , /// ????
181+ "eth_getLogs" :
182+ res = JSONRPCResponse {
183+ JSONRPC : "2.0" ,
184+ Error : & JSONRPCError {Code : - 32600 , Message : "Method not supported yet" },
185+ ID : 1 ,
186+ }
187+ json .NewEncoder (w ).Encode (res )
188+ return
189+ case "eth_getBalance" ,// param 1
190+ "eth_getTransactionCount" ,
191+ "eth_getCode" ,
192+ "eth_call" :
193+ height , err = getHeightFromParams (paramsMap , 1 )
194+ if err != nil {
195+ res = JSONRPCResponse {
196+ JSONRPC : "2.0" ,
197+ Error : & JSONRPCError {Code : - 32600 , Message : err .Error ()},
198+ ID : 1 ,
199+ }
200+ json .NewEncoder (w ).Encode (res )
201+ return
202+ }
203+ case "eth_getStorageAt" : // param 2
204+ height , err = getHeightFromParams (paramsMap , 2 )
205+ if err != nil {
206+ res = JSONRPCResponse {
207+ JSONRPC : "2.0" ,
208+ Error : & JSONRPCError {Code : - 32600 , Message : err .Error ()},
209+ ID : 1 ,
210+ }
211+ json .NewEncoder (w ).Encode (res )
212+ return
213+ }
214+ case "eth_getBlockTransactionCountByNumber" , // param 0
215+ "eth_getBlockByNumber" ,
216+ "eth_getTransactionByBlockNumberAndIndex" ,
217+ "eth_getUncleByBlockNumberAndIndex" :
218+ height , err = getHeightFromParams (paramsMap , 0 )
219+ if err != nil {
220+ res = JSONRPCResponse {
221+ JSONRPC : "2.0" ,
222+ Error : & JSONRPCError {Code : - 32600 , Message : err .Error ()},
223+ ID : 1 ,
224+ }
225+ json .NewEncoder (w ).Encode (res )
226+ return
227+ }
228+ default :
229+ height = 0
230+ }
141231
142- var height uint64
143- if params , ok := req .Params .(map [string ]interface {}); ok {
144- if h , ok := params ["height" ].(float64 ); ok {
145- height = uint64 (h )
232+ fmt .Printf ("Height: %d\n " , height )
233+ node := config .GetNodebyHeight (height )
234+ if node == nil {
235+ res = JSONRPCResponse {
236+ JSONRPC : "2.0" ,
237+ Error : & JSONRPCError {Code : - 32602 , Message : "No nodes found" },
238+ ID : 1 ,
146239 }
240+
241+ json .NewEncoder (w ).Encode (res )
242+ return
147243 }
244+ fmt .Println ("Node called:" , node .JSONRPC )
245+ httpUtils .FowardRequest (w , r , node .JSONRPC )
246+ }
148247
149248
150- if height > 0 {
151- node = config .GetNodebyHeight (height )
152- if node == nil {
153- http .Error (w , "Node not found" , http .StatusNotFound )
154- return
249+ func getHeightFromParams (params []any , index int ) (uint64 , error ) {
250+ if len (params ) > index {
251+ height , found := params [index ].(string )
252+ if found {
253+ if height == "latest" || height == "pending" {
254+ return 0 , nil
255+ } else if height == "earliest" {
256+ return 1 , nil // temporary, should be earliest possible
257+ } else if strings .HasPrefix (height , "0x" ) {
258+ height = strings .TrimPrefix (height , "0x" )
259+ if h , err := strconv .ParseUint (height , 16 , 64 ); err == nil {
260+ return h , nil
261+ } else {
262+ return math .MaxUint64 , fmt .Errorf ("invalid height parameter: %w" , err )
263+ }
264+ } else {
265+ return math .MaxUint64 , fmt .Errorf ("invalid height parameter" )
266+ }
267+ } else {
268+ return math .MaxUint64 , fmt .Errorf ("height not found" )
155269 }
270+ } else {
271+ return math .MaxUint64 , fmt .Errorf ("invalid params" )
156272 }
273+ }
157274
158- if node != nil {
159- fmt .Printf ("Forwarding to Node:" , node .JSONRPC )
160275
161- reqForward , err := http .NewRequest ("POST" , node .JSONRPC , bytes .NewReader (body ))
162- if err != nil {
163- http .Error (w , "Failed to create request" , http .StatusInternalServerError )
164- return
165- }
276+ func checkRequestManually (w http.ResponseWriter , r * http.Request ) {
277+ ETH_nodes := config .GetNodesByType ("jsonrpc" )
278+ var msg JSONRPCResponse
166279
167- reqForward .Header .Set ("Content-Type" , "application/json" )
280+ bodyBytes , err := io .ReadAll (r .Body )
281+ if err != nil {
282+ msg = JSONRPCResponse {
283+ JSONRPC : "2.0" ,
284+ Error : & JSONRPCError {Code : - 32600 , Message : "Parse error. Invalid JSON: " + err .Error ()},
285+ ID : 1 ,
286+ }
287+ json .NewEncoder (w ).Encode (msg )
288+ return
289+ }
168290
169- client := & http.Client {}
170- resp , err := client .Do (reqForward )
291+ for _ , url := range ETH_nodes {
292+ new_r := r .Clone (r .Context ())
293+ new_r .Body = io .NopCloser (bytes .NewReader (bodyBytes ))
294+ res , err := httpUtils .CheckRequest (new_r , url )
171295 if err != nil {
172- http .Error (w , "Failed to forward request" , http .StatusBadGateway )
173- return
296+ continue
174297 }
175- defer resp .Body .Close ()
176298
177- w .WriteHeader (resp .StatusCode )
178- io .Copy (w , resp .Body )
179- return
299+ if res == nil {
300+ continue
301+ } else { // node always returns a 200 response
302+ fmt .Println ("Node called:" , url )
303+ if res .Body != nil {
304+ body , err := io .ReadAll (res .Body )
305+ if err != nil {
306+ msg = JSONRPCResponse {
307+ JSONRPC : "2.0" ,
308+ Error : & JSONRPCError {Code : - 32600 , Message : "Parse error. Invalid JSON: " + err .Error ()},
309+ ID : 1 ,
310+ }
311+ json .NewEncoder (w ).Encode (msg )
312+ return
313+ }
314+
315+ json .Unmarshal (body , & msg )
316+ defer res .Body .Close ()
317+ }
318+
319+ if msg .Error != nil || msg .Result != nil { // errors in handling request, all the nodes will return the same
320+ json .NewEncoder (w ).Encode (msg )
321+ return
322+ } else if msg .Result == nil {
323+ fmt .Println ("Result is empty" )
324+ continue
325+ }
326+ }
180327 }
181328
182- resp := JSONRPCResponse {
183- JSONRPC : "2.0" ,
184- Result : fmt .Sprintf ("Method %s executed successfully" , req .Method ),
185- ID : req .ID ,
329+ if msg .Result == nil { // after all loop, result is still nil?
330+ nil_msg := map [string ]interface {}{
331+ "jsonrpc" : msg .JSONRPC ,
332+ "id" : msg .ID ,
333+ "result" : msg .Result ,
334+ }
335+ json .NewEncoder (w ).Encode (nil_msg )
336+ return
186337 }
187-
188- w .Header ().Set ("Content-Type" , "application/json" )
189- json .NewEncoder (w ).Encode (resp )
190- }
338+ }
0 commit comments