feat: bridgeservicefinder: add IgnoreNetworkIDs to skip dead networks 1809 - #1810
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 209b94fb90
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if _, ignored := f.ignoreNetworkIDs[rollupID]; ignored { | ||
| f.logger.Infof("network %d is in IgnoreNetworkIDs, skipping on-chain resolution", rollupID) | ||
| continue |
There was a problem hiding this comment.
Skip startup probes for ignored static overrides
When a network appears in both IgnoreNetworkIDs and BridgeURLs, the earlier config-seeding loop still places it in the cache, and Start subsequently calls probeAll, which probes every cached entry. An unreachable ignored override therefore still incurs the configured health-check timeout and, with RequireAllHealthyOnStart=true, makes startup fail with ErrServicesUnhealthyOnStart, contrary to the new option's promise to avoid health probes for ignored networks while continuing to serve their static overrides. The startup probing step needs to exclude ignored IDs.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch — fixed in 769721c: probeAll now skips any networkID in IgnoreNetworkIDs, so a config-overridden-but-ignored network's cache entry is served but never health-probed (never counted toward RequireAllHealthyOnStart either). Added TestStart_IgnoreNetworkIDs_SkipsHealthProbe to cover it.
…alth probe probeAll iterated every cache entry unconditionally, including one installed by the config-seeding step for a networkID that is both config-overridden and listed in IgnoreNetworkIDs. That defeated the point of ignoring a known-dead network: its /health probe still incurred the configured timeout, and under RequireAllHealthyOnStart=true an unreachable ignored override could still fail Start with ErrServicesUnhealthyOnStart. probeAll now skips any networkID in Config.IgnoreNetworkIDs; its entry is still served by GetURL, with healthy defaulting to false (never probed). Found by review on #1810.
L1's chain data is baked into its image at build time and never advances past block 384. Anchoring L2 genesis to L1's head (previous commit) fixes LoadEnv, but once the sequencer has produced ~600s (max_sequencer_drift) worth of L2 blocks since genesis, op-node's origin-selector needs a newer L1 origin than block 384 to keep going and never finds one, stalling the chain forever mid-test-run. Raise max_sequencer_drift to a week so the sequencer never needs to look for a newer L1 origin within the lifetime of a test run. Found while investigating CI failures on #1810.
Config.IgnoreNetworkIDs lists networkIDs (rollupIDs) that are excluded entirely from on-chain resolution: buildInitialCache skips them during enumeration (no RollupIDToRollupData call, no contract reads, no health probe) and the live listener skips them when a rollup-manager lifecycle event announces them (CreateNewRollup/CreateNewAggchain/AddExistingRollup). This avoids known-dead networks (decommissioned or unreachable test rollups) slowing down proxy startup and event processing with on-chain reads and health-check timeouts that can never succeed. A static Config.BridgeURLs override for a networkID is still served even if that networkID is also listed in IgnoreNetworkIDs. Fixes #1809
Documents the new bridgeservicefinder.Config.IgnoreNetworkIDs field in the proxy binary's default TOML template. No other doc location lists BridgeServiceFinder fields individually for the proxy binary (only docs/autoclaim.md has a per-field table, already updated for AutoClaim).
…alth probe probeAll iterated every cache entry unconditionally, including one installed by the config-seeding step for a networkID that is both config-overridden and listed in IgnoreNetworkIDs. That defeated the point of ignoring a known-dead network: its /health probe still incurred the configured timeout, and under RequireAllHealthyOnStart=true an unreachable ignored override could still fail Start with ErrServicesUnhealthyOnStart. probeAll now skips any networkID in Config.IgnoreNetworkIDs; its entry is still served by GetURL, with healthy defaulting to false (never probed). Found by review on #1810.
Add the missing BridgeServiceFinder fields (BlockFinality, BlockChunkSize, HealthCheckPath, HealthCheckTimeout, RequireAllHealthyOnStart, IgnoreNetworkIDs) and the empty BridgeURLs/RPCURLs map sections to config/default.go, matching the values already used in proxy/config/default.go and the Default* constants defined in bridgeservicefinder/config.go. Also add the missing BridgeURLs/RPCURLs sections to proxy/config/default.go for the same reason. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…l-clock op-pp's L1 image (arnaubennassar/geth:op-pp) has its chain data baked in at build time and never advances past that snapshot. The op-geth entrypoint was patching the L2 genesis timestamp to date +%s (real wall-clock) on every start, so the gap between the L2 genesis and its L1 origin block grows by one day for every day that passes since the L1 image was built. Once that drift exceeded rollup.json's max_sequencer_drift (600s), op-node's sequencer could never find a valid L1 origin for the first post-genesis block and the L2 chain stalled forever at block 0 -- surfacing as "wait for MintableERC20 deployment: context deadline exceeded" during LoadEnv, since op-pp's L1 snapshot is from Feb 2026 (~6 months of drift by now). Fix: read L1's actual head timestamp and use it to patch the L2 genesis instead of wall-clock time, keeping L2 genesis anchored to L1's frozen origin regardless of what day the test actually runs. Verified locally: op-geth-001/op-node-001 went from stuck at block 0 to actively sequencing new L2 blocks. Note: since L1 never advances, the chain still stalls again once L2's virtual time drifts past max_sequencer_drift from the anchored origin (~1800s of L2 time in local testing) -- well past LoadEnv/MintableERC20 deployment, but a longer-running test could still hit it. Left as a known follow-up rather than widening scope here.
L1's chain data is baked into its image at build time and never advances past block 384. Anchoring L2 genesis to L1's head (previous commit) fixes LoadEnv, but once the sequencer has produced ~600s (max_sequencer_drift) worth of L2 blocks since genesis, op-node's origin-selector needs a newer L1 origin than block 384 to keep going and never finds one, stalling the chain forever mid-test-run. Raise max_sequencer_drift to a week so the sequencer never needs to look for a newer L1 origin within the lifetime of a test run. Found while investigating CI failures on #1810.
…r ~30min" This reverts commit bf18a77.
…d of wall-clock" This reverts commit e01e556.
2923a82 to
4e855a1
Compare
L1's chain data is baked into its image at build time and never advances past block 384. Anchoring L2 genesis to L1's head (previous commit) fixes LoadEnv, but once the sequencer has produced ~600s (max_sequencer_drift) worth of L2 blocks since genesis, op-node's origin-selector needs a newer L1 origin than block 384 to keep going and never finds one, stalling the chain forever mid-test-run. Raise max_sequencer_drift to a week so the sequencer never needs to look for a newer L1 origin within the lifetime of a test run. Found while investigating CI failures on #1810.
…, GER settlement/injection fixes (#1815) ## 🔄 Changes Summary - **`feat(bridgetracker)`**: new `GET /tracker/v1/activity/from/{from_address}` endpoint that scans every bridge service known to the `bridgeservicefinder` (via new `Finder.NetworkIDs()`) for bridges sent by an address and resolves each one's claim state (`ActivityCache`, `ActivitySource`, `ActivityQuerier`/`ActivityBridgeScanner`/`ActivityClaimChecker` ports). Opt-in via `includeTracking=true` to also register still-unclaimed bridges with the tracker. Endpoint is only registered when wired (`proxy/cmd/run.go`), and swagger docs are regenerated. - **`feat(bridgetracker)`**: `StepClaimed` is now its own tracked step with a dedicated `ClaimedResolver` that fetches the claim tx/block from the destination network's bridge service, decoupled from `StepWaitingClaim`, which now checks `isClaimed()` on-chain directly through a new `ClaimChecker` port instead of waiting on the indexed claim record — faster and authoritative, at the cost of not carrying claim tx details itself (that's what the new `Claimed` step result is for). The on-chain binding/cache logic is factored out of `ActivitySource` into `sources/claim_checker.go` (`contractClaimCheckers`) and shared by both the tracker engine and the activity endpoint. - **`fix(bridgetracker)`**: introduce `domain.ScannedBridge` to track which network's bridge service actually reported a scanned bridge (`NetworkID`), distinct from `Bridge.OriginNetwork` (the bridged *asset*'s origin network) — the two diverge when an asset is re-bridged across more than one hop, which was feeding the wrong `sourceBridgeNetwork` into `isClaimed()` and the wrong network into `TrackingID`/the activity endpoint's `bridge_network_id` for such bridges. - **`feat(bridgetracker)`**: new `GET /bridge-address[/{network_id}]` endpoint, resolving the bridge contract address for one network or every network currently known — opt-in via `Config.BridgeAddressResolver`, wired in `proxy/cmd/run.go` off `bridgeservicefinder.Finder` directly. Backed by a new `Config.BridgeAddress` override map on `bridgeservicefinder.Finder` itself (priority: per-network entry, then `[0]` as the default, then the rollup manager's own on-chain `BridgeAddress()`). - **`feat(bridgetracker)`**: `ClaimResult` now also carries `BlockTimestamp` alongside `BlockNumber`. `InjectedGERResult` is restructured into `L1InfoTreeLeaf` (the L1 `UpdateL1InfoTree`/`UpdateL1InfoTreeV2` event that produced the covering leaf) and an optional `L2InjectedGER` (the actual L2 block/timestamp the GER was injected at on the destination network) — fixing #1818, where the L1 block was returned in the L2 field's place. When the destination's bridge-service instance doesn't report the L2 injection block itself (`injected_l2_block_num`/`injected_l2_block_timestamp` on `GET /bridge/v1/injected-l1-info-leaf`), `GERSource` falls back to scanning that network's own `GlobalExitRootManagerL2` contract for the `UpdateHashChainValue` event backwards in chunks, via the new `Tracker.L2GlobalExitRootAddress` per-network contract address map (a workaround-only fallback, see its doc) — bounded by a new `Tracker.L2InjectionLookbackBlocks` (default 1,000 blocks) instead of always walking back to genesis. - **`fix(bridgetracker)`**: resolve the settled GER correctly when the settlement tx has no `UpdateL1InfoTree` event: walk backwards on L1 for the most recent earlier one instead of surfacing "not ready" forever, excluding same-block logs from a later transaction, and querying `eth_getLogs` in chunks that respect providers' 10,000-block range cap. - **`fix(bridgetracker)`**: gate certificate settlement on the settlement tx actually being visible on L1 (not just the agglayer client reporting `Settled`), avoiding a premature `TransactionReceipt` lookup that would otherwise fail. ##⚠️ Breaking Changes - None. `Config.BridgeAddressResolver` and the new `bridgetracker` activity/bridge-address ports are additive/opt-in. ## 📋 Config Updates - 🧾 New optional `BridgeServiceFinder.BridgeAddress` map (networkID → address override), default empty — consulted by the new `GET /bridge-address[/{network_id}]` endpoint before falling back to the rollup manager's on-chain `BridgeAddress()`. That endpoint itself is gated by `Config.BridgeAddressResolver` (Go-level wiring, not a TOML key); unset leaves both routes unregistered. - 🧾 New optional `Tracker.ActivityIdleTimeout` (defaults to `IdleTimeout` if unset) — idle timeout for the activity endpoint's own cache. - 🧾 New optional `Tracker.L2GlobalExitRootAddress` (networkID → `GlobalExitRootManagerL2` address map, default empty) and `Tracker.L2InjectionLookbackBlocks` (default `1000`) — workaround-only fallback for a destination network whose bridge-service instance predates L2 injection block reporting (#1819); defaults leave behavior unchanged for everyone else. ```toml [BridgeServiceFinder.BridgeAddress] # optional per-network override, consulted in this priority order by Finder.BridgeAddress: # BridgeAddress[networkID], then BridgeAddress[0] (default for every other network), then the # rollup manager's own on-chain BridgeAddress() 0 = "0xShared_L1_bridge_address_used_as_the_default" 5 = "0xNetwork5s_own_bridge_address" [Tracker] ActivityIdleTimeout = "30m" L2InjectionLookbackBlocks = 1000 # Workaround only: uncomment for a destination network whose bridge-service instance does not # report the L2 block a covering GER was injected at. # [Tracker.L2GlobalExitRootAddress] # 1 = "0x..." ``` ## ✅ Testing - 🤖 **Automatic**: `go build ./...` and `go test ./bridgetracker/... ./bridgeservicefinder/... ./autoclaim/... ./proxy/...` pass, including new/updated regression tests for the activity endpoint (`activity_test.go`, `sources/activity_test.go`, `cache_test.go`), the claimed-step split (`resolve_steps_test.go`, `engine_test.go`), the new bridge-address endpoint (`bridge_address_test.go`), the settlement GER backwards-search fix (`settlement_test.go`), and the L2 injection fallback/lookback cap (`sources_test.go`'s `TestGERSourceInjectedGER_FallsBackToL2Scan` and `TestFindL2InjectionBlockBackwards`, `proxy/config/config_test.go`). ## 🐞 Issues - Fixes #1818. ## 🔗 Related PRs - None. ## 📝 Notes - This branch's history still contains commits for `bridgeservicefinder`'s `IgnoreNetworkIDs` and the `l2gersync` `eth_getLogs` chunk-cap fix (originally developed alongside this work) — both landed directly on `develop` in the meantime (#1810, #1813), so neither shows up in this PR's diff anymore despite still being present in the branch's git history. --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
🔄 Changes Summary
Config.IgnoreNetworkIDs []uint32tobridgeservicefinder: networkIDs (rollupIDs) listed here are excluded entirely from on-chain resolution.buildInitialCacheskips them during enumeration (noRollupIDToRollupDatacall, no contract reads, no health probe).CreateNewRollup/CreateNewAggchain/AddExistingRollup) announces them, so they are never added to the watched-address set either.Config.BridgeURLsoverride for a networkID is still served even if that networkID is also listed inIgnoreNetworkIDs— the ignore only skips on-chain inspection, never a config override.proxybinary's default TOML config template.IgnoreNetworkIDsdefaults to an empty list, so existing deployments are unaffected.📋 Config Updates
🧾 New optional field
IgnoreNetworkIDsonBridgeServiceFinder, shared by theautoclaimandproxybinaries since both reuse the samebridgeservicefinderpackage. Defaults to[](nothing ignored). Documented indocs/autoclaim.md's config table and added (empty) to theproxydefault config template.AutoClaim (
AutoClaim.BridgeServiceFindersection):Proxy (
[BridgeServiceFinder]section):IgnoreNetworkIDstakes a list of the dead networks' networkIDs (rollupIDs) — e.g.[5, 12]above skips networks5and12— and can be pasted directly under the existingBridgeServiceFinderblock in either config, replacing the defaultIgnoreNetworkIDs = [].✅ Testing
TestStart_IgnoreNetworkIDs_SkipsEnumerationandTestLiveDiscovery_IgnoredNetworkIsNeverRegisteredinbridgeservicefinder; fullbridgeservicefinderandproxy/configsuites pass;golangci-lintclean;go build ./...passes.🐞 Issues
🔗 Related PRs
📝 Notes
buildInitialCache(which installsBridgeURLs/RPCURLsoverrides) runs independently and is unaffected, so an ignored+config-overridden network is still served.BridgeServiceFinderfields individually (onlydocs/autoclaim.mdhas a per-field table, since AutoClaim reuses the same package), so no further doc update was needed there.