Commit 7846f410 authored by Hamdi Allam's avatar Hamdi Allam Committed by GitHub

Merge pull request #8087 from ethereum-optimism/indexer.ovm1.withdrawals

fix(indexer) skip ovm 1.0 hashes
parents d82e51e7 fe65051c
......@@ -13,8 +13,10 @@ import (
"github.com/ethereum-optimism/optimism/indexer/client"
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/prometheus/client_golang/prometheus"
op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
......@@ -31,6 +33,7 @@ import (
type E2ETestSuite struct {
t *testing.T
MetricsRegistry *prometheus.Registry
// API
Client *client.Client
......@@ -153,6 +156,7 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
return E2ETestSuite{
t: t,
MetricsRegistry: metrics.NewRegistry(),
Client: client,
DB: ix.DB,
Indexer: ix,
......
......@@ -312,7 +312,7 @@ func (b *BridgeProcessor) processFinalizedL1Events() error {
legacyBridgeLog := l1BridgeLog.New("mode", "legacy", "from_block_number", legacyFromL1Height, "to_block_number", legacyToL1Height)
legacyBridgeLog.Info("scanning for finalized bridge events")
if err := bridge.LegacyL1ProcessFinalizedBridgeEvents(legacyBridgeLog, tx, b.metrics, b.l1Etl.EthClient, b.chainConfig.L1Contracts, legacyFromL1Height, legacyToL1Height); err != nil {
if err := bridge.LegacyL1ProcessFinalizedBridgeEvents(legacyBridgeLog, tx, b.metrics, b.chainConfig.L1Contracts, legacyFromL1Height, legacyToL1Height); err != nil {
return err
} else if legacyToL1Height.Cmp(toL1Height) == 0 {
return nil // a-ok! Entire range was legacy blocks
......
......@@ -7,6 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/indexer/bigint"
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/processors/bridge/ovm1"
"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum/go-ethereum/common"
......@@ -155,21 +156,30 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L1M
log.Info("detected proven withdrawals", "size", len(provenWithdrawals))
}
skippedOVM1ProvenWithdrawals := 0
for i := range provenWithdrawals {
proven := provenWithdrawals[i]
withdrawal, err := db.BridgeTransactions.L2TransactionWithdrawal(proven.WithdrawalHash)
provenWithdrawal := provenWithdrawals[i]
withdrawal, err := db.BridgeTransactions.L2TransactionWithdrawal(provenWithdrawal.WithdrawalHash)
if err != nil {
return err
} else if withdrawal == nil {
return fmt.Errorf("missing indexed withdrawal! tx_hash = %s", proven.Event.TransactionHash)
if _, ok := ovm1.PortalWithdrawalTransactions[provenWithdrawal.WithdrawalHash]; ok {
skippedOVM1ProvenWithdrawals++
continue
}
return fmt.Errorf("missing indexed withdrawal! tx_hash = %s", provenWithdrawal.Event.TransactionHash)
}
if err := db.BridgeTransactions.MarkL2TransactionWithdrawalProvenEvent(proven.WithdrawalHash, provenWithdrawals[i].Event.GUID); err != nil {
return fmt.Errorf("failed to mark withdrawal as proven. tx_hash = %s: %w", proven.Event.TransactionHash, err)
if err := db.BridgeTransactions.MarkL2TransactionWithdrawalProvenEvent(provenWithdrawal.WithdrawalHash, provenWithdrawals[i].Event.GUID); err != nil {
return fmt.Errorf("failed to mark withdrawal as proven. tx_hash = %s: %w", provenWithdrawal.Event.TransactionHash, err)
}
}
if len(provenWithdrawals) > 0 {
metrics.RecordL1ProvenWithdrawals(len(provenWithdrawals))
if skippedOVM1ProvenWithdrawals > 0 {
metrics.RecordL1SkippedOVM1ProvenWithdrawals(skippedOVM1ProvenWithdrawals)
log.Info("skipped OVM 1.0 proven withdrawals", "size", skippedOVM1ProvenWithdrawals)
}
}
// (2) OptimismPortal (finalized withdrawals)
......@@ -181,12 +191,17 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L1M
log.Info("detected finalized withdrawals", "size", len(finalizedWithdrawals))
}
skippedOVM1FinalizedWithdrawals := 0
for i := range finalizedWithdrawals {
finalizedWithdrawal := finalizedWithdrawals[i]
withdrawal, err := db.BridgeTransactions.L2TransactionWithdrawal(finalizedWithdrawal.WithdrawalHash)
if err != nil {
return err
} else if withdrawal == nil {
if _, ok := ovm1.PortalWithdrawalTransactions[finalizedWithdrawal.WithdrawalHash]; ok {
skippedOVM1FinalizedWithdrawals++
continue
}
return fmt.Errorf("missing indexed withdrawal on finalization! tx_hash = %s", finalizedWithdrawal.Event.TransactionHash.String())
}
......@@ -196,6 +211,10 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L1M
}
if len(finalizedWithdrawals) > 0 {
metrics.RecordL1FinalizedWithdrawals(len(finalizedWithdrawals))
if skippedOVM1ProvenWithdrawals > 0 {
metrics.RecordL1SkippedOVM1FinalizedWithdrawals(skippedOVM1FinalizedWithdrawals)
log.Info("skipped OVM 1.0 finalized withdrawals", "size", skippedOVM1FinalizedWithdrawals)
}
}
// (3) L1CrossDomainMessenger
......@@ -207,21 +226,30 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L1M
log.Info("detected relayed messages", "size", len(crossDomainRelayedMessages))
}
skippedOVM1Messages := 0
for i := range crossDomainRelayedMessages {
relayed := crossDomainRelayedMessages[i]
message, err := db.BridgeMessages.L2BridgeMessage(relayed.MessageHash)
relayedMessage := crossDomainRelayedMessages[i]
message, err := db.BridgeMessages.L2BridgeMessage(relayedMessage.MessageHash)
if err != nil {
return err
} else if message == nil {
return fmt.Errorf("missing indexed L2CrossDomainMessager message! tx_hash = %s", relayed.Event.TransactionHash.String())
if _, ok := ovm1.L1RelayedMessages[relayedMessage.MessageHash]; ok {
skippedOVM1Messages++
continue
}
return fmt.Errorf("missing indexed L2CrossDomainMessager message! tx_hash = %s", relayedMessage.Event.TransactionHash.String())
}
if err := db.BridgeMessages.MarkRelayedL2BridgeMessage(relayed.MessageHash, relayed.Event.GUID); err != nil {
return fmt.Errorf("failed to relay cross domain message. tx_hash = %s: %w", relayed.Event.TransactionHash, err)
if err := db.BridgeMessages.MarkRelayedL2BridgeMessage(relayedMessage.MessageHash, relayedMessage.Event.GUID); err != nil {
return fmt.Errorf("failed to relay cross domain message. tx_hash = %s: %w", relayedMessage.Event.TransactionHash, err)
}
}
if len(crossDomainRelayedMessages) > 0 {
metrics.RecordL1CrossDomainRelayedMessages(len(crossDomainRelayedMessages))
if skippedOVM1Messages > 0 { // Logged as a warning just for visibility
metrics.RecordL1SkippedOVM1CrossDomainRelayedMessages(skippedOVM1Messages)
log.Info("skipped OVM 1.0 relayed L2CrossDomainMessenger withdrawals", "size", skippedOVM1Messages)
}
}
// (4) L1StandardBridge
......
......@@ -10,8 +10,9 @@ import (
"github.com/ethereum-optimism/optimism/indexer/bigint"
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum-optimism/optimism/indexer/processors/bridge/ovm1"
"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
)
......@@ -270,7 +271,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, metri
// according to the pre-bedrock protocol. This follows:
// 1. L1CrossDomainMessenger
// 2. L1StandardBridge
func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Client node.EthClient, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
// (1) L1CrossDomainMessenger -> This is the root-most contract from which bridge events are finalized since withdrawals must be initiated from the
// L2CrossDomainMessenger. Since there's no two-step withdrawal process, we mark the transaction as proven/finalized in the same step
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
......@@ -281,38 +282,19 @@ func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metri
log.Info("detected relayed messages", "size", len(crossDomainRelayedMessages))
}
skippedPreRegenesisMessages := 0
skippedOVM1Messages := 0
for i := range crossDomainRelayedMessages {
relayedMessage := crossDomainRelayedMessages[i]
message, err := db.BridgeMessages.L2BridgeMessage(relayedMessage.MessageHash)
if err != nil {
return err
} else if message == nil {
// Before surfacing an error about a missing withdrawal, we need to handle an edge case
// for OP-Mainnet pre-regensis withdrawals that no longer exist on L2.
tx, err := l1Client.TxByHash(relayedMessage.Event.TransactionHash)
if err != nil {
return fmt.Errorf("unable to query legacy relayed. tx_hash = %s: %w", relayedMessage.Event.TransactionHash, err)
} else if tx == nil {
return fmt.Errorf("missing tx for relayed message! tx_hash = %s", relayedMessage.Event.TransactionHash)
}
relayMessageData := tx.Data()[4:]
inputs, err := contracts.CrossDomainMessengerLegacyRelayMessageEncoding.Inputs.Unpack(relayMessageData)
if err != nil || inputs == nil {
return fmt.Errorf("unable to extract XDomainCallData from relayMessage transaction. tx_hash = %s: %w", relayedMessage.Event.TransactionHash, err)
}
// NOTE: Since OP-Mainnet is the only network to go through a regensis we can simply harcode the
// the starting message nonce at genesis (100k). Any relayed withdrawal on L1 with a lesser nonce
// is a clear indicator of a pre-regenesis withdrawal.
if inputs[3].(*big.Int).Int64() < 100_000 {
// skip pre-regenesis withdrawals
skippedPreRegenesisMessages++
if _, ok := ovm1.L1RelayedMessages[relayedMessage.MessageHash]; ok {
skippedOVM1Messages++
continue
} else {
return fmt.Errorf("missing indexed L2CrossDomainMessenger message! tx_hash = %s", relayedMessage.Event.TransactionHash)
}
return fmt.Errorf("missing indexed L2CrossDomainMessenger message! tx_hash %s", relayedMessage.Event.TransactionHash.String())
}
if err := db.BridgeMessages.MarkRelayedL2BridgeMessage(relayedMessage.MessageHash, relayedMessage.Event.GUID); err != nil {
......@@ -328,13 +310,11 @@ func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metri
}
}
if len(crossDomainRelayedMessages) > 0 {
metrics.RecordL1ProvenWithdrawals(len(crossDomainRelayedMessages))
metrics.RecordL1FinalizedWithdrawals(len(crossDomainRelayedMessages))
metrics.RecordL1CrossDomainRelayedMessages(len(crossDomainRelayedMessages))
if skippedOVM1Messages > 0 { // Logged as a warning just for visibility
metrics.RecordL1SkippedOVM1CrossDomainRelayedMessages(skippedOVM1Messages)
log.Info("skipped OVM 1.0 relayed L2CrossDomainMessenger withdrawals", "size", skippedOVM1Messages)
}
if skippedPreRegenesisMessages > 0 {
// Logged as a warning just for visibility
log.Warn("skipped pre-regensis relayed L2CrossDomainMessenger withdrawals", "size", skippedPreRegenesisMessages)
}
// (2) L1StandardBridge
......
......@@ -25,6 +25,10 @@ type L1Metricer interface {
RecordL1CrossDomainSentMessages(size int)
RecordL1CrossDomainRelayedMessages(size int)
RecordL1SkippedOVM1ProvenWithdrawals(size int)
RecordL1SkippedOVM1FinalizedWithdrawals(size int)
RecordL1SkippedOVM1CrossDomainRelayedMessages(size int)
RecordL1InitiatedBridgeTransfers(token common.Address, size int)
RecordL1FinalizedBridgeTransfers(token common.Address, size int)
}
......@@ -56,15 +60,19 @@ type bridgeMetrics struct {
intervalFailures *prometheus.CounterVec
txDeposits prometheus.Counter
txMintedETH prometheus.Counter
txWithdrawals prometheus.Counter
txWithdrawnETH prometheus.Counter
provenWithdrawals prometheus.Counter
finalizedWithdrawals prometheus.Counter
txMintedETH prometheus.Counter
txWithdrawnETH prometheus.Counter
sentMessages *prometheus.CounterVec
relayedMessages *prometheus.CounterVec
skippedOVM1Withdrawals *prometheus.CounterVec
skippedOVM1RelayedMessages prometheus.Counter
initiatedBridgeTransfers *prometheus.CounterVec
finalizedBridgeTransfers *prometheus.CounterVec
}
......@@ -114,7 +122,7 @@ func NewMetrics(registry *prometheus.Registry) Metricer {
txWithdrawals: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "tx_withdrawals",
Help: "number of processed transactions withdrawn from l2",
Help: "number of initiated transaction withdrawals from l2",
}),
txWithdrawnETH: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
......@@ -145,6 +153,18 @@ func NewMetrics(registry *prometheus.Registry) Metricer {
}, []string{
"chain",
}),
skippedOVM1Withdrawals: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "skipped_ovm1_withdrawals",
Help: "number of skipped ovm 1.0 withdrawals on l1 (proven|finalized)",
}, []string{
"stage",
}),
skippedOVM1RelayedMessages: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "skipped_ovm1_relayed_messages",
Help: "number of skipped ovm 1.0 relayed messages on l1",
}),
initiatedBridgeTransfers: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "initiated_token_transfers",
......@@ -206,6 +226,18 @@ func (m *bridgeMetrics) RecordL1CrossDomainRelayedMessages(size int) {
m.relayedMessages.WithLabelValues("l1").Add(float64(size))
}
func (m *bridgeMetrics) RecordL1SkippedOVM1ProvenWithdrawals(size int) {
m.skippedOVM1Withdrawals.WithLabelValues("stage", "proven").Add(float64(size))
}
func (m *bridgeMetrics) RecordL1SkippedOVM1FinalizedWithdrawals(size int) {
m.skippedOVM1Withdrawals.WithLabelValues("stage", "finalized").Add(float64(size))
}
func (m *bridgeMetrics) RecordL1SkippedOVM1CrossDomainRelayedMessages(size int) {
m.skippedOVM1RelayedMessages.Add(float64(size))
}
func (m *bridgeMetrics) RecordL1InitiatedBridgeTransfers(tokenAddr common.Address, size int) {
m.initiatedBridgeTransfers.WithLabelValues("l1", tokenAddr.String()).Add(float64(size))
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -4,7 +4,6 @@ import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/indexer/bigint"
......@@ -13,31 +12,6 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
)
var (
// Standard ABI types copied from golang ABI tests
addressType, _ = abi.NewType("address", "", nil)
bytesType, _ = abi.NewType("bytes", "", nil)
uint256Type, _ = abi.NewType("uint256", "", nil)
CrossDomainMessengerLegacyRelayMessageEncoding = abi.NewMethod(
"relayMessage",
"relayMessage",
abi.Function,
"external", // mutability
false, // isConst
true, // payable
abi.Arguments{ // inputs
{Name: "target", Type: addressType},
{Name: "sender", Type: addressType},
{Name: "data", Type: bytesType},
{Name: "nonce", Type: uint256Type},
// The actual transaction on the legacy L1CrossDomainMessenger has a trailing
// proof argument but is ignored for the `XDomainCallData` encoding
},
abi.Arguments{}, // outputs
)
)
type CrossDomainMessengerSentMessageEvent struct {
Event *database.ContractEvent
BridgeMessage database.BridgeMessage
......@@ -117,7 +91,8 @@ func CrossDomainMessengerSentMessageEvents(chainSelector string, contractAddress
default:
// NOTE: We explicitly fail here since the presence of a new version means finalization
// logic needs to be updated to ensure L1 finalization can run from genesis and handle
// the changing version formats. This is meant to be a serving indicator of this.
// the changing version formats. Any unrelayed OVM1 messages that have been harcoded with
// the v1 hash format also need to be updated. This failure is a serving indicator
return nil, fmt.Errorf("expected cross domain version 0 or version 1: %d", version)
}
......
......@@ -30,12 +30,6 @@ type OptimismPortalWithdrawalFinalizedEvent struct {
Event *database.ContractEvent
}
type OptimismPortalProvenWithdrawal struct {
OutputRoot [32]byte
Timestamp *big.Int
L2OutputIndex *big.Int
}
func OptimismPortalTransactionDepositEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]OptimismPortalTransactionDepositEvent, error) {
optimismPortalAbi, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment