From 88bb56c4b58e1f5bee5efc9d2fd0753a22a53520 Mon Sep 17 00:00:00 2001
From: Raffaele <151576068+raffaele-oplabs@users.noreply.github.com>
Date: Thu, 10 Oct 2024 11:15:06 +0200
Subject: [PATCH] 85 faultproof withdrawals does not report connection errors
metrics correctly (#90)
* fixed bug, of not setting correct number of nodeconnection errors on metrics
* fixed bugs and added features. Still missing proper metrics
* fixing metrics
* fixing metrics
* fixes
* completing changes
* Update op-monitorism/faultproof_withdrawals/validator/optimism_portal2_helper.go
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fixing review issues
* fixing review issues
* fixing review issues
* fixing review issues
* fixing review issues
* fixing review issues
* Update op-monitorism/faultproof_withdrawals/monitor_live_sepolia_test.go
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update op-monitorism/faultproof_withdrawals/monitor_live_sepolia_test.go
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fixing review issues
* Update op-monitorism/faultproof_withdrawals/monitor_live_mainnet_test.go
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fixing review issues
* fixing review issues
* Apply suggestions from code review
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fixing review issues
* fixing review issues
* fixing tests
* fixing libraries
* fixing minor issues
* Update state.go
Adding extra comment description
* fixing comment
* fxing one piece of code
* cleaning up and fixing documentation
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
---
README.md | 2 +-
op-monitorism/README.md | 146 -------
.../.env.op.mainnet.example | 9 +
.../faultproof_withdrawals/README.md | 2 -
.../faultproof_withdrawals/monitor.go | 84 ++--
.../monitor_live_mainnet_test.go | 173 ++++++++
...r_test.go => monitor_live_sepolia_test.go} | 202 +++++-----
op-monitorism/faultproof_withdrawals/state.go | 372 ++++++++++++++----
.../validator/fault_dispute_game_helper.go | 2 +-
.../validator/op_node_helper.go | 59 ++-
.../validator/optimism_portal2_helper.go | 14 +-
.../validator/proven_withdrawal_validator.go | 59 ++-
.../faultproof_withdrawals/validator/utils.go | 6 +
op-monitorism/go.mod | 4 +-
op-monitorism/go.sum | 1 -
15 files changed, 709 insertions(+), 426 deletions(-)
delete mode 100644 op-monitorism/README.md
create mode 100644 op-monitorism/faultproof_withdrawals/.env.op.mainnet.example
create mode 100644 op-monitorism/faultproof_withdrawals/monitor_live_mainnet_test.go
rename op-monitorism/faultproof_withdrawals/{monitor_test.go => monitor_live_sepolia_test.go} (60%)
diff --git a/README.md b/README.md
index d9cddba8..68dc9845 100644
--- a/README.md
+++ b/README.md
@@ -131,7 +131,7 @@ This component is designed to work exclusively with chains that are already util
This is a new version of the deprecated [chain-mon faultproof-wd-mon](https://github.com/ethereum-optimism/optimism/tree/chain-mon/v1.2.1/packages/chain-mon/src/faultproof-wd-mon).
For detailed information on how the component works and the algorithms used, please refer to the component README.
-| `op-monitorism/faultproof-withdrawals` | [README](https://github.com/ethereum-optimism/monitorism/blob/main/op-monitorism/faultproof-withdrawals/README.md) |
+| `op-monitorism/faultproof-withdrawals` | [README](https://github.com/ethereum-optimism/monitorism/blob/main/op-monitorism/faultproof_withdrawals/README.md) |
| ----------------------- | --------------------------------------------------------------------------------------------------- |
diff --git a/op-monitorism/README.md b/op-monitorism/README.md
deleted file mode 100644
index a3b04fb0..00000000
--- a/op-monitorism/README.md
+++ /dev/null
@@ -1,146 +0,0 @@
-# Monitors
-
-`op-monitorism` is a collection of monitoring tools for the OP stack. Each monitor is designed to track a specific aspect of the Optimism stack and emit metrics that can be used to set up alerts.
-
-The following the commands are currently available:
-
-```bash
-NAME:
- Monitorism - OP Stack Monitoring
-
-USAGE:
- Monitorism [global options] command [command options]
-
-VERSION:
- 0.1.0-unstable
-
-DESCRIPTION:
- OP Stack Monitoring
-
-COMMANDS:
- multisig Monitors OptimismPortal pause status, Safe nonce, and Pre-Signed nonce stored in 1Password
- fault Monitors output roots posted on L1 against L2
- withdrawals Monitors proven withdrawals on L1 against L2
- balances Monitors account balances
- drippie Monitors Drippie contract
- secrets Monitors secrets revealed in the CheckSecrets dripcheck
- global_events Monitors global events with YAML configuration
- liveness_expiration Monitor the liveness expiration on Gnosis Safe.
- version Show version
- help, h Shows a list of commands or help for one command
-
-GLOBAL OPTIONS:
- --help, -h show help
- --version, -v print the version
-```
-
-Each _monitor_ has some common configuration, configurable both via cli or env with defaults.
-
-```bash
-OPTIONS:
- --log.level value [$MONITORISM_LOG_LEVEL] The lowest log level that will be output (default: INFO)
- --log.format value [$MONITORISM_LOG_FORMAT] Format the log output. Supported formats: 'text', 'terminal', 'logfmt', 'json', 'json-pretty', (default: text)
- --log.color [$MONITORISM_LOG_COLOR] Color the log output if in terminal mode (default: false)
- --metrics.enabled [$MONITORISM_METRICS_ENABLED] Enable the metrics server (default: false)
- --metrics.addr value [$MONITORISM_METRICS_ADDR] Metrics listening address (default: "0.0.0.0")
- --metrics.port value [$MONITORISM_METRICS_PORT] Metrics listening port (default: 7300)
- --loop.interval.msec value [$MONITORISM_LOOP_INTERVAL_MSEC] Loop interval of the monitor in milliseconds (default: 60000)
-```
-
-### Liveness Expiration Monitor
-
-![ab27497cea05fbd51b7b1c2ecde5bc69307ac0f27349f6bba4f3f21423116071](https://github.com/ethereum-optimism/monitorism/assets/23560242/af7a7e29-fff5-4df3-82f0-94c2f28fde84)
-
-The Liveness Expiration Monitor is made for monitoring the liveness expiration on Safes.
-
-| `op-monitorism/liveness_expiration` | [README](https://github.com/ethereum-optimism/monitorism/blob/main/op-monitorism/liveness_expiration/README.md) |
-| ----------------------------------- | --------------------------------------------------------------------------------------------------------------- |
-
-### Withdrawals Monitor
-
-![6d5477f5585cb49ff2f8bd147c2e7037772de6a1dd128ce4331596b011ce6ea9](https://github.com/user-attachments/assets/ac5e0a61-b495-4254-b32a-86abf61f0dc1)
-
-The withdrawals monitor checks for new withdrawals that have been proven to the `OptimismPortal` contract.
-Each withdrawal is checked against the `L2ToL1MessagePasser` contract.
-
-| `op-monitorism/withdrawals` | [README](https://github.com/ethereum-optimism/monitorism/blob/main/op-monitorism/withdrawals/README.md) |
-| --------------------------- | ------------------------------------------------------------------------------------------------------- |
-
-### Balances Monitor
-
-![5cd47a6e0f2fb7d921001db9eea24bb62bb892615011d03f275e02a147823827](https://github.com/user-attachments/assets/44884a76-e06d-4f58-a21f-94c2275e9d8b)
-
-The balances monitor simply emits a metric reporting the balances for the configured accounts.
-
-| `op-monitorism/balances` | [README](https://github.com/ethereum-optimism/monitorism/blob/main/op-monitorism/balances/README.md) |
-| ------------------------ | ---------------------------------------------------------------------------------------------------- |
-
-### Fault Monitor
-
-
-
-
-
-The fault monitor checks for changes in output roots posted to the `L2OutputOracle` contract.
-On change, reconstructing the output root from a trusted L2 source and looking for a match.
-
-| `op-monitorism/fault` | [README](https://github.com/ethereum-optimism/monitorism/blob/main/op-monitorism/fault/README.md) |
-| --------------------- | ------------------------------------------------------------------------------------------------- |
-
-### Multisig Monitor
-
-![7dab260ee38122980274fee27b114c590405cff2e5a68e6090290ecb786b68f2](https://github.com/user-attachments/assets/0eeb161b-923a-40fd-b561-468df3d5091d)
-
-The multisig monitor reports the paused status of the `OptimismPortal` contract.
-If set, the latest nonce of the configued `Safe` address. And also if set, the latest presigned nonce stored in One Password.
-The latest presigned nonce is identifyed by looking for items in the configued vault that follow a `ready-.json` name.
-The highest nonce of this item name format is reported.
-
-| `op-monitorism/multisig` | [README](https://github.com/ethereum-optimism/monitorism/blob/main/op-monitorism/multisig/README.md) |
-| ------------------------ | ---------------------------------------------------------------------------------------------------- |
-
-### Drippie Monitor
-
-The drippie monitor tracks the execution and executability of drips within a Drippie contract.
-
-| `op-monitorism/drippie` | [README](https://github.com/ethereum-optimism/monitorism/blob/main/op-monitorism/drippie/README.md) |
-| ----------------------- | ---------------------------------------------------------------------------------------------------- |
-
-### Secrets Monitor
-
-The secrets monitor takes a Drippie contract as a parameter and monitors for any drips within that contract that use the CheckSecrets dripcheck contract. CheckSecrets is a dripcheck that allows a drip to begin once a specific secret has been revealed (after a delay period) and cancels the drip if a second secret is revealed. It's important to monitor for these secrets being revealed as this could be a sign that the secret storage platform has been compromised and someone is attempting to exflitrate the ETH controlled by that drip.
-
-| `op-monitorism/secrets` | [README](https://github.com/ethereum-optimism/monitorism/blob/main/op-monitorism/secrets/README.md) |
-| ----------------------- | ---------------------------------------------------------------------------------------------------- |
-
-## CLI and Docs
-
-## Development
-
-After cloning, please run `./bootstrap.sh` to set up the development environment correctly.
-
-## Intro
-
-The cli has the ability to spin up a monitor for varying activities, each emmitting metrics used to setup alerts.
-
-```
-COMMANDS:
- multisig Monitors OptimismPortal pause status, Safe nonce, and Pre-Signed nonce stored in 1Password
- fault Monitors output roots posted on L1 against L2
- withdrawals Monitors proven withdrawals on L1 against L2
- balances Monitors account balances
- secrets Monitors secrets revealed in the CheckSecrets dripcheck
-```
-
-Each monitor has some common configuration, configurable both via cli or env with defaults.
-
-```
-OPTIONS:
- --log.level value [$MONITORISM_LOG_LEVEL] The lowest log level that will be output (default: INFO)
- --log.format value [$MONITORISM_LOG_FORMAT] Format the log output. Supported formats: 'text', 'terminal', 'logfmt', 'json', 'json-pretty', (default: text)
- --log.color [$MONITORISM_LOG_COLOR] Color the log output if in terminal mode (default: false)
- --metrics.enabled [$MONITORISM_METRICS_ENABLED] Enable the metrics server (default: false)
- --metrics.addr value [$MONITORISM_METRICS_ADDR] Metrics listening address (default: "0.0.0.0")
- --metrics.port value [$MONITORISM_METRICS_PORT] Metrics listening port (default: 7300)
- --loop.interval.msec value [$MONITORISM_LOOP_INTERVAL_MSEC] Loop interval of the monitor in milliseconds (default: 60000)
-```
diff --git a/op-monitorism/faultproof_withdrawals/.env.op.mainnet.example b/op-monitorism/faultproof_withdrawals/.env.op.mainnet.example
new file mode 100644
index 00000000..4c2e5da6
--- /dev/null
+++ b/op-monitorism/faultproof_withdrawals/.env.op.mainnet.example
@@ -0,0 +1,9 @@
+FAULTPROOF_WITHDRAWAL_MON_L1_GETH_URL=""
+FAULTPROOF_WITHDRAWAL_MON_L2_OP_NODE_URL=""
+FAULTPROOF_WITHDRAWAL_MON_L2_OP_GETH_URL=""
+FAULTPROOF_WITHDRAWAL_MON_OPTIMISM_PORTAL="0xbEb5Fc579115071764c7423A4f12eDde41f106Ed" # This is the address of the Optimism portal contract, this should be for the chain you are monitoring
+FAULTPROOF_WITHDRAWAL_MON_START_BLOCK_HEIGHT=20872390 # This is the block height from which the monitoring will start, decide at which block height you want to start monitoring
+FAULTPROOF_WITHDRAWAL_MON_EVENT_BLOCK_RANGE=1000 # This is the range of blocks to be monitored
+MONITORISM_LOOP_INTERVAL_MSEC=100 # This is the interval in milliseconds for the monitoring loop
+MONITORISM_METRICS_PORT=7300 # This is the port on which the metrics server will run
+MONITORISM_METRICS_ENABLED=true # This is the flag to enable/disable the metrics server
diff --git a/op-monitorism/faultproof_withdrawals/README.md b/op-monitorism/faultproof_withdrawals/README.md
index a29966de..3bcfb3ac 100644
--- a/op-monitorism/faultproof_withdrawals/README.md
+++ b/op-monitorism/faultproof_withdrawals/README.md
@@ -22,8 +22,6 @@ or
cd ../
go run ./cmd/monitorism faultproof_withdrawals --metrics.enabled --metrics.port 7300
```
-## Available Metrics and Meaning
-
# Cli options
diff --git a/op-monitorism/faultproof_withdrawals/monitor.go b/op-monitorism/faultproof_withdrawals/monitor.go
index c4fc5ba6..adfbd9c9 100644
--- a/op-monitorism/faultproof_withdrawals/monitor.go
+++ b/op-monitorism/faultproof_withdrawals/monitor.go
@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/monitorism/op-monitorism/faultproof_withdrawals/validator"
"github.com/ethereum-optimism/optimism/op-service/metrics"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
@@ -123,14 +124,14 @@ func NewMonitor(ctx context.Context, log log.Logger, m metrics.Factory, cfg CLIC
startingL1BlockHeight = uint64(cfg.StartingL1BlockHeight)
}
- state, err := NewState(log, startingL1BlockHeight, latestL1Height)
+ state, err := NewState(log, startingL1BlockHeight, latestL1Height, ret.withdrawalValidator.GetLatestL2Height())
if err != nil {
return nil, fmt.Errorf("failed to create state: %w", err)
}
ret.state = *state
// log state and metrics
- ret.state.LogState(ret.log)
+ ret.state.LogState()
ret.metrics.UpdateMetricsFromState(&ret.state)
return ret, nil
@@ -228,6 +229,10 @@ func (m *Monitor) GetMaxBlock() (uint64, error) {
// Run executes the main monitoring loop.
// It retrieves new events, processes them, and updates the state accordingly.
func (m *Monitor) Run(ctx context.Context) {
+ // Defer the update function
+ defer m.metrics.UpdateMetricsFromState(&m.state)
+ defer m.state.LogState()
+
start := m.state.nextL1Height
stop, err := m.GetMaxBlock()
@@ -238,18 +243,16 @@ func (m *Monitor) Run(ctx context.Context) {
}
// review previous invalidProposalWithdrawalsEvents
- invalidProposalWithdrawalsEvents, err := m.ConsumeEvents(m.state.invalidProposalWithdrawalsEvents)
+ err = m.ConsumeEvents(m.state.potentialAttackOnInProgressGames)
if err != nil {
+ m.state.nodeConnectionFailures++
m.log.Error("failed to consume events", "error", err)
return
}
- // update state
- m.state.invalidProposalWithdrawalsEvents = *invalidProposalWithdrawalsEvents
-
// get new events
-
- newEvents, err := m.withdrawalValidator.GetEnrichedWithdrawalsEvents(start, &stop)
+ m.log.Info("getting enriched withdrawal events", "start", fmt.Sprintf("%d", start), "stop", fmt.Sprintf("%d", stop))
+ newEvents, err := m.withdrawalValidator.GetEnrichedWithdrawalsEventsMap(start, &stop)
if err != nil {
if start >= stop {
m.log.Info("no new events to process", "start", start, "stop", stop)
@@ -262,99 +265,76 @@ func (m *Monitor) Run(ctx context.Context) {
}
return
}
- newInvalidProposalWithdrawalsEvents, err := m.ConsumeEvents(newEvents)
+
+ err = m.ConsumeEvents(newEvents)
if err != nil {
+ m.state.nodeConnectionFailures++
m.log.Error("failed to consume events", "error", err)
return
}
- // update state
- if len(*newInvalidProposalWithdrawalsEvents) > 0 && newInvalidProposalWithdrawalsEvents != nil {
- m.state.invalidProposalWithdrawalsEvents = append(m.state.invalidProposalWithdrawalsEvents, *newInvalidProposalWithdrawalsEvents...)
- }
-
// update state
m.state.nextL1Height = stop
- // log state and metrics
- m.state.LogState(m.log)
- m.metrics.UpdateMetricsFromState(&m.state)
}
// ConsumeEvents processes a slice of enriched withdrawal events and updates their states.
// It returns any events detected during the consumption that requires to be re-analysed again at a later stage (when the event referenced DisputeGame completes).
-func (m *Monitor) ConsumeEvents(enrichedWithdrawalEvent []validator.EnrichedProvenWithdrawalEvent) (*[]validator.EnrichedProvenWithdrawalEvent, error) {
- var newForgeriesGameInProgressEvent []validator.EnrichedProvenWithdrawalEvent = make([]validator.EnrichedProvenWithdrawalEvent, 0)
- for _, enrichedWithdrawalEvent := range enrichedWithdrawalEvent {
+func (m *Monitor) ConsumeEvents(enrichedWithdrawalEvents map[common.Hash]validator.EnrichedProvenWithdrawalEvent) error {
+ for _, enrichedWithdrawalEvent := range enrichedWithdrawalEvents {
+ m.log.Info("processing withdrawal event", "event", &enrichedWithdrawalEvent)
err := m.withdrawalValidator.UpdateEnrichedWithdrawalEvent(&enrichedWithdrawalEvent)
+ //upgrade state to the latest L2 height after the event is processed
+ m.state.latestL2Height = m.withdrawalValidator.GetLatestL2Height()
if err != nil {
- m.state.nodeConnectionFailures++
m.log.Error("failed to update enriched withdrawal event", "error", err)
- return nil, err
+ return err
}
- consumedEvent, err := m.ConsumeEvent(enrichedWithdrawalEvent)
+ err = m.ConsumeEvent(enrichedWithdrawalEvent)
if err != nil {
m.log.Error("failed to consume event", "error", err)
- return nil, err
- } else if !consumedEvent {
- newForgeriesGameInProgressEvent = append(newForgeriesGameInProgressEvent, enrichedWithdrawalEvent)
+ return err
}
}
- return &newForgeriesGameInProgressEvent, nil
+ return nil
}
// ConsumeEvent processes a single enriched withdrawal event.
// It logs the event details and checks for any forgery detection.
-func (m *Monitor) ConsumeEvent(enrichedWithdrawalEvent validator.EnrichedProvenWithdrawalEvent) (bool, error) {
- m.log.Info("processing withdrawal event", "event", enrichedWithdrawalEvent.Event)
+func (m *Monitor) ConsumeEvent(enrichedWithdrawalEvent validator.EnrichedProvenWithdrawalEvent) error {
if enrichedWithdrawalEvent.DisputeGame.DisputeGameData.L2ChainID.Cmp(m.l2ChainID) != 0 {
m.log.Error("l2ChainID mismatch", "expected", fmt.Sprintf("%d", m.l2ChainID), "got", fmt.Sprintf("%d", enrichedWithdrawalEvent.DisputeGame.DisputeGameData.L2ChainID))
}
valid, err := m.withdrawalValidator.IsWithdrawalEventValid(&enrichedWithdrawalEvent)
if err != nil {
- m.state.nodeConnectionFailures++
m.log.Error("failed to check if forgery detected", "error", err)
- return false, err
+ return err
}
- eventConsumed := false
if !valid {
- m.state.numberOfInvalidWithdrawals++
if !enrichedWithdrawalEvent.Blacklisted {
if enrichedWithdrawalEvent.DisputeGame.DisputeGameData.Status == validator.CHALLENGER_WINS {
- m.log.Warn("WITHDRAWAL: is NOT valid, but the game is correctly resolved", "enrichedWithdrawalEvent", enrichedWithdrawalEvent)
- m.state.withdrawalsValidated++
- eventConsumed = true
+ m.state.IncrementSuspiciousEventsOnChallengerWinsGames(enrichedWithdrawalEvent)
} else if enrichedWithdrawalEvent.DisputeGame.DisputeGameData.Status == validator.DEFENDER_WINS {
- m.log.Error("WITHDRAWAL: is NOT valid, forgery detected", "enrichedWithdrawalEvent", enrichedWithdrawalEvent)
- m.state.numberOfDetectedForgery++
- // add to forgeries
- m.state.forgeriesWithdrawalsEvents = append(m.state.forgeriesWithdrawalsEvents, enrichedWithdrawalEvent)
- eventConsumed = true
+ m.state.IncrementPotentialAttackOnDefenderWinsGames(enrichedWithdrawalEvent)
} else if enrichedWithdrawalEvent.DisputeGame.DisputeGameData.Status == validator.IN_PROGRESS {
- m.log.Warn("WITHDRAWAL: is NOT valid, game is still in progress.", "enrichedWithdrawalEvent", enrichedWithdrawalEvent)
+ m.state.IncrementPotentialAttackOnInProgressGames(enrichedWithdrawalEvent)
// add to events to be re-processed
- eventConsumed = false
} else {
m.log.Error("WITHDRAWAL: is NOT valid, game status is unknown. UNKNOWN STATE SHOULD NEVER HAPPEN", "enrichedWithdrawalEvent", enrichedWithdrawalEvent)
- eventConsumed = false
}
} else {
- m.log.Warn("WITHDRAWAL: is NOT valid, but game is blacklisted", "enrichedWithdrawalEvent", enrichedWithdrawalEvent)
- m.state.withdrawalsValidated++
- eventConsumed = true
+ m.state.IncrementSuspiciousEventsOnChallengerWinsGames(enrichedWithdrawalEvent)
}
} else {
- m.log.Info("WITHDRAWAL: is valid", "enrichedWithdrawalEvent", enrichedWithdrawalEvent)
- m.state.withdrawalsValidated++
- eventConsumed = true
+ m.state.IncrementWithdrawalsValidated(enrichedWithdrawalEvent)
}
- m.state.processedProvenWithdrawalsExtension1Events++
+ m.state.eventsProcessed++
m.metrics.UpdateMetricsFromState(&m.state)
- return eventConsumed, nil
+ return nil
}
// Close gracefully shuts down the Monitor by closing the Geth clients.
diff --git a/op-monitorism/faultproof_withdrawals/monitor_live_mainnet_test.go b/op-monitorism/faultproof_withdrawals/monitor_live_mainnet_test.go
new file mode 100644
index 00000000..ad0dbf36
--- /dev/null
+++ b/op-monitorism/faultproof_withdrawals/monitor_live_mainnet_test.go
@@ -0,0 +1,173 @@
+//go:build live
+// +build live
+
+package faultproof_withdrawals
+
+import (
+ "context"
+ "io"
+ "math/big"
+ "testing"
+
+ oplog "github.com/ethereum-optimism/optimism/op-service/log"
+ opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/joho/godotenv"
+ "github.com/stretchr/testify/require"
+)
+
+// NewTestMonitorMainnet initializes and returns a new Monitor instance for testing.
+// It sets up the necessary environment variables and configurations required for the monitor.
+func NewTestMonitorMainnet() *Monitor {
+ envmap, err := godotenv.Read(".env.op.mainnet")
+ if err != nil {
+ panic("error")
+ }
+
+ ctx := context.Background()
+ L1GethURL := envmap["FAULTPROOF_WITHDRAWAL_MON_L1_GETH_URL"]
+ L2OpNodeURL := envmap["FAULTPROOF_WITHDRAWAL_MON_L2_OP_NODE_URL"]
+ L2OpGethURL := envmap["FAULTPROOF_WITHDRAWAL_MON_L2_OP_GETH_URL"]
+
+ FAULTPROOF_WITHDRAWAL_MON_OPTIMISM_PORTAL := "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"
+ FAULTPROOF_WITHDRAWAL_MON_EVENT_BLOCK_RANGE := uint64(1000)
+ FAULTPROOF_WITHDRAWAL_MON_START_BLOCK_HEIGHT := int64(6789100)
+
+ cfg := CLIConfig{
+ L1GethURL: L1GethURL,
+ L2OpGethURL: L2OpGethURL,
+ L2OpNodeURL: L2OpNodeURL,
+ EventBlockRange: FAULTPROOF_WITHDRAWAL_MON_EVENT_BLOCK_RANGE,
+ StartingL1BlockHeight: FAULTPROOF_WITHDRAWAL_MON_START_BLOCK_HEIGHT,
+ OptimismPortalAddress: common.HexToAddress(FAULTPROOF_WITHDRAWAL_MON_OPTIMISM_PORTAL),
+ }
+
+ clicfg := oplog.DefaultCLIConfig()
+ output_writer := io.Discard // discard log output during tests to avoid pollution of the standard output
+ log := oplog.NewLogger(output_writer, clicfg)
+
+ metricsRegistry := opmetrics.NewRegistry()
+ monitor, err := NewMonitor(ctx, log, opmetrics.With(metricsRegistry), cfg)
+ if err != nil {
+ panic(err)
+ }
+ return monitor
+}
+
+// TestSingleRunMainnet tests a single execution of the monitor's Run method.
+// It verifies that the state updates correctly after running.
+func TestSingleRunMainnet(t *testing.T) {
+ test_monitor := NewTestMonitorMainnet()
+
+ initialBlock := test_monitor.state.nextL1Height
+ blockIncrement := test_monitor.maxBlockRange
+ finalBlock := initialBlock + blockIncrement
+
+ test_monitor.Run(test_monitor.ctx)
+
+ require.Equal(t, finalBlock, test_monitor.state.nextL1Height)
+ require.Equal(t, uint64(0), test_monitor.state.withdrawalsProcessed)
+ require.Equal(t, uint64(0), test_monitor.state.eventsProcessed)
+ require.Equal(t, uint64(0), test_monitor.state.numberOfPotentialAttackOnInProgressGames)
+ require.Equal(t, uint64(0), test_monitor.state.numberOfPotentialAttacksOnDefenderWinsGames)
+ require.Equal(t, uint64(0), test_monitor.state.numberOfSuspiciousEventsOnChallengerWinsGames)
+
+ require.Equal(t, test_monitor.state.numberOfPotentialAttackOnInProgressGames, uint64(len(test_monitor.state.potentialAttackOnInProgressGames)))
+ require.Equal(t, test_monitor.state.numberOfPotentialAttacksOnDefenderWinsGames, uint64(len(test_monitor.state.potentialAttackOnDefenderWinsGames)))
+ require.Equal(t, test_monitor.state.numberOfSuspiciousEventsOnChallengerWinsGames, uint64(test_monitor.state.suspiciousEventsOnChallengerWinsGames.Len()))
+
+}
+
+// TestRun5Cycle1000BlocksMainnet tests multiple executions of the monitor's Run method over several cycles.
+// It verifies that the state updates correctly after each cycle.
+func TestRun5Cycle1000BlocksMainnet(t *testing.T) {
+ test_monitor := NewTestMonitorMainnet()
+
+ maxCycle := uint64(5)
+ initialBlock := test_monitor.state.nextL1Height
+ blockIncrement := test_monitor.maxBlockRange
+
+ for cycle := uint64(1); cycle <= maxCycle; cycle++ {
+ test_monitor.Run(test_monitor.ctx)
+ }
+
+ initialL1HeightGaugeValue, _ := GetGaugeValue(test_monitor.metrics.InitialL1HeightGauge)
+ nextL1HeightGaugeValue, _ := GetGaugeValue(test_monitor.metrics.NextL1HeightGauge)
+
+ withdrawalsProcessedCounterValue, _ := GetCounterValue(test_monitor.metrics.WithdrawalsProcessedCounter)
+ eventsProcessedCounterValue, _ := GetCounterValue(test_monitor.metrics.EventsProcessedCounter)
+
+ nodeConnectionFailuresCounterValue, _ := GetCounterValue(test_monitor.metrics.NodeConnectionFailuresCounter)
+
+ expected_end_block := blockIncrement*maxCycle + initialBlock
+ require.Equal(t, uint64(initialBlock), uint64(initialL1HeightGaugeValue))
+ require.Equal(t, uint64(expected_end_block), uint64(nextL1HeightGaugeValue))
+
+ require.Equal(t, uint64(0), uint64(eventsProcessedCounterValue))
+ require.Equal(t, uint64(0), uint64(withdrawalsProcessedCounterValue))
+ require.Equal(t, uint64(0), uint64(nodeConnectionFailuresCounterValue))
+
+ require.Equal(t, uint64(0), test_monitor.metrics.previousEventsProcessed)
+ require.Equal(t, uint64(0), test_monitor.metrics.previousWithdrawalsProcessed)
+
+}
+
+func TestRunSingleBlocksMainnet(t *testing.T) {
+ test_monitor := NewTestMonitorMainnet()
+
+ maxCycle := 1
+ initialBlock := test_monitor.state.nextL1Height
+ blockIncrement := test_monitor.maxBlockRange
+ finalBlock := initialBlock + blockIncrement
+
+ for cycle := 1; cycle <= maxCycle; cycle++ {
+ test_monitor.Run(test_monitor.ctx)
+ }
+
+ require.Equal(t, test_monitor.state.nextL1Height, finalBlock)
+ require.Equal(t, uint64(0), test_monitor.state.withdrawalsProcessed)
+ require.Equal(t, uint64(0), test_monitor.state.eventsProcessed)
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnDefenderWinsGames))
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnInProgressGames))
+ require.Equal(t, 0, test_monitor.state.suspiciousEventsOnChallengerWinsGames.Len())
+}
+
+func TestInvalidWithdrawalsOnMainnet(t *testing.T) {
+ test_monitor := NewTestMonitorMainnet()
+
+ // On mainnet for OP OptimismPortal, the block number 20873192 is known to have only 1 event
+ start := uint64(20873192)
+ stop := uint64(20873193)
+ newEvents, err := test_monitor.withdrawalValidator.GetEnrichedWithdrawalsEvents(start, &stop)
+ require.NoError(t, err)
+ require.Equal(t, len(newEvents), 1)
+
+ event := newEvents[0]
+ require.NotNil(t, event)
+
+ // Expected event:
+ //{WithdrawalHash: 0x45fd4bbcf3386b1fdf75929345b9243c05cd7431a707e84c293b710d40220ebd, ProofSubmitter: 0x394400571C825Da37ca4D6780417DFB514141b1f}
+ require.Equal(t, event.Event.WithdrawalHash, [32]byte(common.HexToHash("0x45fd4bbcf3386b1fdf75929345b9243c05cd7431a707e84c293b710d40220ebd")))
+ require.Equal(t, event.Event.ProofSubmitter, common.HexToAddress("0x394400571C825Da37ca4D6780417DFB514141b1f"))
+
+ //Expected DisputeGameData:
+ // Game address: 0x52cE243d552369b11D6445Cd187F6393d3B42D4a
+ require.Equal(t, event.DisputeGame.DisputeGameData.ProxyAddress, common.HexToAddress("0x52cE243d552369b11D6445Cd187F6393d3B42D4a"))
+
+ // Expected Game root claim
+ // 0xbc1c5ba13b936c6c23b7c51d425f25a8c9444771e851b6790f817a6002a14a33
+ require.Equal(t, event.DisputeGame.DisputeGameData.RootClaim, [32]byte(common.HexToHash("0xbc1c5ba13b936c6c23b7c51d425f25a8c9444771e851b6790f817a6002a14a33")))
+
+ // Expected L2 block number 1276288764
+ require.Equal(t, event.DisputeGame.DisputeGameData.L2blockNumber, big.NewInt(1276288764))
+
+ isValid, err := test_monitor.withdrawalValidator.IsWithdrawalEventValid(&event)
+ require.EqualError(t, err, "game not enriched")
+ require.False(t, isValid)
+ err = test_monitor.withdrawalValidator.UpdateEnrichedWithdrawalEvent(&event)
+ require.NoError(t, err)
+ isValid, err = test_monitor.withdrawalValidator.IsWithdrawalEventValid(&event)
+ require.NoError(t, err)
+ require.False(t, isValid)
+
+}
diff --git a/op-monitorism/faultproof_withdrawals/monitor_test.go b/op-monitorism/faultproof_withdrawals/monitor_live_sepolia_test.go
similarity index 60%
rename from op-monitorism/faultproof_withdrawals/monitor_test.go
rename to op-monitorism/faultproof_withdrawals/monitor_live_sepolia_test.go
index 9c5f04ad..d9d712f7 100644
--- a/op-monitorism/faultproof_withdrawals/monitor_test.go
+++ b/op-monitorism/faultproof_withdrawals/monitor_live_sepolia_test.go
@@ -7,8 +7,6 @@ import (
"context"
"io"
"math/big"
- "os"
- "strconv"
"testing"
"github.com/ethereum-optimism/monitorism/op-monitorism/faultproof_withdrawals/validator"
@@ -19,43 +17,30 @@ import (
"github.com/stretchr/testify/require"
)
-// TestMain runs the tests in the package and exits with the appropriate exit code.
-func TestMain(m *testing.M) {
- exitVal := m.Run()
- os.Exit(exitVal)
-}
-
-// loadEnv loads environment variables from the specified .env file.
-func loadEnv(env string) error {
- return godotenv.Load(env)
-}
-
-// NewTestMonitor initializes and returns a new Monitor instance for testing.
+// NewTestMonitorSepolia initializes and returns a new Monitor instance for testing.
// It sets up the necessary environment variables and configurations required for the monitor.
-func NewTestMonitor() *Monitor {
- loadEnv(".env.op.sepolia")
- ctx := context.Background()
- L1GethURL := os.Getenv("FAULTPROOF_WITHDRAWAL_MON_L1_GETH_URL")
- L2OpNodeURL := os.Getenv("FAULTPROOF_WITHDRAWAL_MON_L2_OP_NODE_URL")
- L2OpGethURL := os.Getenv("FAULTPROOF_WITHDRAWAL_MON_L2_OP_GETH_URL")
- EventBlockRangeStr := os.Getenv("FAULTPROOF_WITHDRAWAL_MON_EVENT_BLOCK_RANGE")
- EventBlockRange, err := strconv.ParseUint(EventBlockRangeStr, 10, 64)
+func NewTestMonitorSepolia() *Monitor {
+ envmap, err := godotenv.Read(".env.op.sepolia")
if err != nil {
- panic(err)
- }
- StartingL1BlockHeightStr := os.Getenv("FAULTPROOF_WITHDRAWAL_MON_START_BLOCK_HEIGHT")
- StartingL1BlockHeight, err := strconv.ParseInt(StartingL1BlockHeightStr, 10, 64)
- if err != nil {
- panic(err)
+ panic("error")
}
+ ctx := context.Background()
+ L1GethURL := envmap["FAULTPROOF_WITHDRAWAL_MON_L1_GETH_URL"]
+ L2OpNodeURL := envmap["FAULTPROOF_WITHDRAWAL_MON_L2_OP_NODE_URL"]
+ L2OpGethURL := envmap["FAULTPROOF_WITHDRAWAL_MON_L2_OP_GETH_URL"]
+
+ FAULTPROOF_WITHDRAWAL_MON_OPTIMISM_PORTAL := "0x16Fc5058F25648194471939df75CF27A2fdC48BC"
+ FAULTPROOF_WITHDRAWAL_MON_EVENT_BLOCK_RANGE := uint64(1000)
+ FAULTPROOF_WITHDRAWAL_MON_START_BLOCK_HEIGHT := int64(6789100)
+
cfg := CLIConfig{
L1GethURL: L1GethURL,
L2OpGethURL: L2OpGethURL,
L2OpNodeURL: L2OpNodeURL,
- EventBlockRange: EventBlockRange,
- StartingL1BlockHeight: StartingL1BlockHeight,
- OptimismPortalAddress: common.HexToAddress(os.Getenv("FAULTPROOF_WITHDRAWAL_MON_OPTIMISM_PORTAL")),
+ EventBlockRange: FAULTPROOF_WITHDRAWAL_MON_EVENT_BLOCK_RANGE,
+ StartingL1BlockHeight: FAULTPROOF_WITHDRAWAL_MON_START_BLOCK_HEIGHT,
+ OptimismPortalAddress: common.HexToAddress(FAULTPROOF_WITHDRAWAL_MON_OPTIMISM_PORTAL),
}
clicfg := oplog.DefaultCLIConfig()
@@ -70,49 +55,46 @@ func NewTestMonitor() *Monitor {
return monitor
}
-// TestSingleRun tests a single execution of the monitor's Run method.
+// TestSingleRunSepolia tests a single execution of the monitor's Run method.
// It verifies that the state updates correctly after running.
-func TestSingleRun(t *testing.T) {
- test_monitor := NewTestMonitor()
+func TestSingleRunSepolia(t *testing.T) {
+ test_monitor := NewTestMonitorSepolia()
- initialBlock := uint64(5914813)
- blockIncrement := uint64(1000)
+ initialBlock := test_monitor.state.nextL1Height
+ blockIncrement := test_monitor.maxBlockRange
finalBlock := initialBlock + blockIncrement
- test_monitor.state.nextL1Height = initialBlock
- test_monitor.maxBlockRange = blockIncrement
- test_monitor.Run(test_monitor.ctx)
+ test_monitor.Run(context.Background())
require.Equal(t, test_monitor.state.nextL1Height, finalBlock)
- require.Equal(t, test_monitor.state.withdrawalsValidated, uint64(1))
- require.Equal(t, test_monitor.state.processedProvenWithdrawalsExtension1Events, uint64(1))
- require.Equal(t, test_monitor.state.numberOfDetectedForgery, uint64(0))
- require.Equal(t, len(test_monitor.state.forgeriesWithdrawalsEvents), 0)
- require.Equal(t, len(test_monitor.state.invalidProposalWithdrawalsEvents), 0)
+ require.Equal(t, uint64(1), test_monitor.state.withdrawalsProcessed)
+ require.Equal(t, uint64(1), test_monitor.state.eventsProcessed)
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnDefenderWinsGames))
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnInProgressGames))
+ require.Equal(t, 0, test_monitor.state.suspiciousEventsOnChallengerWinsGames.Len())
}
-// TestConsumeEvents tests the consumption of enriched withdrawal events.
+// TestConsumeEventsSepolia tests the consumption of enriched withdrawal events.
// It verifies that new events can be processed correctly.
-func TestConsumeEvents(t *testing.T) {
- test_monitor := NewTestMonitor()
+func TestConsumeEventsSepolia(t *testing.T) {
+ test_monitor := NewTestMonitorSepolia()
- initialBlock := uint64(5914813)
- blockIncrement := uint64(1000)
+ initialBlock := test_monitor.state.nextL1Height
+ blockIncrement := test_monitor.maxBlockRange
finalBlock := initialBlock + blockIncrement
- newEvents, err := test_monitor.withdrawalValidator.GetEnrichedWithdrawalsEvents(initialBlock, &finalBlock)
+ newEvents, err := test_monitor.withdrawalValidator.GetEnrichedWithdrawalsEventsMap(initialBlock, &finalBlock)
require.NoError(t, err)
- require.NotEqual(t, len(newEvents), 0)
+ require.NotEqual(t, 0, len(newEvents))
- newInvalidProposalWithdrawalsEvents, err := test_monitor.ConsumeEvents(newEvents)
+ err = test_monitor.ConsumeEvents(newEvents)
require.NoError(t, err)
- require.Equal(t, len(*newInvalidProposalWithdrawalsEvents), 0)
}
-// TestConsumeEventValid_DEFENDER_WINS tests the consumption of a valid event where the defender wins.
+// TestConsumeEventValid_DEFENDER_WINS_Sepolia tests the consumption of a valid event where the defender wins.
// It checks that the state updates correctly after processing the event.
-func TestConsumeEventValid_DEFENDER_WINS(t *testing.T) {
- test_monitor := NewTestMonitor()
+func TestConsumeEventValid_DEFENDER_WINS_Sepolia(t *testing.T) {
+ test_monitor := NewTestMonitorSepolia()
expectedRootClaim := common.HexToHash("0x763d50048ccdb85fded935ff88c9e6b2284fd981da8ed7ae892f36b8761f7597")
@@ -146,20 +128,23 @@ func TestConsumeEventValid_DEFENDER_WINS(t *testing.T) {
},
}
- consumedEvent, err := test_monitor.ConsumeEvent(validEvent)
+ eventsMap := map[common.Hash]validator.EnrichedProvenWithdrawalEvent{
+ validEvent.Event.WithdrawalHash: validEvent,
+ }
+ err := test_monitor.ConsumeEvents(eventsMap)
require.NoError(t, err)
- require.True(t, consumedEvent)
- require.Equal(t, test_monitor.state.withdrawalsValidated, uint64(1))
- require.Equal(t, test_monitor.state.processedProvenWithdrawalsExtension1Events, uint64(1))
- require.Equal(t, test_monitor.state.numberOfDetectedForgery, uint64(0))
- require.Equal(t, len(test_monitor.state.forgeriesWithdrawalsEvents), 0)
- require.Equal(t, len(test_monitor.state.invalidProposalWithdrawalsEvents), 0)
+ require.Equal(t, uint64(1), test_monitor.state.withdrawalsProcessed)
+ require.Equal(t, uint64(1), test_monitor.state.eventsProcessed)
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnDefenderWinsGames))
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnInProgressGames))
+ require.Equal(t, 0, test_monitor.state.suspiciousEventsOnChallengerWinsGames.Len())
+
}
-// TestConsumeEventValid_CHALLENGER_WINS tests the consumption of a valid event where the challenger wins.
+// TestConsumeEventValid_CHALLENGER_WINS_Sepolia tests the consumption of a valid event where the challenger wins.
// It checks that the state updates correctly after processing the event.
-func TestConsumeEventValid_CHALLENGER_WINS(t *testing.T) {
- test_monitor := NewTestMonitor()
+func TestConsumeEventValid_CHALLENGER_WINS_Sepolia(t *testing.T) {
+ test_monitor := NewTestMonitorSepolia()
expectedRootClaim := common.HexToHash("0x763d50048ccdb85fded935ff88c9e6b2284fd981da8ed7ae892f36b8761f7597")
rootClaim := common.HexToHash("0x763d50048ccdb85fded935ff88c9e6b2284fd981da8ed7ae892f36b8761f7596") // different root claim, last number is 6 instead of 7
@@ -194,20 +179,23 @@ func TestConsumeEventValid_CHALLENGER_WINS(t *testing.T) {
},
}
- consumedEvent, err := test_monitor.ConsumeEvent(event)
+ eventsMap := map[common.Hash]validator.EnrichedProvenWithdrawalEvent{
+ event.Event.WithdrawalHash: event,
+ }
+ err := test_monitor.ConsumeEvents(eventsMap)
require.NoError(t, err)
- require.True(t, consumedEvent)
- require.Equal(t, test_monitor.state.withdrawalsValidated, uint64(1))
- require.Equal(t, test_monitor.state.processedProvenWithdrawalsExtension1Events, uint64(1))
- require.Equal(t, test_monitor.state.numberOfDetectedForgery, uint64(0))
- require.Equal(t, len(test_monitor.state.forgeriesWithdrawalsEvents), 0)
- require.Equal(t, len(test_monitor.state.invalidProposalWithdrawalsEvents), 0)
+ require.Equal(t, uint64(1), test_monitor.state.withdrawalsProcessed)
+ require.Equal(t, uint64(1), test_monitor.state.eventsProcessed)
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnDefenderWinsGames))
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnInProgressGames))
+ require.Equal(t, 1, test_monitor.state.suspiciousEventsOnChallengerWinsGames.Len())
+
}
-// TestConsumeEventValid_Blacklisted tests the consumption of a valid event that is blacklisted.
+// TestConsumeEventValid_BlacklistedSepolia tests the consumption of a valid event that is blacklisted.
// It checks that the state updates correctly after processing the event.
-func TestConsumeEventValid_Blacklisted(t *testing.T) {
- test_monitor := NewTestMonitor()
+func TestConsumeEventValid_BlacklistedSepolia(t *testing.T) {
+ test_monitor := NewTestMonitorSepolia()
expectedRootClaim := common.HexToHash("0x763d50048ccdb85fded935ff88c9e6b2284fd981da8ed7ae892f36b8761f7597")
rootClaim := common.HexToHash("0x763d50048ccdb85fded935ff88c9e6b2284fd981da8ed7ae892f36b8761f7596") // different root claim, last number is 6 instead of 7
@@ -242,20 +230,23 @@ func TestConsumeEventValid_Blacklisted(t *testing.T) {
},
}
- consumedEvent, err := test_monitor.ConsumeEvent(event)
+ eventsMap := map[common.Hash]validator.EnrichedProvenWithdrawalEvent{
+ event.Event.WithdrawalHash: event,
+ }
+ err := test_monitor.ConsumeEvents(eventsMap)
require.NoError(t, err)
- require.True(t, consumedEvent)
- require.Equal(t, test_monitor.state.withdrawalsValidated, uint64(1))
- require.Equal(t, test_monitor.state.processedProvenWithdrawalsExtension1Events, uint64(1))
- require.Equal(t, test_monitor.state.numberOfDetectedForgery, uint64(0))
- require.Equal(t, len(test_monitor.state.forgeriesWithdrawalsEvents), 0)
- require.Equal(t, len(test_monitor.state.invalidProposalWithdrawalsEvents), 0)
+ require.Equal(t, uint64(1), test_monitor.state.withdrawalsProcessed)
+ require.Equal(t, uint64(1), test_monitor.state.eventsProcessed)
+ require.Equal(t, 1, len(test_monitor.state.potentialAttackOnDefenderWinsGames))
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnInProgressGames))
+ require.Equal(t, 0, test_monitor.state.suspiciousEventsOnChallengerWinsGames.Len())
+
}
-// TestConsumeEventForgery1 tests the consumption of an event that indicates a forgery.
+// TestConsumeEventForgery1Sepolia tests the consumption of an event that indicates a forgery.
// It checks that the state updates correctly after processing the event.
-func TestConsumeEventForgery1(t *testing.T) {
- test_monitor := NewTestMonitor()
+func TestConsumeEventForgery1Sepolia(t *testing.T) {
+ test_monitor := NewTestMonitorSepolia()
expectedRootClaim := common.HexToHash("0x763d50048ccdb85fded935ff88c9e6b2284fd981da8ed7ae892f36b8761f7597")
@@ -289,20 +280,22 @@ func TestConsumeEventForgery1(t *testing.T) {
},
}
- consumedEvent, err := test_monitor.ConsumeEvent(validEvent)
+ eventsMap := map[common.Hash]validator.EnrichedProvenWithdrawalEvent{
+ validEvent.Event.WithdrawalHash: validEvent,
+ }
+ err := test_monitor.ConsumeEvents(eventsMap)
require.NoError(t, err)
- require.True(t, consumedEvent)
- require.Equal(t, test_monitor.state.withdrawalsValidated, uint64(0))
- require.Equal(t, test_monitor.state.processedProvenWithdrawalsExtension1Events, uint64(1))
- require.Equal(t, test_monitor.state.numberOfDetectedForgery, uint64(1))
- require.Equal(t, len(test_monitor.state.forgeriesWithdrawalsEvents), 1)
- require.Equal(t, len(test_monitor.state.invalidProposalWithdrawalsEvents), 0)
+ require.Equal(t, uint64(1), test_monitor.state.withdrawalsProcessed)
+ require.Equal(t, uint64(1), test_monitor.state.eventsProcessed)
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnDefenderWinsGames))
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnInProgressGames))
+ require.Equal(t, 0, test_monitor.state.suspiciousEventsOnChallengerWinsGames.Len())
}
-// TestConsumeEventForgery2 tests the consumption of another event that indicates a forgery.
+// TestConsumeEventForgery2Sepolia tests the consumption of another event that indicates a forgery.
// It checks that the state updates correctly after processing the event.
-func TestConsumeEventForgery2(t *testing.T) {
- test_monitor := NewTestMonitor()
+func TestConsumeEventForgery2Sepolia(t *testing.T) {
+ test_monitor := NewTestMonitorSepolia()
expectedRootClaim := common.HexToHash("0x763d50048ccdb85fded935ff88c9e6b2284fd981da8ed7ae892f36b8761f7597")
rootClaim := common.HexToHash("0x763d50048ccdb85fded935ff88c9e6b2284fd981da8ed7ae892f36b8761f7596") // different root claim, last number is 6 instead of 7
@@ -337,12 +330,15 @@ func TestConsumeEventForgery2(t *testing.T) {
},
}
- consumedEvent, err := test_monitor.ConsumeEvent(event)
+ eventsMap := map[common.Hash]validator.EnrichedProvenWithdrawalEvent{
+ event.Event.WithdrawalHash: event,
+ }
+ err := test_monitor.ConsumeEvents(eventsMap)
require.NoError(t, err)
- require.True(t, consumedEvent)
- require.Equal(t, test_monitor.state.withdrawalsValidated, uint64(0))
- require.Equal(t, test_monitor.state.processedProvenWithdrawalsExtension1Events, uint64(1))
- require.Equal(t, test_monitor.state.numberOfDetectedForgery, uint64(1))
- require.Equal(t, len(test_monitor.state.forgeriesWithdrawalsEvents), 1)
- require.Equal(t, len(test_monitor.state.invalidProposalWithdrawalsEvents), 0)
+ require.Equal(t, uint64(1), test_monitor.state.withdrawalsProcessed)
+ require.Equal(t, uint64(1), test_monitor.state.eventsProcessed)
+ require.Equal(t, 1, len(test_monitor.state.potentialAttackOnDefenderWinsGames))
+ require.Equal(t, 0, len(test_monitor.state.potentialAttackOnInProgressGames))
+ require.Equal(t, 0, test_monitor.state.suspiciousEventsOnChallengerWinsGames.Len())
+
}
diff --git a/op-monitorism/faultproof_withdrawals/state.go b/op-monitorism/faultproof_withdrawals/state.go
index bd13d90f..2b4bd268 100644
--- a/op-monitorism/faultproof_withdrawals/state.go
+++ b/op-monitorism/faultproof_withdrawals/state.go
@@ -8,66 +8,156 @@ import (
"github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
+ lru "github.com/hashicorp/golang-lru"
"github.com/prometheus/client_golang/prometheus"
+ dto "github.com/prometheus/client_model/go"
+)
+
+const (
+ suspiciousEventsOnChallengerWinsGamesCacheSize = 1000
)
type State struct {
+ logger log.Logger
nextL1Height uint64
latestL1Height uint64
initialL1Height uint64
+ latestL2Height uint64
- processedProvenWithdrawalsExtension1Events uint64
-
- numberOfDetectedForgery uint64
- numberOfInvalidWithdrawals uint64
- withdrawalsValidated uint64
+ eventsProcessed uint64 // This counts the events that we have taken care of, and we are aware of.
+ withdrawalsProcessed uint64 // This counts the withdrawals that have being completed and processed and we are not tracking anymore. eventProcessed >= withdrawalsProcessed. withdrawalsProcessed does not includes potential attacks with games in progress.
nodeConnectionFailures uint64
- forgeriesWithdrawalsEvents []validator.EnrichedProvenWithdrawalEvent
- invalidProposalWithdrawalsEvents []validator.EnrichedProvenWithdrawalEvent
+ // possible attacks detected
+
+ // Forgeries detected on games that are already resolved
+ potentialAttackOnDefenderWinsGames map[common.Hash]validator.EnrichedProvenWithdrawalEvent
+ numberOfPotentialAttacksOnDefenderWinsGames uint64
+
+ // Forgeries detected on games that are still in progress
+ // Faultproof system should make them invalid
+ potentialAttackOnInProgressGames map[common.Hash]validator.EnrichedProvenWithdrawalEvent
+ numberOfPotentialAttackOnInProgressGames uint64
+
+ // Suspicious events
+ // It is unlikely that someone is going to use a withdrawal hash on a games that resolved with ChallengerWins. If this happens, maybe there is a bug somewhere in the UI used by the users or it is a malicious attack that failed
+ suspiciousEventsOnChallengerWinsGames *lru.Cache
+ numberOfSuspiciousEventsOnChallengerWinsGames uint64
}
-func NewState(log log.Logger, nextL1Height uint64, latestL1Height uint64) (*State, error) {
+func NewState(logger log.Logger, nextL1Height uint64, latestL1Height uint64, latestL2Height uint64) (*State, error) {
if nextL1Height > latestL1Height {
- log.Info("nextL1Height is greater than latestL1Height, starting from latest", "nextL1Height", nextL1Height, "latestL1Height", latestL1Height)
+ logger.Info("nextL1Height is greater than latestL1Height, starting from latest", "nextL1Height", nextL1Height, "latestL1Height", latestL1Height)
nextL1Height = latestL1Height
}
ret := State{
- processedProvenWithdrawalsExtension1Events: 0,
- nextL1Height: nextL1Height,
- latestL1Height: latestL1Height,
- numberOfDetectedForgery: 0,
- withdrawalsValidated: 0,
- nodeConnectionFailures: 0,
- numberOfInvalidWithdrawals: 0,
- initialL1Height: nextL1Height,
+ potentialAttackOnDefenderWinsGames: make(map[common.Hash]validator.EnrichedProvenWithdrawalEvent),
+ numberOfPotentialAttacksOnDefenderWinsGames: 0,
+ suspiciousEventsOnChallengerWinsGames: func() *lru.Cache {
+ cache, err := lru.New(suspiciousEventsOnChallengerWinsGamesCacheSize)
+ if err != nil {
+ logger.Error("Failed to create LRU cache", "error", err)
+ return nil
+ }
+ return cache
+ }(),
+ numberOfSuspiciousEventsOnChallengerWinsGames: 0,
+
+ potentialAttackOnInProgressGames: make(map[common.Hash]validator.EnrichedProvenWithdrawalEvent),
+ numberOfPotentialAttackOnInProgressGames: 0,
+
+ eventsProcessed: 0,
+
+ withdrawalsProcessed: 0,
+ nodeConnectionFailures: 0,
+
+ nextL1Height: nextL1Height,
+ latestL1Height: latestL1Height,
+ initialL1Height: nextL1Height,
+ latestL2Height: latestL2Height,
+ logger: logger,
}
return &ret, nil
}
-func (s *State) LogState(log log.Logger) {
+func (s *State) LogState() {
blockToProcess, syncPercentage := s.GetPercentages()
- log.Info("STATE:",
- "withdrawalsValidated", fmt.Sprintf("%d", s.withdrawalsValidated),
+ s.logger.Info("STATE:",
+ "withdrawalsProcessed", fmt.Sprintf("%d", s.withdrawalsProcessed),
+
"initialL1Height", fmt.Sprintf("%d", s.initialL1Height),
"nextL1Height", fmt.Sprintf("%d", s.nextL1Height),
"latestL1Height", fmt.Sprintf("%d", s.latestL1Height),
+ "latestL2Height", fmt.Sprintf("%d", s.latestL2Height),
"blockToProcess", fmt.Sprintf("%d", blockToProcess),
"syncPercentage", fmt.Sprintf("%d%%", syncPercentage),
- "processedProvenWithdrawalsExtension1Events", fmt.Sprintf("%d", s.processedProvenWithdrawalsExtension1Events),
- "numberOfDetectedForgery", fmt.Sprintf("%d", s.numberOfDetectedForgery),
- "numberOfInvalidWithdrawals", fmt.Sprintf("%d", s.numberOfInvalidWithdrawals),
+
+ "eventsProcessed", fmt.Sprintf("%d", s.eventsProcessed),
"nodeConnectionFailures", fmt.Sprintf("%d", s.nodeConnectionFailures),
- "forgeriesWithdrawalsEvents", fmt.Sprintf("%d", len(s.forgeriesWithdrawalsEvents)),
- "invalidProposalWithdrawalsEvents", fmt.Sprintf("%d", len(s.invalidProposalWithdrawalsEvents)),
+
+ "potentialAttackOnDefenderWinsGames", fmt.Sprintf("%d", s.numberOfPotentialAttacksOnDefenderWinsGames),
+ "potentialAttackOnInProgressGames", fmt.Sprintf("%d", s.numberOfPotentialAttackOnInProgressGames),
+ "suspiciousEventsOnChallengerWinsGames", fmt.Sprintf("%d", s.numberOfSuspiciousEventsOnChallengerWinsGames),
)
}
+func (s *State) IncrementWithdrawalsValidated(enrichedWithdrawalEvent validator.EnrichedProvenWithdrawalEvent) {
+ s.logger.Info("STATE WITHDRAWAL: valid", "TxHash", fmt.Sprintf("%v", enrichedWithdrawalEvent.Event.Raw.TxHash), "enrichedWithdrawalEvent", &enrichedWithdrawalEvent)
+ s.withdrawalsProcessed++
+}
+
+func (s *State) IncrementPotentialAttackOnDefenderWinsGames(enrichedWithdrawalEvent validator.EnrichedProvenWithdrawalEvent) {
+ key := enrichedWithdrawalEvent.Event.Raw.TxHash
+
+ s.logger.Error("STATE WITHDRAWAL: is NOT valid, forgery detected", "TxHash", fmt.Sprintf("%v", enrichedWithdrawalEvent.Event.Raw.TxHash), "enrichedWithdrawalEvent", &enrichedWithdrawalEvent)
+ s.potentialAttackOnDefenderWinsGames[key] = enrichedWithdrawalEvent
+ s.numberOfPotentialAttacksOnDefenderWinsGames++
+
+ if _, ok := s.potentialAttackOnInProgressGames[key]; ok {
+ s.logger.Error("STATE WITHDRAWAL: added to potential attacks. Removing from inProgress", "TxHash", fmt.Sprintf("%v", enrichedWithdrawalEvent.Event.Raw.TxHash), "enrichedWithdrawalEvent", &enrichedWithdrawalEvent)
+ delete(s.potentialAttackOnInProgressGames, key)
+ s.numberOfPotentialAttackOnInProgressGames--
+ }
+
+ s.withdrawalsProcessed++
+}
+
+func (s *State) IncrementPotentialAttackOnInProgressGames(enrichedWithdrawalEvent validator.EnrichedProvenWithdrawalEvent) {
+ key := enrichedWithdrawalEvent.Event.Raw.TxHash
+ // check if key already exists
+ if _, ok := s.potentialAttackOnInProgressGames[key]; ok {
+ s.logger.Error("STATE WITHDRAWAL:is NOT valid, game is still in progress", "TxHash", fmt.Sprintf("%v", enrichedWithdrawalEvent.Event.Raw.TxHash), "enrichedWithdrawalEvent", &enrichedWithdrawalEvent)
+ } else {
+ s.logger.Error("STATE WITHDRAWAL:is NOT valid, game is still in progress. New game found In Progress", "TxHash", fmt.Sprintf("%v", enrichedWithdrawalEvent.Event.Raw.TxHash), "enrichedWithdrawalEvent", &enrichedWithdrawalEvent)
+ s.numberOfPotentialAttackOnInProgressGames++
+ }
+
+ // eventually update the map with the new enrichedWithdrawalEvent
+ s.potentialAttackOnInProgressGames[key] = enrichedWithdrawalEvent
+}
+
+func (s *State) IncrementSuspiciousEventsOnChallengerWinsGames(enrichedWithdrawalEvent validator.EnrichedProvenWithdrawalEvent) {
+ key := enrichedWithdrawalEvent.Event.Raw.TxHash
+
+ s.logger.Error("STATE WITHDRAWAL:is NOT valid, but the game is correctly resolved", "TxHash", fmt.Sprintf("%v", enrichedWithdrawalEvent.Event.Raw.TxHash), "enrichedWithdrawalEvent", &enrichedWithdrawalEvent)
+ s.suspiciousEventsOnChallengerWinsGames.Add(key, enrichedWithdrawalEvent)
+ s.numberOfSuspiciousEventsOnChallengerWinsGames++
+
+ if _, ok := s.potentialAttackOnInProgressGames[key]; ok {
+ s.logger.Error("STATE WITHDRAWAL: added to suspicious attacks. Removing from inProgress", "TxHash", fmt.Sprintf("%v", enrichedWithdrawalEvent.Event.Raw.TxHash), "enrichedWithdrawalEvent", &enrichedWithdrawalEvent)
+ delete(s.potentialAttackOnInProgressGames, key)
+ s.numberOfPotentialAttackOnInProgressGames--
+ }
+
+ s.withdrawalsProcessed++
+
+}
+
func (s *State) GetPercentages() (uint64, uint64) {
blockToProcess := s.latestL1Height - s.nextL1Height
divisor := float64(s.latestL1Height) * 100
@@ -80,23 +170,99 @@ func (s *State) GetPercentages() (uint64, uint64) {
}
type Metrics struct {
- InitialL1HeightGauge prometheus.Gauge
- NextL1HeightGauge prometheus.Gauge
- LatestL1HeightGauge prometheus.Gauge
- ProcessedProvenWithdrawalsEventsExtensions1Counter prometheus.Counter
- NumberOfDetectedForgeryGauge prometheus.Gauge
- NumberOfInvalidWithdrawalsGauge prometheus.Gauge
- WithdrawalsValidatedCounter prometheus.Counter
- NodeConnectionFailuresCounter prometheus.Counter
- ForgeriesWithdrawalsEventsGauge prometheus.Gauge
- InvalidProposalWithdrawalsEventsGauge prometheus.Gauge
- ForgeriesWithdrawalsEventsGaugeVec *prometheus.GaugeVec
- InvalidProposalWithdrawalsEventsGaugeVec *prometheus.GaugeVec
+ InitialL1HeightGauge prometheus.Gauge
+ NextL1HeightGauge prometheus.Gauge
+ LatestL1HeightGauge prometheus.Gauge
+ LatestL2HeightGauge prometheus.Gauge
+
+ EventsProcessedCounter prometheus.Counter
+ WithdrawalsProcessedCounter prometheus.Counter
+
+ NodeConnectionFailuresCounter prometheus.Counter
+
+ PotentialAttackOnDefenderWinsGamesGauge prometheus.Gauge
+ PotentialAttackOnInProgressGamesGauge prometheus.Gauge
+ SuspiciousEventsOnChallengerWinsGamesGauge prometheus.Gauge
+
+ PotentialAttackOnDefenderWinsGamesGaugeVec *prometheus.GaugeVec
+ PotentialAttackOnInProgressGamesGaugeVec *prometheus.GaugeVec
+ SuspiciousEventsOnChallengerWinsGamesGaugeVec *prometheus.GaugeVec
// Previous values for counters
- previousProcessedProvenWithdrawalsExtension1Events uint64
- previousWithdrawalsValidated uint64
- previousNodeConnectionFailures uint64
+ previousEventsProcessed uint64
+ previousWithdrawalsProcessed uint64
+ previousNodeConnectionFailures uint64
+}
+
+func (m *Metrics) String() string {
+ initialL1HeightGaugeValue, _ := GetGaugeValue(m.InitialL1HeightGauge)
+ nextL1HeightGaugeValue, _ := GetGaugeValue(m.NextL1HeightGauge)
+ latestL1HeightGaugeValue, _ := GetGaugeValue(m.LatestL1HeightGauge)
+ latestL2HeightGaugeValue, _ := GetGaugeValue(m.LatestL2HeightGauge)
+
+ withdrawalsProcessedCounterValue, _ := GetCounterValue(m.WithdrawalsProcessedCounter)
+ eventsProcessedCounterValue, _ := GetCounterValue(m.EventsProcessedCounter)
+
+ nodeConnectionFailuresCounterValue, _ := GetCounterValue(m.NodeConnectionFailuresCounter)
+
+ potentialAttackOnDefenderWinsGamesGaugeValue, _ := GetGaugeValue(m.PotentialAttackOnDefenderWinsGamesGauge)
+ potentialAttackOnInProgressGamesGaugeValue, _ := GetGaugeValue(m.PotentialAttackOnInProgressGamesGauge)
+
+ forgeriesWithdrawalsEventsGaugeVecValue, _ := GetGaugeVecValue(m.PotentialAttackOnDefenderWinsGamesGaugeVec, prometheus.Labels{})
+ invalidProposalWithdrawalsEventsGaugeVecValue, _ := GetGaugeVecValue(m.PotentialAttackOnInProgressGamesGaugeVec, prometheus.Labels{})
+
+ return fmt.Sprintf(
+ "InitialL1HeightGauge: %d\nNextL1HeightGauge: %d\nLatestL1HeightGauge: %d\n latestL2HeightGaugeValue: %d\n eventsProcessedCounterValue: %d\nwithdrawalsProcessedCounterValue: %d\nnodeConnectionFailuresCounterValue: %d\n potentialAttackOnDefenderWinsGamesGaugeValue: %d\n potentialAttackOnInProgressGamesGaugeValue: %d\n forgeriesWithdrawalsEventsGaugeVecValue: %d\n invalidProposalWithdrawalsEventsGaugeVecValue: %d\n previousEventsProcessed: %d\n previousWithdrawalsProcessed: %d\n previousNodeConnectionFailures: %d\n",
+ uint64(initialL1HeightGaugeValue),
+ uint64(nextL1HeightGaugeValue),
+ uint64(latestL1HeightGaugeValue),
+ uint64(latestL2HeightGaugeValue),
+ uint64(eventsProcessedCounterValue),
+ uint64(withdrawalsProcessedCounterValue),
+ uint64(nodeConnectionFailuresCounterValue),
+ uint64(potentialAttackOnDefenderWinsGamesGaugeValue),
+ uint64(potentialAttackOnInProgressGamesGaugeValue),
+ uint64(forgeriesWithdrawalsEventsGaugeVecValue),
+ uint64(invalidProposalWithdrawalsEventsGaugeVecValue),
+ m.previousEventsProcessed,
+ m.previousWithdrawalsProcessed,
+ m.previousNodeConnectionFailures,
+ )
+}
+
+// Generic function to get the value of any prometheus.Counter
+func GetCounterValue(counter prometheus.Counter) (float64, error) {
+ metric := &dto.Metric{}
+ err := counter.Write(metric)
+ if err != nil {
+ return 0, err
+ }
+ return metric.GetCounter().GetValue(), nil
+}
+
+// Generic function to get the value of any prometheus.Gauge
+func GetGaugeValue(gauge prometheus.Gauge) (float64, error) {
+ metric := &dto.Metric{}
+ err := gauge.Write(metric)
+ if err != nil {
+ return 0, err
+ }
+ return metric.GetGauge().GetValue(), nil
+}
+
+// Function to get the value of a specific Gauge within a GaugeVec
+func GetGaugeVecValue(gaugeVec *prometheus.GaugeVec, labels prometheus.Labels) (float64, error) {
+ gauge, err := gaugeVec.GetMetricWith(labels)
+ if err != nil {
+ return 0, err
+ }
+
+ metric := &dto.Metric{}
+ err = gauge.Write(metric)
+ if err != nil {
+ return 0, err
+ }
+ return metric.GetGauge().GetValue(), nil
}
func NewMetrics(m metrics.Factory) *Metrics {
@@ -116,56 +282,64 @@ func NewMetrics(m metrics.Factory) *Metrics {
Name: "latest_l1_height",
Help: "Latest L1 Height",
}),
- ProcessedProvenWithdrawalsEventsExtensions1Counter: m.NewCounter(prometheus.CounterOpts{
+ LatestL2HeightGauge: m.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
- Name: "processed_provenwithdrawalsextension1_events_total",
- Help: "Total number of processed provenwithdrawalsextension1 events",
+ Name: "latest_l2_height",
+ Help: "Latest L2 Height",
}),
- NumberOfDetectedForgeryGauge: m.NewGauge(prometheus.GaugeOpts{
+ EventsProcessedCounter: m.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
- Name: "number_of_detected_forgeries",
- Help: "Number of detected forgeries",
+ Name: "events_processed_total",
+ Help: "Total number of events processed",
}),
- NumberOfInvalidWithdrawalsGauge: m.NewGauge(prometheus.GaugeOpts{
+ WithdrawalsProcessedCounter: m.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
- Name: "number_of_invalid_withdrawals",
- Help: "Number of invalid withdrawals",
- }),
- WithdrawalsValidatedCounter: m.NewCounter(prometheus.CounterOpts{
- Namespace: MetricsNamespace,
- Name: "withdrawals_validated_total",
- Help: "Total number of withdrawals validated",
+ Name: "withdrawals_processed_total",
+ Help: "Total number of withdrawals processed",
}),
NodeConnectionFailuresCounter: m.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "node_connection_failures_total",
Help: "Total number of node connection failures",
}),
- ForgeriesWithdrawalsEventsGauge: m.NewGauge(prometheus.GaugeOpts{
+ PotentialAttackOnDefenderWinsGamesGauge: m.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
- Name: "forgeries_withdrawals_events_count",
- Help: "Number of forgeries withdrawals events",
+ Name: "potential_attack_on_defender_wins_games_count",
+ Help: "Number of potential attacks on defender wins games",
}),
- InvalidProposalWithdrawalsEventsGauge: m.NewGauge(prometheus.GaugeOpts{
+ PotentialAttackOnInProgressGamesGauge: m.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
- Name: "invalid_proposal_withdrawals_events_count",
- Help: "Number of invalid proposal withdrawals events",
+ Name: "potential_attack_on_in_progress_games_count",
+ Help: "Number of potential attacks on in progress games",
}),
- ForgeriesWithdrawalsEventsGaugeVec: m.NewGaugeVec(
+ SuspiciousEventsOnChallengerWinsGamesGauge: m.NewGauge(prometheus.GaugeOpts{
+ Namespace: MetricsNamespace,
+ Name: "suspicious_events_on_challenger_wins_games_count",
+ Help: "Number of suspicious events on challenger wins games",
+ }),
+ PotentialAttackOnDefenderWinsGamesGaugeVec: m.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: MetricsNamespace,
- Name: "forgeries_withdrawals_events_info",
- Help: "Information about forgeries withdrawals events.",
+ Name: "potential_attack_on_defender_wins_games_gauge_vec",
+ Help: "Information about potential attacks on defender wins games.",
},
- []string{"withdrawal_hash", "proof_submitter", "status", "blacklisted", "withdrawal_hash_present", "enriched", "event_block_number", "event_tx_hash", "event_index"},
+ []string{"withdrawal_hash", "proof_submitter", "status", "blacklisted", "withdrawal_hash_present", "enriched", "event_block_number", "event_tx_hash"},
),
- InvalidProposalWithdrawalsEventsGaugeVec: m.NewGaugeVec(
+ PotentialAttackOnInProgressGamesGaugeVec: m.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: MetricsNamespace,
- Name: "invalid_proposal_withdrawals_events_info",
- Help: "Information about invalid proposal withdrawals events.",
+ Name: "potential_attack_on_in_progress_games_gauge_vec",
+ Help: "Information about potential attacks on in progress games.",
},
- []string{"withdrawal_hash", "proof_submitter", "status", "blacklisted", "withdrawal_hash_present", "enriched", "event_block_number", "event_tx_hash", "event_index"},
+ []string{"withdrawal_hash", "proof_submitter", "status", "blacklisted", "withdrawal_hash_present", "enriched", "event_block_number", "event_tx_hash"},
+ ),
+ SuspiciousEventsOnChallengerWinsGamesGaugeVec: m.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Namespace: MetricsNamespace,
+ Name: "suspicious_events_on_challenger_wins_games_info",
+ Help: "Information about suspicious events on challenger wins games.",
+ },
+ []string{"withdrawal_hash", "proof_submitter", "status", "blacklisted", "withdrawal_hash_present", "enriched", "event_block_number", "event_tx_hash"},
),
}
@@ -178,26 +352,26 @@ func (m *Metrics) UpdateMetricsFromState(state *State) {
m.InitialL1HeightGauge.Set(float64(state.initialL1Height))
m.NextL1HeightGauge.Set(float64(state.nextL1Height))
m.LatestL1HeightGauge.Set(float64(state.latestL1Height))
+ m.LatestL2HeightGauge.Set(float64(state.latestL2Height))
- m.NumberOfDetectedForgeryGauge.Set(float64(state.numberOfDetectedForgery))
- m.NumberOfInvalidWithdrawalsGauge.Set(float64(state.numberOfInvalidWithdrawals))
- m.ForgeriesWithdrawalsEventsGauge.Set(float64(len(state.forgeriesWithdrawalsEvents)))
- m.InvalidProposalWithdrawalsEventsGauge.Set(float64(len(state.invalidProposalWithdrawalsEvents)))
+ m.PotentialAttackOnDefenderWinsGamesGauge.Set(float64(state.numberOfPotentialAttacksOnDefenderWinsGames))
+ m.PotentialAttackOnInProgressGamesGauge.Set(float64(state.numberOfPotentialAttackOnInProgressGames))
+ m.SuspiciousEventsOnChallengerWinsGamesGauge.Set(float64(state.numberOfSuspiciousEventsOnChallengerWinsGames))
// Update Counters by calculating deltas
// Processed Withdrawals
- processedWithdrawalsDelta := state.processedProvenWithdrawalsExtension1Events - m.previousProcessedProvenWithdrawalsExtension1Events
- if processedWithdrawalsDelta > 0 {
- m.ProcessedProvenWithdrawalsEventsExtensions1Counter.Add(float64(processedWithdrawalsDelta))
+ eventsProcessedDelta := state.eventsProcessed - m.previousEventsProcessed
+ if eventsProcessedDelta > 0 {
+ m.EventsProcessedCounter.Add(float64(eventsProcessedDelta))
}
- m.previousProcessedProvenWithdrawalsExtension1Events = state.processedProvenWithdrawalsExtension1Events
+ m.previousEventsProcessed = state.eventsProcessed
// Withdrawals Validated
- withdrawalsValidatedDelta := state.withdrawalsValidated - m.previousWithdrawalsValidated
- if withdrawalsValidatedDelta > 0 {
- m.WithdrawalsValidatedCounter.Add(float64(withdrawalsValidatedDelta))
+ withdrawalsProcessedDelta := state.withdrawalsProcessed - m.previousWithdrawalsProcessed
+ if withdrawalsProcessedDelta > 0 {
+ m.WithdrawalsProcessedCounter.Add(float64(withdrawalsProcessedDelta))
}
- m.previousWithdrawalsValidated = state.withdrawalsValidated
+ m.previousWithdrawalsProcessed = state.withdrawalsProcessed
// Node Connection Failures
nodeConnectionFailuresDelta := state.nodeConnectionFailures - m.previousNodeConnectionFailures
@@ -207,12 +381,12 @@ func (m *Metrics) UpdateMetricsFromState(state *State) {
m.previousNodeConnectionFailures = state.nodeConnectionFailures
// Update metrics for forgeries withdrawals events
- for index, event := range state.forgeriesWithdrawalsEvents {
+ for _, event := range state.potentialAttackOnDefenderWinsGames {
withdrawalHash := common.BytesToHash(event.Event.WithdrawalHash[:]).Hex()
proofSubmitter := event.Event.ProofSubmitter.String()
status := event.DisputeGame.DisputeGameData.Status.String()
- m.ForgeriesWithdrawalsEventsGaugeVec.WithLabelValues(
+ m.PotentialAttackOnDefenderWinsGamesGaugeVec.WithLabelValues(
withdrawalHash,
proofSubmitter,
status,
@@ -221,20 +395,19 @@ func (m *Metrics) UpdateMetricsFromState(state *State) {
fmt.Sprintf("%v", event.Enriched),
fmt.Sprintf("%v", event.Event.Raw.BlockNumber),
event.Event.Raw.TxHash.String(),
- fmt.Sprintf("%v", index),
).Set(1) // Set a value for existence
}
// Clear the previous values
- m.InvalidProposalWithdrawalsEventsGaugeVec.Reset()
+ m.PotentialAttackOnInProgressGamesGaugeVec.Reset()
// Update metrics for invalid proposal withdrawals events
- for index, event := range state.invalidProposalWithdrawalsEvents {
+ for _, event := range state.potentialAttackOnInProgressGames {
withdrawalHash := common.BytesToHash(event.Event.WithdrawalHash[:]).Hex()
proofSubmitter := event.Event.ProofSubmitter.String()
status := event.DisputeGame.DisputeGameData.Status.String()
- m.InvalidProposalWithdrawalsEventsGaugeVec.WithLabelValues(
+ m.PotentialAttackOnInProgressGamesGaugeVec.WithLabelValues(
withdrawalHash,
proofSubmitter,
status,
@@ -243,7 +416,32 @@ func (m *Metrics) UpdateMetricsFromState(state *State) {
fmt.Sprintf("%v", event.Enriched),
fmt.Sprintf("%v", event.Event.Raw.BlockNumber),
event.Event.Raw.TxHash.String(),
- fmt.Sprintf("%v", index),
).Set(1) // Set a value for existence
}
+
+ // Clear the previous values
+ m.SuspiciousEventsOnChallengerWinsGamesGaugeVec.Reset()
+ // Update metrics for invalid proposal withdrawals events
+ for _, key := range state.suspiciousEventsOnChallengerWinsGames.Keys() {
+ enrichedEvent, ok := state.suspiciousEventsOnChallengerWinsGames.Get(key)
+ if ok {
+ event := enrichedEvent.(validator.EnrichedProvenWithdrawalEvent)
+
+ withdrawalHash := common.BytesToHash(event.Event.WithdrawalHash[:]).Hex()
+ proofSubmitter := event.Event.ProofSubmitter.String()
+ status := event.DisputeGame.DisputeGameData.Status.String()
+
+ m.SuspiciousEventsOnChallengerWinsGamesGaugeVec.WithLabelValues(
+ withdrawalHash,
+ proofSubmitter,
+ status,
+ fmt.Sprintf("%v", event.Blacklisted),
+ fmt.Sprintf("%v", event.WithdrawalHashPresentOnL2),
+ fmt.Sprintf("%v", event.Enriched),
+ fmt.Sprintf("%v", event.Event.Raw.BlockNumber),
+ event.Event.Raw.TxHash.String(),
+ ).Set(1) // Set a value for existence
+ }
+ }
+
}
diff --git a/op-monitorism/faultproof_withdrawals/validator/fault_dispute_game_helper.go b/op-monitorism/faultproof_withdrawals/validator/fault_dispute_game_helper.go
index 92d8b867..1b559e1c 100644
--- a/op-monitorism/faultproof_withdrawals/validator/fault_dispute_game_helper.go
+++ b/op-monitorism/faultproof_withdrawals/validator/fault_dispute_game_helper.go
@@ -69,7 +69,7 @@ func (gs GameStatus) String() string {
// String provides a string representation of DisputeGameData.
func (d DisputeGameData) String() string {
- return fmt.Sprintf("DisputeGame[ disputeGameProxyAddress=%v rootClaim=%s l2blockNumber=%s l2ChainID=%s status=%v createdAt=%v resolvedAt=%v ]",
+ return fmt.Sprintf("DisputeGame[ disputeGameProxyAddress: %v rootClaim: %s l2blockNumber: %s l2ChainID: %s status: %v createdAt: %v resolvedAt: %v ]",
d.ProxyAddress,
common.BytesToHash(d.RootClaim[:]),
d.L2blockNumber.String(),
diff --git a/op-monitorism/faultproof_withdrawals/validator/op_node_helper.go b/op-monitorism/faultproof_withdrawals/validator/op_node_helper.go
index 67d63cb4..7fa0543c 100644
--- a/op-monitorism/faultproof_withdrawals/validator/op_node_helper.go
+++ b/op-monitorism/faultproof_withdrawals/validator/op_node_helper.go
@@ -17,55 +17,82 @@ import (
// OpNodeHelper assists in interacting with the op-node
type OpNodeHelper struct {
// objects
- l2OpNodeClient *ethclient.Client // The op-node (consensus) client.
- rpc_l2Client *rpc.Client // The RPC client for the L2 node.
- ctx context.Context // Context for managing cancellation and timeouts.
- l2OutputRootCache *lru.Cache // Cache for storing L2 output roots.
+ l2OpNodeClient *ethclient.Client // The op-node (consensus) client.
+ l2OpGethClient *ethclient.Client // The op-geth client.
+ rpc_l2Client *rpc.Client // The RPC client for the L2 node.
+ ctx context.Context // Context for managing cancellation and timeouts.
+ l2OutputRootCache *lru.Cache // Cache for storing L2 output roots.
+ LatestKnownL2BlockNumber uint64 // The latest known L2 block number.
}
const outputRootCacheSize = 1000 // Size of the output root cache.
// NewOpNodeHelper initializes a new OpNodeHelper.
// It creates a cache for storing output roots and binds to the L2 node client.
-func NewOpNodeHelper(ctx context.Context, l2OpNodeClient *ethclient.Client) (*OpNodeHelper, error) {
+func NewOpNodeHelper(ctx context.Context, l2OpNodeClient *ethclient.Client, l2OpGethClient *ethclient.Client) (*OpNodeHelper, error) {
l2OutputRootCache, err := lru.New(outputRootCacheSize)
if err != nil {
return nil, fmt.Errorf("failed to create cache: %w", err)
}
rpc_l2Client := l2OpNodeClient.Client()
- return &OpNodeHelper{
- l2OpNodeClient: l2OpNodeClient,
- rpc_l2Client: rpc_l2Client,
- ctx: ctx,
- l2OutputRootCache: l2OutputRootCache,
- }, nil
+ ret := OpNodeHelper{
+ l2OpNodeClient: l2OpNodeClient,
+ l2OpGethClient: l2OpGethClient,
+ rpc_l2Client: rpc_l2Client,
+ ctx: ctx,
+ l2OutputRootCache: l2OutputRootCache,
+ LatestKnownL2BlockNumber: 0,
+ }
+
+ //ignoring the return value as it is already stored in the struct by the method
+ latestBlockNumber, err := ret.GetLatestKnownL2BlockNumber()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get latest known L2 block number: %w", err)
+ }
+
+ ret.LatestKnownL2BlockNumber = latestBlockNumber
+ return &ret, nil
+
+}
+
+// get latest known L2 block number
+func (on *OpNodeHelper) GetLatestKnownL2BlockNumber() (uint64, error) {
+ LatestKnownL2BlockNumber, err := on.l2OpGethClient.BlockNumber(on.ctx)
+ if err != nil {
+ return 0, fmt.Errorf("failed to get latest known L2 block number: %w", err)
+ }
+ on.LatestKnownL2BlockNumber = LatestKnownL2BlockNumber
+ return LatestKnownL2BlockNumber, nil
}
// GetOutputRootFromTrustedL2Node retrieves the output root for a given L2 block number from a trusted L2 node.
-// It returns the output root as a bytes32 array.
+// It returns the output root as a Bytes32 array.
func (on *OpNodeHelper) GetOutputRootFromTrustedL2Node(l2blockNumber *big.Int) ([32]byte, error) {
ret, found := on.l2OutputRootCache.Get(l2blockNumber)
+
if !found {
var result OutputResponse
l2blockNumberHex := hexutil.EncodeBig(l2blockNumber)
err := on.rpc_l2Client.CallContext(on.ctx, &result, "optimism_outputAtBlock", l2blockNumberHex)
+ //check if error contains "failed to determine L2BlockRef of height"
if err != nil {
return [32]byte{}, fmt.Errorf("failed to get output at block for game block:%v : %w", l2blockNumberHex, err)
}
trustedRootProof, err := StringToBytes32(result.OutputRoot)
if err != nil {
- return [32]byte{}, fmt.Errorf("failed to convert output root to bytes32: %w", err)
+ return [32]byte{}, fmt.Errorf("failed to convert output root to Bytes32: %w", err)
}
- ret = trustedRootProof
+ ret = [32]byte(trustedRootProof)
+ on.l2OutputRootCache.Add(l2blockNumber, ret)
}
return ret.([32]byte), nil
}
// GetOutputRootFromCalculation retrieves the output root by calculating it from the given block number.
-// It returns the calculated output root as a bytes32 array.
+// It returns the calculated output root as a Bytes32 array.
func (on *OpNodeHelper) GetOutputRootFromCalculation(blockNumber *big.Int) ([32]byte, error) {
block, err := on.l2OpNodeClient.BlockByNumber(on.ctx, blockNumber)
if err != nil {
@@ -78,6 +105,6 @@ func (on *OpNodeHelper) GetOutputRootFromCalculation(blockNumber *big.Int) ([32]
return [32]byte{}, fmt.Errorf("failed to get proof: %w", err)
}
- outputRoot := eth.OutputRoot(ð.OutputV0{StateRoot: eth.Bytes32(block.Root()), MessagePasserStorageRoot: eth.Bytes32(proof.StorageHash), BlockHash: block.Hash()})
+ outputRoot := eth.OutputRoot(ð.OutputV0{StateRoot: [32]byte(block.Root()), MessagePasserStorageRoot: [32]byte(proof.StorageHash), BlockHash: block.Hash()})
return outputRoot, nil
}
diff --git a/op-monitorism/faultproof_withdrawals/validator/optimism_portal2_helper.go b/op-monitorism/faultproof_withdrawals/validator/optimism_portal2_helper.go
index ef784449..32f5484f 100644
--- a/op-monitorism/faultproof_withdrawals/validator/optimism_portal2_helper.go
+++ b/op-monitorism/faultproof_withdrawals/validator/optimism_portal2_helper.go
@@ -41,13 +41,13 @@ type OptimismPortal2Helper struct {
}
// String provides a string representation of WithdrawalProvenExtension1Event.
-func (e *WithdrawalProvenExtension1Event) String() string {
- return fmt.Sprintf("WithdrawalHash: %x, ProofSubmitter: %v, Raw: %v", e.WithdrawalHash, e.ProofSubmitter, e.Raw)
+func (e WithdrawalProvenExtension1Event) String() string {
+ return fmt.Sprintf("WithdrawalHash: %s, ProofSubmitter: %v, Raw: %v", common.BytesToHash(e.WithdrawalHash[:]), e.ProofSubmitter, e.Raw)
}
// String provides a string representation of WithdrawalProvenEvent.
-func (e *WithdrawalProvenEvent) String() string {
- return fmt.Sprintf("WithdrawalHash: %x, Raw: %v", e.WithdrawalHash, e.Raw)
+func (e WithdrawalProvenEvent) String() string {
+ return fmt.Sprintf("WithdrawalHash: %s, Raw: %v", common.BytesToHash(e.WithdrawalHash[:]), e.Raw)
}
// String provides a string representation of SubmittedProofData.
@@ -126,9 +126,9 @@ func (op *OptimismPortal2Helper) GetProvenWithdrawalsEvents(start uint64, end *u
return events, nil
}
-// GetProvenWithdrawalsExtension1EventsIterartor creates an iterator for proven withdrawal extension 1 events within the specified block range.
+// GetProvenWithdrawalsExtension1EventsIterator creates an iterator for proven withdrawal extension 1 events within the specified block range.
// It returns the iterator along with any error encountered.
-func (op *OptimismPortal2Helper) GetProvenWithdrawalsExtension1EventsIterartor(start uint64, end *uint64) (*l1.OptimismPortal2WithdrawalProvenExtension1Iterator, error) {
+func (op *OptimismPortal2Helper) GetProvenWithdrawalsExtension1EventsIterator(start uint64, end *uint64) (*l1.OptimismPortal2WithdrawalProvenExtension1Iterator, error) {
filterOpts := &bind.FilterOpts{Context: op.ctx, Start: start, End: end}
iterator, err := op.optimismPortal2.FilterWithdrawalProvenExtension1(filterOpts, nil, nil)
if err != nil {
@@ -141,7 +141,7 @@ func (op *OptimismPortal2Helper) GetProvenWithdrawalsExtension1EventsIterartor(s
// GetProvenWithdrawalsExtension1Events retrieves proven withdrawal extension 1 events within the specified block range.
// It returns a slice of WithdrawalProvenExtension1Event along with any error encountered.
func (op *OptimismPortal2Helper) GetProvenWithdrawalsExtension1Events(start uint64, end *uint64) ([]WithdrawalProvenExtension1Event, error) {
- iterator, err := op.GetProvenWithdrawalsExtension1EventsIterartor(start, end)
+ iterator, err := op.GetProvenWithdrawalsExtension1EventsIterator(start, end)
if err != nil {
return nil, fmt.Errorf("failed to get proven withdrawals extension1 iterator error:%w", err)
}
diff --git a/op-monitorism/faultproof_withdrawals/validator/proven_withdrawal_validator.go b/op-monitorism/faultproof_withdrawals/validator/proven_withdrawal_validator.go
index e7c21c1f..f20e235c 100644
--- a/op-monitorism/faultproof_withdrawals/validator/proven_withdrawal_validator.go
+++ b/op-monitorism/faultproof_withdrawals/validator/proven_withdrawal_validator.go
@@ -45,7 +45,7 @@ func (e *EnrichedProvenWithdrawalEvent) String() string {
return fmt.Sprintf("Event: %v, DisputeGame: %v, ExpectedRootClaim: %s, Blacklisted: %v, withdrawalHashPresentOnL2: %v, Enriched: %v",
e.Event,
e.DisputeGame,
- common.BytesToHash(e.ExpectedRootClaim[:]),
+ common.Bytes2Hex(e.ExpectedRootClaim[:]),
e.Blacklisted,
e.WithdrawalHashPresentOnL2,
e.Enriched)
@@ -74,7 +74,7 @@ func NewWithdrawalValidator(ctx context.Context, l1GethClient *ethclient.Client,
return nil, fmt.Errorf("failed to create l2 to l1 message passer helper: %w", err)
}
- l2NodeHelper, err := NewOpNodeHelper(ctx, l2OpNodeClient)
+ l2NodeHelper, err := NewOpNodeHelper(ctx, l2OpNodeClient, l2OpGethClient)
if err != nil {
return nil, fmt.Errorf("failed to create l2 node helper: %w", err)
}
@@ -112,11 +112,20 @@ func (wv *ProvenWithdrawalValidator) UpdateEnrichedWithdrawalEvent(event *Enrich
// Check if the game root claim is valid on L2 only if not confirmed already that it is on L2
if !event.Enriched {
- trustedRootClaim, err := wv.l2NodeHelper.GetOutputRootFromTrustedL2Node(event.DisputeGame.DisputeGameData.L2blockNumber)
+ latest_known_l2_block, err := wv.l2NodeHelper.GetLatestKnownL2BlockNumber()
if err != nil {
- return fmt.Errorf("failed to get trustedRootClaim from Op-node: %w", err)
+ return fmt.Errorf("failed to get latest known L2 block number: %w", err)
}
- event.ExpectedRootClaim = trustedRootClaim
+ if latest_known_l2_block >= event.DisputeGame.DisputeGameData.L2blockNumber.Uint64() {
+ trustedRootClaim, err := wv.l2NodeHelper.GetOutputRootFromTrustedL2Node(event.DisputeGame.DisputeGameData.L2blockNumber)
+ if err != nil {
+ return fmt.Errorf("failed to get trustedRootClaim from Op-node: %w", err)
+ }
+ event.ExpectedRootClaim = trustedRootClaim
+ } else {
+ event.ExpectedRootClaim = [32]byte{}
+ }
+
}
// Check if the withdrawal exists on L2 only if not confirmed already that it is on L2
@@ -194,11 +203,43 @@ func (wv *ProvenWithdrawalValidator) GetEnrichedWithdrawalsEvents(start uint64,
return enrichedProvenWithdrawalEvents, nil
}
+// GetEnrichedWithdrawalsEvents retrieves enriched withdrawal events within the specified block range.
+// It returns a slice of EnrichedProvenWithdrawalEvent along with any error encountered.
+func (wv *ProvenWithdrawalValidator) GetEnrichedWithdrawalsEventsMap(start uint64, end *uint64) (map[common.Hash]EnrichedProvenWithdrawalEvent, error) {
+ iterator, err := wv.optimismPortal2Helper.GetProvenWithdrawalsExtension1EventsIterator(start, end)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get proven withdrawals extension1 iterator error:%w", err)
+ }
+
+ enrichedProvenWithdrawalEvents := make(map[common.Hash]EnrichedProvenWithdrawalEvent)
+
+ for iterator.Next() {
+ event := iterator.Event
+
+ enrichedWithdrawalEvent, err := wv.GetEnrichedWithdrawalEvent(&WithdrawalProvenExtension1Event{
+ WithdrawalHash: event.WithdrawalHash,
+ ProofSubmitter: event.ProofSubmitter,
+ Raw: Raw{
+ BlockNumber: event.Raw.BlockNumber,
+ TxHash: event.Raw.TxHash,
+ },
+ })
+ if err != nil {
+ return nil, fmt.Errorf("failed to get enriched withdrawal event: %w", err)
+ }
+
+ key := enrichedWithdrawalEvent.Event.Raw.TxHash
+ enrichedProvenWithdrawalEvents[key] = *enrichedWithdrawalEvent
+ }
+
+ return enrichedProvenWithdrawalEvents, nil
+}
+
// IsWithdrawalEventValid checks if the enriched withdrawal event is valid.
// It returns true if the event is valid, otherwise returns false.
func (wv *ProvenWithdrawalValidator) IsWithdrawalEventValid(enrichedWithdrawalEvent *EnrichedProvenWithdrawalEvent) (bool, error) {
- if enrichedWithdrawalEvent.ExpectedRootClaim == [32]byte{} {
- return false, fmt.Errorf("trustedRootClaim is nil, game not enriched")
+ if !enrichedWithdrawalEvent.Enriched {
+ return false, fmt.Errorf("game not enriched")
}
validGameRootClaim := enrichedWithdrawalEvent.DisputeGame.DisputeGameData.RootClaim == enrichedWithdrawalEvent.ExpectedRootClaim
@@ -208,3 +249,7 @@ func (wv *ProvenWithdrawalValidator) IsWithdrawalEventValid(enrichedWithdrawalEv
return false, nil
}
}
+
+func (wv *ProvenWithdrawalValidator) GetLatestL2Height() uint64 {
+ return wv.l2NodeHelper.LatestKnownL2BlockNumber
+}
diff --git a/op-monitorism/faultproof_withdrawals/validator/utils.go b/op-monitorism/faultproof_withdrawals/validator/utils.go
index 7d0c1443..40ffe2d7 100644
--- a/op-monitorism/faultproof_withdrawals/validator/utils.go
+++ b/op-monitorism/faultproof_withdrawals/validator/utils.go
@@ -2,6 +2,7 @@ package validator
import (
"encoding/hex"
+ "fmt"
"strings"
"time"
@@ -14,6 +15,11 @@ type Raw struct {
TxHash common.Hash // The hash of the transaction.
}
+// String provides a string representation of Raw.
+func (r Raw) String() string {
+ return fmt.Sprintf("{BlockNumber: %d, TxHash: %s}", r.BlockNumber, r.TxHash.String())
+}
+
// Timestamp represents a Unix timestamp.
type Timestamp uint64
diff --git a/op-monitorism/go.mod b/op-monitorism/go.mod
index 080c5ee6..4ce4ec20 100644
--- a/op-monitorism/go.mod
+++ b/op-monitorism/go.mod
@@ -11,6 +11,7 @@ require (
github.com/hashicorp/golang-lru v0.5.0
github.com/joho/godotenv v1.5.1
github.com/prometheus/client_golang v1.20.2
+ github.com/prometheus/client_model v0.6.1
github.com/stretchr/testify v1.9.0
github.com/urfave/cli/v2 v2.27.4
gopkg.in/yaml.v3 v3.0.1
@@ -50,8 +51,6 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/holiman/uint256 v1.3.1 // indirect
- github.com/huin/goupnp v1.3.0 // indirect
- github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
@@ -62,7 +61,6 @@ require (
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
- github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
diff --git a/op-monitorism/go.sum b/op-monitorism/go.sum
index 03a2f695..49f5cb7a 100644
--- a/op-monitorism/go.sum
+++ b/op-monitorism/go.sum
@@ -262,7 +262,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=