Commit 037ec87d authored by Hamdi Allam's avatar Hamdi Allam

indexer.ovm1.withdrawals

parent 7046de8e
......@@ -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 { // Logged as a warning for visibility
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,17 @@ type bridgeMetrics struct {
intervalFailures *prometheus.CounterVec
txDeposits prometheus.Counter
txWithdrawals *prometheus.CounterVec
txMintedETH prometheus.Counter
txWithdrawals prometheus.Counter
txWithdrawnETH prometheus.Counter
provenWithdrawals prometheus.Counter
finalizedWithdrawals prometheus.Counter
sentMessages *prometheus.CounterVec
relayedMessages *prometheus.CounterVec
skippedOVM1Withdrawals *prometheus.CounterVec
skippedOVM1RelayedMessages prometheus.Counter
initiatedBridgeTransfers *prometheus.CounterVec
finalizedBridgeTransfers *prometheus.CounterVec
}
......@@ -111,26 +117,18 @@ func NewMetrics(registry *prometheus.Registry) Metricer {
Name: "tx_minted_eth",
Help: "amount of eth bridged from l1",
}),
txWithdrawals: factory.NewCounter(prometheus.CounterOpts{
txWithdrawals: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "tx_withdrawals",
Help: "number of processed transactions withdrawn from l2",
Help: "number of processed transactions withdrawals (initiated|proven|finalized)",
}, []string{
"stage",
}),
txWithdrawnETH: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "tx_withdrawn_eth",
Help: "amount of eth withdrawn from l2",
}),
provenWithdrawals: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "proven_withdrawals",
Help: "number of proven tx withdrawals on l1",
}),
finalizedWithdrawals: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "finalized_withdrawals",
Help: "number of finalized tx withdrawals on l1",
}),
sentMessages: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "sent_messages",
......@@ -145,6 +143,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",
......@@ -191,11 +201,11 @@ func (m *bridgeMetrics) RecordL1TransactionDeposits(size int, mintedETH float64)
}
func (m *bridgeMetrics) RecordL1ProvenWithdrawals(size int) {
m.provenWithdrawals.Add(float64(size))
m.txWithdrawals.WithLabelValues("stage", "proven").Add(float64(size))
}
func (m *bridgeMetrics) RecordL1FinalizedWithdrawals(size int) {
m.finalizedWithdrawals.Add(float64(size))
m.txWithdrawals.WithLabelValues("stage", "finalized").Add(float64(size))
}
func (m *bridgeMetrics) RecordL1CrossDomainSentMessages(size int) {
......@@ -206,6 +216,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))
}
......@@ -236,7 +258,7 @@ func (m *bridgeMetrics) RecordL2LatestFinalizedHeight(height *big.Int) {
}
func (m *bridgeMetrics) RecordL2TransactionWithdrawals(size int, withdrawnETH float64) {
m.txWithdrawals.Add(float64(size))
m.txWithdrawals.WithLabelValues("stage", "initiated").Add(float64(size))
m.txWithdrawnETH.Add(withdrawnETH)
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -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