Commit 38d3505b authored by Hamdi Allam's avatar Hamdi Allam

bridge metrics

parent be3952fa
...@@ -71,10 +71,10 @@ func (etl *ETL) Start(ctx context.Context) error { ...@@ -71,10 +71,10 @@ func (etl *ETL) Start(ctx context.Context) error {
etl.log.Error("error querying for headers", "err", err) etl.log.Error("error querying for headers", "err", err)
} else if len(newHeaders) == 0 { } else if len(newHeaders) == 0 {
etl.log.Warn("no new headers. processor unexpectedly at head...") etl.log.Warn("no new headers. processor unexpectedly at head...")
} else {
headers = newHeaders
etl.metrics.RecordBatchHeaders(len(newHeaders))
} }
headers = newHeaders
etl.metrics.RecordBatchHeaders(len(newHeaders))
} }
// only clear the reference if we were able to process this batch // only clear the reference if we were able to process this batch
...@@ -107,7 +107,7 @@ func (etl *ETL) processBatch(headers []types.Header) error { ...@@ -107,7 +107,7 @@ func (etl *ETL) processBatch(headers []types.Header) error {
headersWithLog := make(map[common.Hash]bool, len(headers)) headersWithLog := make(map[common.Hash]bool, len(headers))
logs, err := etl.EthClient.FilterLogs(ethereum.FilterQuery{FromBlock: firstHeader.Number, ToBlock: lastHeader.Number, Addresses: etl.contracts}) logs, err := etl.EthClient.FilterLogs(ethereum.FilterQuery{FromBlock: firstHeader.Number, ToBlock: lastHeader.Number, Addresses: etl.contracts})
if err != nil { if err != nil {
batchLog.Info("unable to extract logs", "err", err) batchLog.Info("failed to extract logs", "err", err)
return err return err
} }
if len(logs) > 0 { if len(logs) > 0 {
...@@ -118,7 +118,8 @@ func (etl *ETL) processBatch(headers []types.Header) error { ...@@ -118,7 +118,8 @@ func (etl *ETL) processBatch(headers []types.Header) error {
log := logs[i] log := logs[i]
if _, ok := headerMap[log.BlockHash]; !ok { if _, ok := headerMap[log.BlockHash]; !ok {
// NOTE. Definitely an error state if the none of the headers were re-orged out in between // NOTE. Definitely an error state if the none of the headers were re-orged out in between
// the blocks and logs retrieval operations. However, we need to gracefully handle reorgs // the blocks and logs retrieval operations. Unlikely as long as the confirmation depth has
// been appropriately set or when we get to natively handling reorgs.
batchLog.Error("log found with block hash not in the batch", "block_hash", logs[i].BlockHash, "log_index", logs[i].Index) batchLog.Error("log found with block hash not in the batch", "block_hash", logs[i].BlockHash, "log_index", logs[i].Index)
return errors.New("parsed log with a block hash not in the batch") return errors.New("parsed log with a block hash not in the batch")
} }
......
...@@ -16,7 +16,6 @@ type Metricer interface { ...@@ -16,7 +16,6 @@ type Metricer interface {
RecordInterval() (done func(err error)) RecordInterval() (done func(err error))
// Batch Extraction // Batch Extraction
RecordBatchFailure()
RecordBatchLatestHeight(height *big.Int) RecordBatchLatestHeight(height *big.Int)
RecordBatchHeaders(size int) RecordBatchHeaders(size int)
RecordBatchLog(contractAddress common.Address) RecordBatchLog(contractAddress common.Address)
...@@ -108,17 +107,13 @@ func (m *etlMetrics) RecordInterval() func(error) { ...@@ -108,17 +107,13 @@ func (m *etlMetrics) RecordInterval() func(error) {
timer := prometheus.NewTimer(m.intervalDuration) timer := prometheus.NewTimer(m.intervalDuration)
return func(err error) { return func(err error) {
if err != nil { if err != nil {
m.RecordBatchFailure() m.batchFailures.Inc()
} }
timer.ObserveDuration() timer.ObserveDuration()
} }
} }
func (m *etlMetrics) RecordBatchFailure() {
m.batchFailures.Inc()
}
func (m *etlMetrics) RecordBatchLatestHeight(height *big.Int) { func (m *etlMetrics) RecordBatchLatestHeight(height *big.Int) {
m.batchLatestHeight.Set(float64(height.Uint64())) m.batchLatestHeight.Set(float64(height.Uint64()))
} }
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"github.com/ethereum-optimism/optimism/indexer/etl" "github.com/ethereum-optimism/optimism/indexer/etl"
"github.com/ethereum-optimism/optimism/indexer/node" "github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum-optimism/optimism/indexer/processors" "github.com/ethereum-optimism/optimism/indexer/processors"
"github.com/ethereum-optimism/optimism/indexer/processors/bridge"
"github.com/ethereum-optimism/optimism/op-service/httputil" "github.com/ethereum-optimism/optimism/op-service/httputil"
"github.com/ethereum-optimism/optimism/op-service/metrics" "github.com/ethereum-optimism/optimism/op-service/metrics"
) )
...@@ -82,7 +83,7 @@ func NewIndexer( ...@@ -82,7 +83,7 @@ func NewIndexer(
} }
// Bridge // Bridge
bridgeProcessor, err := processors.NewBridgeProcessor(log, db, l1Etl, chainConfig) bridgeProcessor, err := processors.NewBridgeProcessor(log, db, bridge.NewMetrics(metricsRegistry), l1Etl, chainConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/metrics" "github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
......
...@@ -16,8 +16,10 @@ import ( ...@@ -16,8 +16,10 @@ import (
) )
type BridgeProcessor struct { type BridgeProcessor struct {
log log.Logger log log.Logger
db *database.DB db *database.DB
metrics bridge.Metricer
l1Etl *etl.L1ETL l1Etl *etl.L1ETL
chainConfig config.ChainConfig chainConfig config.ChainConfig
...@@ -25,7 +27,7 @@ type BridgeProcessor struct { ...@@ -25,7 +27,7 @@ type BridgeProcessor struct {
LatestL2Header *types.Header LatestL2Header *types.Header
} }
func NewBridgeProcessor(log log.Logger, db *database.DB, l1Etl *etl.L1ETL, chainConfig config.ChainConfig) (*BridgeProcessor, error) { func NewBridgeProcessor(log log.Logger, db *database.DB, metrics bridge.Metricer, l1Etl *etl.L1ETL, chainConfig config.ChainConfig) (*BridgeProcessor, error) {
log = log.New("processor", "bridge") log = log.New("processor", "bridge")
latestL1Header, err := db.BridgeTransactions.L1LatestBlockHeader() latestL1Header, err := db.BridgeTransactions.L1LatestBlockHeader()
...@@ -45,28 +47,22 @@ func NewBridgeProcessor(log log.Logger, db *database.DB, l1Etl *etl.L1ETL, chain ...@@ -45,28 +47,22 @@ func NewBridgeProcessor(log log.Logger, db *database.DB, l1Etl *etl.L1ETL, chain
if latestL1Header != nil { if latestL1Header != nil {
l1Height = latestL1Header.Number l1Height = latestL1Header.Number
l1Header = latestL1Header.RLPHeader.Header() l1Header = latestL1Header.RLPHeader.Header()
metrics.RecordLatestIndexedL1Height(l1Height)
} }
if latestL2Header != nil { if latestL2Header != nil {
l2Height = latestL2Header.Number l2Height = latestL2Header.Number
l2Header = latestL2Header.RLPHeader.Header() l2Header = latestL2Header.RLPHeader.Header()
metrics.RecordLatestIndexedL2Height(l2Height)
} }
log.Info("detected latest indexed bridge state", "l1_block_number", l1Height, "l2_block_number", l2Height) log.Info("detected latest indexed bridge state", "l1_block_number", l1Height, "l2_block_number", l2Height)
} }
return &BridgeProcessor{log, db, l1Etl, chainConfig, l1Header, l2Header}, nil return &BridgeProcessor{log, db, metrics, l1Etl, chainConfig, l1Header, l2Header}, nil
} }
func (b *BridgeProcessor) Start(ctx context.Context) error { func (b *BridgeProcessor) Start(ctx context.Context) error {
done := ctx.Done() done := ctx.Done()
// In order to ensure all seen bridge finalization events correspond with seen
// bridge initiated events, we establish a shared marker between L1 and L2 when
// processing events.
//
// As L1 and L2 blocks are indexed, the highest indexed L2 block starting a new
// sequencing epoch and corresponding L1 origin that has also been indexed
// serves as this shared marker.
// Fire off independently on startup to check for // Fire off independently on startup to check for
// new data or if we've indexed new L1 data. // new data or if we've indexed new L1 data.
l1EtlUpdates := b.l1Etl.Notify() l1EtlUpdates := b.l1Etl.Notify()
...@@ -85,143 +81,149 @@ func (b *BridgeProcessor) Start(ctx context.Context) error { ...@@ -85,143 +81,149 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
case <-l1EtlUpdates: case <-l1EtlUpdates:
} }
// In the event where we have a large number of un-observed epochs, we cap the search done := b.metrics.RecordInterval()
// of epochs by 10k. If this turns out to be a bottleneck, we can parallelize the processing done(b.run())
// of epochs to significantly speed up sync times. }
maxEpochRange := uint64(10_000) }
var lastEpoch *big.Int
if b.LatestL1Header != nil {
lastEpoch = b.LatestL1Header.Number
}
latestEpoch, err := b.db.Blocks.LatestObservedEpoch(lastEpoch, maxEpochRange) // Runs the processing loop. In order to ensure all seen bridge finalization events
if err != nil { // can be correlated with bridge initiated events, we establish a shared marker between
return err // L1 and L2 when processing events. The lastest shared indexed time (epochs) between
} else if latestEpoch == nil { // L1 and L2 serves as this shared marker.
if b.LatestL1Header != nil || b.LatestL2Header != nil { func (b *BridgeProcessor) run() error {
// Once we have some indexed state `latestEpoch != nil` as `LatestObservedEpoch` is inclusive in its search with the last provided epoch. // In the event where we have a large number of un-observed epochs, we cap the search
b.log.Error("bridge events indexed, but no observed epoch returned", "latest_bridge_l1_block_number", b.LatestL1Header.Number) // of epochs by 10k. If this turns out to be a bottleneck, we can parallelize the processing
return errors.New("bridge events indexed, but no observed epoch returned") // of epochs to significantly speed up sync times.
} maxEpochRange := uint64(10_000)
b.log.Warn("no observed epochs available. waiting...") var lastEpoch *big.Int
continue if b.LatestL1Header != nil {
} lastEpoch = b.LatestL1Header.Number
}
if b.LatestL1Header != nil && latestEpoch.L1BlockHeader.Hash == b.LatestL1Header.Hash() { latestEpoch, err := b.db.Blocks.LatestObservedEpoch(lastEpoch, maxEpochRange)
b.log.Warn("all available epochs indexed", "latest_bridge_l1_block_number", b.LatestL1Header.Number) if err != nil {
continue return err
} else if latestEpoch == nil {
if b.LatestL1Header != nil || b.LatestL2Header != nil {
// Once we have some indexed state `latestEpoch != nil` as `LatestObservedEpoch` is inclusive in its search with the last provided epoch.
b.log.Error("bridge events indexed, but no observed epoch returned", "latest_bridge_l1_block_number", b.LatestL1Header.Number)
return errors.New("bridge events indexed, but no observed epoch returned")
} }
b.log.Warn("no observed epochs available. waiting...")
return nil
}
// Integrity Checks if b.LatestL1Header != nil && latestEpoch.L1BlockHeader.Hash == b.LatestL1Header.Hash() {
b.log.Warn("all available epochs indexed", "latest_bridge_l1_block_number", b.LatestL1Header.Number)
return nil
}
genesisL1Height := big.NewInt(int64(b.chainConfig.L1StartingHeight)) // Integrity Checks
if latestEpoch.L1BlockHeader.Number.Cmp(genesisL1Height) < 0 {
b.log.Error("L1 epoch less than starting L1 height observed", "l1_starting_number", genesisL1Height, "latest_epoch_number", latestEpoch.L1BlockHeader.Number)
return errors.New("L1 epoch less than starting L1 height observed")
}
if b.LatestL1Header != nil && latestEpoch.L1BlockHeader.Number.Cmp(b.LatestL1Header.Number) <= 0 {
b.log.Error("decreasing l1 block height observed", "latest_bridge_l1_block_number", b.LatestL1Header.Number, "latest_epoch_number", latestEpoch.L1BlockHeader.Number)
return errors.New("decreasing l1 block heght observed")
}
if b.LatestL2Header != nil && latestEpoch.L2BlockHeader.Number.Cmp(b.LatestL2Header.Number) <= 0 {
b.log.Error("decreasing l2 block height observed", "latest_bridge_l2_block_number", b.LatestL2Header.Number, "latest_epoch_number", latestEpoch.L2BlockHeader.Number)
return errors.New("decreasing l2 block heght observed")
}
// Process Bridge Events genesisL1Height := big.NewInt(int64(b.chainConfig.L1StartingHeight))
if latestEpoch.L1BlockHeader.Number.Cmp(genesisL1Height) < 0 {
b.log.Error("L1 epoch less than starting L1 height observed", "l1_starting_number", genesisL1Height, "latest_epoch_number", latestEpoch.L1BlockHeader.Number)
return errors.New("L1 epoch less than starting L1 height observed")
}
if b.LatestL1Header != nil && latestEpoch.L1BlockHeader.Number.Cmp(b.LatestL1Header.Number) <= 0 {
b.log.Error("non-increasing l1 block height observed", "latest_bridge_l1_block_number", b.LatestL1Header.Number, "latest_epoch_l1_block_number", latestEpoch.L1BlockHeader.Number)
return errors.New("non-increasing l1 block heght observed")
}
if b.LatestL2Header != nil && latestEpoch.L2BlockHeader.Number.Cmp(b.LatestL2Header.Number) <= 0 {
b.log.Error("non-increasing l2 block height observed", "latest_bridge_l2_block_number", b.LatestL2Header.Number, "latest_epoch_l2_block_number", latestEpoch.L2BlockHeader.Number)
return errors.New("non-increasing l2 block heght observed")
}
toL1Height, toL2Height := latestEpoch.L1BlockHeader.Number, latestEpoch.L2BlockHeader.Number toL1Height, toL2Height := latestEpoch.L1BlockHeader.Number, latestEpoch.L2BlockHeader.Number
fromL1Height, fromL2Height := genesisL1Height, bigint.Zero fromL1Height, fromL2Height := genesisL1Height, bigint.Zero
if b.LatestL1Header != nil { if b.LatestL1Header != nil {
fromL1Height = new(big.Int).Add(b.LatestL1Header.Number, bigint.One) fromL1Height = new(big.Int).Add(b.LatestL1Header.Number, bigint.One)
} }
if b.LatestL2Header != nil { if b.LatestL2Header != nil {
fromL2Height = new(big.Int).Add(b.LatestL2Header.Number, bigint.One) fromL2Height = new(big.Int).Add(b.LatestL2Header.Number, bigint.One)
} }
l1BedrockStartingHeight := big.NewInt(int64(b.chainConfig.L1BedrockStartingHeight)) l1BedrockStartingHeight := big.NewInt(int64(b.chainConfig.L1BedrockStartingHeight))
l2BedrockStartingHeight := big.NewInt(int64(b.chainConfig.L2BedrockStartingHeight)) l2BedrockStartingHeight := big.NewInt(int64(b.chainConfig.L2BedrockStartingHeight))
batchLog := b.log.New("epoch_start_number", fromL1Height, "epoch_end_number", toL1Height) batchLog := b.log.New("epoch_start_number", fromL1Height, "epoch_end_number", toL1Height)
batchLog.Info("unobserved epochs", "latest_l1_block_number", fromL1Height, "latest_l2_block_number", fromL2Height) batchLog.Info("unobserved epochs", "latest_l1_block_number", fromL1Height, "latest_l2_block_number", fromL2Height)
if err := b.db.Transaction(func(tx *database.DB) error { if err := b.db.Transaction(func(tx *database.DB) error {
l1BridgeLog := b.log.New("bridge", "l1") l1BridgeLog := b.log.New("bridge", "l1")
l2BridgeLog := b.log.New("bridge", "l2") l2BridgeLog := b.log.New("bridge", "l2")
// FOR OP-MAINNET, OP-GOERLI ONLY! Specially handle the existence of pre-bedrock blocks // FOR OP-MAINNET, OP-GOERLI ONLY! Specially handle the existence of pre-bedrock blocks
if l1BedrockStartingHeight.Cmp(fromL1Height) > 0 { if l1BedrockStartingHeight.Cmp(fromL1Height) > 0 {
legacyFromL1Height, legacyToL1Height := fromL1Height, toL1Height legacyFromL1Height, legacyToL1Height := fromL1Height, toL1Height
legacyFromL2Height, legacyToL2Height := fromL2Height, toL2Height legacyFromL2Height, legacyToL2Height := fromL2Height, toL2Height
if l1BedrockStartingHeight.Cmp(toL1Height) <= 0 { if l1BedrockStartingHeight.Cmp(toL1Height) <= 0 {
legacyToL1Height = new(big.Int).Sub(l1BedrockStartingHeight, bigint.One) legacyToL1Height = new(big.Int).Sub(l1BedrockStartingHeight, bigint.One)
legacyToL2Height = new(big.Int).Sub(l2BedrockStartingHeight, bigint.One) legacyToL2Height = new(big.Int).Sub(l2BedrockStartingHeight, bigint.One)
}
l1BridgeLog = l1BridgeLog.New("mode", "legacy", "from_l1_block_number", legacyFromL1Height, "to_l1_block_number", legacyToL1Height)
l1BridgeLog.Info("scanning for bridge events")
l2BridgeLog = l2BridgeLog.New("mode", "legacy", "from_l2_block_number", legacyFromL2Height, "to_l2_block_number", legacyToL2Height)
l2BridgeLog.Info("scanning for bridge events")
// First, find all possible initiated bridge events
if err := bridge.LegacyL1ProcessInitiatedBridgeEvents(l1BridgeLog, tx, b.chainConfig.L1Contracts, legacyFromL1Height, legacyToL1Height); err != nil {
return err
}
if err := bridge.LegacyL2ProcessInitiatedBridgeEvents(l2BridgeLog, tx, b.chainConfig.L2Contracts, legacyFromL2Height, legacyToL2Height); err != nil {
return err
}
// Now that all initiated events have been indexed, it is ensured that all finalization can find their counterpart.
if err := bridge.LegacyL1ProcessFinalizedBridgeEvents(l1BridgeLog, tx, b.l1Etl.EthClient, b.chainConfig.L1Contracts, legacyFromL1Height, legacyToL1Height); err != nil {
return err
}
if err := bridge.LegacyL2ProcessFinalizedBridgeEvents(l2BridgeLog, tx, b.chainConfig.L2Contracts, legacyFromL2Height, legacyToL2Height); err != nil {
return err
}
if legacyToL1Height.Cmp(toL1Height) == 0 {
// a-ok! entire batch was legacy blocks
return nil
}
batchLog.Info("detected switch to bedrock", "l1_bedrock_starting_height", l1BedrockStartingHeight, "l2_bedrock_starting_height", l2BedrockStartingHeight)
fromL1Height = l1BedrockStartingHeight
fromL2Height = l2BedrockStartingHeight
} }
l1BridgeLog = l1BridgeLog.New("from_l1_block_number", fromL1Height, "to_l1_block_number", toL1Height) l1BridgeLog = l1BridgeLog.New("mode", "legacy", "from_l1_block_number", legacyFromL1Height, "to_l1_block_number", legacyToL1Height)
l1BridgeLog.Info("scanning for bridge events") l1BridgeLog.Info("scanning for bridge events")
l2BridgeLog = l2BridgeLog.New("from_l2_block_number", fromL2Height, "to_l2_block_number", toL2Height) l2BridgeLog = l2BridgeLog.New("mode", "legacy", "from_l2_block_number", legacyFromL2Height, "to_l2_block_number", legacyToL2Height)
l2BridgeLog.Info("scanning for bridge events") l2BridgeLog.Info("scanning for bridge events")
// First, find all possible initiated bridge events // First, find all possible initiated bridge events
if err := bridge.L1ProcessInitiatedBridgeEvents(l1BridgeLog, tx, b.chainConfig.L1Contracts, fromL1Height, toL1Height); err != nil { if err := bridge.LegacyL1ProcessInitiatedBridgeEvents(l1BridgeLog, tx, b.metrics, b.chainConfig.L1Contracts, legacyFromL1Height, legacyToL1Height); err != nil {
return err return err
} }
if err := bridge.L2ProcessInitiatedBridgeEvents(l2BridgeLog, tx, b.chainConfig.L2Contracts, fromL2Height, toL2Height); err != nil { if err := bridge.LegacyL2ProcessInitiatedBridgeEvents(l2BridgeLog, tx, b.metrics, b.chainConfig.L2Contracts, legacyFromL2Height, legacyToL2Height); err != nil {
return err return err
} }
// Now all finalization events can find their counterpart. // Now that all initiated events have been indexed, it is ensured that all finalization can find their counterpart.
if err := bridge.L1ProcessFinalizedBridgeEvents(l1BridgeLog, tx, b.chainConfig.L1Contracts, fromL1Height, toL1Height); err != nil { if err := bridge.LegacyL1ProcessFinalizedBridgeEvents(l1BridgeLog, tx, b.metrics, b.l1Etl.EthClient, b.chainConfig.L1Contracts, legacyFromL1Height, legacyToL1Height); err != nil {
return err return err
} }
if err := bridge.L2ProcessFinalizedBridgeEvents(l2BridgeLog, tx, b.chainConfig.L2Contracts, fromL2Height, toL2Height); err != nil { if err := bridge.LegacyL2ProcessFinalizedBridgeEvents(l2BridgeLog, tx, b.metrics, b.chainConfig.L2Contracts, legacyFromL2Height, legacyToL2Height); err != nil {
return err return err
} }
// a-ok if legacyToL1Height.Cmp(toL1Height) == 0 {
return nil // a-ok! entire batch was legacy blocks
}); err != nil { return nil
// Try again on a subsequent interval }
batchLog.Error("failed to index bridge events", "err", err)
batchLog.Info("detected switch to bedrock", "l1_bedrock_starting_height", l1BedrockStartingHeight, "l2_bedrock_starting_height", l2BedrockStartingHeight)
fromL1Height = l1BedrockStartingHeight
fromL2Height = l2BedrockStartingHeight
}
l1BridgeLog = l1BridgeLog.New("from_l1_block_number", fromL1Height, "to_l1_block_number", toL1Height)
l1BridgeLog.Info("scanning for bridge events")
l2BridgeLog = l2BridgeLog.New("from_l2_block_number", fromL2Height, "to_l2_block_number", toL2Height)
l2BridgeLog.Info("scanning for bridge events")
// First, find all possible initiated bridge events
if err := bridge.L1ProcessInitiatedBridgeEvents(l1BridgeLog, tx, b.metrics, b.chainConfig.L1Contracts, fromL1Height, toL1Height); err != nil {
return err
}
if err := bridge.L2ProcessInitiatedBridgeEvents(l2BridgeLog, tx, b.metrics, b.chainConfig.L2Contracts, fromL2Height, toL2Height); err != nil {
return err
}
// Now all finalization events can find their counterpart.
if err := bridge.L1ProcessFinalizedBridgeEvents(l1BridgeLog, tx, b.metrics, b.chainConfig.L1Contracts, fromL1Height, toL1Height); err != nil {
return err
}
if err := bridge.L2ProcessFinalizedBridgeEvents(l2BridgeLog, tx, b.metrics, b.chainConfig.L2Contracts, fromL2Height, toL2Height); err != nil {
return err return err
} }
batchLog.Info("indexed bridge events", "latest_l1_block_number", toL1Height, "latest_l2_block_number", toL2Height) // a-ok
b.LatestL1Header = latestEpoch.L1BlockHeader.RLPHeader.Header() return nil
b.LatestL2Header = latestEpoch.L2BlockHeader.RLPHeader.Header() }); err != nil {
batchLog.Error("failed to index bridge events", "err", err)
return err
} }
batchLog.Info("indexed bridge events", "latest_l1_block_number", toL1Height, "latest_l2_block_number", toL2Height)
b.LatestL1Header = latestEpoch.L1BlockHeader.RLPHeader.Header()
b.LatestL2Header = latestEpoch.L2BlockHeader.RLPHeader.Header()
return nil
} }
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/indexer/database" "github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/processors/contracts" "github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -17,7 +18,7 @@ import ( ...@@ -17,7 +18,7 @@ import (
// 1. OptimismPortal // 1. OptimismPortal
// 2. L1CrossDomainMessenger // 2. L1CrossDomainMessenger
// 3. L1StandardBridge // 3. L1StandardBridge
func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error { func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
// (1) OptimismPortal // (1) OptimismPortal
optimismPortalTxDeposits, err := contracts.OptimismPortalTransactionDepositEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight) optimismPortalTxDeposits, err := contracts.OptimismPortalTransactionDepositEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
if err != nil { if err != nil {
...@@ -44,6 +45,7 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -44,6 +45,7 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
if err := db.BridgeTransactions.StoreL1TransactionDeposits(transactionDeposits); err != nil { if err := db.BridgeTransactions.StoreL1TransactionDeposits(transactionDeposits); err != nil {
return err return err
} }
metrics.RecordL1TransactionDeposits(len(transactionDeposits))
} }
// (2) L1CrossDomainMessenger // (2) L1CrossDomainMessenger
...@@ -56,7 +58,7 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -56,7 +58,7 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
} }
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages)) sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l1BridgeMessages := make([]database.L1BridgeMessage, len(crossDomainSentMessages)) bridgeMessages := make([]database.L1BridgeMessage, len(crossDomainSentMessages))
for i := range crossDomainSentMessages { for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i] sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
...@@ -68,12 +70,13 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -68,12 +70,13 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
return fmt.Errorf("expected TransactionDeposit preceding SentMessage event. tx_hash = %s", sentMessage.Event.TransactionHash.String()) return fmt.Errorf("expected TransactionDeposit preceding SentMessage event. tx_hash = %s", sentMessage.Event.TransactionHash.String())
} }
l1BridgeMessages[i] = database.L1BridgeMessage{TransactionSourceHash: portalDeposit.DepositTx.SourceHash, BridgeMessage: sentMessage.BridgeMessage} bridgeMessages[i] = database.L1BridgeMessage{TransactionSourceHash: portalDeposit.DepositTx.SourceHash, BridgeMessage: sentMessage.BridgeMessage}
} }
if len(l1BridgeMessages) > 0 { if len(bridgeMessages) > 0 {
if err := db.BridgeMessages.StoreL1BridgeMessages(l1BridgeMessages); err != nil { if err := db.BridgeMessages.StoreL1BridgeMessages(bridgeMessages); err != nil {
return err return err
} }
metrics.RecordL1CrossDomainSentMessages(len(bridgeMessages))
} }
// (3) L1StandardBridge // (3) L1StandardBridge
...@@ -85,7 +88,8 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -85,7 +88,8 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
log.Info("detected bridge deposits", "size", len(initiatedBridges)) log.Info("detected bridge deposits", "size", len(initiatedBridges))
} }
l1BridgeDeposits := make([]database.L1BridgeDeposit, len(initiatedBridges)) bridgedTokens := make(map[common.Address]int)
bridgeDeposits := make([]database.L1BridgeDeposit, len(initiatedBridges))
for i := range initiatedBridges { for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i] initiatedBridge := initiatedBridges[i]
...@@ -102,15 +106,19 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -102,15 +106,19 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
} }
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
l1BridgeDeposits[i] = database.L1BridgeDeposit{ bridgedTokens[initiatedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
bridgeDeposits[i] = database.L1BridgeDeposit{
TransactionSourceHash: portalDeposit.DepositTx.SourceHash, TransactionSourceHash: portalDeposit.DepositTx.SourceHash,
BridgeTransfer: initiatedBridge.BridgeTransfer, BridgeTransfer: initiatedBridge.BridgeTransfer,
} }
} }
if len(l1BridgeDeposits) > 0 { if len(bridgeDeposits) > 0 {
if err := db.BridgeTransfers.StoreL1BridgeDeposits(l1BridgeDeposits); err != nil { if err := db.BridgeTransfers.StoreL1BridgeDeposits(bridgeDeposits); err != nil {
return err return err
} }
for tokenAddr, size := range bridgedTokens {
metrics.RecordL1InitiatedBridgeTransfers(tokenAddr, size)
}
} }
return nil return nil
...@@ -121,7 +129,7 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -121,7 +129,7 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
// 1. OptimismPortal (Bedrock prove & finalize steps) // 1. OptimismPortal (Bedrock prove & finalize steps)
// 2. L1CrossDomainMessenger (relayMessage marker) // 2. L1CrossDomainMessenger (relayMessage marker)
// 3. L1StandardBridge (no-op, since this is simply a wrapper over the L1CrossDomainMessenger) // 3. L1StandardBridge (no-op, since this is simply a wrapper over the L1CrossDomainMessenger)
func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error { func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
// (1) OptimismPortal (proven withdrawals) // (1) OptimismPortal (proven withdrawals)
provenWithdrawals, err := contracts.OptimismPortalWithdrawalProvenEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight) provenWithdrawals, err := contracts.OptimismPortalWithdrawalProvenEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
if err != nil { if err != nil {
...@@ -146,6 +154,9 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -146,6 +154,9 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
return err return err
} }
} }
if len(provenWithdrawals) > 0 {
metrics.RecordL1ProvenWithdrawals(len(provenWithdrawals))
}
// (2) OptimismPortal (finalized withdrawals) // (2) OptimismPortal (finalized withdrawals)
finalizedWithdrawals, err := contracts.OptimismPortalWithdrawalFinalizedEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight) finalizedWithdrawals, err := contracts.OptimismPortalWithdrawalFinalizedEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
...@@ -171,6 +182,9 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -171,6 +182,9 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
return err return err
} }
} }
if len(finalizedWithdrawals) > 0 {
metrics.RecordL1FinalizedWithdrawals(len(finalizedWithdrawals))
}
// (3) L1CrossDomainMessenger // (3) L1CrossDomainMessenger
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight) crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
...@@ -198,6 +212,9 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -198,6 +212,9 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
return err return err
} }
} }
if len(crossDomainRelayedMessages) > 0 {
metrics.RecordL1CrossDomainRelayedMessages(len(crossDomainRelayedMessages))
}
// (4) L1StandardBridge // (4) L1StandardBridge
finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l1", l1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight) finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l1", l1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
...@@ -208,6 +225,7 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -208,6 +225,7 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
log.Info("detected finalized bridge withdrawals", "size", len(finalizedBridges)) log.Info("detected finalized bridge withdrawals", "size", len(finalizedBridges))
} }
finalizedTokens := make(map[common.Address]int)
for i := range finalizedBridges { for i := range finalizedBridges {
// Nothing actionable on the database. However, we can treat the relayed message // Nothing actionable on the database. However, we can treat the relayed message
// as an invariant by ensuring we can query for a deposit by the same hash // as an invariant by ensuring we can query for a deposit by the same hash
...@@ -218,8 +236,7 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -218,8 +236,7 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
return fmt.Errorf("expected RelayedMessage following BridgeFinalized event. tx_hash = %s", finalizedBridge.Event.TransactionHash.String()) return fmt.Errorf("expected RelayedMessage following BridgeFinalized event. tx_hash = %s", finalizedBridge.Event.TransactionHash.String())
} }
// Since the message hash is computed from the relayed message, this ensures the deposit fields must match. For good measure, // Since the message hash is computed from the relayed message, this ensures the deposit fields must match
// we may choose to make sure `withdrawal.BridgeTransfer` matches with the finalized bridge
withdrawal, err := db.BridgeTransfers.L2BridgeWithdrawalWithFilter(database.BridgeTransfer{CrossDomainMessageHash: &relayedMessage.MessageHash}) withdrawal, err := db.BridgeTransfers.L2BridgeWithdrawalWithFilter(database.BridgeTransfer{CrossDomainMessageHash: &relayedMessage.MessageHash})
if err != nil { if err != nil {
return err return err
...@@ -227,6 +244,13 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts ...@@ -227,6 +244,13 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
log.Error("missing L2StandardBridge withdrawal on L1 finalization", "tx_hash", finalizedBridge.Event.TransactionHash.String()) log.Error("missing L2StandardBridge withdrawal on L1 finalization", "tx_hash", finalizedBridge.Event.TransactionHash.String())
return fmt.Errorf("missing L2StandardBridge withdrawal on L1 finalization. tx_hash: %s", finalizedBridge.Event.TransactionHash.String()) return fmt.Errorf("missing L2StandardBridge withdrawal on L1 finalization. tx_hash: %s", finalizedBridge.Event.TransactionHash.String())
} }
finalizedTokens[finalizedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
}
if len(finalizedBridges) > 0 {
for tokenAddr, size := range finalizedTokens {
metrics.RecordL1FinalizedBridgeTransfers(tokenAddr, size)
}
} }
// a-ok! // a-ok!
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/indexer/database" "github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/processors/contracts" "github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -17,7 +18,7 @@ import ( ...@@ -17,7 +18,7 @@ import (
// 1. OptimismPortal // 1. OptimismPortal
// 2. L2CrossDomainMessenger // 2. L2CrossDomainMessenger
// 3. L2StandardBridge // 3. L2StandardBridge
func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error { func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, metrics L2Metricer, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
// (1) L2ToL1MessagePasser // (1) L2ToL1MessagePasser
l2ToL1MPMessagesPassed, err := contracts.L2ToL1MessagePasserMessagePassedEvents(l2Contracts.L2ToL1MessagePasser, db, fromHeight, toHeight) l2ToL1MPMessagesPassed, err := contracts.L2ToL1MessagePasserMessagePassedEvents(l2Contracts.L2ToL1MessagePasser, db, fromHeight, toHeight)
if err != nil { if err != nil {
...@@ -44,6 +45,7 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts ...@@ -44,6 +45,7 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
if err := db.BridgeTransactions.StoreL2TransactionWithdrawals(transactionWithdrawals); err != nil { if err := db.BridgeTransactions.StoreL2TransactionWithdrawals(transactionWithdrawals); err != nil {
return err return err
} }
metrics.RecordL2TransactionWithdrawals(len(transactionWithdrawals))
} }
// (2) L2CrossDomainMessenger // (2) L2CrossDomainMessenger
...@@ -56,7 +58,7 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts ...@@ -56,7 +58,7 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
} }
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages)) sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l2BridgeMessages := make([]database.L2BridgeMessage, len(crossDomainSentMessages)) bridgeMessages := make([]database.L2BridgeMessage, len(crossDomainSentMessages))
for i := range crossDomainSentMessages { for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i] sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
...@@ -68,13 +70,13 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts ...@@ -68,13 +70,13 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
return fmt.Errorf("expected MessagePassedEvent preceding SentMessage. tx_hash = %s", sentMessage.Event.TransactionHash.String()) return fmt.Errorf("expected MessagePassedEvent preceding SentMessage. tx_hash = %s", sentMessage.Event.TransactionHash.String())
} }
l2BridgeMessages[i] = database.L2BridgeMessage{TransactionWithdrawalHash: messagePassed.WithdrawalHash, BridgeMessage: sentMessage.BridgeMessage} bridgeMessages[i] = database.L2BridgeMessage{TransactionWithdrawalHash: messagePassed.WithdrawalHash, BridgeMessage: sentMessage.BridgeMessage}
} }
if len(bridgeMessages) > 0 {
if len(l2BridgeMessages) > 0 { if err := db.BridgeMessages.StoreL2BridgeMessages(bridgeMessages); err != nil {
if err := db.BridgeMessages.StoreL2BridgeMessages(l2BridgeMessages); err != nil {
return err return err
} }
metrics.RecordL2CrossDomainSentMessages(len(bridgeMessages))
} }
// (3) L2StandardBridge // (3) L2StandardBridge
...@@ -86,7 +88,8 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts ...@@ -86,7 +88,8 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
log.Info("detected bridge withdrawals", "size", len(initiatedBridges)) log.Info("detected bridge withdrawals", "size", len(initiatedBridges))
} }
l2BridgeWithdrawals := make([]database.L2BridgeWithdrawal, len(initiatedBridges)) bridgedTokens := make(map[common.Address]int)
bridgeWithdrawals := make([]database.L2BridgeWithdrawal, len(initiatedBridges))
for i := range initiatedBridges { for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i] initiatedBridge := initiatedBridges[i]
...@@ -103,13 +106,19 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts ...@@ -103,13 +106,19 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
} }
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
l2BridgeWithdrawals[i] = database.L2BridgeWithdrawal{TransactionWithdrawalHash: messagePassed.WithdrawalHash, BridgeTransfer: initiatedBridge.BridgeTransfer} bridgedTokens[initiatedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
bridgeWithdrawals[i] = database.L2BridgeWithdrawal{
TransactionWithdrawalHash: messagePassed.WithdrawalHash,
BridgeTransfer: initiatedBridge.BridgeTransfer,
}
} }
if len(bridgeWithdrawals) > 0 {
if len(l2BridgeWithdrawals) > 0 { if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(bridgeWithdrawals); err != nil {
if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(l2BridgeWithdrawals); err != nil {
return err return err
} }
for tokenAddr, size := range bridgedTokens {
metrics.RecordL2InitiatedBridgeTransfers(tokenAddr, size)
}
} }
// a-ok! // a-ok!
...@@ -122,7 +131,7 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts ...@@ -122,7 +131,7 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
// 2. L2StandardBridge (no-op, since this is simply a wrapper over the L2CrossDomainMEssenger) // 2. L2StandardBridge (no-op, since this is simply a wrapper over the L2CrossDomainMEssenger)
// //
// NOTE: Unlike L1, there's no L2ToL1MessagePasser stage since transaction deposits are apart of the block derivation process. // NOTE: Unlike L1, there's no L2ToL1MessagePasser stage since transaction deposits are apart of the block derivation process.
func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error { func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L2Metricer, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
// (1) L2CrossDomainMessenger // (1) L2CrossDomainMessenger
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight) crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
if err != nil { if err != nil {
...@@ -149,6 +158,9 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts ...@@ -149,6 +158,9 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
return err return err
} }
} }
if len(relayedMessages) > 0 {
metrics.RecordL2CrossDomainRelayedMessages(len(relayedMessages))
}
// (2) L2StandardBridge // (2) L2StandardBridge
finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l2", l2Contracts.L2StandardBridge, db, fromHeight, toHeight) finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l2", l2Contracts.L2StandardBridge, db, fromHeight, toHeight)
...@@ -159,6 +171,7 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts ...@@ -159,6 +171,7 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
log.Info("detected finalized bridge deposits", "size", len(finalizedBridges)) log.Info("detected finalized bridge deposits", "size", len(finalizedBridges))
} }
finalizedTokens := make(map[common.Address]int)
for i := range finalizedBridges { for i := range finalizedBridges {
// Nothing actionable on the database. However, we can treat the relayed message // Nothing actionable on the database. However, we can treat the relayed message
// as an invariant by ensuring we can query for a deposit by the same hash // as an invariant by ensuring we can query for a deposit by the same hash
...@@ -169,8 +182,7 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts ...@@ -169,8 +182,7 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
return fmt.Errorf("expected RelayedMessage following BridgeFinalized event. tx_hash = %s", finalizedBridge.Event.TransactionHash.String()) return fmt.Errorf("expected RelayedMessage following BridgeFinalized event. tx_hash = %s", finalizedBridge.Event.TransactionHash.String())
} }
// Since the message hash is computed from the relayed message, this ensures the withdrawal fields must match. For good measure, // Since the message hash is computed from the relayed message, this ensures the withdrawal fields must match
// we may choose to make sure `deposit.BridgeTransfer` matches with the finalized bridge
deposit, err := db.BridgeTransfers.L1BridgeDepositWithFilter(database.BridgeTransfer{CrossDomainMessageHash: &relayedMessage.MessageHash}) deposit, err := db.BridgeTransfers.L1BridgeDepositWithFilter(database.BridgeTransfer{CrossDomainMessageHash: &relayedMessage.MessageHash})
if err != nil { if err != nil {
return err return err
...@@ -178,6 +190,13 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts ...@@ -178,6 +190,13 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
log.Error("missing L1StandardBridge deposit on L2 finalization", "tx_hash", finalizedBridge.Event.TransactionHash.String()) log.Error("missing L1StandardBridge deposit on L2 finalization", "tx_hash", finalizedBridge.Event.TransactionHash.String())
return errors.New("missing L1StandardBridge deposit on L2 finalization") return errors.New("missing L1StandardBridge deposit on L2 finalization")
} }
finalizedTokens[finalizedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
}
if len(finalizedBridges) > 0 {
for tokenAddr, size := range finalizedTokens {
metrics.RecordL2FinalizedBridgeTransfers(tokenAddr, size)
}
} }
// a-ok! // a-ok!
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -20,7 +21,7 @@ import ( ...@@ -20,7 +21,7 @@ import (
// 1. CanonicalTransactionChain // 1. CanonicalTransactionChain
// 2. L1CrossDomainMessenger // 2. L1CrossDomainMessenger
// 3. L1StandardBridge // 3. L1StandardBridge
func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error { func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
// (1) CanonicalTransactionChain // (1) CanonicalTransactionChain
ctcTxDepositEvents, err := contracts.LegacyCTCDepositEvents(l1Contracts.LegacyCanonicalTransactionChain, db, fromHeight, toHeight) ctcTxDepositEvents, err := contracts.LegacyCTCDepositEvents(l1Contracts.LegacyCanonicalTransactionChain, db, fromHeight, toHeight)
if err != nil { if err != nil {
...@@ -50,6 +51,7 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con ...@@ -50,6 +51,7 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
if err := db.BridgeTransactions.StoreL1TransactionDeposits(transactionDeposits); err != nil { if err := db.BridgeTransactions.StoreL1TransactionDeposits(transactionDeposits); err != nil {
return err return err
} }
metrics.RecordL1TransactionDeposits(len(transactionDeposits))
} }
// (2) L1CrossDomainMessenger // (2) L1CrossDomainMessenger
...@@ -62,7 +64,7 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con ...@@ -62,7 +64,7 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
} }
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages)) sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l1BridgeMessages := make([]database.L1BridgeMessage, len(crossDomainSentMessages)) bridgeMessages := make([]database.L1BridgeMessage, len(crossDomainSentMessages))
for i := range crossDomainSentMessages { for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i] sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
...@@ -74,12 +76,13 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con ...@@ -74,12 +76,13 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
return fmt.Errorf("missing preceding TransactionEnqueued for SentMessage event. tx_hash = %s", sentMessage.Event.TransactionHash.String()) return fmt.Errorf("missing preceding TransactionEnqueued for SentMessage event. tx_hash = %s", sentMessage.Event.TransactionHash.String())
} }
l1BridgeMessages[i] = database.L1BridgeMessage{TransactionSourceHash: ctcTxDeposit.TxHash, BridgeMessage: sentMessage.BridgeMessage} bridgeMessages[i] = database.L1BridgeMessage{TransactionSourceHash: ctcTxDeposit.TxHash, BridgeMessage: sentMessage.BridgeMessage}
} }
if len(l1BridgeMessages) > 0 { if len(bridgeMessages) > 0 {
if err := db.BridgeMessages.StoreL1BridgeMessages(l1BridgeMessages); err != nil { if err := db.BridgeMessages.StoreL1BridgeMessages(bridgeMessages); err != nil {
return err return err
} }
metrics.RecordL1CrossDomainSentMessages(len(bridgeMessages))
} }
// (3) L1StandardBridge // (3) L1StandardBridge
...@@ -91,7 +94,8 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con ...@@ -91,7 +94,8 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
log.Info("detected iegacy bridge deposits", "size", len(initiatedBridges)) log.Info("detected iegacy bridge deposits", "size", len(initiatedBridges))
} }
l1BridgeDeposits := make([]database.L1BridgeDeposit, len(initiatedBridges)) bridgedTokens := make(map[common.Address]int)
bridgeDeposits := make([]database.L1BridgeDeposit, len(initiatedBridges))
for i := range initiatedBridges { for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i] initiatedBridge := initiatedBridges[i]
...@@ -110,15 +114,19 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con ...@@ -110,15 +114,19 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
} }
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
l1BridgeDeposits[i] = database.L1BridgeDeposit{ bridgedTokens[initiatedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
bridgeDeposits[i] = database.L1BridgeDeposit{
TransactionSourceHash: ctcTxDeposit.TxHash, TransactionSourceHash: ctcTxDeposit.TxHash,
BridgeTransfer: initiatedBridge.BridgeTransfer, BridgeTransfer: initiatedBridge.BridgeTransfer,
} }
} }
if len(l1BridgeDeposits) > 0 { if len(bridgeDeposits) > 0 {
if err := db.BridgeTransfers.StoreL1BridgeDeposits(l1BridgeDeposits); err != nil { if err := db.BridgeTransfers.StoreL1BridgeDeposits(bridgeDeposits); err != nil {
return err return err
} }
for tokenAddr, size := range bridgedTokens {
metrics.RecordL1InitiatedBridgeTransfers(tokenAddr, size)
}
} }
// a-ok! // a-ok!
...@@ -130,20 +138,19 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con ...@@ -130,20 +138,19 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
// 1. L2CrossDomainMessenger - The LegacyMessagePasser contract cannot be used as entrypoint to bridge transactions from L2. The protocol // 1. L2CrossDomainMessenger - The LegacyMessagePasser contract cannot be used as entrypoint to bridge transactions from L2. The protocol
// only allows the L2CrossDomainMessenger as the sole sender when relaying a bridged message. // only allows the L2CrossDomainMessenger as the sole sender when relaying a bridged message.
// 2. L2StandardBridge // 2. L2StandardBridge
func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error { func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, metrics L2Metricer, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
// (1) L2CrossDomainMessenger // (1) L2CrossDomainMessenger
crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight) crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
if err != nil { if err != nil {
return err return err
} }
if len(crossDomainSentMessages) > 0 { if len(crossDomainSentMessages) > 0 {
log.Info("detected legacy transaction withdrawals (via L2CrossDomainMessenger)", "size", len(crossDomainSentMessages)) log.Info("detected legacy transaction withdrawals (via L2CrossDomainMessenger)", "size", len(crossDomainSentMessages))
} }
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages)) sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l2BridgeMessages := make([]database.L2BridgeMessage, len(crossDomainSentMessages)) bridgeMessages := make([]database.L2BridgeMessage, len(crossDomainSentMessages))
l2TransactionWithdrawals := make([]database.L2TransactionWithdrawal, len(crossDomainSentMessages)) transactionWithdrawals := make([]database.L2TransactionWithdrawal, len(crossDomainSentMessages))
for i := range crossDomainSentMessages { for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i] sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
...@@ -151,7 +158,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con ...@@ -151,7 +158,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
// To ensure consistency in the schema, we duplicate this as the "root" transaction withdrawal. The storage key in the message // To ensure consistency in the schema, we duplicate this as the "root" transaction withdrawal. The storage key in the message
// passer contract is sha3(calldata + sender). The sender always being the L2CrossDomainMessenger pre-bedrock. // passer contract is sha3(calldata + sender). The sender always being the L2CrossDomainMessenger pre-bedrock.
withdrawalHash := crypto.Keccak256Hash(append(sentMessage.MessageCalldata, l2Contracts.L2CrossDomainMessenger[:]...)) withdrawalHash := crypto.Keccak256Hash(append(sentMessage.MessageCalldata, l2Contracts.L2CrossDomainMessenger[:]...))
l2TransactionWithdrawals[i] = database.L2TransactionWithdrawal{ transactionWithdrawals[i] = database.L2TransactionWithdrawal{
WithdrawalHash: withdrawalHash, WithdrawalHash: withdrawalHash,
InitiatedL2EventGUID: sentMessage.Event.GUID, InitiatedL2EventGUID: sentMessage.Event.GUID,
Nonce: sentMessage.BridgeMessage.Nonce, Nonce: sentMessage.BridgeMessage.Nonce,
...@@ -165,19 +172,20 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con ...@@ -165,19 +172,20 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
}, },
} }
l2BridgeMessages[i] = database.L2BridgeMessage{ bridgeMessages[i] = database.L2BridgeMessage{
TransactionWithdrawalHash: withdrawalHash, TransactionWithdrawalHash: withdrawalHash,
BridgeMessage: sentMessage.BridgeMessage, BridgeMessage: sentMessage.BridgeMessage,
} }
} }
if len(bridgeMessages) > 0 {
if len(l2BridgeMessages) > 0 { if err := db.BridgeTransactions.StoreL2TransactionWithdrawals(transactionWithdrawals); err != nil {
if err := db.BridgeTransactions.StoreL2TransactionWithdrawals(l2TransactionWithdrawals); err != nil {
return err return err
} }
if err := db.BridgeMessages.StoreL2BridgeMessages(l2BridgeMessages); err != nil { if err := db.BridgeMessages.StoreL2BridgeMessages(bridgeMessages); err != nil {
return err return err
} }
metrics.RecordL2TransactionWithdrawals(len(transactionWithdrawals))
metrics.RecordL2CrossDomainSentMessages(len(bridgeMessages))
} }
// (2) L2StandardBridge // (2) L2StandardBridge
...@@ -189,6 +197,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con ...@@ -189,6 +197,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
log.Info("detected legacy bridge withdrawals", "size", len(initiatedBridges)) log.Info("detected legacy bridge withdrawals", "size", len(initiatedBridges))
} }
bridgedTokens := make(map[common.Address]int)
l2BridgeWithdrawals := make([]database.L2BridgeWithdrawal, len(initiatedBridges)) l2BridgeWithdrawals := make([]database.L2BridgeWithdrawal, len(initiatedBridges))
for i := range initiatedBridges { for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i] initiatedBridge := initiatedBridges[i]
...@@ -201,8 +210,9 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con ...@@ -201,8 +210,9 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
log.Error("expected SentMessage preceding DepositInitiated event", "tx_hash", initiatedBridge.Event.TransactionHash.String()) log.Error("expected SentMessage preceding DepositInitiated event", "tx_hash", initiatedBridge.Event.TransactionHash.String())
return fmt.Errorf("expected SentMessage preceding DepositInitiated event. tx_hash = %s", initiatedBridge.Event.TransactionHash) return fmt.Errorf("expected SentMessage preceding DepositInitiated event. tx_hash = %s", initiatedBridge.Event.TransactionHash)
} }
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
bridgedTokens[initiatedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
l2BridgeWithdrawals[i] = database.L2BridgeWithdrawal{ l2BridgeWithdrawals[i] = database.L2BridgeWithdrawal{
TransactionWithdrawalHash: sentMessage.BridgeMessage.MessageHash, TransactionWithdrawalHash: sentMessage.BridgeMessage.MessageHash,
BridgeTransfer: initiatedBridge.BridgeTransfer, BridgeTransfer: initiatedBridge.BridgeTransfer,
...@@ -212,6 +222,9 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con ...@@ -212,6 +222,9 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(l2BridgeWithdrawals); err != nil { if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(l2BridgeWithdrawals); err != nil {
return err return err
} }
for tokenAddr, size := range bridgedTokens {
metrics.RecordL2InitiatedBridgeTransfers(tokenAddr, size)
}
} }
// a-ok // a-ok
...@@ -224,7 +237,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con ...@@ -224,7 +237,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
// according to the pre-bedrock protocol. This follows: // according to the pre-bedrock protocol. This follows:
// 1. L1CrossDomainMessenger // 1. L1CrossDomainMessenger
// 2. L1StandardBridge // 2. L1StandardBridge
func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Client node.EthClient, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error { func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Client node.EthClient, 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 // (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 // 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) crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
...@@ -286,15 +299,20 @@ func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Cli ...@@ -286,15 +299,20 @@ func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Cli
return err return err
} }
} }
if len(crossDomainRelayedMessages) > 0 {
metrics.RecordL1ProvenWithdrawals(len(crossDomainRelayedMessages))
metrics.RecordL1FinalizedWithdrawals(len(crossDomainRelayedMessages))
metrics.RecordL1CrossDomainRelayedMessages(len(crossDomainRelayedMessages))
}
if skippedPreRegenesisMessages > 0 { if skippedPreRegenesisMessages > 0 {
// Logged as a warning just for visibility // Logged as a warning just for visibility
log.Warn("skipped pre-regensis relayed L2CrossDomainMessenger withdrawals", "size", skippedPreRegenesisMessages) log.Warn("skipped pre-regensis relayed L2CrossDomainMessenger withdrawals", "size", skippedPreRegenesisMessages)
} }
// (2) L2StandardBridge -- no-op for now as there's nothing actionable to do here besides // (2) L2StandardBridge -- no-op for now as there's nothing actionable to do here besides
// santiy checks which is not important for legacy code. The message status is already tracked // santiy checks which is not important for legacy code. Not worth extra code pre-bedrock.
// via the relayed bridge messed through the cross domain messenger. // The message status is already tracked via the relayed bridge messed through the cross domain messenger.
// - NOTE: This means we dont increment metrics for finalized bridge transfers
// a-ok! // a-ok!
return nil return nil
...@@ -304,7 +322,7 @@ func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Cli ...@@ -304,7 +322,7 @@ func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Cli
// according to the pre-bedrock protocol. This follows: // according to the pre-bedrock protocol. This follows:
// 1. L2CrossDomainMessenger // 1. L2CrossDomainMessenger
// 2. L2StandardBridge // 2. L2StandardBridge
func LegacyL2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error { func LegacyL2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L2Metricer, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
// (1) L2CrossDomainMessenger // (1) L2CrossDomainMessenger
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight) crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
if err != nil { if err != nil {
...@@ -329,10 +347,14 @@ func LegacyL2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Con ...@@ -329,10 +347,14 @@ func LegacyL2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Con
return err return err
} }
} }
if len(crossDomainRelayedMessages) > 0 {
metrics.RecordL2CrossDomainRelayedMessages(len(crossDomainRelayedMessages))
}
// (2) L2StandardBridge -- no-op for now as there's nothing actionable to do here besides // (2) L2StandardBridge -- no-op for now as there's nothing actionable to do here besides
// santiy checks which is not important for legacy code. The message status is already tracked // santiy checks which is not important for legacy code. Not worth the extra code pre-bedorck.
// via the relayed bridge messed through the cross domain messenger. // The message status is already tracked via the relayed bridge messed through the cross domain messenger.
// - NOTE: This means we dont increment metrics for finalized bridge transfers
// a-ok! // a-ok!
return nil return nil
......
package bridge
import (
"math/big"
"github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum/go-ethereum/common"
"github.com/prometheus/client_golang/prometheus"
)
var (
MetricsNamespace string = "bridge"
)
type L1Metricer interface {
RecordLatestIndexedL1Height(height *big.Int)
RecordL1TransactionDeposits(size int)
RecordL1ProvenWithdrawals(size int)
RecordL1FinalizedWithdrawals(size int)
RecordL1CrossDomainSentMessages(size int)
RecordL1CrossDomainRelayedMessages(size int)
RecordL1InitiatedBridgeTransfers(token common.Address, size int)
RecordL1FinalizedBridgeTransfers(token common.Address, size int)
}
type L2Metricer interface {
RecordLatestIndexedL2Height(height *big.Int)
RecordL2TransactionWithdrawals(size int)
RecordL2CrossDomainSentMessages(size int)
RecordL2CrossDomainRelayedMessages(size int)
RecordL2InitiatedBridgeTransfers(token common.Address, size int)
RecordL2FinalizedBridgeTransfers(token common.Address, size int)
}
type Metricer interface {
L1Metricer
L2Metricer
RecordInterval() (done func(err error))
}
type bridgeMetrics struct {
intervalTick prometheus.Counter
intervalDuration prometheus.Histogram
intervalFailures prometheus.Counter
latestL1Height prometheus.Gauge
latestL2Height prometheus.Gauge
txDeposits prometheus.Counter
txWithdrawals prometheus.Counter
provenWithdrawals prometheus.Counter
finalizedWithdrawals prometheus.Counter
sentMessages *prometheus.CounterVec
relayedMessages *prometheus.CounterVec
initiatedBridgeTransfers *prometheus.CounterVec
finalizedBridgeTransfers *prometheus.CounterVec
}
func NewMetrics(registry *prometheus.Registry) Metricer {
factory := metrics.With(registry)
return &bridgeMetrics{
intervalTick: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "intervals_total",
Help: "number of times processing loop has run",
}),
intervalDuration: factory.NewHistogram(prometheus.HistogramOpts{
Namespace: MetricsNamespace,
Name: "interval_seconds",
Help: "duration elapsed in the processing loop",
}),
intervalFailures: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "failures_total",
Help: "number of failures encountered",
}),
latestL1Height: factory.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
Subsystem: "l1",
Name: "height",
Help: "the latest processed l1 block height",
}),
latestL2Height: factory.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
Subsystem: "l2",
Name: "height",
Help: "the latest processed l2 block height",
}),
txDeposits: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "tx_deposits",
Help: "number of processed transactions deposited from l1",
}),
txWithdrawals: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "tx_withdrawals",
Help: "number of processed transactions 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",
Help: "number of bridged messages between l1 and l2",
}, []string{
"chain",
}),
relayedMessages: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "relayed_messages",
Help: "number of relayed messages between l1 and l2",
}, []string{
"chain",
}),
initiatedBridgeTransfers: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "initiated_token_transfers",
Help: "number of bridged tokens between l1 and l2",
}, []string{
"chain",
"token_address",
}),
finalizedBridgeTransfers: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "finalized_token_transfers",
Help: "number of finalized token transfers between l1 and l2",
}, []string{
"chain",
"token_address",
}),
}
}
func (m *bridgeMetrics) RecordInterval() func(error) {
m.intervalTick.Inc()
timer := prometheus.NewTimer(m.intervalDuration)
return func(err error) {
timer.ObserveDuration()
if err != nil {
m.intervalFailures.Inc()
}
}
}
// L1Metricer
func (m *bridgeMetrics) RecordLatestIndexedL1Height(height *big.Int) {
m.latestL1Height.Set(float64(height.Uint64()))
}
func (m *bridgeMetrics) RecordL1TransactionDeposits(size int) {
m.txDeposits.Add(float64(size))
}
func (m *bridgeMetrics) RecordL1ProvenWithdrawals(size int) {
m.provenWithdrawals.Add(float64(size))
}
func (m *bridgeMetrics) RecordL1FinalizedWithdrawals(size int) {
m.finalizedWithdrawals.Add(float64(size))
}
func (m *bridgeMetrics) RecordL1CrossDomainSentMessages(size int) {
m.sentMessages.WithLabelValues("l1").Add(float64(size))
}
func (m *bridgeMetrics) RecordL1CrossDomainRelayedMessages(size int) {
m.relayedMessages.WithLabelValues("l1").Add(float64(size))
}
func (m *bridgeMetrics) RecordL1InitiatedBridgeTransfers(tokenAddr common.Address, size int) {
m.initiatedBridgeTransfers.WithLabelValues("l1", tokenAddr.String()).Add(float64(size))
}
func (m *bridgeMetrics) RecordL1FinalizedBridgeTransfers(tokenAddr common.Address, size int) {
m.finalizedBridgeTransfers.WithLabelValues("l1", tokenAddr.String()).Add(float64(size))
}
// L2Metricer
func (m *bridgeMetrics) RecordLatestIndexedL2Height(height *big.Int) {
m.latestL2Height.Set(float64(height.Uint64()))
}
func (m *bridgeMetrics) RecordL2TransactionWithdrawals(size int) {
m.txWithdrawals.Add(float64(size))
}
func (m *bridgeMetrics) RecordL2CrossDomainSentMessages(size int) {
m.sentMessages.WithLabelValues("l2").Add(float64(size))
}
func (m *bridgeMetrics) RecordL2CrossDomainRelayedMessages(size int) {
m.relayedMessages.WithLabelValues("l2").Add(float64(size))
}
func (m *bridgeMetrics) RecordL2InitiatedBridgeTransfers(tokenAddr common.Address, size int) {
m.initiatedBridgeTransfers.WithLabelValues("l2", tokenAddr.String()).Add(float64(size))
}
func (m *bridgeMetrics) RecordL2FinalizedBridgeTransfers(tokenAddr common.Address, size int) {
m.finalizedBridgeTransfers.WithLabelValues("l2", tokenAddr.String()).Add(float64(size))
}
...@@ -15,21 +15,23 @@ type LegacyBridgeEvent struct { ...@@ -15,21 +15,23 @@ type LegacyBridgeEvent struct {
} }
func L1StandardBridgeLegacyDepositInitiatedEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]LegacyBridgeEvent, error) { func L1StandardBridgeLegacyDepositInitiatedEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]LegacyBridgeEvent, error) {
// The L1StandardBridge ABI contains the legacy events
l1StandardBridgeAbi, err := bindings.L1StandardBridgeMetaData.GetAbi() l1StandardBridgeAbi, err := bindings.L1StandardBridgeMetaData.GetAbi()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// The L1StandardBridge contains the legacy events
ethDepositEventAbi := l1StandardBridgeAbi.Events["ETHDepositInitiated"] ethDepositEventAbi := l1StandardBridgeAbi.Events["ETHDepositInitiated"]
erc20DepositEventAbi := l1StandardBridgeAbi.Events["ERC20DepositInitiated"] erc20DepositEventAbi := l1StandardBridgeAbi.Events["ERC20DepositInitiated"]
// Grab both ETH & ERC20 Events // Grab both ETH & ERC20 Events
ethDepositEvents, err := db.ContractEvents.L1ContractEventsWithFilter(database.ContractEvent{ContractAddress: contractAddress, EventSignature: ethDepositEventAbi.ID}, fromHeight, toHeight) contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: ethDepositEventAbi.ID}
ethDepositEvents, err := db.ContractEvents.L1ContractEventsWithFilter(contractEventFilter, fromHeight, toHeight)
if err != nil { if err != nil {
return nil, err return nil, err
} }
erc20DepositEvents, err := db.ContractEvents.L1ContractEventsWithFilter(database.ContractEvent{ContractAddress: contractAddress, EventSignature: erc20DepositEventAbi.ID}, fromHeight, toHeight) contractEventFilter.EventSignature = erc20DepositEventAbi.ID
erc20DepositEvents, err := db.ContractEvents.L1ContractEventsWithFilter(contractEventFilter, fromHeight, toHeight)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -81,13 +83,15 @@ func L1StandardBridgeLegacyDepositInitiatedEvents(contractAddress common.Address ...@@ -81,13 +83,15 @@ func L1StandardBridgeLegacyDepositInitiatedEvents(contractAddress common.Address
} }
func L2StandardBridgeLegacyWithdrawalInitiatedEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]LegacyBridgeEvent, error) { func L2StandardBridgeLegacyWithdrawalInitiatedEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]LegacyBridgeEvent, error) {
// The L2StandardBridge ABI contains the legacy events
l2StandardBridgeAbi, err := bindings.L2StandardBridgeMetaData.GetAbi() l2StandardBridgeAbi, err := bindings.L2StandardBridgeMetaData.GetAbi()
if err != nil { if err != nil {
return nil, err return nil, err
} }
withdrawalInitiatedEventAbi := l2StandardBridgeAbi.Events["WithdrawalInitiated"] withdrawalInitiatedEventAbi := l2StandardBridgeAbi.Events["WithdrawalInitiated"]
withdrawalEvents, err := db.ContractEvents.L2ContractEventsWithFilter(database.ContractEvent{ContractAddress: contractAddress, EventSignature: withdrawalInitiatedEventAbi.ID}, fromHeight, toHeight) contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: withdrawalInitiatedEventAbi.ID}
withdrawalEvents, err := db.ContractEvents.L2ContractEventsWithFilter(contractEventFilter, fromHeight, toHeight)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
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