feat(proxyd): support eip-1898 in tag rewritting

This commit is contained in:
Felipe Andrade 2023-10-27 15:34:07 -07:00
parent 92058943d8
commit 22b7237389
2 changed files with 179 additions and 23 deletions

@ -66,25 +66,26 @@ func RewriteRequest(rctx RewriteContext, req *RPCReq, res *RPCRes) (RewriteResul
"eth_newFilter": "eth_newFilter":
return rewriteRange(rctx, req, res, 0) return rewriteRange(rctx, req, res, 0)
case "debug_getRawReceipts", "consensus_getReceipts": case "debug_getRawReceipts", "consensus_getReceipts":
return rewriteParam(rctx, req, res, 0, true) return rewriteParam(rctx, req, res, 0, true, false)
case "eth_getBalance", case "eth_getBalance",
"eth_getCode", "eth_getCode",
"eth_getTransactionCount", "eth_getTransactionCount",
"eth_call": "eth_call":
return rewriteParam(rctx, req, res, 1, false) return rewriteParam(rctx, req, res, 1, false, true)
case "eth_getStorageAt": case "eth_getStorageAt",
return rewriteParam(rctx, req, res, 2, false) "eth_getProof":
return rewriteParam(rctx, req, res, 2, false, true)
case "eth_getBlockTransactionCountByNumber", case "eth_getBlockTransactionCountByNumber",
"eth_getUncleCountByBlockNumber", "eth_getUncleCountByBlockNumber",
"eth_getBlockByNumber", "eth_getBlockByNumber",
"eth_getTransactionByBlockNumberAndIndex", "eth_getTransactionByBlockNumberAndIndex",
"eth_getUncleByBlockNumberAndIndex": "eth_getUncleByBlockNumberAndIndex":
return rewriteParam(rctx, req, res, 0, false) return rewriteParam(rctx, req, res, 0, false, false)
} }
return RewriteNone, nil return RewriteNone, nil
} }
func rewriteParam(rctx RewriteContext, req *RPCReq, res *RPCRes, pos int, required bool) (RewriteResult, error) { func rewriteParam(rctx RewriteContext, req *RPCReq, res *RPCRes, pos int, required bool, blockNrOrHash bool) (RewriteResult, error) {
var p []interface{} var p []interface{}
err := json.Unmarshal(req.Params, &p) err := json.Unmarshal(req.Params, &p)
if err != nil { if err != nil {
@ -99,13 +100,38 @@ func rewriteParam(rctx RewriteContext, req *RPCReq, res *RPCRes, pos int, requir
return RewriteNone, nil return RewriteNone, nil
} }
s, ok := p[pos].(string) // support for https://eips.ethereum.org/EIPS/eip-1898
if !ok { var val interface{}
return RewriteOverrideError, errors.New("expected string") var rw bool
} if blockNrOrHash {
val, rw, err := rewriteTag(rctx, s) bnh, err := remarshalBlockNumberOrHash(p[pos])
if err != nil { if err != nil {
return RewriteOverrideError, err // fallback to string
s, ok := p[pos].(string)
if ok {
val, rw, err = rewriteTag(rctx, s)
if err != nil {
return RewriteOverrideError, err
}
} else {
return RewriteOverrideError, errors.New("expected BlockNumberOrHash or string")
}
} else {
val, rw, err = rewriteTagBlockNumberOrHash(rctx, bnh)
if err != nil {
return RewriteOverrideError, err
}
}
} else {
s, ok := p[pos].(string)
if !ok {
return RewriteOverrideError, errors.New("expected string")
}
val, rw, err = rewriteTag(rctx, s)
if err != nil {
return RewriteOverrideError, err
}
} }
if rw { if rw {
@ -210,14 +236,23 @@ func rewriteTagMap(rctx RewriteContext, m map[string]interface{}, key string) (b
return false, nil return false, nil
} }
func rewriteTag(rctx RewriteContext, current string) (string, bool, error) { func remarshalBlockNumberOrHash(current interface{}) (*rpc.BlockNumberOrHash, error) {
jv, err := json.Marshal(current) jv, err := json.Marshal(current)
if err != nil { if err != nil {
return "", false, err return nil, err
} }
var bnh rpc.BlockNumberOrHash var bnh rpc.BlockNumberOrHash
err = bnh.UnmarshalJSON(jv) err = bnh.UnmarshalJSON(jv)
if err != nil {
return nil, err
}
return &bnh, nil
}
func rewriteTag(rctx RewriteContext, current string) (string, bool, error) {
bnh, err := remarshalBlockNumberOrHash(current)
if err != nil { if err != nil {
return "", false, err return "", false, err
} }
@ -245,3 +280,31 @@ func rewriteTag(rctx RewriteContext, current string) (string, bool, error) {
return current, false, nil return current, false, nil
} }
func rewriteTagBlockNumberOrHash(rctx RewriteContext, current *rpc.BlockNumberOrHash) (*rpc.BlockNumberOrHash, bool, error) {
// this is a hash, not a block number
if current.BlockNumber == nil {
return current, false, nil
}
switch *current.BlockNumber {
case rpc.PendingBlockNumber,
rpc.EarliestBlockNumber:
return current, false, nil
case rpc.FinalizedBlockNumber:
bn := rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(rctx.finalized))
return &bn, true, nil
case rpc.SafeBlockNumber:
bn := rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(rctx.safe))
return &bn, true, nil
case rpc.LatestBlockNumber:
bn := rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(rctx.latest))
return &bn, true, nil
default:
if current.BlockNumber.Int64() > int64(rctx.latest) {
return nil, false, ErrRewriteBlockOutOfRange
}
}
return current, false, nil
}

@ -5,7 +5,9 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -282,12 +284,14 @@ func TestRewriteRequest(t *testing.T) {
}, },
expected: RewriteOverrideRequest, expected: RewriteOverrideRequest,
check: func(t *testing.T, args args) { check: func(t *testing.T, args args) {
var p []string var p []interface{}
err := json.Unmarshal(args.req.Params, &p) err := json.Unmarshal(args.req.Params, &p)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, 2, len(p)) require.Equal(t, 2, len(p))
require.Equal(t, "0x123", p[0]) require.Equal(t, "0x123", p[0])
require.Equal(t, hexutil.Uint64(100).String(), p[1]) bnh, err := remarshalBlockNumberOrHash(p[1])
require.Nil(t, err)
require.Equal(t, rpc.BlockNumberOrHashWithNumber(100), *bnh)
}, },
}, },
{ {
@ -314,12 +318,14 @@ func TestRewriteRequest(t *testing.T) {
}, },
expected: RewriteOverrideRequest, expected: RewriteOverrideRequest,
check: func(t *testing.T, args args) { check: func(t *testing.T, args args) {
var p []string var p []interface{}
err := json.Unmarshal(args.req.Params, &p) err := json.Unmarshal(args.req.Params, &p)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, 2, len(p)) require.Equal(t, 2, len(p))
require.Equal(t, "0x123", p[0]) require.Equal(t, "0x123", p[0])
require.Equal(t, hexutil.Uint64(100).String(), p[1]) bnh, err := remarshalBlockNumberOrHash(p[1])
require.Nil(t, err)
require.Equal(t, rpc.BlockNumberOrHashWithNumber(100), *bnh)
}, },
}, },
{ {
@ -359,13 +365,15 @@ func TestRewriteRequest(t *testing.T) {
}, },
expected: RewriteOverrideRequest, expected: RewriteOverrideRequest,
check: func(t *testing.T, args args) { check: func(t *testing.T, args args) {
var p []string var p []interface{}
err := json.Unmarshal(args.req.Params, &p) err := json.Unmarshal(args.req.Params, &p)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, 3, len(p)) require.Equal(t, 3, len(p))
require.Equal(t, "0x123", p[0]) require.Equal(t, "0x123", p[0])
require.Equal(t, "5", p[1]) require.Equal(t, "5", p[1])
require.Equal(t, hexutil.Uint64(100).String(), p[2]) bnh, err := remarshalBlockNumberOrHash(p[2])
require.Nil(t, err)
require.Equal(t, rpc.BlockNumberOrHashWithNumber(100), *bnh)
}, },
}, },
{ {
@ -377,13 +385,15 @@ func TestRewriteRequest(t *testing.T) {
}, },
expected: RewriteOverrideRequest, expected: RewriteOverrideRequest,
check: func(t *testing.T, args args) { check: func(t *testing.T, args args) {
var p []string var p []interface{}
err := json.Unmarshal(args.req.Params, &p) err := json.Unmarshal(args.req.Params, &p)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, 3, len(p)) require.Equal(t, 3, len(p))
require.Equal(t, "0x123", p[0]) require.Equal(t, "0x123", p[0])
require.Equal(t, "5", p[1]) require.Equal(t, "5", p[1])
require.Equal(t, hexutil.Uint64(100).String(), p[2]) bnh, err := remarshalBlockNumberOrHash(p[2])
require.Nil(t, err)
require.Equal(t, rpc.BlockNumberOrHashWithNumber(100), *bnh)
}, },
}, },
{ {
@ -517,6 +527,88 @@ func TestRewriteRequest(t *testing.T) {
}, },
expected: RewriteNone, expected: RewriteNone,
}, },
// eip1898
{
name: "eth_getStorageAt using rpc.BlockNumberOrHash at genesis (blockNumber)",
args: args{
rctx: RewriteContext{latest: hexutil.Uint64(100)},
req: &RPCReq{Method: "eth_getStorageAt", Params: mustMarshalJSON([]interface{}{
"0xae851f927ee40de99aabb7461c00f9622ab91d60",
"10",
map[string]interface{}{
"blockNumber": "0x0",
}})},
res: nil,
},
expected: RewriteNone,
},
{
name: "eth_getStorageAt using rpc.BlockNumberOrHash at genesis (hash)",
args: args{
rctx: RewriteContext{latest: hexutil.Uint64(100)},
req: &RPCReq{Method: "eth_getStorageAt", Params: mustMarshalJSON([]interface{}{
"0xae851f927ee40de99aabb7461c00f9622ab91d60",
"10",
map[string]interface{}{
"blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3",
"requireCanonical": true,
}})},
res: nil,
},
expected: RewriteNone,
check: func(t *testing.T, args args) {
var p []interface{}
err := json.Unmarshal(args.req.Params, &p)
require.Nil(t, err)
require.Equal(t, 3, len(p))
require.Equal(t, "0xae851f927ee40de99aabb7461c00f9622ab91d60", p[0])
require.Equal(t, "10", p[1])
bnh, err := remarshalBlockNumberOrHash(p[2])
require.Nil(t, err)
require.Equal(t, rpc.BlockNumberOrHashWithHash(common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"), true), *bnh)
require.True(t, bnh.RequireCanonical)
},
},
{
name: "eth_getStorageAt using rpc.BlockNumberOrHash at latest (blockNumber)",
args: args{
rctx: RewriteContext{latest: hexutil.Uint64(100)},
req: &RPCReq{Method: "eth_getStorageAt", Params: mustMarshalJSON([]interface{}{
"0xae851f927ee40de99aabb7461c00f9622ab91d60",
"10",
map[string]interface{}{
"blockNumber": "latest",
}})},
res: nil,
},
expected: RewriteOverrideRequest,
check: func(t *testing.T, args args) {
var p []interface{}
err := json.Unmarshal(args.req.Params, &p)
require.Nil(t, err)
require.Equal(t, 3, len(p))
require.Equal(t, "0xae851f927ee40de99aabb7461c00f9622ab91d60", p[0])
require.Equal(t, "10", p[1])
bnh, err := remarshalBlockNumberOrHash(p[2])
require.Nil(t, err)
require.Equal(t, rpc.BlockNumberOrHashWithNumber(100), *bnh)
},
},
{
name: "eth_getStorageAt using rpc.BlockNumberOrHash out of range",
args: args{
rctx: RewriteContext{latest: hexutil.Uint64(100)},
req: &RPCReq{Method: "eth_getStorageAt", Params: mustMarshalJSON([]interface{}{
"0xae851f927ee40de99aabb7461c00f9622ab91d60",
"10",
map[string]interface{}{
"blockNumber": "0x111",
}})},
res: nil,
},
expected: RewriteOverrideError,
expectedErr: ErrRewriteBlockOutOfRange,
},
} }
// generalize tests for other methods with same interface and behavior // generalize tests for other methods with same interface and behavior
@ -528,6 +620,7 @@ func TestRewriteRequest(t *testing.T) {
tests = generalize(tests, "eth_getBlockByNumber", "eth_getUncleCountByBlockNumber") tests = generalize(tests, "eth_getBlockByNumber", "eth_getUncleCountByBlockNumber")
tests = generalize(tests, "eth_getBlockByNumber", "eth_getTransactionByBlockNumberAndIndex") tests = generalize(tests, "eth_getBlockByNumber", "eth_getTransactionByBlockNumberAndIndex")
tests = generalize(tests, "eth_getBlockByNumber", "eth_getUncleByBlockNumberAndIndex") tests = generalize(tests, "eth_getBlockByNumber", "eth_getUncleByBlockNumberAndIndex")
tests = generalize(tests, "eth_getStorageSlotAt", "eth_getProof")
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {