Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eth JSON-RPC: support passing uint64 in JSON-RPC arguments for EthUint64 #9961

Merged
merged 10 commits into from
Feb 11, 2023
5 changes: 5 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,11 @@ workflows:
suite: itest-eth_deploy
target: "./itests/eth_deploy_test.go"

- test:
name: test-itest-eth_fee_history
suite: itest-eth_fee_history
target: "./itests/eth_fee_history_test.go"

- test:
name: test-itest-eth_filter
suite: itest-eth_filter
Expand Down
18 changes: 9 additions & 9 deletions api/api_full.go
Original file line number Diff line number Diff line change
Expand Up @@ -786,15 +786,15 @@ type FullNode interface {
EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) //perm:read
EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum ethtypes.EthUint64, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) //perm:read

EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) //perm:read
EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) //perm:read
EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) //perm:read
EthChainId(ctx context.Context) (ethtypes.EthUint64, error) //perm:read
NetVersion(ctx context.Context) (string, error) //perm:read
NetListening(ctx context.Context) (bool, error) //perm:read
EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) //perm:read
EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read
EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) //perm:read
EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) //perm:read
EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) //perm:read
EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) //perm:read
EthChainId(ctx context.Context) (ethtypes.EthUint64, error) //perm:read
NetVersion(ctx context.Context) (string, error) //perm:read
NetListening(ctx context.Context) (bool, error) //perm:read
EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) //perm:read
EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read
EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) //perm:read

EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read
EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) //perm:read
Expand Down
2 changes: 1 addition & 1 deletion api/api_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ type Gateway interface {
NetListening(ctx context.Context) (bool, error)
EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error)
EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error)
EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error)
EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error)
EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error)
EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error)
EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error)
Expand Down
8 changes: 4 additions & 4 deletions api/mocks/mock_full.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions api/proxy_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified build/openrpc/full.json.gz
Binary file not shown.
Binary file modified build/openrpc/gateway.json.gz
Binary file not shown.
71 changes: 62 additions & 9 deletions chain/types/ethtypes/eth_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,29 @@ func (e EthUint64) MarshalJSON() ([]byte, error) {
return json.Marshal(e.Hex())
}

// UnmarshalJSON should be able to parse these types of input:
// 1. a JSON string containing a hex-encoded uint64 starting with 0x
// 2. a JSON string containing an uint64 in decimal
// 3. a string containing an uint64 in decimal
func (e *EthUint64) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
parsedInt, err := strconv.ParseUint(strings.Replace(s, "0x", "", -1), 16, 64)
if err != nil {
return err
if err := json.Unmarshal(b, &s); err == nil {
base := 10
if strings.HasPrefix(s, "0x") {
base = 16
}
parsedInt, err := strconv.ParseUint(strings.Replace(s, "0x", "", -1), base, 64)
if err != nil {
return err
}
eint := EthUint64(parsedInt)
*e = eint
return nil
} else if eint, err := strconv.ParseUint(string(b), 10, 64); err == nil {
*e = EthUint64(eint)
return nil
}
eint := EthUint64(parsedInt)
*e = eint
return nil
return fmt.Errorf("cannot interpret %s as a hex-encoded uint64, or a number", string(b))
}

func EthUint64FromHex(s string) (EthUint64, error) {
Expand Down Expand Up @@ -719,3 +730,45 @@ func GetContractEthAddressFromCode(sender EthAddress, salt [32]byte, initcode []

return ethAddr, nil
}

// EthFeeHistoryParams handles raw jsonrpc params for eth_feeHistory
type EthFeeHistoryParams struct {
BlkCount EthUint64
NewestBlkNum string
RewardPercentiles *[]float64
}

func (e *EthFeeHistoryParams) UnmarshalJSON(b []byte) error {
var params []json.RawMessage
err := json.Unmarshal(b, &params)
if err != nil {
return err
}
switch len(params) {
case 3:
err = json.Unmarshal(params[2], &e.RewardPercentiles)
if err != nil {
return err
}
fallthrough
case 2:
err = json.Unmarshal(params[1], &e.NewestBlkNum)
if err != nil {
return err
}
err = json.Unmarshal(params[0], &e.BlkCount)
if err != nil {
return err
}
default:
return xerrors.Errorf("expected 2 or 3 params, got %d", len(params))
}
return nil
}

func (e EthFeeHistoryParams) MarshalJSON() ([]byte, error) {
if e.RewardPercentiles != nil {
return json.Marshal([]interface{}{e.BlkCount, e.NewestBlkNum, e.RewardPercentiles})
}
return json.Marshal([]interface{}{e.BlkCount, e.NewestBlkNum})
}
8 changes: 7 additions & 1 deletion chain/types/ethtypes/eth_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,19 @@ func TestEthIntUnmarshalJSON(t *testing.T) {
{[]byte("\"0x0\""), EthUint64(0)},
{[]byte("\"0x41\""), EthUint64(65)},
{[]byte("\"0x400\""), EthUint64(1024)},
{[]byte("\"0\""), EthUint64(0)},
{[]byte("\"41\""), EthUint64(41)},
{[]byte("\"400\""), EthUint64(400)},
{[]byte("0"), EthUint64(0)},
{[]byte("100"), EthUint64(100)},
{[]byte("1024"), EthUint64(1024)},
}

for _, tc := range testcases {
var i EthUint64
err := i.UnmarshalJSON(tc.Input.([]byte))
require.Nil(t, err)
require.Equal(t, i, tc.Output)
require.Equal(t, tc.Output, i)
}
}

Expand Down
6 changes: 1 addition & 5 deletions documentation/en/api-v1-unstable-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -2332,11 +2332,7 @@ Perms: read
Inputs:
```json
[
"0x5",
"string value",
[
12.3
]
"Bw=="
]
```

Expand Down
2 changes: 1 addition & 1 deletion gateway/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ type TargetAPI interface {
NetListening(ctx context.Context) (bool, error)
EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error)
EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error)
EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error)
EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error)
EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error)
EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error)
EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error)
Expand Down
13 changes: 9 additions & 4 deletions gateway/proxy_eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,20 +294,25 @@ func (gw *Node) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) {

var EthFeeHistoryMaxBlockCount = 128 // this seems to be expensive; todo: figure out what is a good number that works with everything

func (gw *Node) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) {
func (gw *Node) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) {
params, err := jsonrpc.DecodeParams[ethtypes.EthFeeHistoryParams](p)
if err != nil {
return ethtypes.EthFeeHistory{}, xerrors.Errorf("decoding params: %w", err)
}

if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
return ethtypes.EthFeeHistory{}, err
}

if err := gw.checkBlkParam(ctx, newestBlk); err != nil {
if err := gw.checkBlkParam(ctx, params.NewestBlkNum); err != nil {
return ethtypes.EthFeeHistory{}, err
}

if blkCount > ethtypes.EthUint64(EthFeeHistoryMaxBlockCount) {
if params.BlkCount > ethtypes.EthUint64(EthFeeHistoryMaxBlockCount) {
return ethtypes.EthFeeHistory{}, fmt.Errorf("block count too high")
}

return gw.target.EthFeeHistory(ctx, blkCount, newestBlk, rewardPercentiles)
return gw.target.EthFeeHistory(ctx, p)
}

func (gw *Node) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) {
Expand Down
2 changes: 1 addition & 1 deletion itests/eth_conformance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func TestEthOpenRPCConformance(t *testing.T) {
{
method: "eth_feeHistory",
call: func(a *ethAPIRaw) (json.RawMessage, error) {
return ethapi.EthFeeHistory(context.Background(), ethtypes.EthUint64(2), "", nil)
return ethapi.EthFeeHistory(context.Background(), ethtypes.EthUint64(2), "latest", nil)
},
},

Expand Down
87 changes: 87 additions & 0 deletions itests/eth_fee_history_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package itests

import (
"context"
"encoding/json"
"testing"
"time"

"github.com/stretchr/testify/require"

"github.com/filecoin-project/go-jsonrpc"

"github.com/filecoin-project/lotus/chain/types/ethtypes"
"github.com/filecoin-project/lotus/itests/kit"
"github.com/filecoin-project/lotus/lib/result"
)

func TestEthFeeHistory(t *testing.T) {
require := require.New(t)

kit.QuietAllLogsExcept()

blockTime := 100 * time.Millisecond
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)

ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

// Wait for the network to create 20 blocks
<-time.After(20 * blockTime)

history, err := client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{5, "0x10"}),
).Assert(require.NoError))
require.NoError(err)
require.Equal(6, len(history.BaseFeePerGas))
require.Equal(5, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(16-5+1), history.OldestBlock)

history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{"5", "0x10"}),
).Assert(require.NoError))
require.NoError(err)
require.Equal(6, len(history.BaseFeePerGas))
require.Equal(5, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(16-5+1), history.OldestBlock)

history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{"0x10", "0x12"}),
).Assert(require.NoError))
require.NoError(err)
require.Equal(17, len(history.BaseFeePerGas))
require.Equal(16, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(18-16+1), history.OldestBlock)

history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{5, "0x10"}),
).Assert(require.NoError))
require.NoError(err)
require.Equal(6, len(history.BaseFeePerGas))
require.Equal(5, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(16-5+1), history.OldestBlock)

history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{5, "10"}),
).Assert(require.NoError))
require.NoError(err)
require.Equal(6, len(history.BaseFeePerGas))
require.Equal(5, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(10-5+1), history.OldestBlock)

history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{5, "10", &[]float64{0.25, 0.50, 0.75}}),
).Assert(require.NoError))
require.NoError(err)
require.Equal(6, len(history.BaseFeePerGas))
require.Equal(5, len(history.GasUsedRatio))
require.Equal(ethtypes.EthUint64(10-5+1), history.OldestBlock)
require.NotNil(history.Reward)
require.Equal(0, len(*history.Reward))

history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams](
json.Marshal([]interface{}{1025, "10", &[]float64{0.25, 0.50, 0.75}}),
).Assert(require.NoError))
require.Error(err)
}
2 changes: 1 addition & 1 deletion node/impl/full/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (e *EthModuleDummy) EthGetBalance(ctx context.Context, address ethtypes.Eth
return ethtypes.EthBigIntZero, ErrModuleDisabled
}

func (e *EthModuleDummy) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) {
func (e *EthModuleDummy) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) {
return ethtypes.EthFeeHistory{}, ErrModuleDisabled
}

Expand Down
Loading