|
|
@@ -186,6 +186,8 @@ func (w *Watcher) Run(ctx 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.
|
|
|
@@ -197,14 +199,20 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
return fmt.Errorf("dialing eth client failed: %w", err)
|
|
|
}
|
|
|
} else if useFinalizedBlocks {
|
|
|
- logger.Info("using finalized blocks")
|
|
|
+ if w.chainID == vaa.ChainIDEthereum && !w.unsafeDevMode {
|
|
|
+ safeBlocksSupported = true
|
|
|
+ logger.Info("using finalized blocks, will publish safe blocks")
|
|
|
+ } else {
|
|
|
+ logger.Info("using finalized 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, true)
|
|
|
+ w.ethConn, err = connectors.NewBlockPollConnector(ctx, baseConnector, finalizers.NewDefaultFinalizer(), 250*time.Millisecond, true, safeBlocksSupported)
|
|
|
if err != nil {
|
|
|
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
|
|
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
|
|
@@ -218,7 +226,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
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)
|
|
|
+ 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)
|
|
|
@@ -235,7 +243,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
return fmt.Errorf("dialing eth client failed: %w", err)
|
|
|
}
|
|
|
finalizer := finalizers.NewNeonFinalizer(logger, baseConnector, baseConnector.Client(), w.l1Finalizer)
|
|
|
- pollConnector, err := connectors.NewBlockPollConnector(ctx, baseConnector, finalizer, 250*time.Millisecond, false)
|
|
|
+ pollConnector, 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)
|
|
|
@@ -258,7 +266,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
return fmt.Errorf("dialing eth client failed: %w", err)
|
|
|
}
|
|
|
finalizer := finalizers.NewArbitrumFinalizer(logger, baseConnector, baseConnector.Client(), w.l1Finalizer)
|
|
|
- pollConnector, err := connectors.NewBlockPollConnector(ctx, baseConnector, finalizer, 250*time.Millisecond, false)
|
|
|
+ pollConnector, 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)
|
|
|
@@ -281,7 +289,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
return fmt.Errorf("dialing eth client failed: %w", err)
|
|
|
}
|
|
|
finalizer := finalizers.NewOptimismFinalizer(timeout, logger, baseConnector, w.l1Finalizer)
|
|
|
- w.ethConn, err = connectors.NewBlockPollConnector(ctx, baseConnector, finalizer, 250*time.Millisecond, false)
|
|
|
+ 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)
|
|
|
@@ -348,9 +356,10 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
}
|
|
|
}()
|
|
|
|
|
|
- // Track the current block number so we can compare it to the block number of
|
|
|
+ // Track the current block numbers so we can compare it to the block number of
|
|
|
// the message publication for observation requests.
|
|
|
var currentBlockNumber uint64
|
|
|
+ var currentSafeBlockNumber uint64
|
|
|
|
|
|
go func() {
|
|
|
for {
|
|
|
@@ -377,11 +386,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
// always sends the head before it sends the logs (implicit synchronization
|
|
|
// by relying on the same websocket connection).
|
|
|
blockNumberU := atomic.LoadUint64(¤tBlockNumber)
|
|
|
- if blockNumberU == 0 {
|
|
|
- logger.Error("no block number available, ignoring observation request",
|
|
|
- zap.String("eth_network", w.networkName))
|
|
|
- continue
|
|
|
- }
|
|
|
+ safeBlockNumberU := atomic.LoadUint64(¤tSafeBlockNumber)
|
|
|
|
|
|
timeout, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
|
blockNumber, msgs, err := MessageEventsForTransaction(timeout, w.ethConn, w.contract, w.chainID, tx)
|
|
|
@@ -407,6 +412,43 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
+ if msg.ConsistencyLevel == vaa.ConsistencyLevelSafe && safeBlocksSupported {
|
|
|
+ if safeBlockNumberU == 0 {
|
|
|
+ logger.Error("no safe block number available, ignoring observation request",
|
|
|
+ zap.String("eth_network", w.networkName))
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if blockNumber <= safeBlockNumberU {
|
|
|
+ logger.Info("re-observed message publication transaction",
|
|
|
+ zap.Stringer("tx", msg.TxHash),
|
|
|
+ zap.Stringer("emitter_address", msg.EmitterAddress),
|
|
|
+ zap.Uint64("sequence", msg.Sequence),
|
|
|
+ zap.Uint64("current_safe_block", safeBlockNumberU),
|
|
|
+ zap.Uint64("observed_block", blockNumber),
|
|
|
+ zap.String("eth_network", w.networkName),
|
|
|
+ )
|
|
|
+ w.msgChan <- msg
|
|
|
+ } else {
|
|
|
+ logger.Info("ignoring re-observed message publication transaction",
|
|
|
+ zap.Stringer("tx", msg.TxHash),
|
|
|
+ zap.Stringer("emitter_address", msg.EmitterAddress),
|
|
|
+ zap.Uint64("sequence", msg.Sequence),
|
|
|
+ zap.Uint64("current_safe_block", safeBlockNumberU),
|
|
|
+ zap.Uint64("observed_block", blockNumber),
|
|
|
+ zap.String("eth_network", w.networkName),
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if blockNumberU == 0 {
|
|
|
+ logger.Error("no block number available, ignoring observation request",
|
|
|
+ zap.String("eth_network", w.networkName))
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
var expectedConfirmations uint64
|
|
|
if w.waitForConfirmations {
|
|
|
expectedConfirmations = uint64(msg.ConsistencyLevel)
|
|
|
@@ -554,7 +596,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
continue
|
|
|
}
|
|
|
if ev.Number == nil {
|
|
|
- logger.Error("new header block number is nil", zap.String("eth_network", w.networkName))
|
|
|
+ logger.Error("new header block number is nil", zap.String("eth_network", w.networkName), zap.Bool("is_safe_block", ev.Safe))
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
@@ -563,6 +605,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
logger.Info("processing new header",
|
|
|
zap.Stringer("current_block", ev.Number),
|
|
|
zap.Stringer("current_blockhash", currentHash),
|
|
|
+ zap.Bool("is_safe_block", ev.Safe),
|
|
|
zap.String("eth_network", w.networkName))
|
|
|
currentEthHeight.WithLabelValues(w.networkName).Set(float64(ev.Number.Int64()))
|
|
|
readiness.SetReady(w.readiness)
|
|
|
@@ -574,12 +617,24 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
w.pendingMu.Lock()
|
|
|
|
|
|
blockNumberU := ev.Number.Uint64()
|
|
|
- atomic.StoreUint64(¤tBlockNumber, blockNumberU)
|
|
|
- atomic.StoreUint64(&w.latestFinalizedBlockNumber, blockNumberU)
|
|
|
+ if ev.Safe {
|
|
|
+ atomic.StoreUint64(¤tSafeBlockNumber, blockNumberU)
|
|
|
+ } else {
|
|
|
+ atomic.StoreUint64(¤tBlockNumber, blockNumberU)
|
|
|
+ atomic.StoreUint64(&w.latestFinalizedBlockNumber, blockNumberU)
|
|
|
+ }
|
|
|
|
|
|
for key, pLock := range w.pending {
|
|
|
+ // If this block is safe, only process messages wanting safe.
|
|
|
+ // If it's not safe, only process messages wanting finalized.
|
|
|
+ if safeBlocksSupported {
|
|
|
+ if ev.Safe != (pLock.message.ConsistencyLevel == vaa.ConsistencyLevelSafe) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
var expectedConfirmations uint64
|
|
|
- if w.waitForConfirmations {
|
|
|
+ if w.waitForConfirmations && !ev.Safe {
|
|
|
expectedConfirmations = uint64(pLock.message.ConsistencyLevel)
|
|
|
}
|
|
|
|
|
|
@@ -591,6 +646,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
zap.Stringer("emitter_address", key.EmitterAddress),
|
|
|
zap.Uint64("sequence", key.Sequence),
|
|
|
zap.Stringer("current_block", ev.Number),
|
|
|
+ zap.Bool("is_safe_block", ev.Safe),
|
|
|
zap.Stringer("current_blockhash", currentHash),
|
|
|
zap.String("eth_network", w.networkName),
|
|
|
zap.Uint64("expectedConfirmations", expectedConfirmations),
|
|
|
@@ -622,6 +678,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
zap.Stringer("emitter_address", key.EmitterAddress),
|
|
|
zap.Uint64("sequence", key.Sequence),
|
|
|
zap.Stringer("current_block", ev.Number),
|
|
|
+ zap.Bool("is_safe_block", ev.Safe),
|
|
|
zap.Stringer("current_blockhash", currentHash),
|
|
|
zap.String("eth_network", w.networkName),
|
|
|
zap.Error(err))
|
|
|
@@ -640,6 +697,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
zap.Stringer("emitter_address", key.EmitterAddress),
|
|
|
zap.Uint64("sequence", key.Sequence),
|
|
|
zap.Stringer("current_block", ev.Number),
|
|
|
+ zap.Bool("is_safe_block", ev.Safe),
|
|
|
zap.Stringer("current_blockhash", currentHash),
|
|
|
zap.String("eth_network", w.networkName),
|
|
|
zap.Error(err))
|
|
|
@@ -656,6 +714,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
zap.Stringer("emitter_address", key.EmitterAddress),
|
|
|
zap.Uint64("sequence", key.Sequence),
|
|
|
zap.Stringer("current_block", ev.Number),
|
|
|
+ zap.Bool("is_safe_block", ev.Safe),
|
|
|
zap.Stringer("current_blockhash", currentHash),
|
|
|
zap.String("eth_network", w.networkName),
|
|
|
zap.Error(err))
|
|
|
@@ -672,6 +731,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
zap.Stringer("emitter_address", key.EmitterAddress),
|
|
|
zap.Uint64("sequence", key.Sequence),
|
|
|
zap.Stringer("current_block", ev.Number),
|
|
|
+ zap.Bool("is_safe_block", ev.Safe),
|
|
|
zap.Stringer("current_blockhash", currentHash),
|
|
|
zap.String("eth_network", w.networkName))
|
|
|
delete(w.pending, key)
|
|
|
@@ -685,6 +745,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
zap.Stringer("emitter_address", key.EmitterAddress),
|
|
|
zap.Uint64("sequence", key.Sequence),
|
|
|
zap.Stringer("current_block", ev.Number),
|
|
|
+ zap.Bool("is_safe_block", ev.Safe),
|
|
|
zap.Stringer("current_blockhash", currentHash),
|
|
|
zap.String("eth_network", w.networkName))
|
|
|
delete(w.pending, key)
|
|
|
@@ -696,6 +757,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|
|
w.pendingMu.Unlock()
|
|
|
logger.Info("processed new header",
|
|
|
zap.Stringer("current_block", ev.Number),
|
|
|
+ zap.Bool("is_safe_block", ev.Safe),
|
|
|
zap.Stringer("current_blockhash", currentHash),
|
|
|
zap.Duration("took", time.Since(start)),
|
|
|
zap.String("eth_network", w.networkName))
|