From a2e1667b17ca4a26b2376a1c1e62788b7747869d Mon Sep 17 00:00:00 2001 From: Felipe Andrade Date: Wed, 31 May 2023 10:04:20 -0700 Subject: [PATCH 1/4] feat(proxyd): add debug_getRawReceipts to consensus --- proxyd/proxyd/README.md | 1 + proxyd/proxyd/rewriter.go | 14 +++++---- proxyd/proxyd/rewriter_test.go | 52 ++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/proxyd/proxyd/README.md b/proxyd/proxyd/README.md index 346831f..e0735c0 100644 --- a/proxyd/proxyd/README.md +++ b/proxyd/proxyd/README.md @@ -71,6 +71,7 @@ The following request methods are rewritten: * `eth_getBlockByNumber` * `eth_getTransactionByBlockNumberAndIndex` * `eth_getUncleByBlockNumberAndIndex` +* `debug_getRawReceipts` And `eth_blockNumber` response is overridden with current block consensus. diff --git a/proxyd/proxyd/rewriter.go b/proxyd/proxyd/rewriter.go index 08d5638..71dd361 100644 --- a/proxyd/proxyd/rewriter.go +++ b/proxyd/proxyd/rewriter.go @@ -63,24 +63,26 @@ func RewriteRequest(rctx RewriteContext, req *RPCReq, res *RPCRes) (RewriteResul case "eth_getLogs", "eth_newFilter": return rewriteRange(rctx, req, res, 0) + case "debug_getRawReceipts": + return rewriteParam(rctx, req, res, 0, true) case "eth_getBalance", "eth_getCode", "eth_getTransactionCount", "eth_call": - return rewriteParam(rctx, req, res, 1) + return rewriteParam(rctx, req, res, 1, false) case "eth_getStorageAt": - return rewriteParam(rctx, req, res, 2) + return rewriteParam(rctx, req, res, 2, false) case "eth_getBlockTransactionCountByNumber", "eth_getUncleCountByBlockNumber", "eth_getBlockByNumber", "eth_getTransactionByBlockNumberAndIndex", "eth_getUncleByBlockNumberAndIndex": - return rewriteParam(rctx, req, res, 0) + return rewriteParam(rctx, req, res, 0, false) } return RewriteNone, nil } -func rewriteParam(rctx RewriteContext, req *RPCReq, res *RPCRes, pos int) (RewriteResult, error) { +func rewriteParam(rctx RewriteContext, req *RPCReq, res *RPCRes, pos int, required bool) (RewriteResult, error) { var p []interface{} err := json.Unmarshal(req.Params, &p) if err != nil { @@ -89,9 +91,9 @@ func rewriteParam(rctx RewriteContext, req *RPCReq, res *RPCRes, pos int) (Rewri // we assume latest if the param is missing, // and we don't rewrite if there is not enough params - if len(p) == pos { + if len(p) == pos && !required { p = append(p, "latest") - } else if len(p) < pos { + } else if len(p) <= pos { return RewriteNone, nil } diff --git a/proxyd/proxyd/rewriter_test.go b/proxyd/proxyd/rewriter_test.go index eb7f18f..087b804 100644 --- a/proxyd/proxyd/rewriter_test.go +++ b/proxyd/proxyd/rewriter_test.go @@ -148,6 +148,58 @@ func TestRewriteRequest(t *testing.T) { expected: RewriteOverrideError, expectedErr: ErrRewriteBlockOutOfRange, }, + /* required parameter at pos 0 */ + { + name: "debug_getRawReceipts latest", + args: args{ + rctx: RewriteContext{latest: hexutil.Uint64(100)}, + req: &RPCReq{Method: "debug_getRawReceipts", Params: mustMarshalJSON([]string{"latest"})}, + res: nil, + }, + expected: RewriteOverrideRequest, + check: func(t *testing.T, args args) { + var p []string + err := json.Unmarshal(args.req.Params, &p) + require.Nil(t, err) + require.Equal(t, 1, len(p)) + require.Equal(t, hexutil.Uint64(100).String(), p[0]) + }, + }, + { + name: "debug_getRawReceipts within range", + args: args{ + rctx: RewriteContext{latest: hexutil.Uint64(100)}, + req: &RPCReq{Method: "debug_getRawReceipts", Params: mustMarshalJSON([]string{hexutil.Uint64(55).String()})}, + res: nil, + }, + expected: RewriteNone, + check: func(t *testing.T, args args) { + var p []string + err := json.Unmarshal(args.req.Params, &p) + require.Nil(t, err) + require.Equal(t, 1, len(p)) + require.Equal(t, hexutil.Uint64(55).String(), p[0]) + }, + }, + { + name: "debug_getRawReceipts out of range", + args: args{ + rctx: RewriteContext{latest: hexutil.Uint64(100)}, + req: &RPCReq{Method: "debug_getRawReceipts", Params: mustMarshalJSON([]string{hexutil.Uint64(111).String()})}, + res: nil, + }, + expected: RewriteOverrideError, + expectedErr: ErrRewriteBlockOutOfRange, + }, + { + name: "debug_getRawReceipts missing parameter", + args: args{ + rctx: RewriteContext{latest: hexutil.Uint64(100)}, + req: &RPCReq{Method: "debug_getRawReceipts", Params: mustMarshalJSON([]string{})}, + res: nil, + }, + expected: RewriteNone, + }, /* default block parameter */ { name: "eth_getCode omit block, should add", From 64dab786cf6f2e2ce808be612580576b5f7bb05b Mon Sep 17 00:00:00 2001 From: Felipe Andrade Date: Wed, 31 May 2023 10:48:21 -0700 Subject: [PATCH 2/4] add cache support for debug_getRawReceipts when request is for block hash --- proxyd/proxyd/README.md | 2 +- proxyd/proxyd/cache.go | 18 ++++++++++++++++++ proxyd/proxyd/cache_test.go | 23 +++++++++++++++++++++++ proxyd/proxyd/methods.go | 12 ++++++++++-- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/proxyd/proxyd/README.md b/proxyd/proxyd/README.md index e0735c0..d3bfd50 100644 --- a/proxyd/proxyd/README.md +++ b/proxyd/proxyd/README.md @@ -87,7 +87,7 @@ Cache use Redis and can be enabled for the following immutable methods: * `eth_getBlockByHash` * `eth_getTransactionByBlockHashAndIndex` * `eth_getUncleByBlockHashAndIndex` - +* `debug_getRawReceipts` (block hash only) ## Metrics diff --git a/proxyd/proxyd/cache.go b/proxyd/proxyd/cache.go index 9684b75..0a88f7b 100644 --- a/proxyd/proxyd/cache.go +++ b/proxyd/proxyd/cache.go @@ -2,6 +2,8 @@ package proxyd import ( "context" + "encoding/json" + "github.com/ethereum/go-ethereum/rpc" "strings" "time" @@ -124,6 +126,21 @@ type rpcCache struct { func newRPCCache(cache Cache) RPCCache { staticHandler := &StaticMethodHandler{cache: cache} + debugGetRawReceiptsHandler := &StaticMethodHandler{cache: cache, + filter: func(req *RPCReq) bool { + // cache only if the request is for a block hash + + var p []rpc.BlockNumberOrHash + err := json.Unmarshal(req.Params, &p) + if err != nil { + return false + } + if len(p) != 1 { + return false + } + return p[0].BlockHash != nil + }, + } handlers := map[string]RPCMethodHandler{ "eth_chainId": staticHandler, "net_version": staticHandler, @@ -132,6 +149,7 @@ func newRPCCache(cache Cache) RPCCache { "eth_getBlockByHash": staticHandler, "eth_getTransactionByBlockHashAndIndex": staticHandler, "eth_getUncleByBlockHashAndIndex": staticHandler, + "debug_getRawReceipts": debugGetRawReceiptsHandler, } return &rpcCache{ cache: cache, diff --git a/proxyd/proxyd/cache_test.go b/proxyd/proxyd/cache_test.go index 727cdc7..93d0b8e 100644 --- a/proxyd/proxyd/cache_test.go +++ b/proxyd/proxyd/cache_test.go @@ -101,6 +101,20 @@ func TestRPCCacheImmutableRPCs(t *testing.T) { }, name: "eth_getUncleByBlockHashAndIndex", }, + { + req: &RPCReq{ + JSONRPC: "2.0", + Method: "debug_getRawReceipts", + Params: mustMarshalJSON([]string{"0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"}), + ID: ID, + }, + res: &RPCRes{ + JSONRPC: "2.0", + Result: `{"debug_getRawReceipts":"!"}`, + ID: ID, + }, + name: "debug_getRawReceipts", + }, } for _, rpc := range rpcs { @@ -173,6 +187,15 @@ func TestRPCCacheUnsupportedMethod(t *testing.T) { ID: ID, }, }, + { + req: &RPCReq{ + JSONRPC: "2.0", + Method: "debug_getRawReceipts", + Params: mustMarshalJSON([]string{"0x100"}), + ID: ID, + }, + name: "debug_getRawReceipts", + }, } for _, rpc := range rpcs { diff --git a/proxyd/proxyd/methods.go b/proxyd/proxyd/methods.go index 60c0595..ffaf89d 100644 --- a/proxyd/proxyd/methods.go +++ b/proxyd/proxyd/methods.go @@ -17,8 +17,9 @@ type RPCMethodHandler interface { } type StaticMethodHandler struct { - cache Cache - m sync.RWMutex + cache Cache + m sync.RWMutex + filter func(*RPCReq) bool } func (e *StaticMethodHandler) key(req *RPCReq) string { @@ -33,6 +34,10 @@ func (e *StaticMethodHandler) GetRPCMethod(ctx context.Context, req *RPCReq) (*R if e.cache == nil { return nil, nil } + if e.filter != nil && !e.filter(req) { + return nil, nil + } + e.m.RLock() defer e.m.RUnlock() @@ -62,6 +67,9 @@ func (e *StaticMethodHandler) PutRPCMethod(ctx context.Context, req *RPCReq, res if e.cache == nil { return nil } + if e.filter != nil && !e.filter(req) { + return nil + } e.m.Lock() defer e.m.Unlock() From 52ceaa581f0b1ea00d167f23e95931014f5d3935 Mon Sep 17 00:00:00 2001 From: Felipe Andrade Date: Wed, 31 May 2023 10:51:46 -0700 Subject: [PATCH 3/4] lint --- proxyd/proxyd/cache.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proxyd/proxyd/cache.go b/proxyd/proxyd/cache.go index 0a88f7b..1cb8c38 100644 --- a/proxyd/proxyd/cache.go +++ b/proxyd/proxyd/cache.go @@ -3,10 +3,11 @@ package proxyd import ( "context" "encoding/json" - "github.com/ethereum/go-ethereum/rpc" "strings" "time" + "github.com/ethereum/go-ethereum/rpc" + "github.com/go-redis/redis/v8" "github.com/golang/snappy" lru "github.com/hashicorp/golang-lru" From 20648112b5c88e677945723665b7fbee0520d41d Mon Sep 17 00:00:00 2001 From: Felipe Andrade Date: Wed, 31 May 2023 10:56:59 -0700 Subject: [PATCH 4/4] add test for debug_getRawReceipts with block hash --- proxyd/proxyd/rewriter_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/proxyd/proxyd/rewriter_test.go b/proxyd/proxyd/rewriter_test.go index 087b804..0637262 100644 --- a/proxyd/proxyd/rewriter_test.go +++ b/proxyd/proxyd/rewriter_test.go @@ -200,6 +200,22 @@ func TestRewriteRequest(t *testing.T) { }, expected: RewriteNone, }, + { + name: "debug_getRawReceipts with block hash", + args: args{ + rctx: RewriteContext{latest: hexutil.Uint64(100)}, + req: &RPCReq{Method: "debug_getRawReceipts", Params: mustMarshalJSON([]string{"0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"})}, + res: nil, + }, + expected: RewriteNone, + check: func(t *testing.T, args args) { + var p []string + err := json.Unmarshal(args.req.Params, &p) + require.Nil(t, err) + require.Equal(t, 1, len(p)) + require.Equal(t, "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", p[0]) + }, + }, /* default block parameter */ { name: "eth_getCode omit block, should add",