Repository navigation
feat(bridge): refuse to bridge a pair while a pause switch is set on either side - #86
Open
iamalwaysuncomfortable wants to merge 13 commits into
Open
iamalwaysuncomfortable wants to merge 13 commits into
iamalwaysuncomfortable wants to merge 13 commits into
Conversation
…use, paused or pause__)
… profile RPC chain ids, cap public RPC timeout
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Every bridge deployment has an operator kill switch: the
pausemapping on the Aleo warp and USDCx programs,paused()on Circle's xReserve and the CCTP transmitter, aPausableHook/PausableIsminside the Ethereum Hyperlane Mailbox's hook and ISM trees, and the enrolled-router table of the Solana warp program. The SDK read none of them, so a transfer into a paused far side dispatched, locked the funds on the source chain and stalled at delivery.quote,execute,resumeandcompletenow read every switch governing the route, on both ends, right before they act, and refuse withRoutePausedErrornaming the switch. A switch that cannot be read after retries refuses withPauseCheckError. Both subclassRouteUnavailableError, so existing handlers keep working.completereads the destination side only, since the deposit is already committed.Bridge.pause_status(route)and abridge_pause_statusagent/MCP tool expose the reads. Existing tool schemas are byte-identical to a snapshot taken before this change (tests/fixtures/agent-tools-2026-10-30.json).pause[true],paused[true], ARC-20pause__[false]); the reader follows what each program declares and refuses a warp program that declares none. A program the node does not serve is aConfigurationError, never "open" (the node answersnullfor a missing key, an undeclared mapping and a missing program alike).Profile.rpc_urls/set_rpc_url), then a registry-pinned public read endpoint (PUBLIC_READ_RPC_URLS, 10 s timeout, pause reads only, never signing).ETHEREUM_RPC_URLalone now yields a read-only connection, matching the other chains.remote_routers[<aleo domain>], decoded against two vendored mainnet fixtures.Backwards compatible: no existing signature, dataclass field, tool schema or checkpoint format changes.
Registrygains an optional constructor keyword andpublic_read_rpc_urls();Profilegains an optionalrpc_urlskey older files simply lack; legacy registry fingerprints are byte-identical.Live state (mainnet, 2026-10-30)
tests/live/test_pause_reads.pyreads every active route over the public endpoints: 28 routes, 0 unknown, 16 paused. Every Hyperlane warp program except USDT is currently paused (ETH, WBTC, BAT, USDG, SOL, ZEC, both directions). xReserve and CCTP are open.Test plan
cd bridge-sdk && python -m pytest -q -m "not live"→ 1261 passedpython codegen/gen_context.py --check→ currentBRIDGE_LIVE_READS=1 python -m pytest -m live tests/live/test_pause_reads.py→ 28 passedtest_aleo_reads,test_eth_reads,test_sol_reads,test_arc_reads,test_compliance_live,test_frozen_live) → 53 passed, 4 skippedDeferred (minor, from review):
fallbackHook()of a routing hook is not walked when the domain slot is zero;sides=is not validated against typos; an all-zero Solana router entry reads as enrolled; the NULL-ISM probe does not catch web3's empty-response error.