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:
Mikers 2024-06-20 15:38:21 -10:00 committed by GitHub
parent c87e2f2a64
commit 6408709018
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 63 additions and 0 deletions

View File

@ -28,6 +28,12 @@ import (
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
func (e EthUint64) MarshalJSON() ([]byte, error) {

View File

@ -17,6 +17,7 @@ import (
"github.com/filecoin-project/go-state-types/big"
"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/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
@ -142,6 +143,10 @@ func (gw *Node) checkBlkParam(ctx context.Context, blkParam string, lookback eth
break
}
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:
if err := num.UnmarshalJSON([]byte(`"` + blkParam + `"`)); err != nil {
return fmt.Errorf("cannot parse block number: %v", err)

View File

@ -124,3 +124,39 @@ func TestNetVersion(t *testing.T) {
require.NoError(t, err)
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))
}

View File

@ -57,6 +57,22 @@ func getTipsetByBlockNumber(ctx context.Context, chain *store.ChainStore, blkPar
return nil, fmt.Errorf("cannot get parent tipset")
}
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:
var num ethtypes.EthUint64
err := num.UnmarshalJSON([]byte(`"` + blkParam + `"`))