README
¶
Optimism Interop Monitor
Purpose
The Optimism Interop Monitor is a service that monitors Executing Messages between OP Protocol chains to detect and report any invalid messages on chains. It helps ensure the reliability and correctness of cross-chain communication.
Interop Monitor's primary output is a collection of metrics which can alert operators for fast response and insight. The design of this service follows the Design Doc.
Validity model
The monitor is an independent watchdog: it reads each chain's L2 receipts directly and decides validity itself, rather than trusting any other service. For every Executing Message it validates, against the initiating chain's block, that:
- the initiating log exists at the referenced log index,
- the log address matches the message's declared
Origin, - the initiating block timestamp matches the timestamp bound into the message identifier
(otherwise
timestamp_mismatch), - the payload hash matches, and
- the message is within the expiry window — i.e.
init.Timestamp <= exec.Timestamp <= init.Timestamp + MessageExpiryWindow(otherwiseexpired).
These checks together are equivalent to the MessageChecksum binding used by interop_checkAccessList.
The chain set and the message expiry window are sourced from an interop dependency-set JSON file via
--dependency-set (the same format consumed by op-supernode / op-node).
This service belongs to the post-op-supervisor interop topology: op-supernode (consensus layer,
makes the cross-chain safety decision), Light CL follower nodes, and op-interop-filter (execution
layer, holds the failsafe and answers interop_checkAccessList). The monitor optionally cross-checks
the interop-filter and supernode read-only (see flags) but never depends on them to function.
Optional cross-checks
These observers are read-only and never gate the monitor's own verdict; they only emit additional observability metrics. They degrade gracefully if the observed service is unreachable.
--interop-filter-endpoint(with--interop-filter-min-safety, defaultcross-unsafe): replays each terminal job's executing message tointerop_checkAccessListand recordsfilter_divergence_total{executing_chain_id,initiating_chain_id,monitor_status,filter_status}when the filter disagrees with the monitor. Also pollsadmin_getFailsafeEnabledinto theinterop_filter_failsafegauge.--supernode-endpoints(repeatable): for eachop-supernode, probesheartbeat_checkintosupernode_up{endpoint}, records per-chainsupernode_safe_head{chain_id,level}(levelscross_safeandfinalized) fromsupernode_syncStatus, and incrementscross_safety_violations_total{executing_chain_id,initiating_chain_id,level}when a bad executing message (invalid/expired/timestamp_mismatch) is observed at or below the supernode's cross-safe head.
Architecture
The service consists of several key components working together:
- A main service (
InteropMonitorService) that coordinates everything - A set of RPC Clients specified from command line, and given to each sub-component
- Multiple
Finderinstances that scan chains for relevant transactions - Multiple
Updaterinstances that takejobs for their chain and update them - A
MetricCollectorthat regularly scans ongoing jobs to emit gauge metrics
The components use a collection of channels, callbacks and visitor-pattern style data collection to share Job information.
flowchart TD
subgraph execution-client A
ra[RPC]
end
subgraph execution-client B
rb[RPC]
end
subgraph grafana
g[Grafana]
end
%% Main Monitoring Group
subgraph service["op-interop-mon"]
s[Service Routing]
m[Metric Collector]
fa[Chain A Finder]
fb[Chain B Finder]
ua[Chain A Updater️]
ub[Chain B Updater]
end
ra --"New Unsafe and Finalized Blocks"--> fa
ra --"Receipt Data for EM Validation"--> ua
fa --"Executing Messages and Finalized Block Info" --> s
s --"New Jobs and Finalized Block Info" --> ub
rb --"New Unsafe and Finalized Blocks"--> fb
rb --"Receipt Data for EM Validation"--> ub
fb --"Executing Messages and Finalized Block Info" --> s
s --"New Jobs and Finalized Block Info" --> ua
ua --"All Current Jobs"--> m
ub --"All Current Jobs"--> m
m --"Executing and Initiating Message Prometheus Stats"--> g
MetricCollector
The MetricCollector consolidates metrics from all jobs across chains. It:
- Scans all jobs from all updaters periodically
- Tracks executing message metrics by:
- Chain ID
- Block number
- Block hash
- Message status
- Tracks initiating message metrics by:
- Chain ID
- Block number
- Message status
- Detects and records terminal state changes (valid->invalid or invalid->valid)
- Emits metrics for:
- Executing message stats per chain/block/status (statuses:
valid,invalid,expired,timestamp_mismatch,unknown) - Initiating message stats per chain/block/status
- Terminal status changes between chains
- Initiating-chain reorgs (
initiating_reorgs_total): a job whose initiating block was observed at more than one hash
- Executing message stats per chain/block/status (statuses:
Updaters
Updaters are chain specific processors that take jobs and update them:
- Maintains a map of all
jobs it is updating - Evaluates all
jobs regularly - Expires old
jobs based on Finality of both Initiating and Executing side - Operates independently per chain
Finders
Finders scan individual chains for relevant transactions. Each Finder:
- Subscribes to new blocks on its assigned chain
- Processes block receipts to identify Executing Messages
- Creates
jobs for each relevant transaction found - Sends
jobs to the Finders (via a centralized router) - Operates independently per chain
Jobs
jobs represent individual Executing Messages that need to be tracked. A job contains:
- Timestamps for first/last seen
- Transaction hashes and Initiating/Executing identifiers
- Current status and status history
- More, as needed by the service
jobs move through different states (unknown -> valid/invalid/expired/timestamp_mismatch) as the updater processes them.