Skip to content

feat(contracts): Add DelegatedDisputeGame for per-chain withdrawal proving#16

Closed
opsuperchain wants to merge 9 commits intodevelopfrom
super-dispute-game
Closed

feat(contracts): Add DelegatedDisputeGame for per-chain withdrawal proving#16
opsuperchain wants to merge 9 commits intodevelopfrom
super-dispute-game

Conversation

@opsuperchain
Copy link
Copy Markdown
Collaborator

@opsuperchain opsuperchain commented Dec 20, 2025

Summary

Introduces DelegatedDisputeGame, a lightweight dispute game that enables per-chain withdrawals by delegating dispute resolution to a shared SuperFaultDisputeGame.

┌────────────────────────────────────────────────────────────────────────────────────┐
│                                 SUPERCHAIN LEVEL                                   │
│                                                                                    │
│  ┌──────────────────────────────────────────────────────────────────────────────┐ │
│  │                         SuperFaultDisputeGame                                 │ │
│  │                                                                               │ │
│  │   • Single game containing output roots for ALL chains                        │ │
│  │   • Full bisection game with bonds (economic security)                        │ │
│  │   • status() and resolvedAt() determined here                                 │ │
│  │                                                                               │ │
│  │   Output Roots:                                                               │ │
│  │   ┌─────────────────────────────────────────────────────────────────────┐    │ │
│  │   │  Chain 901: 0xabc...    Chain 902: 0xdef...    Chain 903: 0x123...  │    │ │
│  │   └─────────────────────────────────────────────────────────────────────┘    │ │
│  └──────────────────────────────────────────────────────────────────────────────┘ │
│                                        │                                           │
│                                        │ registered in                             │
│                                        ▼                                           │
│  ┌──────────────────────────────────────────────────────────────────────────────┐ │
│  │                    Superchain AnchorStateRegistry                             │ │
│  │                                                                               │ │
│  │   • isGameRegistered(SuperGame) - was it created by superchain factory?       │ │
│  │   • isGameBlacklisted(SuperGame) - has guardian invalidated it?               │ │
│  │   • isGameRetired(SuperGame) - created before retirement timestamp?           │ │
│  └──────────────────────────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────────────────┘
                                         │
            ┌────────────────────────────┴────────────────────────────┐
            │                                                         │
            ▼                                                         ▼
┌───────────────────────────────────┐             ┌───────────────────────────────────┐
│          CHAIN 901                │             │          CHAIN 902                │
│                                   │             │                                   │
│  ┌─────────────────────────────┐  │             │  ┌─────────────────────────────┐  │
│  │   DelegatedDisputeGame      │  │             │  │   DelegatedDisputeGame      │  │
│  │   (output root game)        │  │             │  │   (output root game)        │  │
│  │                             │  │             │  │                             │  │
│  │  rootClaim: 0xabc...        │  │             │  │  rootClaim: 0xdef...        │  │
│  │  chainId: 901               │  │             │  │  chainId: 902               │  │
│  │  superGame: ────────────────┼──┼─────────────┼──┼── (same SuperGame)          │  │
│  │                             │  │             │  │                             │  │
│  │  DELEGATION:                │  │             │  │  DELEGATION:                │  │
│  │  status() → superGame.status()              │  │  status() → superGame.status()│
│  │  resolvedAt() → superGame.resolvedAt()      │  │  resolvedAt() → superGame.resolvedAt()
│  │                             │  │             │  │                             │  │
│  │  INIT VALIDATION:           │  │             │  │  INIT VALIDATION:           │  │
│  │  • SuperGame registered in  │  │             │  │  • SuperGame registered in  │  │
│  │    Superchain ASR           │  │             │  │    Superchain ASR           │  │
│  │  • rootClaim matches        │  │             │  │  • rootClaim matches        │  │
│  │    SuperGame.rootClaimByChainId()           │  │    SuperGame.rootClaimByChainId()
│  │  • Block number verified    │  │             │  │  • Block number verified    │  │
│  │    via OutputRootProof      │  │             │  │    via OutputRootProof      │  │
│  └──────────────┬──────────────┘  │             │  └──────────────┬──────────────┘  │
│                 │                 │             │                 │                 │
│                 │ validated by    │             │                 │ validated by    │
│                 ▼                 │             │                 ▼                 │
│  ┌─────────────────────────────┐  │             │  ┌─────────────────────────────┐  │
│  │  Per-Chain AnchorState      │  │             │  │  Per-Chain AnchorState      │  │
│  │  Registry                   │  │             │  │  Registry                   │  │
│  │                             │  │             │  │                             │  │
│  │  isGameProper() checks:     │  │             │  │  isGameProper() checks:     │  │
│  │  1. DelegatedGame registered│  │             │  │  1. DelegatedGame registered│  │
│  │  2. Not blacklisted         │  │             │  │  2. Not blacklisted         │  │
│  │  3. Not retired             │  │             │  │  3. Not retired             │  │
│  │  4. SuperGame valid in      │  │             │  │  4. SuperGame valid in      │  │
│  │     Superchain ASR ─────────┼──┼─► CASCADE   │  │     Superchain ASR          │  │
│  └─────────────────────────────┘  │             │  └─────────────────────────────┘  │
└───────────────────────────────────┘             └───────────────────────────────────┘

CASCADE INVALIDATION:
═════════════════════
When Guardian blacklists/retires SuperGame in Superchain AnchorStateRegistry:
  → ALL DelegatedDisputeGames referencing it become invalid
  → isGameProper() returns false for all of them
  → Per-chain withdrawals blocked until new valid game created

How It Works

  • No dispute logic in DelegatedDisputeGame - status() and resolvedAt() delegate directly to SuperGame
  • Shared economic security - All bonds and bisection happen in SuperGame, not per-chain
  • Cascading invalidation - Blacklisting SuperGame invalidates all DelegatedGames across all chains
  • Per-chain validation - Each chain's AnchorStateRegistry validates both the DelegatedGame AND the SuperGame

Changes

  • src/dispute/DelegatedDisputeGame.sol - New contract implementing delegation pattern
  • interfaces/dispute/IDelegatedDisputeGame.sol - Interface
  • src/dispute/AnchorStateRegistry.sol - Added SuperGame validation in isGameProper()
  • test/dispute/DelegatedDisputeGame.t.sol - 43 tests

Future Work

  • OPCM v2 integration for deployment
  • Additional tests for SuperGame invalidation cascade scenarios

🤖 Generated with Claude Code

@opsuperchain opsuperchain force-pushed the super-dispute-game branch 4 times, most recently from 0da0c6b to df8d2c0 Compare December 21, 2025 01:31
…oving

Introduces DelegatedDisputeGame, a minimal dispute game that delegates
verification to a SuperFaultDisputeGame. This enables per-chain dispute
games that share economic security through a single super game.

Key features:
- Works with standard DisputeGameFactory.create() - no factory changes needed
- Delegates status() and resolvedAt() to the linked SuperGame
- Block number verified against output root proof and header RLP
- l2SequenceNumber() provides viem-compatible block number access
- No bonds required - all economic security comes from the SuperGame

CWIA Layout uses extended extraData format containing:
- l2BlockNumber, superGameAddress, chainId, OutputRootProof, headerRLP

Security:
- CEI pattern: initialized flag set before external calls
- RLP bounds checking for header decoding
- SuperGame address validation

Includes 43 passing tests covering:
- Game creation via standard factory
- Status/resolvedAt delegation to SuperGame
- AnchorStateRegistry validation
- Resolution flow and edge cases

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
opsuperchain and others added 5 commits December 21, 2025 03:54
…isputeGame

When a SuperGame is blacklisted or retired in the superchain AnchorStateRegistry,
all DelegatedDisputeGames that reference it are now automatically invalidated.

Changes:
- Add IDelegatedDisputeGame interface for SuperGame detection
- Add try/catch in AnchorStateRegistry.isGameProper() to check SuperGame validity
- Remove registry mismatch check from DelegatedDisputeGame (registries will differ)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
No special IAnchorRegistryConfig interface needed. Just deploy a standard
SystemConfig with zero/minimal values for the superchain-level AnchorStateRegistry.
This matches the existing pattern used by SuperFaultDisputeGame tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add isGameRegistered() check for the SuperGame in isGameProper().
This prevents fake SuperGames from being used - the SuperGame must
be created by the legitimate superchain DisputeGameFactory.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add SUPERCHAIN_REGISTRY immutable to DelegatedDisputeGame that holds a
trusted reference to the superchain-level AnchorStateRegistry. This is
used in initialize() to verify the SuperGame is actually registered in
the legitimate superchain factory, preventing fake SuperGames.

Changes:
- Add SUPERCHAIN_REGISTRY immutable to DelegatedDisputeGame
- Add SuperGameNotRegistered error
- Check isGameRegistered on superchain registry in initialize()
- Add superchainRegistry() getter
- Update IDelegatedDisputeGame interface

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
opsuperchain and others added 3 commits December 21, 2025 04:38
Add check in initialize() to ensure the SuperGame's anchorStateRegistry()
matches the SUPERCHAIN_REGISTRY passed to constructor. This ensures
consistency between validation in initialize() and isGameProper().

Without this check, an attacker could deploy a DelegatedDisputeGame with
a fake SUPERCHAIN_REGISTRY that validates any SuperGame, while the real
SuperGame uses a different registry.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add validation that the chain ID in extraData matches the per-chain
factory's L2 chain ID from SystemConfig. This prevents creating
delegated games for the wrong chain on a given factory.

Also updates tests to:
- Mock l2ChainId() to return 5 for testing
- Use existing delegatedGameProxy instead of creating duplicates
- Set respected game type in setUp for AnchorRegistry tests
- Simplify tests that previously tried to create multiple games

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove vm.mockCall for l2ChainId and instead use the actual chain ID
(901) from the deploy config. This makes tests more realistic by using
actual contract values rather than mocks.

Changes:
- Replace chain 5/6 with chain 901/902 throughout tests
- Remove ISystemConfig import (no longer needed)
- Update all proof generation to use 901/902 storage roots
- Update helper functions and assertions to use correct chain IDs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
karlfloersch pushed a commit that referenced this pull request Mar 4, 2026
…16 (ethereum-optimism#19271)

Add missing @param blueprint NatSpec to OpcmContractRef struct (#2).
Add comments about pause blocking interop upgrades (#3). Document
migrate() scope limitations and re-migration risks (#7, #15). Update
PERMIT_ALL_CONTRACTS_INSTRUCTION comment (#12). Document intentional
use of chainSystemConfigs[0] for shared contracts (#16).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants