feat: eth: support "safe" and "finalized" for eth_getBlockByNumber (#12110)
* add support for eth_getBlockByNumber to accept the term safe which we are using as 30 blocks * fix lint catch of unnecessary cast * add finalized to get block by number * Update chain/types/ethtypes/eth_types.go Co-authored-by: Rod Vagg <rod@vagg.org> * add test for eth get block by number to accept latest and safe and finalized as arguments --------- Co-authored-by: Rod Vagg <rod@vagg.org>
This commit is contained in:
parent
c87e2f2a64
commit
6408709018
@ -28,6 +28,12 @@ import (
|
|||||||
|
|
||||||
var ErrInvalidAddress = errors.New("invalid Filecoin Eth address")
|
var ErrInvalidAddress = errors.New("invalid Filecoin Eth address")
|
||||||
|
|
||||||
|
// Research into Filecoin chain behaviour suggests that probabilistic finality
|
||||||
|
// generally approaches the intended stability guarantee at, or near, 30 epochs.
|
||||||
|
// Although a strictly "finalized" safe recommendation remains 900 epochs.
|
||||||
|
// See https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0089.md
|
||||||
|
const SafeEpochDelay = abi.ChainEpoch(30)
|
||||||
|
|
||||||
type EthUint64 uint64
|
type EthUint64 uint64
|
||||||
|
|
||||||
func (e EthUint64) MarshalJSON() ([]byte, error) {
|
func (e EthUint64) MarshalJSON() ([]byte, error) {
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/filecoin-project/go-state-types/big"
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
"github.com/filecoin-project/lotus/chain/events/filter"
|
"github.com/filecoin-project/lotus/chain/events/filter"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
||||||
@ -142,6 +143,10 @@ func (gw *Node) checkBlkParam(ctx context.Context, blkParam string, lookback eth
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
num = ethtypes.EthUint64(head.Height()) - lookback
|
num = ethtypes.EthUint64(head.Height()) - lookback
|
||||||
|
case "safe":
|
||||||
|
num = ethtypes.EthUint64(head.Height()) - lookback - ethtypes.EthUint64(ethtypes.SafeEpochDelay)
|
||||||
|
case "finalized":
|
||||||
|
num = ethtypes.EthUint64(head.Height()) - lookback - ethtypes.EthUint64(build.Finality)
|
||||||
default:
|
default:
|
||||||
if err := num.UnmarshalJSON([]byte(`"` + blkParam + `"`)); err != nil {
|
if err := num.UnmarshalJSON([]byte(`"` + blkParam + `"`)); err != nil {
|
||||||
return fmt.Errorf("cannot parse block number: %v", err)
|
return fmt.Errorf("cannot parse block number: %v", err)
|
||||||
|
@ -124,3 +124,39 @@ func TestNetVersion(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, strconv.Itoa(build.Eip155ChainId), version)
|
require.Equal(t, strconv.Itoa(build.Eip155ChainId), version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEthBlockNumberAliases(t *testing.T) {
|
||||||
|
blockTime := 2 * time.Millisecond
|
||||||
|
kit.QuietMiningLogs()
|
||||||
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
|
||||||
|
ens.InterconnectAll().BeginMining(blockTime)
|
||||||
|
ens.Start()
|
||||||
|
|
||||||
|
build.Clock.Sleep(time.Second)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
head := client.WaitTillChain(ctx, kit.HeightAtLeast(build.Finality+100))
|
||||||
|
|
||||||
|
// latest should be head-1 (parents)
|
||||||
|
latestEthBlk, err := client.EVM().EthGetBlockByNumber(ctx, "latest", true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
diff := int64(latestEthBlk.Number) - int64(head.Height()-1)
|
||||||
|
require.GreaterOrEqual(t, diff, int64(0))
|
||||||
|
require.LessOrEqual(t, diff, int64(2))
|
||||||
|
|
||||||
|
// safe should be latest-30
|
||||||
|
safeEthBlk, err := client.EVM().EthGetBlockByNumber(ctx, "safe", true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
diff = int64(latestEthBlk.Number-30) - int64(safeEthBlk.Number)
|
||||||
|
require.GreaterOrEqual(t, diff, int64(0))
|
||||||
|
require.LessOrEqual(t, diff, int64(2))
|
||||||
|
|
||||||
|
// finalized should be Finality blocks behind latest
|
||||||
|
finalityEthBlk, err := client.EVM().EthGetBlockByNumber(ctx, "finalized", true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
diff = int64(latestEthBlk.Number) - int64(build.Finality) - int64(finalityEthBlk.Number)
|
||||||
|
require.GreaterOrEqual(t, diff, int64(0))
|
||||||
|
require.LessOrEqual(t, diff, int64(2))
|
||||||
|
}
|
||||||
|
@ -57,6 +57,22 @@ func getTipsetByBlockNumber(ctx context.Context, chain *store.ChainStore, blkPar
|
|||||||
return nil, fmt.Errorf("cannot get parent tipset")
|
return nil, fmt.Errorf("cannot get parent tipset")
|
||||||
}
|
}
|
||||||
return parent, nil
|
return parent, nil
|
||||||
|
case "safe":
|
||||||
|
latestHeight := head.Height() - 1
|
||||||
|
safeHeight := latestHeight - ethtypes.SafeEpochDelay
|
||||||
|
ts, err := chain.GetTipsetByHeight(ctx, safeHeight, head, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot get tipset at height: %v", safeHeight)
|
||||||
|
}
|
||||||
|
return ts, nil
|
||||||
|
case "finalized":
|
||||||
|
latestHeight := head.Height() - 1
|
||||||
|
safeHeight := latestHeight - build.Finality
|
||||||
|
ts, err := chain.GetTipsetByHeight(ctx, safeHeight, head, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot get tipset at height: %v", safeHeight)
|
||||||
|
}
|
||||||
|
return ts, nil
|
||||||
default:
|
default:
|
||||||
var num ethtypes.EthUint64
|
var num ethtypes.EthUint64
|
||||||
err := num.UnmarshalJSON([]byte(`"` + blkParam + `"`))
|
err := num.UnmarshalJSON([]byte(`"` + blkParam + `"`))
|
||||||
|
Loading…
Reference in New Issue
Block a user