translate only methods with block params

This commit is contained in:
Felipe Andrade 2023-06-02 13:07:18 -07:00
parent 924030dd70
commit a3cf38f8b2
4 changed files with 149 additions and 108 deletions

@ -95,43 +95,36 @@ To support backends with different specifications in the same backend group,
proxyd exposes a convenient method to fetch receipts abstracting away proxyd exposes a convenient method to fetch receipts abstracting away
what specific backend will serve the request. what specific backend will serve the request.
Each backend can specify their preferred method to fetch receipts with `consensus_receipts_target`. Each backend specifies their preferred method to fetch receipts with `consensus_receipts_target` config,
which will be translated from `consensus_getReceipts`.
This method takes **both** the blockNumberOrHash **and** list of transaction hashes to fetch the receipts, This method takes a `blockNumberOrHash` (i.e. `tag|qty|hash`)
and then after selecting the backend to serve the request, and returns the receipts for all transactions in the block.
it translates to the correct target with the appropriate parameters.
Note that only one of the parameters will be actually used depending on the target. Request example
Request params
```json ```json
{ {
"jsonrpc":"2.0", "jsonrpc":"2.0",
"id": 1, "id": 1,
"params": { "params": ["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"]
"blockNumberOrHash": "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b",
"transactions": [
"0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
"0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b"
]
}
} }
``` ```
It currently supports translation to the following targets: It currently supports translation to the following targets:
* `debug_getRawReceipts(blockOrHash)` (default) * `debug_getRawReceipts(blockOrHash)` (default)
* `alchemy_getTransactionReceipts(blockOrHash)` * `alchemy_getTransactionReceipts(blockOrHash)`
* `eth_getTransactionReceipt(txHash)` batched * `parity_getBlockReceipts(blockOrHash)`
* `eth_getBlockReceipts(blockOrHash)`
The selected target is returned in the response, in a wrapped result. The selected target is returned in the response, in a wrapped result.
Response Response example
```json ```json
{ {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": 1, "id": 1,
"result": { "result": {
"method": "eth_getTransactionReceipt", "method": "debug_getRawReceipts",
"result": { "result": {
// the actual raw result from backend // the actual raw result from backend
} }

@ -17,11 +17,9 @@ import (
"sync" "sync"
"time" "time"
sw "github.com/ethereum-optimism/optimism/proxyd/pkg/avg-sliding-window"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/google/uuid"
sw "github.com/ethereum-optimism/optimism/proxyd/pkg/avg-sliding-window"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@ -103,6 +101,7 @@ var (
ErrBackendUnexpectedJSONRPC = errors.New("backend returned an unexpected JSON-RPC response") ErrBackendUnexpectedJSONRPC = errors.New("backend returned an unexpected JSON-RPC response")
ErrConsensusGetReceiptsCantBeBatched = errors.New("consensus_getReceipts cannot be batched") ErrConsensusGetReceiptsCantBeBatched = errors.New("consensus_getReceipts cannot be batched")
ErrConsensusGetReceiptsInvalidTarget = errors.New("unsupported consensus_receipts_target")
) )
func ErrInvalidRequest(msg string) *RPCErr { func ErrInvalidRequest(msg string) *RPCErr {
@ -252,20 +251,28 @@ type indexedReqRes struct {
} }
const ConsensusGetReceiptsMethod = "consensus_getReceipts" const ConsensusGetReceiptsMethod = "consensus_getReceipts"
const ReceiptsTargetEthTransactionReceipt = "eth_getTransactionReceipt"
const ReceiptsTargetDebugGetRawReceipts = "debug_getRawReceipts"
const ReceiptsTargetGetTransactionReceipts = "alchemy_getTransactionReceipts"
type ConsensusGetReceiptsReq struct { const ReceiptsTargetDebugGetRawReceipts = "debug_getRawReceipts"
const ReceiptsTargetAlchemyGetTransactionReceipts = "alchemy_getTransactionReceipts"
const ReceiptsTargetParityGetTransactionReceipts = "parity_getBlockReceipts"
const ReceiptsTargetEthGetTransactionReceipts = "eth_getBlockReceipts"
type ConsensusGetReceiptsRequest struct {
BlockOrHash *rpc.BlockNumberOrHash `json:"blockOrHash"` BlockOrHash *rpc.BlockNumberOrHash `json:"blockOrHash"`
Transactions []common.Hash `json:"transactions"` Transactions []common.Hash `json:"transactions"`
} }
type ConsensusGetReceiptsRes struct { type ConsensusGetReceiptsResult struct {
Method string `json:"method"` Method string `json:"method"`
Result interface{} `json:"result"` Result interface{} `json:"result"`
} }
// BlockHashOrNumberParameter is a non-conventional wrapper used by alchemy_getTransactionReceipts
type BlockHashOrNumberParameter struct {
BlockHash *common.Hash `json:"blockHash"`
BlockNumber *rpc.BlockNumber `json:"blockNumber"`
}
func NewBackend( func NewBackend(
name string, name string,
rpcURL string, rpcURL string,
@ -331,12 +338,19 @@ func (b *Backend) Forward(ctx context.Context, reqs []*RPCReq, isBatch bool) ([]
switch err { switch err {
case nil: // do nothing case nil: // do nothing
case ErrConsensusGetReceiptsCantBeBatched: case ErrConsensusGetReceiptsCantBeBatched:
log.Debug( log.Warn(
"Received unsupported batch request for consensus_getReceipts", "Received unsupported batch request for consensus_getReceipts",
"name", b.Name, "name", b.Name,
"req_id", GetReqID(ctx), "req_id", GetReqID(ctx),
"err", err, "err", err,
) )
case ErrConsensusGetReceiptsInvalidTarget:
log.Error(
"Unsupported consensus_receipts_target for consensus_getReceipts",
"name", b.Name,
"req_id", GetReqID(ctx),
"err", err,
)
// ErrBackendUnexpectedJSONRPC occurs because infura responds with a single JSON-RPC object // ErrBackendUnexpectedJSONRPC occurs because infura responds with a single JSON-RPC object
// to a batch request whenever any Request Object in the batch would induce a partial error. // to a batch request whenever any Request Object in the batch would induce a partial error.
// We don't label the backend offline in this case. But the error is still returned to // We don't label the backend offline in this case. But the error is still returned to
@ -414,58 +428,56 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
// we are concerned about network error rates, so we record 1 request independently of how many are in the batch // we are concerned about network error rates, so we record 1 request independently of how many are in the batch
b.networkRequestsSlidingWindow.Incr() b.networkRequestsSlidingWindow.Incr()
originalRequests := rpcReqs
translatedReqs := make(map[string]*RPCReq, len(rpcReqs)) translatedReqs := make(map[string]*RPCReq, len(rpcReqs))
derivedRequests := make([]*RPCReq, 0)
// translate consensus_getReceipts to receipts target // translate consensus_getReceipts to receipts target
// right now we only support non-batched // right now we only support non-batched
if !isBatch { if isBatch {
for _, rpcReq := range rpcReqs {
if rpcReq.Method == ConsensusGetReceiptsMethod {
return nil, ErrConsensusGetReceiptsCantBeBatched
}
}
} else {
for _, rpcReq := range rpcReqs { for _, rpcReq := range rpcReqs {
if rpcReq.Method == ConsensusGetReceiptsMethod { if rpcReq.Method == ConsensusGetReceiptsMethod {
translatedReqs[string(rpcReq.ID)] = rpcReq translatedReqs[string(rpcReq.ID)] = rpcReq
rpcReq.Method = b.receiptsTarget rpcReq.Method = b.receiptsTarget
var reqParams []ConsensusGetReceiptsReq var reqParams []ConsensusGetReceiptsRequest
err := json.Unmarshal(rpcReq.Params, &reqParams) err := json.Unmarshal(rpcReq.Params, &reqParams)
if err != nil { if err != nil {
return nil, ErrInvalidRequest("invalid request") return nil, ErrInvalidRequest("invalid request")
} }
bnh := reqParams[0].BlockOrHash bnh := reqParams[0].BlockOrHash
switch b.receiptsTarget {
var translatedParams []byte
switch rpcReq.Method {
case ReceiptsTargetDebugGetRawReceipts, case ReceiptsTargetDebugGetRawReceipts,
ReceiptsTargetGetTransactionReceipts: // block or hash ReceiptsTargetEthGetTransactionReceipts,
ReceiptsTargetParityGetTransactionReceipts:
// conventional methods use an array of strings having either block number or block hash
// i.e. ["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"]
params := make([]string, 1) params := make([]string, 1)
if bnh.BlockNumber != nil { if bnh.BlockNumber != nil {
params[0] = bnh.BlockNumber.String() params[0] = bnh.BlockNumber.String()
} else { } else {
params[0] = bnh.BlockHash.Hex() params[0] = bnh.BlockHash.Hex()
} }
rawParams := mustMarshalJSON(params) translatedParams = mustMarshalJSON(params)
rpcReq.Params = rawParams case ReceiptsTargetAlchemyGetTransactionReceipts:
case ReceiptsTargetEthTransactionReceipt: // list of tx hashes // alchemy uses an array of object with either block number or block hash
for _, txHash := range reqParams[0].Transactions { // i.e. [{ blockHash: "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b" }]
params := make([]common.Hash, 1) params := make([]BlockHashOrNumberParameter, 1)
params[0] = txHash if bnh.BlockNumber != nil {
rawParams := mustMarshalJSON(params) params[0].BlockNumber = bnh.BlockNumber
randomID := mustMarshalJSON(uuid.New().String())
dReq := &RPCReq{
JSONRPC: rpcReq.JSONRPC,
Method: ReceiptsTargetEthTransactionReceipt,
Params: rawParams,
ID: randomID,
}
derivedRequests = append(derivedRequests, dReq)
}
}
}
}
// replace the original request with the derived requests
if len(derivedRequests) > 0 {
rpcReqs = derivedRequests
}
} else { } else {
for _, rpcReq := range rpcReqs { params[0].BlockHash = bnh.BlockHash
if rpcReq.Method == ConsensusGetReceiptsMethod { }
return nil, ErrConsensusGetReceiptsCantBeBatched translatedParams = mustMarshalJSON(params)
default:
return nil, ErrConsensusGetReceiptsInvalidTarget
}
rpcReq.Params = translatedParams
} }
} }
} }
@ -582,33 +594,15 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
for _, res := range rpcRes { for _, res := range rpcRes {
translatedReq, exist := translatedReqs[string(res.ID)] translatedReq, exist := translatedReqs[string(res.ID)]
if exist { if exist {
res.Result = ConsensusGetReceiptsRes{ res.Result = ConsensusGetReceiptsResult{
Method: translatedReq.Method, Method: translatedReq.Method,
Result: res.Result, Result: res.Result,
} }
} }
} }
sortBatchRPCResponse(rpcReqs, rpcRes) sortBatchRPCResponse(rpcReqs, rpcRes)
// if the translated requests originated derived requests, wrap their results
if len(derivedRequests) > 0 {
results := make([]interface{}, 0, len(rpcRes))
for _, res := range rpcRes {
results = append(results, res.Result)
}
wrappedRes := &RPCRes{
JSONRPC: originalRequests[0].JSONRPC,
Result: ConsensusGetReceiptsRes{
Method: rpcReqs[0].Method,
Result: results,
},
ID: originalRequests[0].ID,
}
rpcRes = []*RPCRes{wrappedRes}
}
return rpcRes, nil return rpcRes, nil
} }
@ -728,10 +722,9 @@ func (bg *BackendGroup) Forward(ctx context.Context, rpcReqs []*RPCReq, isBatch
if len(rpcReqs) > 0 { if len(rpcReqs) > 0 {
res, err = back.Forward(ctx, rpcReqs, isBatch) res, err = back.Forward(ctx, rpcReqs, isBatch)
if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) { if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
return nil, err errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) ||
} errors.Is(err, ErrMethodNotWhitelisted) {
if errors.Is(err, ErrMethodNotWhitelisted) {
return nil, err return nil, err
} }
if errors.Is(err, ErrBackendOffline) { if errors.Is(err, ErrBackendOffline) {

@ -788,6 +788,10 @@ func TestConsensus(t *testing.T) {
t.Run("translate consensus_getReceipts to debug_getRawReceipts", func(t *testing.T) { t.Run("translate consensus_getReceipts to debug_getRawReceipts", func(t *testing.T) {
reset() reset()
useOnlyNode1() useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts", []interface{}{map[string]interface{}{ resRaw, statusCode, err := client.SendRPC("consensus_getReceipts", []interface{}{map[string]interface{}{
"blockOrHash": "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"}}) "blockOrHash": "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"}})
@ -811,6 +815,10 @@ func TestConsensus(t *testing.T) {
t.Run("translate consensus_getReceipts to debug_getRawReceipts with latest block tag", func(t *testing.T) { t.Run("translate consensus_getReceipts to debug_getRawReceipts with latest block tag", func(t *testing.T) {
reset() reset()
useOnlyNode1() useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts", []interface{}{map[string]interface{}{ resRaw, statusCode, err := client.SendRPC("consensus_getReceipts", []interface{}{map[string]interface{}{
"blockOrHash": "latest"}}) "blockOrHash": "latest"}})
@ -834,6 +842,10 @@ func TestConsensus(t *testing.T) {
t.Run("translate consensus_getReceipts to debug_getRawReceipts with block number", func(t *testing.T) { t.Run("translate consensus_getReceipts to debug_getRawReceipts with block number", func(t *testing.T) {
reset() reset()
useOnlyNode1() useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts", []interface{}{map[string]interface{}{ resRaw, statusCode, err := client.SendRPC("consensus_getReceipts", []interface{}{map[string]interface{}{
"blockOrHash": "0x55"}}) "blockOrHash": "0x55"}})
@ -854,9 +866,13 @@ func TestConsensus(t *testing.T) {
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"]) require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
}) })
t.Run("translate consensus_getReceipts to alchemy_getTransactionReceipts", func(t *testing.T) { t.Run("translate consensus_getReceipts to alchemy_getTransactionReceipts with block hash", func(t *testing.T) {
reset() reset()
useOnlyNode1() useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("alchemy_getTransactionReceipts")) nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("alchemy_getTransactionReceipts"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts")) defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
@ -871,7 +887,7 @@ func TestConsensus(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", reqJsonMap["method"]) require.Equal(t, "alchemy_getTransactionReceipts", reqJsonMap["method"])
require.Equal(t, "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", reqJsonMap["params"].([]interface{})[0]) require.Equal(t, "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", reqJsonMap["params"].([]interface{})[0].(map[string]interface{})["blockHash"])
var resJsonMap map[string]interface{} var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap) err = json.Unmarshal(resRaw, &resJsonMap)
@ -881,47 +897,79 @@ func TestConsensus(t *testing.T) {
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"]) require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
}) })
t.Run("translate consensus_getReceipts to eth_getTransactionReceipt batched", func(t *testing.T) { t.Run("translate consensus_getReceipts to alchemy_getTransactionReceipts with block number", func(t *testing.T) {
reset() reset()
useOnlyNode1() useOnlyNode1()
update()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("eth_getTransactionReceipt")) // reset request counts
nodes["node1"].mockBackend.Reset()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("alchemy_getTransactionReceipts"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts")) defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts", []interface{}{map[string]interface{}{ resRaw, statusCode, err := client.SendRPC("consensus_getReceipts", []interface{}{map[string]interface{}{
"blockOrHash": "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", "blockOrHash": "0x55"}})
"transactions": []string{
"0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5",
"0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c6",
"0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c7",
"0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c8",
}}})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 200, statusCode) require.Equal(t, 200, statusCode)
var reqJsonMap []map[string]interface{} var reqJsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &reqJsonMap) err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &reqJsonMap)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 4, len(reqJsonMap)) require.Equal(t, "alchemy_getTransactionReceipts", reqJsonMap["method"])
for _, req := range reqJsonMap { require.Equal(t, "0x55", reqJsonMap["params"].([]interface{})[0].(map[string]interface{})["blockNumber"])
require.Equal(t, "eth_getTransactionReceipt", req["method"])
}
require.Equal(t, "0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5", reqJsonMap[0]["params"].([]interface{})[0])
require.Equal(t, "0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c6", reqJsonMap[1]["params"].([]interface{})[0])
require.Equal(t, "0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c7", reqJsonMap[2]["params"].([]interface{})[0])
require.Equal(t, "0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c8", reqJsonMap[3]["params"].([]interface{})[0])
var resJsonMap map[string]interface{} var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap) err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "eth_getTransactionReceipt", resJsonMap["result"].(map[string]interface{})["method"].(string)) require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, 4, len(resJsonMap["result"].(map[string]interface{})["result"].([]interface{}))) require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
for _, res := range resJsonMap["result"].(map[string]interface{})["result"].([]interface{}) { })
require.Equal(t, "eth_getTransactionReceipt", res.(map[string]interface{})["_"])
}
t.Run("translate consensus_getReceipts to alchemy_getTransactionReceipts with latest block tag", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("alchemy_getTransactionReceipts"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts", []interface{}{map[string]interface{}{
"blockOrHash": "latest"}})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var reqJsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &reqJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", reqJsonMap["method"])
require.Equal(t, "0x101", reqJsonMap["params"].([]interface{})[0].(map[string]interface{})["blockNumber"])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to unsupported consensus_receipts_target", func(t *testing.T) {
reset()
useOnlyNode1()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("unsupported_consensus_receipts_target"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
_, statusCode, err := client.SendRPC("consensus_getReceipts", []interface{}{map[string]interface{}{
"blockOrHash": "latest"}})
require.NoError(t, err)
require.Equal(t, 400, statusCode)
}) })
t.Run("consensus_getReceipts should not be used in a batch", func(t *testing.T) { t.Run("consensus_getReceipts should not be used in a batch", func(t *testing.T) {

@ -347,7 +347,8 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
writeRPCError(ctx, w, nil, ErrGatewayTimeout) writeRPCError(ctx, w, nil, ErrGatewayTimeout)
return return
} }
if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) { if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) {
writeRPCError(ctx, w, nil, ErrInvalidRequest(err.Error())) writeRPCError(ctx, w, nil, ErrInvalidRequest(err.Error()))
return return
} }
@ -364,6 +365,11 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
rawBody := json.RawMessage(body) rawBody := json.RawMessage(body)
backendRes, cached, err := s.handleBatchRPC(ctx, []json.RawMessage{rawBody}, isLimited, false) backendRes, cached, err := s.handleBatchRPC(ctx, []json.RawMessage{rawBody}, isLimited, false)
if err != nil { if err != nil {
if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) {
writeRPCError(ctx, w, nil, ErrInvalidRequest(err.Error()))
return
}
writeRPCError(ctx, w, nil, ErrInternal) writeRPCError(ctx, w, nil, ErrInternal)
return return
} }
@ -489,7 +495,8 @@ func (s *Server) handleBatchRPC(ctx context.Context, reqs []json.RawMessage, isL
elems := cacheMisses[start:end] elems := cacheMisses[start:end]
res, err := s.BackendGroups[group.backendGroup].Forward(ctx, createBatchRequest(elems), isBatch) res, err := s.BackendGroups[group.backendGroup].Forward(ctx, createBatchRequest(elems), isBatch)
if err != nil { if err != nil {
if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) { if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) {
return nil, false, err return nil, false, err
} }
log.Error( log.Error(