Commit 853867cc authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into dependabot/npm_and_yarn/glob-10.3.10

parents ca3d6444 4e6ec201
...@@ -875,14 +875,14 @@ jobs: ...@@ -875,14 +875,14 @@ jobs:
- run: - run:
name: generate L1 state name: generate L1 state
command: make devnet-allocs command: make devnet-allocs
- store_test_results:
path: /test-results
- run: - run:
name: Test name: Test
command: | command: |
mkdir -p /test-results mkdir -p /test-results
DB_USER=postgres gotestsum --format=standard-verbose --junitfile /test-results/tests.xml -- -parallel=4 ./... DB_USER=postgres gotestsum --format=standard-verbose --junitfile=/test-results/indexer_tests.xml -- -parallel=4 ./...
working_directory: indexer working_directory: indexer
- store_test_results:
path: /test-results
- run: - run:
name: Build name: Build
command: make indexer command: make indexer
......
...@@ -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
} }
......
...@@ -144,7 +144,7 @@ func NewConfig( ...@@ -144,7 +144,7 @@ func NewConfig(
TraceType: traceType, TraceType: traceType,
TxMgrConfig: txmgr.NewCLIConfig(l1EthRpc), TxMgrConfig: txmgr.NewCLIConfig(l1EthRpc, txmgr.DefaultChallengerFlagValues),
MetricsConfig: opmetrics.DefaultCLIConfig(), MetricsConfig: opmetrics.DefaultCLIConfig(),
PprofConfig: oppprof.DefaultCLIConfig(), PprofConfig: oppprof.DefaultCLIConfig(),
......
...@@ -174,7 +174,7 @@ var optionalFlags = []cli.Flag{ ...@@ -174,7 +174,7 @@ var optionalFlags = []cli.Flag{
func init() { func init() {
optionalFlags = append(optionalFlags, oplog.CLIFlags(envVarPrefix)...) optionalFlags = append(optionalFlags, oplog.CLIFlags(envVarPrefix)...)
optionalFlags = append(optionalFlags, txmgr.CLIFlags(envVarPrefix)...) optionalFlags = append(optionalFlags, txmgr.CLIFlagsWithDefaults(envVarPrefix, txmgr.DefaultChallengerFlagValues)...)
optionalFlags = append(optionalFlags, opmetrics.CLIFlags(envVarPrefix)...) optionalFlags = append(optionalFlags, opmetrics.CLIFlags(envVarPrefix)...)
optionalFlags = append(optionalFlags, oppprof.CLIFlags(envVarPrefix)...) optionalFlags = append(optionalFlags, oppprof.CLIFlags(envVarPrefix)...)
......
...@@ -152,10 +152,10 @@ func (a *Agent) tryResolveClaims(ctx context.Context) error { ...@@ -152,10 +152,10 @@ func (a *Agent) tryResolveClaims(ctx context.Context) error {
resolvableClaims = append(resolvableClaims, int64(claim.ContractIndex)) resolvableClaims = append(resolvableClaims, int64(claim.ContractIndex))
} }
} }
a.log.Info("Resolving claims", "numClaims", len(resolvableClaims))
if len(resolvableClaims) == 0 { if len(resolvableClaims) == 0 {
return errNoResolvableClaims return errNoResolvableClaims
} }
a.log.Info("Resolving claims", "numClaims", len(resolvableClaims))
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(resolvableClaims)) wg.Add(len(resolvableClaims))
......
...@@ -35,10 +35,6 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, game type ...@@ -35,10 +35,6 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, game type
if claim.Depth() == s.gameDepth { if claim.Depth() == s.gameDepth {
return nil, types.ErrGameDepthReached return nil, types.ErrGameDepthReached
} }
agree, err := s.agreeWithClaim(ctx, claim.ClaimData)
if err != nil {
return nil, err
}
// Before challenging this claim, first check that the move wasn't warranted. // Before challenging this claim, first check that the move wasn't warranted.
// If the parent claim is on a dishonest path, then we would have moved against it anyways. So we don't move. // If the parent claim is on a dishonest path, then we would have moved against it anyways. So we don't move.
...@@ -57,6 +53,10 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, game type ...@@ -57,6 +53,10 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, game type
} }
} }
agree, err := s.agreeWithClaim(ctx, claim.ClaimData)
if err != nil {
return nil, err
}
if agree { if agree {
return s.defend(ctx, claim) return s.defend(ctx, claim)
} else { } else {
......
package outputs
import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
var (
GetStepDataErr = fmt.Errorf("GetStepData not supported")
AbsolutePreStateErr = fmt.Errorf("AbsolutePreState not supported")
)
var _ types.TraceProvider = (*OutputTraceProvider)(nil)
type OutputRollupClient interface {
OutputAtBlock(ctx context.Context, blockNum uint64) (*eth.OutputResponse, error)
}
// OutputTraceProvider is a [types.TraceProvider] implementation that uses
// output roots for given L2 Blocks as a trace.
type OutputTraceProvider struct {
logger log.Logger
rollupClient OutputRollupClient
prestateBlock uint64
poststateBlock uint64
gameDepth uint64
}
func NewTraceProvider(ctx context.Context, logger log.Logger, rollupRpc string, gameDepth, prestateBlock, poststateBlock uint64) (*OutputTraceProvider, error) {
rollupClient, err := client.DialRollupClientWithTimeout(client.DefaultDialTimeout, logger, rollupRpc)
if err != nil {
return nil, err
}
return NewTraceProviderFromInputs(logger, rollupClient, gameDepth, prestateBlock, poststateBlock), nil
}
func NewTraceProviderFromInputs(logger log.Logger, rollupClient OutputRollupClient, gameDepth, prestateBlock, poststateBlock uint64) *OutputTraceProvider {
return &OutputTraceProvider{
logger: logger,
rollupClient: rollupClient,
prestateBlock: prestateBlock,
poststateBlock: poststateBlock,
gameDepth: gameDepth,
}
}
func (o *OutputTraceProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) {
outputBlock := pos.TraceIndex(int(o.gameDepth)) + o.prestateBlock + 1
if outputBlock > o.poststateBlock {
outputBlock = o.poststateBlock
}
output, err := o.rollupClient.OutputAtBlock(ctx, outputBlock)
if err != nil {
o.logger.Error("Failed to fetch output", "blockNumber", outputBlock, "err", err)
return common.Hash{}, err
}
return common.Hash(output.OutputRoot), nil
}
// AbsolutePreStateCommitment returns the absolute prestate at the configured prestateBlock.
func (o *OutputTraceProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) {
output, err := o.rollupClient.OutputAtBlock(ctx, o.prestateBlock)
if err != nil {
o.logger.Error("Failed to fetch output", "blockNumber", o.prestateBlock, "err", err)
return common.Hash{}, err
}
return common.Hash(output.OutputRoot), nil
}
// AbsolutePreState is not supported in the [OutputTraceProvider].
func (o *OutputTraceProvider) AbsolutePreState(ctx context.Context) (preimage []byte, err error) {
return nil, AbsolutePreStateErr
}
// GetStepData is not supported in the [OutputTraceProvider].
func (o *OutputTraceProvider) GetStepData(ctx context.Context, pos types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
return nil, nil, nil, GetStepDataErr
}
package outputs
import (
"context"
"fmt"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
var (
prestateBlock = uint64(100)
poststateBlock = uint64(200)
gameDepth = uint64(7) // 128 leaf nodes
prestateOutputRoot = common.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
firstOutputRoot = common.HexToHash("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
poststateOutputRoot = common.HexToHash("0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc")
)
func TestGet(t *testing.T) {
t.Run("PrePrestateErrors", func(t *testing.T) {
provider, _ := setupWithTestData(t, 0, poststateBlock)
_, err := provider.Get(context.Background(), types.NewPosition(1, 0))
require.ErrorAs(t, fmt.Errorf("no output at block %d", 1), &err)
})
t.Run("MisconfiguredPoststateErrors", func(t *testing.T) {
provider, _ := setupWithTestData(t, 0, 0)
_, err := provider.Get(context.Background(), types.NewPosition(1, 0))
require.ErrorAs(t, fmt.Errorf("no output at block %d", 0), &err)
})
t.Run("FirstBlockAfterPrestate", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
value, err := provider.Get(context.Background(), types.NewPositionFromGIndex(128))
require.NoError(t, err)
require.Equal(t, firstOutputRoot, value)
})
t.Run("MissingOutputAtBlock", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
_, err := provider.Get(context.Background(), types.NewPositionFromGIndex(129))
require.ErrorAs(t, fmt.Errorf("no output at block %d", prestateBlock+2), &err)
})
t.Run("PostStateBlock", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
value, err := provider.Get(context.Background(), types.NewPositionFromGIndex(228))
require.NoError(t, err)
require.Equal(t, value, poststateOutputRoot)
})
t.Run("AfterPostStateBlock", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
value, err := provider.Get(context.Background(), types.NewPositionFromGIndex(229))
require.NoError(t, err)
require.Equal(t, value, poststateOutputRoot)
})
}
func TestAbsolutePreStateCommitment(t *testing.T) {
t.Run("FailedToFetchOutput", func(t *testing.T) {
provider, rollupClient := setupWithTestData(t, prestateBlock, poststateBlock)
rollupClient.errorsOnPrestateFetch = true
_, err := provider.AbsolutePreStateCommitment(context.Background())
require.ErrorAs(t, fmt.Errorf("no output at block %d", prestateBlock), &err)
})
t.Run("ReturnsCorrectPrestateOutput", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
value, err := provider.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
require.Equal(t, value, prestateOutputRoot)
})
}
func TestGetStepData(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
_, _, _, err := provider.GetStepData(context.Background(), types.NewPosition(1, 0))
require.ErrorIs(t, err, GetStepDataErr)
}
func TestAbsolutePreState(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
_, err := provider.AbsolutePreState(context.Background())
require.ErrorIs(t, err, AbsolutePreStateErr)
}
func setupWithTestData(t *testing.T, prestateBlock, poststateBlock uint64) (*OutputTraceProvider, *stubRollupClient) {
rollupClient := stubRollupClient{
outputs: map[uint64]*eth.OutputResponse{
prestateBlock: {
OutputRoot: eth.Bytes32(prestateOutputRoot),
},
101: {
OutputRoot: eth.Bytes32(firstOutputRoot),
},
poststateBlock: {
OutputRoot: eth.Bytes32(poststateOutputRoot),
},
},
}
return &OutputTraceProvider{
logger: testlog.Logger(t, log.LvlInfo),
rollupClient: &rollupClient,
prestateBlock: prestateBlock,
poststateBlock: poststateBlock,
gameDepth: gameDepth,
}, &rollupClient
}
type stubRollupClient struct {
errorsOnPrestateFetch bool
outputs map[uint64]*eth.OutputResponse
}
func (s *stubRollupClient) OutputAtBlock(ctx context.Context, blockNum uint64) (*eth.OutputResponse, error) {
output, ok := s.outputs[blockNum]
if !ok || s.errorsOnPrestateFetch {
return nil, fmt.Errorf("no output at block %d", blockNum)
}
return output, nil
}
...@@ -48,17 +48,42 @@ var ( ...@@ -48,17 +48,42 @@ var (
} }
) )
type DefaultFlagValues struct {
NumConfirmations uint64
SafeAbortNonceTooLowCount uint64
ResubmissionTimeout time.Duration
NetworkTimeout time.Duration
TxSendTimeout time.Duration
TxNotInMempoolTimeout time.Duration
ReceiptQueryInterval time.Duration
}
var ( var (
defaultNumConfirmations = uint64(10) DefaultBatcherFlagValues = DefaultFlagValues{
defaultSafeAbortNonceTooLowCount = uint64(3) NumConfirmations: uint64(10),
defaultResubmissionTimeout = 48 * time.Second SafeAbortNonceTooLowCount: uint64(3),
defaultNetworkTimeout = 10 * time.Second ResubmissionTimeout: 48 * time.Second,
defaultTxSendTimeout = 0 * time.Second NetworkTimeout: 10 * time.Second,
defaultTxNotInMempoolTimeout = 2 * time.Minute TxSendTimeout: 0 * time.Second,
defaultReceiptQueryInterval = 12 * time.Second TxNotInMempoolTimeout: 2 * time.Minute,
ReceiptQueryInterval: 12 * time.Second,
}
DefaultChallengerFlagValues = DefaultFlagValues{
NumConfirmations: uint64(3),
SafeAbortNonceTooLowCount: uint64(3),
ResubmissionTimeout: 24 * time.Second,
NetworkTimeout: 10 * time.Second,
TxSendTimeout: 2 * time.Minute,
TxNotInMempoolTimeout: 1 * time.Minute,
ReceiptQueryInterval: 12 * time.Second,
}
) )
func CLIFlags(envPrefix string) []cli.Flag { func CLIFlags(envPrefix string) []cli.Flag {
return CLIFlagsWithDefaults(envPrefix, DefaultBatcherFlagValues)
}
func CLIFlagsWithDefaults(envPrefix string, defaults DefaultFlagValues) []cli.Flag {
prefixEnvVars := func(name string) []string { prefixEnvVars := func(name string) []string {
return opservice.PrefixEnvVar(envPrefix, name) return opservice.PrefixEnvVar(envPrefix, name)
} }
...@@ -81,43 +106,43 @@ func CLIFlags(envPrefix string) []cli.Flag { ...@@ -81,43 +106,43 @@ func CLIFlags(envPrefix string) []cli.Flag {
&cli.Uint64Flag{ &cli.Uint64Flag{
Name: NumConfirmationsFlagName, Name: NumConfirmationsFlagName,
Usage: "Number of confirmations which we will wait after sending a transaction", Usage: "Number of confirmations which we will wait after sending a transaction",
Value: defaultNumConfirmations, Value: defaults.NumConfirmations,
EnvVars: prefixEnvVars("NUM_CONFIRMATIONS"), EnvVars: prefixEnvVars("NUM_CONFIRMATIONS"),
}, },
&cli.Uint64Flag{ &cli.Uint64Flag{
Name: SafeAbortNonceTooLowCountFlagName, Name: SafeAbortNonceTooLowCountFlagName,
Usage: "Number of ErrNonceTooLow observations required to give up on a tx at a particular nonce without receiving confirmation", Usage: "Number of ErrNonceTooLow observations required to give up on a tx at a particular nonce without receiving confirmation",
Value: defaultSafeAbortNonceTooLowCount, Value: defaults.SafeAbortNonceTooLowCount,
EnvVars: prefixEnvVars("SAFE_ABORT_NONCE_TOO_LOW_COUNT"), EnvVars: prefixEnvVars("SAFE_ABORT_NONCE_TOO_LOW_COUNT"),
}, },
&cli.DurationFlag{ &cli.DurationFlag{
Name: ResubmissionTimeoutFlagName, Name: ResubmissionTimeoutFlagName,
Usage: "Duration we will wait before resubmitting a transaction to L1", Usage: "Duration we will wait before resubmitting a transaction to L1",
Value: defaultResubmissionTimeout, Value: defaults.ResubmissionTimeout,
EnvVars: prefixEnvVars("RESUBMISSION_TIMEOUT"), EnvVars: prefixEnvVars("RESUBMISSION_TIMEOUT"),
}, },
&cli.DurationFlag{ &cli.DurationFlag{
Name: NetworkTimeoutFlagName, Name: NetworkTimeoutFlagName,
Usage: "Timeout for all network operations", Usage: "Timeout for all network operations",
Value: defaultNetworkTimeout, Value: defaults.NetworkTimeout,
EnvVars: prefixEnvVars("NETWORK_TIMEOUT"), EnvVars: prefixEnvVars("NETWORK_TIMEOUT"),
}, },
&cli.DurationFlag{ &cli.DurationFlag{
Name: TxSendTimeoutFlagName, Name: TxSendTimeoutFlagName,
Usage: "Timeout for sending transactions. If 0 it is disabled.", Usage: "Timeout for sending transactions. If 0 it is disabled.",
Value: defaultTxSendTimeout, Value: defaults.TxSendTimeout,
EnvVars: prefixEnvVars("TXMGR_TX_SEND_TIMEOUT"), EnvVars: prefixEnvVars("TXMGR_TX_SEND_TIMEOUT"),
}, },
&cli.DurationFlag{ &cli.DurationFlag{
Name: TxNotInMempoolTimeoutFlagName, Name: TxNotInMempoolTimeoutFlagName,
Usage: "Timeout for aborting a tx send if the tx does not make it to the mempool.", Usage: "Timeout for aborting a tx send if the tx does not make it to the mempool.",
Value: defaultTxNotInMempoolTimeout, Value: defaults.TxNotInMempoolTimeout,
EnvVars: prefixEnvVars("TXMGR_TX_NOT_IN_MEMPOOL_TIMEOUT"), EnvVars: prefixEnvVars("TXMGR_TX_NOT_IN_MEMPOOL_TIMEOUT"),
}, },
&cli.DurationFlag{ &cli.DurationFlag{
Name: ReceiptQueryIntervalFlagName, Name: ReceiptQueryIntervalFlagName,
Usage: "Frequency to poll for receipts", Usage: "Frequency to poll for receipts",
Value: defaultReceiptQueryInterval, Value: defaults.ReceiptQueryInterval,
EnvVars: prefixEnvVars("TXMGR_RECEIPT_QUERY_INTERVAL"), EnvVars: prefixEnvVars("TXMGR_RECEIPT_QUERY_INTERVAL"),
}, },
}, client.CLIFlags(envPrefix)...) }, client.CLIFlags(envPrefix)...)
...@@ -140,16 +165,16 @@ type CLIConfig struct { ...@@ -140,16 +165,16 @@ type CLIConfig struct {
TxNotInMempoolTimeout time.Duration TxNotInMempoolTimeout time.Duration
} }
func NewCLIConfig(l1RPCURL string) CLIConfig { func NewCLIConfig(l1RPCURL string, defaults DefaultFlagValues) CLIConfig {
return CLIConfig{ return CLIConfig{
L1RPCURL: l1RPCURL, L1RPCURL: l1RPCURL,
NumConfirmations: defaultNumConfirmations, NumConfirmations: defaults.NumConfirmations,
SafeAbortNonceTooLowCount: defaultSafeAbortNonceTooLowCount, SafeAbortNonceTooLowCount: defaults.SafeAbortNonceTooLowCount,
ResubmissionTimeout: defaultResubmissionTimeout, ResubmissionTimeout: defaults.ResubmissionTimeout,
NetworkTimeout: defaultNetworkTimeout, NetworkTimeout: defaults.NetworkTimeout,
TxSendTimeout: defaultTxSendTimeout, TxSendTimeout: defaults.TxSendTimeout,
TxNotInMempoolTimeout: defaultTxNotInMempoolTimeout, TxNotInMempoolTimeout: defaults.TxNotInMempoolTimeout,
ReceiptQueryInterval: defaultReceiptQueryInterval, ReceiptQueryInterval: defaults.ReceiptQueryInterval,
SignerCLIConfig: client.NewCLIConfig(), SignerCLIConfig: client.NewCLIConfig(),
} }
} }
......
...@@ -13,12 +13,12 @@ var ( ...@@ -13,12 +13,12 @@ var (
func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) { func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
cfg := configForArgs() cfg := configForArgs()
defaultCfg := NewCLIConfig(l1EthRpcValue) defaultCfg := NewCLIConfig(l1EthRpcValue, DefaultBatcherFlagValues)
require.Equal(t, defaultCfg, cfg) require.Equal(t, defaultCfg, cfg)
} }
func TestDefaultConfigIsValid(t *testing.T) { func TestDefaultConfigIsValid(t *testing.T) {
cfg := NewCLIConfig(l1EthRpcValue) cfg := NewCLIConfig(l1EthRpcValue, DefaultBatcherFlagValues)
require.NoError(t, cfg.Check()) require.NoError(t, cfg.Check())
} }
......
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
"@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/abstract-provider": "^5.7.0",
"@nomiclabs/hardhat-ethers": "^2.2.3", "@nomiclabs/hardhat-ethers": "^2.2.3",
"@nomiclabs/hardhat-waffle": "^2.0.6", "@nomiclabs/hardhat-waffle": "^2.0.6",
"hardhat": "^2.17.2", "hardhat": "^2.17.4",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tsx": "^3.12.7" "tsx": "^3.12.7"
} }
......
...@@ -54,11 +54,11 @@ ...@@ -54,11 +54,11 @@
"devDependencies": { "devDependencies": {
"@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/abstract-provider": "^5.7.0",
"@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/abstract-signer": "^5.7.0",
"@types/express": "^4.17.17", "@types/express": "^4.17.18",
"@types/morgan": "^1.9.6", "@types/morgan": "^1.9.6",
"@types/pino": "^7.0.5", "@types/pino": "^7.0.5",
"@types/pino-multi-stream": "^5.1.4", "@types/pino-multi-stream": "^5.1.4",
"chai": "^4.3.9", "chai": "^4.3.10",
"supertest": "^6.3.3" "supertest": "^6.3.3"
} }
} }
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
"@ethersproject/properties": "^5.7.0", "@ethersproject/properties": "^5.7.0",
"@ethersproject/rlp": "^5.7.0", "@ethersproject/rlp": "^5.7.0",
"@ethersproject/web": "^5.7.1", "@ethersproject/web": "^5.7.1",
"chai": "^4.3.9", "chai": "^4.3.10",
"ethers": "^5.7.2", "ethers": "^5.7.2",
"node-fetch": "^2.6.7" "node-fetch": "^2.6.7"
}, },
......
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"ethereum-waffle": "^4.0.10", "ethereum-waffle": "^4.0.10",
"ethers": "^5.7.2", "ethers": "^5.7.2",
"hardhat": "^2.17.2", "hardhat": "^2.17.4",
"hardhat-deploy": "^0.11.4", "hardhat-deploy": "^0.11.4",
"isomorphic-fetch": "^3.0.0", "isomorphic-fetch": "^3.0.0",
"mocha": "^10.2.0", "mocha": "^10.2.0",
......
...@@ -43,8 +43,8 @@ importers: ...@@ -43,8 +43,8 @@ importers:
specifier: ^6.7.0 specifier: ^6.7.0
version: 6.7.3(eslint@8.50.0)(typescript@5.2.2) version: 6.7.3(eslint@8.50.0)(typescript@5.2.2)
chai: chai:
specifier: ^4.3.9 specifier: ^4.3.10
version: 4.3.9 version: 4.3.10
depcheck: depcheck:
specifier: ^1.4.3 specifier: ^1.4.3
version: 1.4.6 version: 1.4.6
...@@ -159,7 +159,7 @@ importers: ...@@ -159,7 +159,7 @@ importers:
version: 5.0.0 version: 5.0.0
chai-as-promised: chai-as-promised:
specifier: ^7.1.1 specifier: ^7.1.1
version: 7.1.1(chai@4.3.9) version: 7.1.1(chai@4.3.10)
dateformat: dateformat:
specifier: ^4.5.1 specifier: ^4.5.1
version: 4.5.1 version: 4.5.1
...@@ -175,13 +175,13 @@ importers: ...@@ -175,13 +175,13 @@ importers:
version: 5.7.0 version: 5.7.0
'@nomiclabs/hardhat-ethers': '@nomiclabs/hardhat-ethers':
specifier: ^2.2.3 specifier: ^2.2.3
version: 2.2.3(ethers@5.7.2)(hardhat@2.17.2) version: 2.2.3(ethers@5.7.2)(hardhat@2.17.4)
'@nomiclabs/hardhat-waffle': '@nomiclabs/hardhat-waffle':
specifier: ^2.0.6 specifier: ^2.0.6
version: 2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.5)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.2) version: 2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.5)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.4)
hardhat: hardhat:
specifier: ^2.17.2 specifier: ^2.17.4
version: 2.17.2(ts-node@10.9.1)(typescript@5.2.2) version: 2.17.4(ts-node@10.9.1)(typescript@5.2.2)
ts-node: ts-node:
specifier: ^10.9.1 specifier: ^10.9.1
version: 10.9.1(@types/node@20.7.0)(typescript@5.2.2) version: 10.9.1(@types/node@20.7.0)(typescript@5.2.2)
...@@ -247,8 +247,8 @@ importers: ...@@ -247,8 +247,8 @@ importers:
specifier: ^5.7.0 specifier: ^5.7.0
version: 5.7.0 version: 5.7.0
'@types/express': '@types/express':
specifier: ^4.17.17 specifier: ^4.17.18
version: 4.17.17 version: 4.17.18
'@types/morgan': '@types/morgan':
specifier: ^1.9.6 specifier: ^1.9.6
version: 1.9.6 version: 1.9.6
...@@ -259,8 +259,8 @@ importers: ...@@ -259,8 +259,8 @@ importers:
specifier: ^5.1.4 specifier: ^5.1.4
version: 5.1.4 version: 5.1.4
chai: chai:
specifier: ^4.3.9 specifier: ^4.3.10
version: 4.3.9 version: 4.3.10
supertest: supertest:
specifier: ^6.3.3 specifier: ^6.3.3
version: 6.3.3 version: 6.3.3
...@@ -383,8 +383,8 @@ importers: ...@@ -383,8 +383,8 @@ importers:
specifier: ^5.7.1 specifier: ^5.7.1
version: 5.7.1 version: 5.7.1
chai: chai:
specifier: ^4.3.9 specifier: ^4.3.10
version: 4.3.9 version: 4.3.10
ethers: ethers:
specifier: ^5.7.2 specifier: ^5.7.2
version: 5.7.2 version: 5.7.2
...@@ -473,10 +473,10 @@ importers: ...@@ -473,10 +473,10 @@ importers:
version: 5.7.0 version: 5.7.0
'@nomiclabs/hardhat-ethers': '@nomiclabs/hardhat-ethers':
specifier: ^2.2.3 specifier: ^2.2.3
version: 2.2.3(ethers@5.7.2)(hardhat@2.17.2) version: 2.2.3(ethers@5.7.2)(hardhat@2.17.4)
'@nomiclabs/hardhat-waffle': '@nomiclabs/hardhat-waffle':
specifier: ^2.0.1 specifier: ^2.0.1
version: 2.0.1(@nomiclabs/hardhat-ethers@2.2.3)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.2) version: 2.0.1(@nomiclabs/hardhat-ethers@2.2.3)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.4)
'@types/chai': '@types/chai':
specifier: ^4.3.6 specifier: ^4.3.6
version: 4.3.6 version: 4.3.6
...@@ -491,7 +491,7 @@ importers: ...@@ -491,7 +491,7 @@ importers:
version: 20.5.0 version: 20.5.0
chai-as-promised: chai-as-promised:
specifier: ^7.1.1 specifier: ^7.1.1
version: 7.1.1(chai@4.3.9) version: 7.1.1(chai@4.3.10)
ethereum-waffle: ethereum-waffle:
specifier: ^4.0.10 specifier: ^4.0.10
version: 4.0.10(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typescript@5.2.2) version: 4.0.10(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typescript@5.2.2)
...@@ -499,8 +499,8 @@ importers: ...@@ -499,8 +499,8 @@ importers:
specifier: ^5.7.2 specifier: ^5.7.2
version: 5.7.2 version: 5.7.2
hardhat: hardhat:
specifier: ^2.17.2 specifier: ^2.17.4
version: 2.17.2(ts-node@10.9.1)(typescript@5.2.2) version: 2.17.4(ts-node@10.9.1)(typescript@5.2.2)
hardhat-deploy: hardhat-deploy:
specifier: ^0.11.4 specifier: ^0.11.4
version: 0.11.10 version: 0.11.10
...@@ -1848,7 +1848,7 @@ packages: ...@@ -1848,7 +1848,7 @@ packages:
'@ethersproject/transactions': 5.7.0 '@ethersproject/transactions': 5.7.0
'@ethersproject/web': 5.7.1 '@ethersproject/web': 5.7.1
bufio: 1.2.0 bufio: 1.2.0
chai: 4.3.9 chai: 4.3.10
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- utf-8-validate - utf-8-validate
...@@ -2924,17 +2924,17 @@ packages: ...@@ -2924,17 +2924,17 @@ packages:
'@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.1 '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.1
dev: true dev: true
/@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.17.2): /@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.17.4):
resolution: {integrity: sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==} resolution: {integrity: sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==}
peerDependencies: peerDependencies:
ethers: ^5.0.0 ethers: ^5.0.0
hardhat: ^2.0.0 hardhat: ^2.0.0
dependencies: dependencies:
ethers: 5.7.2 ethers: 5.7.2
hardhat: 2.17.2(ts-node@10.9.1)(typescript@5.2.2) hardhat: 2.17.4(ts-node@10.9.1)(typescript@5.2.2)
dev: true dev: true
/@nomiclabs/hardhat-waffle@2.0.1(@nomiclabs/hardhat-ethers@2.2.3)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.2): /@nomiclabs/hardhat-waffle@2.0.1(@nomiclabs/hardhat-ethers@2.2.3)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.4):
resolution: {integrity: sha512-2YR2V5zTiztSH9n8BYWgtv3Q+EL0N5Ltm1PAr5z20uAY4SkkfylJ98CIqt18XFvxTD5x4K2wKBzddjV9ViDAZQ==} resolution: {integrity: sha512-2YR2V5zTiztSH9n8BYWgtv3Q+EL0N5Ltm1PAr5z20uAY4SkkfylJ98CIqt18XFvxTD5x4K2wKBzddjV9ViDAZQ==}
peerDependencies: peerDependencies:
'@nomiclabs/hardhat-ethers': ^2.0.0 '@nomiclabs/hardhat-ethers': ^2.0.0
...@@ -2942,15 +2942,15 @@ packages: ...@@ -2942,15 +2942,15 @@ packages:
ethers: ^5.0.0 ethers: ^5.0.0
hardhat: ^2.0.0 hardhat: ^2.0.0
dependencies: dependencies:
'@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.17.2) '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.17.4)
'@types/sinon-chai': 3.2.5 '@types/sinon-chai': 3.2.5
'@types/web3': 1.0.19 '@types/web3': 1.0.19
ethereum-waffle: 4.0.10(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typescript@5.2.2) ethereum-waffle: 4.0.10(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typescript@5.2.2)
ethers: 5.7.2 ethers: 5.7.2
hardhat: 2.17.2(ts-node@10.9.1)(typescript@5.2.2) hardhat: 2.17.4(ts-node@10.9.1)(typescript@5.2.2)
dev: true dev: true
/@nomiclabs/hardhat-waffle@2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.5)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.2): /@nomiclabs/hardhat-waffle@2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.5)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.4):
resolution: {integrity: sha512-+Wz0hwmJGSI17B+BhU/qFRZ1l6/xMW82QGXE/Gi+WTmwgJrQefuBs1lIf7hzQ1hLk6hpkvb/zwcNkpVKRYTQYg==} resolution: {integrity: sha512-+Wz0hwmJGSI17B+BhU/qFRZ1l6/xMW82QGXE/Gi+WTmwgJrQefuBs1lIf7hzQ1hLk6hpkvb/zwcNkpVKRYTQYg==}
peerDependencies: peerDependencies:
'@nomiclabs/hardhat-ethers': ^2.0.0 '@nomiclabs/hardhat-ethers': ^2.0.0
...@@ -2959,11 +2959,11 @@ packages: ...@@ -2959,11 +2959,11 @@ packages:
ethers: ^5.0.0 ethers: ^5.0.0
hardhat: ^2.0.0 hardhat: ^2.0.0
dependencies: dependencies:
'@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.17.2) '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.17.4)
'@types/sinon-chai': 3.2.5 '@types/sinon-chai': 3.2.5
ethereum-waffle: 4.0.10(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typescript@5.2.2) ethereum-waffle: 4.0.10(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typescript@5.2.2)
ethers: 5.7.2 ethers: 5.7.2
hardhat: 2.17.2(ts-node@10.9.1)(typescript@5.2.2) hardhat: 2.17.4(ts-node@10.9.1)(typescript@5.2.2)
dev: true dev: true
/@nrwl/nx-cloud@16.4.0: /@nrwl/nx-cloud@16.4.0:
...@@ -3902,8 +3902,8 @@ packages: ...@@ -3902,8 +3902,8 @@ packages:
'@types/send': 0.17.1 '@types/send': 0.17.1
dev: true dev: true
/@types/express@4.17.17: /@types/express@4.17.18:
resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==} resolution: {integrity: sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==}
dependencies: dependencies:
'@types/body-parser': 1.19.1 '@types/body-parser': 1.19.1
'@types/express-serve-static-core': 4.17.35 '@types/express-serve-static-core': 4.17.35
...@@ -4485,7 +4485,7 @@ packages: ...@@ -4485,7 +4485,7 @@ packages:
dependencies: dependencies:
'@vitest/spy': 0.34.1 '@vitest/spy': 0.34.1
'@vitest/utils': 0.34.1 '@vitest/utils': 0.34.1
chai: 4.3.9 chai: 4.3.10
dev: true dev: true
/@vitest/expect@0.34.2: /@vitest/expect@0.34.2:
...@@ -4493,7 +4493,7 @@ packages: ...@@ -4493,7 +4493,7 @@ packages:
dependencies: dependencies:
'@vitest/spy': 0.34.2 '@vitest/spy': 0.34.2
'@vitest/utils': 0.34.2 '@vitest/utils': 0.34.2
chai: 4.3.9 chai: 4.3.10
dev: true dev: true
/@vitest/expect@0.34.4: /@vitest/expect@0.34.4:
...@@ -4501,7 +4501,7 @@ packages: ...@@ -4501,7 +4501,7 @@ packages:
dependencies: dependencies:
'@vitest/spy': 0.34.4 '@vitest/spy': 0.34.4
'@vitest/utils': 0.34.4 '@vitest/utils': 0.34.4
chai: 4.3.9 chai: 4.3.10
dev: true dev: true
/@vitest/runner@0.34.1: /@vitest/runner@0.34.1:
...@@ -6309,26 +6309,25 @@ packages: ...@@ -6309,26 +6309,25 @@ packages:
resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==}
dev: true dev: true
/chai-as-promised@7.1.1(chai@4.3.9): /chai-as-promised@7.1.1(chai@4.3.10):
resolution: {integrity: sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==} resolution: {integrity: sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==}
peerDependencies: peerDependencies:
chai: '>= 2.1.2 < 5' chai: '>= 2.1.2 < 5'
dependencies: dependencies:
chai: 4.3.9 chai: 4.3.10
check-error: 1.0.2 check-error: 1.0.2
/chai@4.3.8: /chai@4.3.10:
resolution: {integrity: sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==} resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==}
engines: {node: '>=4'} engines: {node: '>=4'}
dependencies: dependencies:
assertion-error: 1.1.0 assertion-error: 1.1.0
check-error: 1.0.2 check-error: 1.0.3
deep-eql: 4.1.3 deep-eql: 4.1.3
get-func-name: 2.0.2 get-func-name: 2.0.2
loupe: 2.3.6 loupe: 2.3.6
pathval: 1.1.1 pathval: 1.1.1
type-detect: 4.0.8 type-detect: 4.0.8
dev: true
/chai@4.3.9: /chai@4.3.9:
resolution: {integrity: sha512-tH8vhfA1CfuYMkALXj+wmZcqiwqOfshU9Gry+NYiiLqIddrobkBhALv6XD4yDz68qapphYI4vSaqhqAdThCAAA==} resolution: {integrity: sha512-tH8vhfA1CfuYMkALXj+wmZcqiwqOfshU9Gry+NYiiLqIddrobkBhALv6XD4yDz68qapphYI4vSaqhqAdThCAAA==}
...@@ -6341,6 +6340,7 @@ packages: ...@@ -6341,6 +6340,7 @@ packages:
loupe: 2.3.6 loupe: 2.3.6
pathval: 1.1.1 pathval: 1.1.1
type-detect: 4.0.8 type-detect: 4.0.8
dev: true
/chalk@2.4.2: /chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
...@@ -8979,8 +8979,8 @@ packages: ...@@ -8979,8 +8979,8 @@ packages:
- utf-8-validate - utf-8-validate
dev: true dev: true
/hardhat@2.17.2(ts-node@10.9.1)(typescript@5.2.2): /hardhat@2.17.4(ts-node@10.9.1)(typescript@5.2.2):
resolution: {integrity: sha512-oUv40jBeHw0dKpbyQ+iH9cmNMziweLoTW3MnkNxJ2Gc0KGLrQR/1n4vV4xY60zn2LdmRgnwPqy3CgtY0mfwIIA==} resolution: {integrity: sha512-YTyHjVc9s14CY/O7Dbtzcr/92fcz6AzhrMaj6lYsZpYPIPLzOrFCZHHPxfGQB6FiE6IPNE0uJaAbr7zGF79goA==}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
ts-node: '*' ts-node: '*'
...@@ -13414,7 +13414,7 @@ packages: ...@@ -13414,7 +13414,7 @@ packages:
dev: true dev: true
/strip-hex-prefix@1.0.0: /strip-hex-prefix@1.0.0:
resolution: {integrity: sha1-DF8VX+8RUTczd96du1iNoFUA428=} resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==}
engines: {node: '>=6.5.0', npm: '>=3'} engines: {node: '>=6.5.0', npm: '>=3'}
dependencies: dependencies:
is-hex-prefixed: 1.0.0 is-hex-prefixed: 1.0.0
...@@ -14640,7 +14640,7 @@ packages: ...@@ -14640,7 +14640,7 @@ packages:
acorn: 8.10.0 acorn: 8.10.0
acorn-walk: 8.2.0 acorn-walk: 8.2.0
cac: 6.7.14 cac: 6.7.14
chai: 4.3.9 chai: 4.3.10
debug: 4.3.4(supports-color@8.1.1) debug: 4.3.4(supports-color@8.1.1)
local-pkg: 0.4.3 local-pkg: 0.4.3
magic-string: 0.30.1 magic-string: 0.30.1
...@@ -14705,7 +14705,7 @@ packages: ...@@ -14705,7 +14705,7 @@ packages:
acorn: 8.10.0 acorn: 8.10.0
acorn-walk: 8.2.0 acorn-walk: 8.2.0
cac: 6.7.14 cac: 6.7.14
chai: 4.3.9 chai: 4.3.10
debug: 4.3.4(supports-color@8.1.1) debug: 4.3.4(supports-color@8.1.1)
jsdom: 22.1.0 jsdom: 22.1.0
local-pkg: 0.4.3 local-pkg: 0.4.3
...@@ -14771,7 +14771,7 @@ packages: ...@@ -14771,7 +14771,7 @@ packages:
acorn: 8.10.0 acorn: 8.10.0
acorn-walk: 8.2.0 acorn-walk: 8.2.0
cac: 6.7.14 cac: 6.7.14
chai: 4.3.8 chai: 4.3.9
debug: 4.3.4(supports-color@8.1.1) debug: 4.3.4(supports-color@8.1.1)
local-pkg: 0.4.3 local-pkg: 0.4.3
magic-string: 0.30.1 magic-string: 0.30.1
......
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