Node/EVM: More nodes support finalized and safe (#3467)
* Node/EVM: More nodes support finalized and safe * Remove unused finalizers
This commit is contained in:
parent
5afd9eab2d
commit
bd7262d819
|
@ -1079,11 +1079,10 @@ func runNode(cmd *cobra.Command, args []string) {
|
|||
|
||||
if shouldStart(bscRPC) {
|
||||
wc := &evm.WatcherConfig{
|
||||
NetworkID: "bsc",
|
||||
ChainID: vaa.ChainIDBSC,
|
||||
Rpc: *bscRPC,
|
||||
Contract: *bscContract,
|
||||
WaitForConfirmations: true,
|
||||
NetworkID: "bsc",
|
||||
ChainID: vaa.ChainIDBSC,
|
||||
Rpc: *bscRPC,
|
||||
Contract: *bscContract,
|
||||
}
|
||||
|
||||
watcherConfigs = append(watcherConfigs, wc)
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
package finalizers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/interfaces"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ArbitrumFinalizer implements the finality check for Arbitrum.
|
||||
// Arbitrum blocks should not be considered finalized until they are finalized on Ethereum.
|
||||
|
||||
type ArbitrumFinalizer struct {
|
||||
logger *zap.Logger
|
||||
l1Finalizer interfaces.L1Finalizer
|
||||
}
|
||||
|
||||
func NewArbitrumFinalizer(logger *zap.Logger, l1Finalizer interfaces.L1Finalizer) *ArbitrumFinalizer {
|
||||
return &ArbitrumFinalizer{
|
||||
logger: logger,
|
||||
l1Finalizer: l1Finalizer,
|
||||
}
|
||||
}
|
||||
|
||||
// IsBlockFinalized compares the number of the L1 block containing the Arbitrum block with the latest finalized block on Ethereum.
|
||||
func (a *ArbitrumFinalizer) IsBlockFinalized(ctx context.Context, block *connectors.NewBlock) (bool, error) {
|
||||
if block == nil {
|
||||
return false, fmt.Errorf("block is nil")
|
||||
}
|
||||
|
||||
if block.L1BlockNumber == nil {
|
||||
return false, fmt.Errorf("l1 block number is nil")
|
||||
}
|
||||
|
||||
latestL1Block := a.l1Finalizer.GetLatestFinalizedBlockNumber()
|
||||
if latestL1Block == 0 {
|
||||
// This happens on start up.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
isFinalized := block.L1BlockNumber.Uint64() <= latestL1Block
|
||||
return isFinalized, nil
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
package finalizers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// mockL1Finalizer implements the L1Finalizer interface for testing purposes.
|
||||
type mockL1Finalizer struct {
|
||||
LatestFinalizedBlockNumber uint64
|
||||
}
|
||||
|
||||
func (m *mockL1Finalizer) GetLatestFinalizedBlockNumber() uint64 {
|
||||
return m.LatestFinalizedBlockNumber
|
||||
}
|
||||
|
||||
func TestArbitrumErrorReturnedIfBlockIsNil(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 125}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, nil)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestArbitrumErrorReturnedIfL1BlockIsNil(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 125}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: nil,
|
||||
}
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestArbitrumNotFinalizedIfNoFinalizedL1BlockYet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: big.NewInt(225),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, finalized)
|
||||
}
|
||||
|
||||
func TestArbitrumNotFinalizedWhenFinalizedL1IsLessThanTargetL1(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 225}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(127),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: big.NewInt(226),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, finalized)
|
||||
}
|
||||
|
||||
func TestArbitrumIsFinalizedWhenFinalizedL1IsEqualsTargetL1(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 225}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: big.NewInt(225),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, finalized)
|
||||
}
|
||||
|
||||
func TestArbitrumIsFinalizedWhenFinalizedL1IsGreaterThanTargetL1(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 227}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: big.NewInt(225),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, finalized)
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package finalizers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// MoonbeamFinalizer implements the finality check for Moonbeam.
|
||||
// Moonbeam can publish blocks before they are marked final. This means we need to sit on the block until a special "is finalized"
|
||||
// query returns true. The assumption is that every block number will eventually be published and finalized, it's just that the contents
|
||||
// of the block (and therefore the hash) might change if there is a rollback.
|
||||
type MoonbeamFinalizer struct {
|
||||
logger *zap.Logger
|
||||
connector connectors.Connector
|
||||
}
|
||||
|
||||
func NewMoonbeamFinalizer(logger *zap.Logger, connector connectors.Connector) *MoonbeamFinalizer {
|
||||
return &MoonbeamFinalizer{
|
||||
logger: logger,
|
||||
connector: connector,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MoonbeamFinalizer) IsBlockFinalized(ctx context.Context, block *connectors.NewBlock) (bool, error) {
|
||||
if block == nil {
|
||||
return false, fmt.Errorf("block is nil")
|
||||
}
|
||||
|
||||
var finalized bool
|
||||
err := m.connector.RawCallContext(ctx, &finalized, "moon_isBlockFinalized", block.Hash.Hex())
|
||||
if err != nil {
|
||||
m.logger.Error("failed to check for finality", zap.String("eth_network", m.connector.NetworkName()), zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
return finalized, nil
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
package finalizers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
|
||||
|
||||
ethAbi "github.com/certusone/wormhole/node/pkg/watchers/evm/connectors/ethabi"
|
||||
|
||||
ethereum "github.com/ethereum/go-ethereum"
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethEvent "github.com/ethereum/go-ethereum/event"
|
||||
ethRpc "github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type moonbeamMockConnector struct {
|
||||
isFinalized string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) RawCallContext(ctx context.Context, result interface{}, method string, args ...interface{}) (err error) {
|
||||
if method != "moon_isBlockFinalized" {
|
||||
panic("method not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(e.isFinalized), &result)
|
||||
return
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) RawBatchCallContext(ctx context.Context, b []ethRpc.BatchElem) error {
|
||||
panic("method not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) NetworkName() string {
|
||||
return "moonbeamMockConnector"
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) ContractAddress() ethCommon.Address {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) GetCurrentGuardianSetIndex(ctx context.Context) (uint32, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) GetGuardianSet(ctx context.Context, index uint32) (ethAbi.StructsGuardianSet, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) WatchLogMessagePublished(ctx context.Context, errC chan error, sink chan<- *ethAbi.AbiLogMessagePublished) (ethEvent.Subscription, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) TransactionReceipt(ctx context.Context, txHash ethCommon.Hash) (*ethTypes.Receipt, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) TimeOfBlockByHash(ctx context.Context, hash ethCommon.Hash) (uint64, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) ParseLogMessagePublished(log ethTypes.Log) (*ethAbi.AbiLogMessagePublished, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) SubscribeForBlocks(ctx context.Context, errC chan error, sink chan<- *connectors.NewBlock) (ethereum.Subscription, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func TestMoonbeamErrorReturnedIfBlockIsNil(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
baseConnector := moonbeamMockConnector{isFinalized: "true", err: nil}
|
||||
|
||||
finalizer := NewMoonbeamFinalizer(logger, &baseConnector)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, nil)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestMoonbeamBlockNotFinalized(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
baseConnector := moonbeamMockConnector{isFinalized: "false", err: nil}
|
||||
|
||||
finalizer := NewMoonbeamFinalizer(logger, &baseConnector)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.HexToHash("0x1076cd8c207f31e1638b37bb358c458f216f5451f06e2ccb4eb9db66ad669f30"),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, finalized)
|
||||
}
|
||||
func TestMoonbeamBlockIsFinalized(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
baseConnector := moonbeamMockConnector{isFinalized: "true", err: nil}
|
||||
|
||||
finalizer := NewMoonbeamFinalizer(logger, &baseConnector)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.HexToHash("0x1076cd8c207f31e1638b37bb358c458f216f5451f06e2ccb4eb9db66ad669f30"),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, finalized)
|
||||
}
|
||||
|
||||
func TestMoonbeamRpcError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
baseConnector := moonbeamMockConnector{isFinalized: "true", err: fmt.Errorf("RPC failed")}
|
||||
|
||||
finalizer := NewMoonbeamFinalizer(logger, &baseConnector)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, nil)
|
||||
require.Error(t, err)
|
||||
}
|
|
@ -15,6 +15,15 @@ import (
|
|||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// mockL1Finalizer implements the L1Finalizer interface for testing purposes.
|
||||
type mockL1Finalizer struct {
|
||||
LatestFinalizedBlockNumber uint64
|
||||
}
|
||||
|
||||
func (m *mockL1Finalizer) GetLatestFinalizedBlockNumber() uint64 {
|
||||
return m.LatestFinalizedBlockNumber
|
||||
}
|
||||
|
||||
func TestNeonErrorReturnedIfBlockIsNil(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
|
|
|
@ -180,6 +180,7 @@ func NewEthWatcher(
|
|||
}
|
||||
|
||||
func (w *Watcher) Run(parentCtx context.Context) error {
|
||||
var err error
|
||||
logger := supervisor.Logger(parentCtx)
|
||||
|
||||
logger.Info("Starting watcher",
|
||||
|
@ -197,16 +198,10 @@ func (w *Watcher) Run(parentCtx context.Context) error {
|
|||
ctx, watcherContextCancelFunc := context.WithCancel(parentCtx)
|
||||
defer watcherContextCancelFunc()
|
||||
|
||||
useFinalizedBlocks := ((w.chainID == vaa.ChainIDEthereum || w.chainID == vaa.ChainIDSepolia) && (!w.unsafeDevMode))
|
||||
if (w.chainID == vaa.ChainIDKarura || w.chainID == vaa.ChainIDAcala) && (!w.unsafeDevMode) {
|
||||
ufb, err := w.getAcalaMode(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ufb {
|
||||
useFinalizedBlocks = true
|
||||
}
|
||||
var useFinalizedBlocks, safeBlocksSupported bool
|
||||
useFinalizedBlocks, safeBlocksSupported, err = w.getFinality(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine finality: %w", err)
|
||||
}
|
||||
|
||||
// Initialize gossip metrics (we want to broadcast the address even if we're not yet syncing)
|
||||
|
@ -217,21 +212,8 @@ func (w *Watcher) Run(parentCtx context.Context) error {
|
|||
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
safeBlocksSupported := false
|
||||
|
||||
var err error
|
||||
if w.chainID == vaa.ChainIDCelo && !w.unsafeDevMode {
|
||||
// When we are running in mainnet or testnet, we need to use the Celo ethereum library rather than go-ethereum.
|
||||
// However, in devnet, we currently run the standard ETH node for Celo, so we need to use the standard go-ethereum.
|
||||
w.ethConn, err = connectors.NewCeloConnector(timeout, w.networkName, w.url, w.contract, logger)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("dialing eth client failed: %w", err)
|
||||
}
|
||||
} else if useFinalizedBlocks {
|
||||
if (w.chainID == vaa.ChainIDEthereum || w.chainID == vaa.ChainIDSepolia) && !w.unsafeDevMode {
|
||||
safeBlocksSupported = true
|
||||
if useFinalizedBlocks {
|
||||
if safeBlocksSupported {
|
||||
logger.Info("using finalized blocks, will publish safe blocks")
|
||||
} else {
|
||||
logger.Info("using finalized blocks")
|
||||
|
@ -249,21 +231,17 @@ func (w *Watcher) Run(parentCtx context.Context) error {
|
|||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("creating block poll connector failed: %w", err)
|
||||
}
|
||||
} else if w.chainID == vaa.ChainIDMoonbeam && !w.unsafeDevMode {
|
||||
baseConnector, err := connectors.NewEthereumConnector(timeout, w.networkName, w.url, w.contract, logger)
|
||||
} else if w.chainID == vaa.ChainIDCelo {
|
||||
// When we are running in mainnet or testnet, we need to use the Celo ethereum library rather than go-ethereum.
|
||||
// However, in devnet, we currently run the standard ETH node for Celo, so we need to use the standard go-ethereum.
|
||||
w.ethConn, err = connectors.NewCeloConnector(timeout, w.networkName, w.url, w.contract, logger)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("dialing eth client failed: %w", err)
|
||||
}
|
||||
finalizer := finalizers.NewMoonbeamFinalizer(logger, baseConnector)
|
||||
w.ethConn, err = connectors.NewBlockPollConnector(ctx, baseConnector, finalizer, 250*time.Millisecond, false, false)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("creating block poll connector failed: %w", err)
|
||||
}
|
||||
} else if w.chainID == vaa.ChainIDNeon && !w.unsafeDevMode {
|
||||
} else if w.chainID == vaa.ChainIDNeon {
|
||||
// Neon needs special handling to read log events.
|
||||
if w.l1Finalizer == nil {
|
||||
return fmt.Errorf("unable to create neon watcher because the l1 finalizer is not set")
|
||||
}
|
||||
|
@ -286,41 +264,8 @@ func (w *Watcher) Run(parentCtx context.Context) error {
|
|||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("creating poll connector failed: %w", err)
|
||||
}
|
||||
} else if w.chainID == vaa.ChainIDArbitrum && !w.unsafeDevMode {
|
||||
if w.l1Finalizer == nil {
|
||||
return fmt.Errorf("unable to create arbitrum watcher because the l1 finalizer is not set")
|
||||
}
|
||||
baseConnector, err := connectors.NewEthereumConnector(timeout, w.networkName, w.url, w.contract, logger)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("dialing eth client failed: %w", err)
|
||||
}
|
||||
finalizer := finalizers.NewArbitrumFinalizer(logger, w.l1Finalizer)
|
||||
w.ethConn, err = connectors.NewBlockPollConnector(ctx, baseConnector, finalizer, 250*time.Millisecond, false, false)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("creating arbitrum connector failed: %w", err)
|
||||
}
|
||||
} else if w.chainID == vaa.ChainIDOptimism && !w.unsafeDevMode {
|
||||
// This only supports Bedrock mode
|
||||
useFinalizedBlocks = true
|
||||
safeBlocksSupported = true
|
||||
logger.Info("using finalized blocks, will publish safe blocks")
|
||||
baseConnector, err := connectors.NewEthereumConnector(timeout, w.networkName, w.url, w.contract, logger)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("dialing eth client failed: %w", err)
|
||||
}
|
||||
w.ethConn, err = connectors.NewBlockPollConnector(ctx, baseConnector, finalizers.NewDefaultFinalizer(), 250*time.Millisecond, useFinalizedBlocks, safeBlocksSupported)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("creating optimism connector failed: %w", err)
|
||||
}
|
||||
} else if w.chainID == vaa.ChainIDPolygon && w.usePolygonCheckpointing() {
|
||||
// Polygon polls the root contract on Ethereum.
|
||||
baseConnector, err := connectors.NewEthereumConnector(timeout, w.networkName, w.url, w.contract, logger)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
|
@ -337,22 +282,8 @@ func (w *Watcher) Run(parentCtx context.Context) error {
|
|||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("failed to create polygon connector: %w", err)
|
||||
}
|
||||
} else if w.chainID == vaa.ChainIDBase && !w.unsafeDevMode {
|
||||
useFinalizedBlocks = true
|
||||
safeBlocksSupported = true
|
||||
baseConnector, err := connectors.NewEthereumConnector(timeout, w.networkName, w.url, w.contract, logger)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("dialing eth client failed: %w", err)
|
||||
}
|
||||
w.ethConn, err = connectors.NewBlockPollConnector(ctx, baseConnector, finalizers.NewDefaultFinalizer(), 250*time.Millisecond, useFinalizedBlocks, safeBlocksSupported)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("creating base connector failed: %w", err)
|
||||
}
|
||||
} else {
|
||||
// Everything else is instant finality.
|
||||
w.ethConn, err = connectors.NewEthereumConnector(timeout, w.networkName, w.url, w.contract, logger)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
|
@ -889,41 +820,56 @@ func fetchCurrentGuardianSet(ctx context.Context, ethConn connectors.Connector)
|
|||
return currentIndex, &gs, nil
|
||||
}
|
||||
|
||||
func (w *Watcher) getAcalaMode(ctx context.Context) (useFinalizedBlocks bool, errRet error) {
|
||||
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
c, err := rpc.DialContext(timeout, w.url)
|
||||
if err != nil {
|
||||
errRet = fmt.Errorf("failed to connect to url %s to check acala mode: %w", w.url, err)
|
||||
return
|
||||
// getFinality determines if the chain supports "finalized" and "safe". This is hard coded so it requires thought to change something. However, it also reads the RPC
|
||||
// to make sure the node actually supports the expected values, and returns an error if it doesn't. Note that we do not support using safe mode but not finalized mode.
|
||||
func (w *Watcher) getFinality(ctx context.Context) (bool, bool, error) {
|
||||
var finalized, safe bool
|
||||
if w.unsafeDevMode {
|
||||
// Devnet supports finalized and safe (although they returns the same value as latest).
|
||||
finalized = true
|
||||
safe = true
|
||||
} else if w.chainID == vaa.ChainIDAcala ||
|
||||
w.chainID == vaa.ChainIDArbitrum ||
|
||||
w.chainID == vaa.ChainIDBase ||
|
||||
w.chainID == vaa.ChainIDBSC ||
|
||||
w.chainID == vaa.ChainIDEthereum ||
|
||||
w.chainID == vaa.ChainIDKarura ||
|
||||
w.chainID == vaa.ChainIDMoonbeam ||
|
||||
w.chainID == vaa.ChainIDOptimism ||
|
||||
w.chainID == vaa.ChainIDSepolia {
|
||||
finalized = true
|
||||
safe = true
|
||||
}
|
||||
|
||||
// First check to see if polling for finalized blocks is suported.
|
||||
type Marshaller struct {
|
||||
Number *eth_hexutil.Big
|
||||
// If finalized / safe should be supported, read the RPC to make sure they actually are.
|
||||
if finalized {
|
||||
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
c, err := rpc.DialContext(timeout, w.url)
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("failed to connect to endpoint: %w", err)
|
||||
}
|
||||
|
||||
type Marshaller struct {
|
||||
Number *eth_hexutil.Big
|
||||
}
|
||||
var m Marshaller
|
||||
|
||||
err = c.CallContext(ctx, &m, "eth_getBlockByNumber", "finalized", false)
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("finalized not supported by the node when it should be: %w", err)
|
||||
}
|
||||
|
||||
if safe {
|
||||
err = c.CallContext(ctx, &m, "eth_getBlockByNumber", "safe", false)
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("safe not supported by the node when it should be: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var m Marshaller
|
||||
err = c.CallContext(ctx, &m, "eth_getBlockByNumber", "finalized", false)
|
||||
if err == nil {
|
||||
useFinalizedBlocks = true
|
||||
return
|
||||
}
|
||||
|
||||
// If finalized blocks are not supported, then we had better be in safe mode!
|
||||
var safe bool
|
||||
err = c.CallContext(ctx, &safe, "net_isSafeMode")
|
||||
if err != nil {
|
||||
errRet = fmt.Errorf("check for safe mode for url %s failed: %w", w.url, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !safe {
|
||||
errRet = fmt.Errorf("url %s does not support finalized blocks and is not using safe mode", w.url)
|
||||
}
|
||||
|
||||
return
|
||||
return finalized, safe, nil
|
||||
}
|
||||
|
||||
// SetL1Finalizer is used to set the layer one finalizer.
|
||||
|
|
Loading…
Reference in New Issue