Skip to content

Commit

Permalink
Fix standalone-proxy EstimateGas (#119)
Browse files Browse the repository at this point in the history
  • Loading branch information
pustovalov authored Dec 27, 2023
1 parent 341a49c commit ddeaac2
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 25 deletions.
69 changes: 44 additions & 25 deletions standaloneproxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"io"
"net"
"os"
"strconv"
"sync"
"syscall"
"time"
Expand Down Expand Up @@ -91,9 +92,6 @@ func (l *StandaloneProxy) Pre(ctx context.Context, name string, _ *endpoint.Endp
return ctx, true, nil

case "eth_estimateGas":
if len(args) != 2 {
return ctx, true, errors.New("invalid params")
}
tx, ok := args[0].(engine.TransactionForCall)
if !ok {
return ctx, true, errors.New("invalid params")
Expand All @@ -103,7 +101,8 @@ func (l *StandaloneProxy) Pre(ctx context.Context, name string, _ *endpoint.Endp
return ctx, true, errors.New("invalid params")
}
if blockNumberOrHash == nil {
return ctx, true, errors.New("string 'latest', 'earliest' or integer block number is required")
latest := common.LatestBlockNumber
blockNumberOrHash = &common.BlockNumberOrHash{BlockNumber: &latest}
}
res, err := l.client.EstimateGas(tx, blockNumberOrHash.BlockNumber)
if err != nil {
Expand Down Expand Up @@ -195,41 +194,61 @@ func (rc *rpcClient) TraceTransaction(hash common.H256) (*response.CallFrame, er
}

func (rc *rpcClient) EstimateGas(tx engine.TransactionForCall, number *common.BN64) (*common.Uint256, error) {
req, err := buildRequest("eth_estimateGas", tx, number)
var blockParam interface{} = number
switch *number {
case common.EarliestBlockNumber:
blockParam = "earliest"
case common.LatestBlockNumber:
blockParam = "latest"
case common.PendingBlockNumber:
blockParam = "pending"
}

req, err := buildRequest("eth_estimateGas", tx, blockParam)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to build request: %w", err)
}

res, err := rc.request(req)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to send request: %w", err)
}

return parseEstimateGasResponse(res)
}

func parseEstimateGasResponse(res []byte) (*common.Uint256, error) {
result, resultType, _, err := jsonparser.Get(res, "result")
if err != nil {
return nil, err
}

switch resultType {
case jsonparser.NotExist:
rpcErr, rpcErrType, _, err := jsonparser.Get(res, "error", "message")
switch {
case err != nil:
return nil, err
case rpcErrType == jsonparser.NotExist:
return nil, errors.New("internal rpc error")
default:
return nil, fmt.Errorf("%s", rpcErr)
if err == nil && resultType == jsonparser.Number {
val, err := strconv.ParseInt(string(result), 10, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse result as integer: %w", err)
}
hexStr := fmt.Sprintf("0x%x", val)

case jsonparser.String:
resp := new(common.Uint256)
err := json.Unmarshal(result, resp)
return resp, err
if err := resp.UnmarshalJSON([]byte(hexStr)); err != nil {
return nil, fmt.Errorf("failed to unmarshal result: %w", err)
}
return resp, nil
}

default:
return nil, errors.New("failed to parse unexpected response")
return handleEstimateGasError(res)
}

func handleEstimateGasError(res []byte) (*common.Uint256, error) {
rpcErrData, _, _, rpcErrDataParseErr := jsonparser.Get(res, "error", "data")
if rpcErrDataParseErr == nil && len(rpcErrData) > 0 {
return nil, fmt.Errorf("engine error: %s", rpcErrData)
}

rpcErrMsg, _, _, rpcErrMsgParseErr := jsonparser.Get(res, "error", "message")
if rpcErrMsgParseErr == nil && len(rpcErrMsg) > 0 {
return nil, fmt.Errorf("engine error: %s", rpcErrMsg)
}

return nil, errors.New("engine error: unknown error occurred")
}

func (rc *rpcClient) reconnect() error {
Expand Down
85 changes: 85 additions & 0 deletions standaloneproxy/proxy_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package standaloneproxy

import (
"errors"
"testing"

"github.com/aurora-is-near/relayer2-base/types/common"
"github.com/aurora-is-near/relayer2-base/types/primitives"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -44,3 +46,86 @@ func TestBuildRequest(t *testing.T) {
})
}
}

func TestParseEstimateGasResponseSuccess(t *testing.T) {
testCases := []struct {
name string
resp []byte
want common.Uint256
}{
{
name: "success",
resp: []byte(`{"id":1,"jsonrpc":"2.0","result":21000}`),
want: common.IntToUint256(21000),
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := parseEstimateGasResponse(tc.resp)
require.NoError(t, err)
require.EqualValues(t, tc.want, *got)
})
}
}

func TestParseEstimateGasResponseError(t *testing.T) {
testCases := []struct {
name string
resp []byte
expectedError error
}{
{
name: "Error with message",
resp: []byte(`{"id":1,"jsonrpc":"2.0","error":{"code":-32000,"message":"Internal error"}}`),
expectedError: errors.New("engine error: Internal error"),
},
{
name: "Error with message and data",
resp: []byte(`{"id":1,"jsonrpc":"2.0","error":{"code":-32000,"message":"Internal error", "data": "StateMissing"}}`),
expectedError: errors.New("engine error: StateMissing"),
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := parseEstimateGasResponse(tc.resp)
require.Nil(t, got)
require.EqualValues(t, tc.expectedError, err)
})
}
}

func TestHandleEstimateGasError(t *testing.T) {
testCases := []struct {
name string
input []byte
expectedError error
}{
{
name: "Error with message",
input: []byte(`{"error":{"code":-32000,"data":"StateMissing","message":"Internal error"}}`),
expectedError: errors.New("engine error: StateMissing"),
},
{
name: "Error without data",
input: []byte(`{"error":{"code":-32000,"message":"Internal error"}}`),
expectedError: errors.New("engine error: Internal error"),
},
{
name: "Broken JSON",
input: []byte(`{"error"}}`),
expectedError: errors.New("engine error: unknown error occurred"),
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
_, err := handleEstimateGasError(tc.input)

if err == nil || err.Error() != tc.expectedError.Error() {
t.Errorf("Test %s failed. Got: %v, want: %v", tc.name, err, tc.expectedError)
}
})
}
}

0 comments on commit ddeaac2

Please sign in to comment.