Operators
Migrating to permissionless fault proofs on OP Stack

Migrating to permissionless fault proofs on OP Stack

This guide provides a high-level overview for chain operators looking to transition their OP Stack from permissioned to permissionless fault proofs. It's designed to be accessible for technical decision makers while providing sufficient detail for implementation teams.

Overview

The OP Stack architecture uses Fault Proofs to ensure the validity of withdrawals from L2 to L1. Transitioning from permissioned to permissionless proofs represents a significant security upgrade, allowing any participant to propose and challenge state output roots. Permissioned games previously relied on a single trusted validator—typically the proposer configured in the PermissionedDisputeGame, usually the network's only sequencer.

This migration involves several key components:

  • Configuring security-critical dispute monitoring services
  • Deploying and configuring smart contracts using op-deployer
  • Testing the new system before activation
  • Setting the respected game type to permissionless fault proofs, specifically using the FaultDisputeGame

Prerequisites

Before beginning this transition, your chain should:

  • Be running a standard OP Stack implementation
  • Be operating with the recommended infrastructure services including op-challenger and op-dispute-mon

1. Configure the dispute components

The op-challenger and op-dispute-mon services are critical security components that participate in the dispute game process to challenge invalid proposals and monitor active games.

Upgrade to the latest op-challenger

Upgrade to the latest release (opens in a new tab), which contains important improvements to simplify the upgrade process.

We recommend using the official Docker images for reliability and ease of deployment:

# Pull the latest version
docker pull us-docker.pkg.dev/oplabs-tools-artifacts/images/op-challenger:latest

Then run the image, for example:

docker run -d --name op-challenger \
  -e OP_CHALLENGER_TRACE_TYPE=permissioned,cannon \
  -e OP_CHALLENGER_PRESTATES_URL=<YOUR_PRESTATES_URL> \
  us-docker.pkg.dev/oplabs-tools-artifacts/images/op-challenger:v1.3.3
 

Replace <YOUR_PRESTATES_URL> with your actual prestates URL.

If your deployment requires building from source, you can alternatively use:

git clone https://github.com/ethereum-optimism/optimism -b op-challenger/v1.3.3 --recurse-submodules
cd optimism
make op-challenger

Update network configuration

Configure op-challenger to load your chain configuration. Even if your chain is not included in the superchain-registry, you can specify a custom configuration:

# For chains in the registry
--network <chain-name>
 
# For chains not in the registry, provide a path to your rollup configuration
*   `<chain-id>-rollup.json` - Your rollup configuration
*   `<chain-id>-genesis-l2.json` - Your L2 genesis file

Enable cannon trace type

Configure op-challenger to support both permissioned and permissionless games by setting:

--trace-type permissioned,cannon

Or by setting the environment variable:

OP_CHALLENGER_TRACE_TYPE=permissioned,cannon

Configure prestates access

Replace the --cannon-prestate flag with --prestates-url, which points to a source containing all required prestates:

--prestates-url <URL_TO_PRESTATES_DIRECTORY>

The URL can use http, https, or file protocols. Each prestate should be named as <PRESTATE_HASH>.json or <PRESTATE_HASH>.bin.gz.

Building required prestates for chains not in the Superchain Registry

You'll need to deploy two new dispute game contracts with the new absolute prestate:

  1. FaultDisputeGame
  2. PermissionedDisputeGame

The initial prestate used for permissioned games doesn't include the necessary chain configuration for the Fault Proof System. The assumption is that the chain operator, the single permissioned actor, will not challenge their own games. So the absolute prestate on the initial PermissionedDisputeGame will never be used. When deploying a new chain, you must first deploy the L1 contracts and then generate the chain genesis file and rollup configuration files. These are inputs to the creation of the absolute prestate and this circular dependency is the reason chains cannot be deployed directly to the permissionless Fault Proof System.

For chains not in the Superchain Registry, you need to build custom prestates with your chain's configuration:

# 1. Clone the repository
git clone https://github.com/ethereum-optimism/optimism --recurse-submodules
cd optimism
 
# 2. Create a branch from the latest op-program release
git checkout -b op-program/chainconfig op-program/v1.5.0
 
# 3. Add your chain configuration to the op-program chainconfig directory
# Your rollup config must be named <chain-id>-rollup.json
# Your L2 genesis must be named <chain-id>-genesis-l2.json
mkdir -p op-program/chainconfig/configs 
cp /path/to/your/rollup-config.json op-program/chainconfig/configs/<YOUR_L2_CHAIN_ID>-rollup.json
cp /path/to/your/l2-genesis.json op-program/chainconfig/configs/<YOUR_L2_CHAIN_ID>-genesis-l2.json
 
# 4. Build the reproducible prestate
make reproducible-prestate

The prestate will typically be generated at:

  • op-program/bin/prestate.json (for older versions)
  • op-program/bin/prestate.bin.gz (for intermediate versions)
  • op-program/bin/prestate-mt64.bin.gz (for chains upgraded to Cannon MT64, starting from upgrade 14)

Post-upgrade 14, chains are expected to use prestate-mt64.bin.gz due to the Fault Proof VM contract upgrade to cannon-mt64. The older prestate.bin.gz will eventually be deprecated but is temporarily retained until all chains complete the upgrade.

Ensure sufficient funds for bonds

Bonds are required for both permissioned and permissionless games. However, with permissioned games, you typically don't post claims regularly, making bond requirements less noticeable. In contrast, the challenger in permissionless games will frequently need to post bonds with each claim it makes. Therefore, ensure your challenger has sufficient funds available.

As a general guideline:

  • Maintain a minimum balance of 50 ETH
  • Have access to a large pool of ETH for potential attack scenarios
  • Implement monitoring to ensure sufficient funds are always available

Set up op-dispute-mon

Ensure op-dispute-mon is properly configured by following these steps

2. Deploy and configure smart contracts using OPCM

This section requires privileged actions by the ProxyAdminOwner and may involve the Guardian role depending on your security setup.

Understanding ProxyAdmin Owner and Guardian roles

This migration requires actions by privileged roles in your system:

  • The ProxyAdmin Owner has the authority to upgrade proxy contracts.

  • The Guardian has emergency powers like pausing withdrawals and changing the respected game type.

For detailed information about privileged roles and their security implications, refer to the privileged roles documentation.

Adding the PermissionlessDisputeGame to a chain

Given that not all chains will support the PermissionlessDisputeGame upon deployment, an OPCM.addGameType() method will be added which will orchestrate the actions required to add a new game type.

This method will:

  1. deploy the FaultDisputeGame contract
  2. setup the DelayedWethProxy for the new game
  3. Reinitialize the AnchorStateRegistry to add the new game type.

See a high‐level implementation from the spec (opens in a new tab)

Set game implementations

Execute the following smart contract changes in a single bundled transaction:

  1. Call setImplementation on the DisputeGameFactoryProxy to:

    • Set the implementation for the new permissionless FaultDisputeGame (game type 0)
    • Upgrade the permissioned game to the new PermissionedDisputeGameAddress (game type 1) with the updated absolute prestate
  2. Upgrade the AnchorStateRegistryProxy (pre-Upgrade 13 only):

    After upgrade 13 (FP incident response improvements), this step is no longer required, as a single anchor state is shared across all game types. If you have not upgraded, perform the following:

    • First upgrade the proxy to point to the StorageSetter contract and clear the initialized flag.
    • Then upgrade back to the AnchorStateRegistry implementation
    • Call initialize with the anchor state for the new game type

Ensure a permissioned game has already resolved with the correct root and updated the anchor state to a valid value. Initial deployments at genesis always have an invalid anchor state.

Post-upgrade 13, the anchor state for permissionless games automatically matches the current anchor state used by permissioned games, since there's only one shared anchor state.

  1. Initialize bond amounts for each game type to exactly 0.08 ETH

3. Testing off-chain agents

After you've set the permissionless FaultDisputeContract implementations on the DisputeGameFactory and before you set the respected game type to it; you can test your off-chain services in the cold path.

Test defending valid proposals

Create a valid proposal using the permissionless game type 0:

  1. Ensure the proposal is from a block at or before the safe head:

    cast block --rpc-url <OP_GETH_ENDPOINT> safe
  2. Get a valid output root:

    cast rpc --rpc-url <OP_NODE_ENDPOINT> optimism_outputAtBlock \
      $(cast 2h <BLOCK_NUMBER>) | jq -r .outputRoot
  3. Create a test game:

    ./op-challenger/bin/op-challenger create-game \
      --l1-eth-rpc=<L1_RPC_ENDPOINT> \
      --game-factory-address <DISPUTE_GAME_FACTORY_ADDR> \
      --l2-block-num <BLOCK_NUMBER> \
      --output-root <OUTPUT_ROOT> \
      <SIGNER_OPTIONS>
  4. Verify:

    • op-challenger logs a message showing the game is in progress
    • op-challenger doesn't post a counter claim (as this is a valid proposal)
    • dispute-mon includes the new game with status="agree_defender_ahead"

Test countering invalid claims

Post an invalid counter claim to the valid proposal created above:

./op-challenger/bin/op-challenger move \
  --l1-eth-rpc <L1_RPC_ENDPOINT> \
  --game-address <GAME_ADDR> \
  --attack \
  --parent-index 0 \
  --claim 0x0000000000000000000000000000000000000000000000000000000000000000 \
  <SIGNER_OPTIONS>

Verify that op-challenger posts a counter-claim to the invalid claim. You can view claims using:

./op-challenger/bin/op-challenger list-claims \
  --l1-eth-rpc <L1_RPC_ENDPOINT> \
  --game-address <GAME_ADDR>

There should be 3 claims in the game after this test.

4. Switch to permissionless proofs

After completing all previous steps and verifying their successful operation:

  1. Set the respectedGameType on the OptimismPortal to CANNON (game type 0) using OPCM:

    opcm set-respected-game-type \
      --game-type 0
  2. Configure op-proposer to create proposals using the permissionless cannon game type:

    # Change from game-type 1 to 0
    --game-type 0

    Or via environment variable:

    OP_PROPOSER_GAME_TYPE=0

Next steps

Conclusion

Transitioning to permissionless proofs represents a significant security improvement for your OP Stack chain. This transition decentralizes the validation process, allowing any participant to challenge invalid withdrawal claims rather than relying on a limited set of trusted validators.

By following this guide, you'll be able to safely configure and test your system before making the switch to permissionless proofs. Remember to thoroughly test each service before proceeding to the next step, and ensure that your security monitoring is properly configured to track the health of the system after the transition.