diff --git a/.github/workflows/docker-images.yml b/.github/workflows/docker-images.yml index e0536385203..39c1477b5a4 100644 --- a/.github/workflows/docker-images.yml +++ b/.github/workflows/docker-images.yml @@ -29,11 +29,6 @@ jobs: toolchain: stable override: true - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 20.x - - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: @@ -344,7 +339,7 @@ jobs: build-op-batcher-tee: needs: prepare-deployment - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-8core permissions: contents: read packages: write diff --git a/README_ESPRESSO.md b/README_ESPRESSO.md index 01327a60557..f303a9ec2b6 100644 --- a/README_ESPRESSO.md +++ b/README_ESPRESSO.md @@ -355,51 +355,6 @@ In order to refresh this AMI one needs to: 2. Copy the script `espresso/scrips/enclave-prepare-ami.sh` in the EC2 instance (e.g. using scp) and run it. 3. [Export the AMI instance](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/tkv-create-ami-from-instance.html). -## Demo to Celo -For convenience some scripts have been added to make it easier to showcase the -results, and monitor the progress of the docker compose file. The primary -script concerns evaluating `optimism_syncStatus` and displaying the results. - -This script requires the commands `tmux`, and `watch` to be installed and -in the `PATH`. Check to see if you have them, and if you don't, be sure to -install them using whatever method you deem necessary in order to run the -script. - -After that has been done you should be able to spin up the simple script -using the following command: -```console -./espresso/scripts/demo_tmux_get_sync_status.sh -``` - -This will launch a `tmux` session setup with a script to automatically -query and display the `optimism_syncStatus` result for the `sequencer`, -`verifier`, and `caff-node`. - -It assumes that the `docker-file.yml` is being run with the default values -and will attempt to connect to them as needed. - -If you're not used to `tmux` you should be able to disconnect from the session -using ` d`. This only detaches from the session, the session will still -exist and be running in the background. You can kill the session using the -following command: -```console -tmux kill-session -``` - -Or you can reattach to it using this command instead: -```console -tmux attach -``` - -If you want to target different RPC endpoints for optimism, if you're not -running the local demo, and want to target the remote, you can always -specify environment variables before running the script: -```console -OP_RPC_SEQUENCER=http://sequencer.example.com:4545 \ -OP_RPC_VERIFIER=http://verifier.example.com:4545 \ -OP_RPC_CAFF=http://caff.example.com:4545 \ -./espresso/scripts/demo_tmux_get_sync_status.sh -``` ## Celo Deployment diff --git a/espresso/.env b/espresso/.env index 0df5761cdf2..32443839196 100644 --- a/espresso/.env +++ b/espresso/.env @@ -49,6 +49,8 @@ BATCH_AUTHENTICATOR_OWNER_PRIVATE_KEY=0x7c852118294e51e653712a81e05800f419141751 # cast wallet address --mnemonic "test test ... junk" --hd-path "m/44'/60'/0'/0/1" PROPOSER_ADDRESS=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 +# cast wallet private-key --mnemonic "test test ... junk" --hd-path "m/44'/60'/0'/0/1" +PROPOSER_PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d # cast wallet address --mnemonic "test test ... junk" --hd-path "m/44'/60'/0'/0/5" NON_TEE_BATCHER_ADDRESS=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc diff --git a/espresso/devnet-tests/challenge_test.go b/espresso/devnet-tests/challenge_test.go index f8dfa4b537d..edfafe8b881 100644 --- a/espresso/devnet-tests/challenge_test.go +++ b/espresso/devnet-tests/challenge_test.go @@ -5,12 +5,16 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/op-challenger/game/types" + "github.com/ethereum-optimism/optimism/op-e2e/bindings" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/stretchr/testify/require" ) +// TestChallengeGame verifies that the succinct proposer creates dispute games +// and that games can be queried from the DisputeGameFactory contract. +// The succinct proposer needs finalized L2 blocks before creating games. func TestChallengeGame(t *testing.T) { - t.Skip("Temporarily skipped: Re-enable once Succinct Integration is investigated.") - ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -20,33 +24,83 @@ func TestChallengeGame(t *testing.T) { require.NoError(t, d.Down()) }() - // Wait for the proposer to make a claim. + // Verify devnet is running and generate some L2 activity + require.NoError(t, d.RunSimpleL2Burn()) + + // Wait a bit for blocks to be produced and batched + t.Log("Waiting for blocks to be produced and batched...") + time.Sleep(10 * time.Second) + + // Wait for the succinct proposer to create a dispute game + // The proposer creates games when safe L2 head >= anchor + proposal_interval (3 blocks) + t.Log("Waiting for succinct-proposer to create a dispute game...") var games []ChallengeGame + maxGameWait := 2 * time.Minute + gameWaitStart := time.Now() + for len(games) == 0 { - var err error - t.Logf("waiting for a challenge game") + if time.Since(gameWaitStart) > maxGameWait { + t.Fatalf("timeout waiting for dispute game to be created (waited %v)", maxGameWait) + } + + t.Logf("waiting for a challenge game to be created by succinct-proposer...") time.Sleep(5 * time.Second) + + var err error games, err = d.ListChallengeGames() - require.NoError(t, err) + if err != nil { + t.Logf("error listing games (will retry): %v", err) + } } - t.Logf("game created: %v", games[0]) - require.Equal(t, uint64(1), games[0].Claims) - // Attack the first claimed state. - t.Logf("attacking game") - require.NoError(t, d.OpChallenger("move", "--attack", "--game-address", games[0].Address.Hex())) + t.Logf("game created: index=%d, address=%s, claims=%d", + games[0].Index, games[0].Address.Hex(), games[0].Claims) + + // Verify the game has at least 1 claim (the root claim from proposer) + require.GreaterOrEqual(t, games[0].Claims, uint64(1), "Game should have at least 1 claim") + + // Bind the dispute game contract and log its initial status. + disputeGame, err := bindings.NewFaultDisputeGame(games[0].Address, d.L1) + require.NoError(t, err) + statusRaw, err := disputeGame.Status(&bind.CallOpts{}) + require.NoError(t, err) + gameStatus, err := types.GameStatusFromUint8(statusRaw) + require.NoError(t, err) + t.Logf("dispute game initial status: %s (%d)", gameStatus.String(), statusRaw) + require.Equal(t, types.GameStatusInProgress, gameStatus, "Dispute game should start InProgress") + + // Observe the dispute game for a limited time to see if it resolves. + maxObservation := 15 * time.Minute + pollInterval := 10 * time.Second + waitStart := time.Now() + finalStatus := gameStatus + finalStatusRaw := statusRaw + + t.Logf("Observing dispute game %s for up to %s to see if it resolves...", games[0].Address.Hex(), maxObservation) - // Check that the proposer correctly responds. - CLAIMS_NUMBER := uint64(3) // First claim by the proposer + attack + response - for { - updatedGames, err := d.ListChallengeGames() + for time.Since(waitStart) < maxObservation { + statusRaw, err := disputeGame.Status(&bind.CallOpts{}) require.NoError(t, err) - if updatedGames[0].Claims == CLAIMS_NUMBER { - require.Equal(t, updatedGames[0].OutputRoot, games[0].OutputRoot) + status, err := types.GameStatusFromUint8(statusRaw) + require.NoError(t, err) + + finalStatus = status + finalStatusRaw = statusRaw + + if status != types.GameStatusInProgress { + t.Logf("dispute game resolved during observation window: %s (%d)", status.String(), statusRaw) + require.Equal(t, types.GameStatusDefenderWon, status, "Expected honest proposer/defender to win succinct dispute game") break } - t.Logf("waiting for a response") - time.Sleep(time.Second) + time.Sleep(pollInterval) } + + t.Logf("dispute game observed final status after %s: %s (%d)", time.Since(waitStart), finalStatus.String(), finalStatusRaw) + require.Equal(t, finalStatus, types.GameStatusDefenderWon, + "succinct dispute game final status must be DefenderWon, got %s (%d)", + finalStatus.String(), finalStatusRaw, + ) + + t.Logf("TestChallengeGame passed: dispute game successfully created by succinct-proposer") } diff --git a/espresso/devnet-tests/devnet_tools.go b/espresso/devnet-tests/devnet_tools.go index adce98af2b9..5bae43e440d 100644 --- a/espresso/devnet-tests/devnet_tools.go +++ b/espresso/devnet-tests/devnet_tools.go @@ -593,29 +593,81 @@ func ParseChallengeGame(s string) (ChallengeGame, error) { } func (d *Devnet) ListChallengeGames() ([]ChallengeGame, error) { - output, err := d.OpChallengerOutput("list-games") + // Succinct only supports contract-based query + games, err := d.ListChallengeGamesFromContract() + if err == nil && len(games) > 0 { + return games, nil + } + + return nil, fmt.Errorf("failed to list challenge games: %w", err) +} + +// ListChallengeGamesFromContract queries games directly from the DisputeGameFactory contract +// Only supports OPSuccinctFaultDisputeGame (game type 42) +func (d *Devnet) ListChallengeGamesFromContract() ([]ChallengeGame, error) { + ctx := d.ctx + + // Get SystemConfig to find DisputeGameFactory address + systemConfig, _, err := d.SystemConfig(ctx) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get system config: %w", err) + } + + factoryAddr, err := systemConfig.DisputeGameFactory(&bind.CallOpts{}) + if err != nil { + return nil, fmt.Errorf("failed to get dispute game factory address: %w", err) + } + + // Bind to DisputeGameFactory + factory, err := bindings.NewDisputeGameFactory(factoryAddr, d.L1) + if err != nil { + return nil, fmt.Errorf("failed to bind to dispute game factory: %w", err) + } + + // Get game count + gameCount, err := factory.GameCount(&bind.CallOpts{}) + if err != nil { + return nil, fmt.Errorf("failed to get game count: %w", err) } var games []ChallengeGame - for i, line := range strings.Split(output, "\n") { - if i == 0 { - // Ignore header. + for i := uint64(0); i < gameCount.Uint64(); i++ { + // Get game at index + gameInfo, err := factory.GameAtIndex(&bind.CallOpts{}, new(big.Int).SetUint64(i)) + if err != nil { + log.Warn("failed to get game at index", "index", i, "error", err) + continue + } + + // Only include game type 42 (OPSuccinctFaultDisputeGame) + if gameInfo.GameType != 42 { continue } - line = strings.TrimSpace(line) - if len(line) == 0 { - // Ignore empty lines (e.g. trailing newline) + + gameProxy := gameInfo.Proxy + + // Get root claim from the game contract + // OPSuccinctFaultDisputeGame only has root claim, no claim tree + disputeGame, err := bindings.NewFaultDisputeGame(gameProxy, d.L1) + if err != nil { + log.Warn("failed to bind to dispute game", "address", gameProxy, "error", err) continue } - game, err := ParseChallengeGame(line) + rootClaim, err := disputeGame.RootClaim(&bind.CallOpts{}) if err != nil { - return nil, fmt.Errorf("game %v is invalid: %w", i, err) + log.Warn("failed to get root claim", "address", gameProxy, "error", err) + continue } - games = append(games, game) + + games = append(games, ChallengeGame{ + Index: i, + Address: gameProxy, + OutputRoot: rootClaim[:], + Claims: 1, // OPSuccinctFaultDisputeGame only has root claim + }) } + return games, nil } diff --git a/espresso/devnet-tests/withdraw_test.go b/espresso/devnet-tests/withdraw_test.go index 4e82589fa82..28cdcec8793 100644 --- a/espresso/devnet-tests/withdraw_test.go +++ b/espresso/devnet-tests/withdraw_test.go @@ -293,7 +293,6 @@ func finalizeWithdrawal(d *Devnet, ctx context.Context, t *testing.T, userAddres func TestWithdrawal(t *testing.T) { t.Skip("Temporarily skipped: Re-enable once Succinct Integration is investigated.") - ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/espresso/docker-compose.yml b/espresso/docker-compose.yml index fd4a769e377..0a366e8d370 100644 --- a/espresso/docker-compose.yml +++ b/espresso/docker-compose.yml @@ -446,8 +446,9 @@ services: export ESPRESSO_URL1="http://127.0.0.1:${ESPRESSO_SEQUENCER_API_PORT}" /source/espresso/docker/op-batcher-tee/run-enclave.sh + # Legacy op-proposer (for non-succinct mode) op-proposer: - profiles: ["default"] + profiles: ["legacy"] build: context: ../ dockerfile: espresso/docker/op-stack/Dockerfile @@ -473,6 +474,39 @@ services: OP_PROPOSER_HD_PATH: "m/44'/60'/0'/0/1" OP_PROPOSER_GAME_TYPE: "1" + # Succinct proposer for ZK fault proofs + succinct-proposer: + profiles: ["default"] + image: ghcr.io/philippecamacho/proposer-eigenda:sha-42c9e14 + depends_on: + l1-data-init: + condition: service_completed_successfully + l2-rollup: + condition: service_completed_successfully + op-node-sequencer: + condition: service_started + op-batcher: + condition: service_started + eigenda-proxy: + condition: service_started + volumes: + - ./deployment/deployer:/deployer:ro + env_file: + - ./deployment/deployer/succinct.env + environment: + L1_RPC: http://l1-geth:${L1_HTTP_PORT} + L1_BEACON_RPC: http://l1-beacon:${L1_BEACON_PORT} + L2_RPC: http://op-geth-verifier:${OP_HTTP_PORT} + L2_NODE_RPC: http://op-node-verifier:${VERIFIER_PORT} + PRIVATE_KEY: ${PROPOSER_PRIVATE_KEY} + GAME_TYPE: "42" + PROPOSAL_INTERVAL_IN_BLOCKS: "3" + FETCH_INTERVAL: "10" + MOCK_MODE: "true" + USE_SAFE_HEAD_FOR_PROPOSALS: "true" + RUST_LOG: info + restart: unless-stopped + op-proposer-tee: profiles: ["tee"] build: @@ -500,8 +534,9 @@ services: OP_PROPOSER_HD_PATH: "m/44'/60'/0'/0/1" OP_PROPOSER_GAME_TYPE: "1" + # Legacy op-challenger (for non-succinct mode) op-challenger: - profiles: ["default"] + profiles: ["legacy"] build: context: ../ dockerfile: espresso/docker/op-stack/Dockerfile @@ -532,6 +567,42 @@ services: OP_CHALLENGER_CANNON_L2_GENESIS: /config/genesis.json OP_CHALLENGER_TRACE_TYPE: permissioned + # Succinct challenger for ZK fault proofs + succinct-challenger: + profiles: ["default"] + image: ghcr.io/philippecamacho/challenger:sha-42c9e14 + depends_on: + l1-geth: + condition: service_started + l1-beacon: + condition: service_started + op-node-sequencer: + condition: service_started + op-geth-sequencer: + condition: service_started + op-node-verifier: + condition: service_started + op-geth-verifier: + condition: service_started + eigenda-proxy: + condition: service_started + volumes: + - ./deployment/deployer:/deployer:ro + env_file: + - ./deployment/deployer/succinct.env + environment: + L1_RPC: http://l1-geth:${L1_HTTP_PORT} + L1_BEACON_RPC: http://l1-beacon:${L1_BEACON_PORT} + L2_RPC: http://op-geth-verifier:${OP_HTTP_PORT} + L2_NODE_RPC: http://op-node-verifier:${VERIFIER_PORT} + PRIVATE_KEY: ${OPERATOR_PRIVATE_KEY} + GAME_TYPE: "42" + FETCH_INTERVAL: "10" + MOCK_MODE: "true" + DISABLE_MONITOR_ONLY: "true" + RUST_LOG: info + restart: unless-stopped + eigenda-proxy: image: ghcr.io/layr-labs/eigenda-proxy:2.2.1 platform: linux/amd64 diff --git a/espresso/docker/op-geth/op-geth-init.sh b/espresso/docker/op-geth/op-geth-init.sh index 12db63dba8c..9ec0b60876c 100644 --- a/espresso/docker/op-geth/op-geth-init.sh +++ b/espresso/docker/op-geth/op-geth-init.sh @@ -72,6 +72,8 @@ elif [ "$MODE" = "geth" ]; then echo "Starting OP Geth..." exec geth \ --datadir=/data/geth \ + --gcmode=archive \ + --state.scheme=hash \ --networkid=${L2_CHAIN_ID} \ --http \ --http.addr=0.0.0.0 \ diff --git a/espresso/scripts/demo_tmux_get_sync_status.sh b/espresso/scripts/demo_tmux_get_sync_status.sh deleted file mode 100755 index 9e3661bfbd8..00000000000 --- a/espresso/scripts/demo_tmux_get_sync_status.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -OP_RPC_SEQUENCER=${OP_RPC_SEQUENCER:-http://localhost:9545} -OP_RPC_VERIFIER=${OP_RPC_VERIFIER:-http://localhost:9546} -OP_RPC_CAFF=${OP_RPC_CAFF:-http://localhost:9547} - -set -euC pipefail - -# Change the current directory to the script's directory -cd "$(dirname "$0")" - -# If the tmux session already exists, we will attach to it. -if tmux has-session -t '=get_sync_status' 2>/dev/null; then - echo "Tmux session 'get_sync_status' already exists. Exiting." - tmux kill-session -t get_sync_status || true -fi - -# Create a new tmux session, detached, named "get_sync_status" -tmux new-session -d -s get_sync_status \; \ - send-keys "NODE_NAME=sequencer RPC_ADDRESS=$OP_RPC_SEQUENCER watch -p -n 1 -c -d ./get_sync_status.sh" ENTER \; \ - split-window -h "NODE_NAME=verifier RPC_ADDRESS=$OP_RPC_VERIFIER watch -p -n 1 -c -d ./get_sync_status.sh" \; \ - split-window -h "NODE_NAME=caff-node RPC_ADDRESS=$OP_RPC_CAFF watch -p -n 1 -c -d ./get_sync_status.sh" \; \ - select-layout even-horizontal \; \ - attach diff --git a/espresso/scripts/prepare-allocs.sh b/espresso/scripts/prepare-allocs.sh index a2505eeb95f..6b1e814fe2d 100755 --- a/espresso/scripts/prepare-allocs.sh +++ b/espresso/scripts/prepare-allocs.sh @@ -38,6 +38,7 @@ trap cleanup EXIT sleep 1 cast rpc anvil_setBalance "${OPERATOR_ADDRESS}" 0x100000000000000000000000000000000000 +cast rpc anvil_setBalance "${PROPOSER_ADDRESS}" 0x100000000000000000000000000000000000 op-deployer bootstrap proxy \ --l1-rpc-url="${ANVIL_URL}" \ @@ -95,10 +96,12 @@ dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].roles.systemConfigOwne dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].roles.unsafeBlockSigner -v "${OPERATOR_ADDRESS}" dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].roles.batcher -v "${OPERATOR_ADDRESS}" dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].roles.proposer -v "${PROPOSER_ADDRESS}" +dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].roles.l1ProxyAdminOwner -v "${OPERATOR_ADDRESS}" +dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].roles.challenger -v "${OPERATOR_ADDRESS}" dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].dangerousAltDAConfig.useAltDA -t bool -v true dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].dangerousAltDAConfig.daCommitmentType -v "GenericCommitment" -dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].dangerousAltDAConfig.daChallengeWindow -t int -v 303 -dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].dangerousAltDAConfig.daResolveWindow -t int -v 303 +dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].dangerousAltDAConfig.daChallengeWindow -t int -v 6 +dasel put -f "${DEPLOYER_DIR}/intent.toml" -s .chains.[0].dangerousAltDAConfig.daResolveWindow -t int -v 1 # Fill in a specified create2Salt for the deployer, in order to ensure that the # contract addresses are deterministic. @@ -108,6 +111,130 @@ BATCH_AUTHENTICATOR_OWNER_ADDRESS="${BATCH_AUTHENTICATOR_OWNER_ADDRESS}" op-depl --workdir "${DEPLOYER_DIR}" \ --private-key="${OPERATOR_PRIVATE_KEY}" +# ===================================================================== +# Deploy op-succinct contracts (OPSuccinctFaultDisputeGame, AccessManager, SP1MockVerifier) +# ===================================================================== +echo "Deploying op-succinct contracts..." + +# Configuration for op-succinct +GAME_TYPE=42 # Succinct game type +MAX_CHALLENGE_DURATION=300 # 5 minutes for devnet +MAX_PROVE_DURATION=1800 # 30 minutes for devnet +FALLBACK_TIMEOUT_FP_SECS=3600 # 1 hour for devnet +INITIAL_BOND_WEI=1000000000000000 # 0.001 ETH +CHALLENGER_BOND_WEI=1000000000000000 # 0.001 ETH +DISPUTE_GAME_FINALITY_DELAY_SECONDS=6 # Fast for devnet + +# Read existing contract addresses from state.json +DISPUTE_GAME_FACTORY=$(jq -r '.opChainDeployments[0].DisputeGameFactoryProxy' "${DEPLOYER_DIR}/state.json") +OPTIMISM_PORTAL=$(jq -r '.opChainDeployments[0].OptimismPortalProxy' "${DEPLOYER_DIR}/state.json") +ANCHOR_STATE_REGISTRY=$(jq -r '.opChainDeployments[0].AnchorStateRegistryProxy' "${DEPLOYER_DIR}/state.json") + +echo "Existing contract addresses:" +echo " DisputeGameFactory: ${DISPUTE_GAME_FACTORY}" +echo " OptimismPortal: ${OPTIMISM_PORTAL}" +echo " AnchorStateRegistry: ${ANCHOR_STATE_REGISTRY}" + +# Get the starting output root from the anchor state registry +# The AnchorStateRegistry was initialized by op-deployer with a starting anchor root +STARTING_ROOT=$(cast call "${ANCHOR_STATE_REGISTRY}" "getAnchorRoot()(bytes32,uint256)" --rpc-url "${ANVIL_URL}" | head -1) +STARTING_L2_BLOCK=$(cast call "${ANCHOR_STATE_REGISTRY}" "getAnchorRoot()(bytes32,uint256)" --rpc-url "${ANVIL_URL}" | tail -1) + +echo "Starting anchor state:" +echo " Root: ${STARTING_ROOT}" +echo " L2 Block: ${STARTING_L2_BLOCK}" + +# Always create succinct.env with contract addresses (even if op-succinct repo not found) +# Note: op-succinct uses FACTORY_ADDRESS not DISPUTE_GAME_FACTORY_ADDRESS +SUCCINCT_ENV_FILE="${DEPLOYER_DIR}/succinct.env" +cat > "${SUCCINCT_ENV_FILE}" << EOF +FACTORY_ADDRESS=${DISPUTE_GAME_FACTORY} +DISPUTE_GAME_FACTORY_ADDRESS=${DISPUTE_GAME_FACTORY} +OPTIMISM_PORTAL2_ADDRESS=${OPTIMISM_PORTAL} +ANCHOR_STATE_REGISTRY_ADDRESS=${ANCHOR_STATE_REGISTRY} +EOF +echo "Created succinct env file at ${SUCCINCT_ENV_FILE}" + +# Deploy op-succinct contracts using local contracts-bedrock deployment script + +echo "Deploying op-succinct contracts from local packages/contracts-bedrock..." + +# Export environment variables consumed by DeployOPSuccinctFDG.s.sol +export FACTORY_ADDRESS="${DISPUTE_GAME_FACTORY}" +export ANCHOR_STATE_REGISTRY_ADDRESS="${ANCHOR_STATE_REGISTRY}" +export GAME_TYPE="${GAME_TYPE}" +export INITIAL_BOND_WEI="${INITIAL_BOND_WEI}" +export CHALLENGER_BOND_WEI="${CHALLENGER_BOND_WEI}" +export MAX_CHALLENGE_DURATION="${MAX_CHALLENGE_DURATION}" +export MAX_PROVE_DURATION="${MAX_PROVE_DURATION}" +export USE_SP1_MOCK_VERIFIER="true" + +pushd "${OP_ROOT}/packages/contracts-bedrock" + +echo "Running local DeployOPSuccinctFDG.s.sol script..." +forge script scripts/deploy/DeployOPSuccinctFDG.s.sol \ + --broadcast \ + --no-storage-caching \ + --slow \ + --rpc-url "${ANVIL_URL}" \ + --private-key "${OPERATOR_PRIVATE_KEY}" || echo "Warning: local op-succinct deployment failed, continuing..." + +popd + +# Wire OPSuccinctFaultDisputeGame implementation into DisputeGameFactory +echo "Configuring DisputeGameFactory for game type ${GAME_TYPE}..." + +# Locate the deployed OPSuccinctFaultDisputeGame implementation address from the forge broadcast +BROADCAST_DIR="${OP_ROOT}/packages/contracts-bedrock/broadcast/DeployOPSuccinctFDG.s.sol/${L1_CHAIN_ID}" +BROADCAST_FILE="${BROADCAST_DIR}/run-latest.json" + +if [ -f "${BROADCAST_FILE}" ]; then + FDG_IMPL=$(jq -r '.transactions[] | select(.contractName=="OPSuccinctFaultDisputeGame") | .contractAddress' "${BROADCAST_FILE}") + if [ -z "${FDG_IMPL}" ] || [ "${FDG_IMPL}" = "null" ]; then + echo "Warning: could not find OPSuccinctFaultDisputeGame in ${BROADCAST_FILE}; skipping factory config" + else + echo " OPSuccinctFaultDisputeGame implementation: ${FDG_IMPL}" + # Set implementation for GAME_TYPE on the factory + cast send "${DISPUTE_GAME_FACTORY}" \ + "setImplementation(uint32,address)" "${GAME_TYPE}" "${FDG_IMPL}" \ + --rpc-url "${ANVIL_URL}" \ + --private-key "${OPERATOR_PRIVATE_KEY}" || echo "Warning: setImplementation failed, continuing..." + + # Set init bond for GAME_TYPE to match challenger bond + cast send "${DISPUTE_GAME_FACTORY}" \ + "setInitBond(uint32,uint256)" "${GAME_TYPE}" "${CHALLENGER_BOND_WEI}" \ + --rpc-url "${ANVIL_URL}" \ + --private-key "${OPERATOR_PRIVATE_KEY}" || echo "Warning: setInitBond failed, continuing..." + fi +else + echo "Warning: broadcast file ${BROADCAST_FILE} not found; skipping factory config" +fi + +# Manually activate game type on AnchorStateRegistry (op-succinct script targets OptimismPortal2) +echo "Activating game type ${GAME_TYPE} on AnchorStateRegistry..." +cast send "${ANCHOR_STATE_REGISTRY}" \ + "setRespectedGameType(uint32)" "${GAME_TYPE}" \ + --rpc-url "${ANVIL_URL}" \ + --private-key "${OPERATOR_PRIVATE_KEY}" || echo "Warning: setRespectedGameType failed (may need Guardian role)" + +# Verify the starting anchor root is set (should be set by op-deployer initialization) +echo "Verifying starting anchor root..." +ANCHOR_ROOT=$(cast call "${ANCHOR_STATE_REGISTRY}" "getAnchorRoot()(bytes32,uint256)" --rpc-url "${ANVIL_URL}" | head -1) +ANCHOR_BLOCK=$(cast call "${ANCHOR_STATE_REGISTRY}" "getAnchorRoot()(bytes32,uint256)" --rpc-url "${ANVIL_URL}" | tail -1) +echo " Anchor root: ${ANCHOR_ROOT}" +echo " Anchor L2 block: ${ANCHOR_BLOCK}" + +if [ "${ANCHOR_ROOT}" = "0x0000000000000000000000000000000000000000000000000000000000000000" ]; then + echo "ERROR: Anchor root is zero! AnchorStateRegistry was not initialized correctly." + echo "This will cause 'AnchorRootNotFound' errors when creating games." +fi + +echo "Local op-succinct contracts deployment complete!" + +# ===================================================================== +# End of op-succinct deployment +# ===================================================================== + # Dump anvil state via RPC before killing it cast rpc anvil_dumpState > "${ANVIL_STATE_FILE}" diff --git a/justfile b/justfile index c63af035b1d..8dcec591871 100644 --- a/justfile +++ b/justfile @@ -15,8 +15,12 @@ devnet-tests: build-devnet devnet-smoke-test-without-tee: build-devnet U_ID={{uid}} GID={{gid}} go test -timeout 30m -p 1 -count 1 -run 'TestSmokeWithoutTEE' -v ./espresso/devnet-tests/... -devnet-withdrawal-test: build-devnet - U_ID={{uid}} GID={{gid}} go test -timeout 30m -p 1 -count 1 -v -run TestWithdraw ./espresso/devnet-tests/... +devnet-challenge-test: build-devnet + U_ID={{uid}} GID={{gid}} go test -timeout 30m -p 1 -count 1 -v -run TestChallengeGame ./espresso/devnet-tests/... + + +devnet-withdraw-test: build-devnet + U_ID={{uid}} GID={{gid}} go test -timeout 30m -p 1 -count 1 -v -run TestWithdrawal ./espresso/devnet-tests/... build-devnet: compile-contracts rm -Rf espresso/deployment diff --git a/op-batcher/bindings/batch_authenticator.go b/op-batcher/bindings/batch_authenticator.go index 2747dea26d1..f279acfe157 100644 --- a/op-batcher/bindings/batch_authenticator.go +++ b/op-batcher/bindings/batch_authenticator.go @@ -31,8 +31,8 @@ var ( // BatchAuthenticatorMetaData contains all meta data concerning the BatchAuthenticator contract. var BatchAuthenticatorMetaData = &bind.MetaData{ - ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_espressoTEEVerifier\",\"type\":\"address\",\"internalType\":\"contractEspressoTEEVerifier\"},{\"name\":\"_teeBatcher\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_nonTeeBatcher\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"activeIsTee\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"authenticateBatchInfo\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"_signature\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"decodeAttestationTbs\",\"inputs\":[{\"name\":\"attestation\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"espressoTEEVerifier\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractEspressoTEEVerifier\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"nitroValidator\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractINitroValidator\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"nonTeeBatcher\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"registerSigner\",\"inputs\":[{\"name\":\"attestationTbs\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"signature\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"registerSignerWithoutAttestationVerification\",\"inputs\":[{\"name\":\"pcr0Hash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"attestationTbs\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"signature\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"enclaveAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"switchBatcher\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"teeBatcher\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"validBatchInfo\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false}]", - Bin: "0x6101006040523461007b5761001e610015610197565b9291909161045e565b610026610080565b611dc1610668823960805181818161088001526115ba015260a0518161075e015260c0518181816109b801528181610bc501528181610f9201526114b9015260e05181818161029b0152610e780152611dc190f35b610086565b60405190565b5f80fd5b601f801991011690565b634e487b7160e01b5f52604160045260245ffd5b906100b29061008a565b810190811060018060401b038211176100ca57604052565b610094565b906100e26100db610080565b92836100a8565b565b5f80fd5b60018060a01b031690565b6100fc906100e8565b90565b610108906100f3565b90565b610114816100ff565b0361011b57565b5f80fd5b9050519061012c8261010b565b565b610137816100f3565b0361013e57565b5f80fd5b9050519061014f8261012e565b565b60808183031261019257610167825f830161011f565b9261018f6101788460208501610142565b936101868160408601610142565b93606001610142565b90565b6100e4565b6101b5612429803803806101aa816100cf565b928339810190610151565b90919293565b90565b90565b6101d56101d06101da926101bb565b6101be565b6100e8565b90565b6101e6906101c1565b90565b60209181520190565b60207f6368657200000000000000000000000000000000000000000000000000000000917f426174636841757468656e74696361746f723a207a65726f20746565206261745f8201520152565b61024c60246040926101e9565b610255816101f2565b0190565b61026e9060208101905f81830391015261023f565b90565b1561027857565b610280610080565b62461bcd60e51b81528061029660048201610259565b0390fd5b60207f2062617463686572000000000000000000000000000000000000000000000000917f426174636841757468656e74696361746f723a207a65726f206e6f6e2d7465655f8201520152565b6102f460286040926101e9565b6102fd8161029a565b0190565b6103169060208101905f8183039101526102e7565b90565b1561032057565b610328610080565b62461bcd60e51b81528061033e60048201610301565b0390fd5b61034c90516100ff565b90565b61036361035e610368926100e8565b6101be565b6100e8565b90565b6103749061034f565b90565b6103809061036b565b90565b60e01b90565b610392906100f3565b90565b61039e81610389565b036103a557565b5f80fd5b905051906103b682610395565b565b906020828203126103d1576103ce915f016103a9565b90565b6100e4565b5f0190565b6103e3610080565b3d5f823e3d90fd5b6103f49061036b565b90565b6104009061034f565b90565b61040c906103f7565b90565b5f1b90565b9061042060ff9161040f565b9181191691161790565b151590565b6104389061042a565b90565b90565b9061045361044e61045a9261042f565b61043b565b8254610414565b9055565b906104ea93929161046d61056a565b6104928261048b6104856104805f6101dd565b6100f3565b916100f3565b1415610271565b6104b7836104b06104aa6104a55f6101dd565b6100f3565b916100f3565b1415610319565b60c05260805260a05260206104d46104cf60c0610342565b610377565b63d80a4c28906104e2610080565b948592610383565b825281806104fa600482016103d6565b03915afa80156105655761051c61052191610535945f91610537575b506103eb565b610403565b60e0526105306001600261043e565b6105f7565b565b610558915060203d811161055e575b61055081836100a8565b8101906103b8565b5f610516565b503d610546565b6103db565b61057a61057561065a565b6105f7565b565b5f1c90565b60018060a01b031690565b61059861059d9161057c565b610581565b90565b6105aa905461058c565b90565b906105be60018060a01b039161040f565b9181191691161790565b6105d19061036b565b90565b90565b906105ec6105e76105f3926105c8565b6105d4565b82546105ad565b9055565b6106005f6105a0565b61060a825f6105d7565b9061063e6106387f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0936105c8565b916105c8565b91610647610080565b80610651816103d6565b0390a3565b5f90565b610662610656565b50339056fe60806040526004361015610013575b610ab7565b61001d5f3561010c565b806302afd6e3146101075780631b076a4c1461010257806354fd4d50146100fd578063715018a6146100f85780637877a9ed146100f35780638da5cb5b146100ee578063a903a277146100e9578063b1bd4285146100e4578063ba58e82a146100df578063bc347f47146100da578063d909ba7c146100d5578063f2fde38b146100d0578063f81f2083146100cb578063fa14fe6d146100c65763fc619e410361000e57610a83565b610a08565b610981565b6108f5565b6108a2565b61084b565b610814565b610780565b610726565b6105c8565b610571565b6104d8565b6104a3565b610316565b610250565b60e01c90565b60405190565b5f80fd5b5f80fd5b5f80fd5b90565b61013081610124565b0361013757565b5f80fd5b9050359061014882610127565b565b5f80fd5b5f80fd5b5f80fd5b909182601f830112156101905781359167ffffffffffffffff831161018b57602001926001830284011161018657565b610152565b61014e565b61014a565b60018060a01b031690565b6101a990610195565b90565b6101b5816101a0565b036101bc57565b5f80fd5b905035906101cd826101ac565b565b9190608083820312610246576101e7815f850161013b565b92602081013567ffffffffffffffff81116102415782610208918301610156565b929093604083013567ffffffffffffffff811161023c5761022e83610239928601610156565b9390946060016101c0565b90565b610120565b610120565b61011c565b5f0190565b346102855761026f6102633660046101cf565b94939093929192610bb6565b610277610112565b806102818161024b565b0390f35b610118565b5f91031261029457565b61011c565b7f000000000000000000000000000000000000000000000000000000000000000090565b90565b6102d46102cf6102d992610195565b6102bd565b610195565b90565b6102e5906102c0565b90565b6102f1906102dc565b90565b6102fd906102e8565b9052565b9190610314905f602085019401906102f4565b565b346103465761032636600461028a565b610342610331610299565b610339610112565b91829182610301565b0390f35b610118565b601f801991011690565b634e487b7160e01b5f52604160045260245ffd5b906103739061034b565b810190811067ffffffffffffffff82111761038d57604052565b610355565b906103a561039e610112565b9283610369565b565b67ffffffffffffffff81116103c5576103c160209161034b565b0190565b610355565b906103dc6103d7836103a7565b610392565b918252565b5f7f312e302e30000000000000000000000000000000000000000000000000000000910152565b61041260056103ca565b9061041f602083016103e1565b565b610429610408565b90565b610434610421565b90565b61043f61042c565b90565b5190565b60209181520190565b90825f9392825e0152565b6104796104826020936104879361047081610442565b93848093610446565b9586910161044f565b61034b565b0190565b6104a09160208201915f81840391015261045a565b90565b346104d3576104b336600461028a565b6104cf6104be610437565b6104c6610112565b9182918261048b565b0390f35b610118565b34610506576104e836600461028a565b6104f0610d34565b6104f8610112565b806105028161024b565b0390f35b610118565b1c90565b60ff1690565b61052590600861052a930261050b565b61050f565b90565b906105389154610515565b90565b61054760025f9061052d565b90565b151590565b6105589061054a565b9052565b919061056f905f6020850194019061054f565b565b346105a15761058136600461028a565b61059d61058c61053b565b610594610112565b9182918261055c565b0390f35b610118565b6105af906101a0565b9052565b91906105c6905f602085019401906105a6565b565b346105f8576105d836600461028a565b6105f46105e3610d73565b6105eb610112565b918291826105b3565b0390f35b610118565b5f80fd5b67ffffffffffffffff811161061f5761061b60209161034b565b0190565b610355565b90825f939282370152565b9092919261064461063f82610601565b610392565b938185526020850190828401116106605761065e92610624565b565b6105fd565b9080601f83011215610683578160206106809335910161062f565b90565b61014a565b906020828203126106b8575f82013567ffffffffffffffff81116106b3576106b09201610665565b90565b610120565b61011c565b5190565b60209181520190565b6106e96106f26020936106f7936106e0816106bd565b938480936106c1565b9586910161044f565b61034b565b0190565b90916107156107239360408401908482035f8601526106ca565b9160208184039101526106ca565b90565b346107575761073e610739366004610688565b610e5b565b9061075361074a610112565b928392836106fb565b0390f35b610118565b7f000000000000000000000000000000000000000000000000000000000000000090565b346107b05761079036600461028a565b6107ac61079b61075c565b6107a3610112565b918291826105b3565b0390f35b610118565b909160408284031261080f575f82013567ffffffffffffffff811161080a57836107e0918401610156565b929093602082013567ffffffffffffffff8111610805576108019201610156565b9091565b610120565b610120565b61011c565b34610846576108306108273660046107b5565b92919091610f8a565b610838610112565b806108428161024b565b0390f35b610118565b346108795761085b36600461028a565b6108636110d9565b61086b610112565b806108758161024b565b0390f35b610118565b7f000000000000000000000000000000000000000000000000000000000000000090565b346108d2576108b236600461028a565b6108ce6108bd61087e565b6108c5610112565b918291826105b3565b0390f35b610118565b906020828203126108f0576108ed915f016101c0565b90565b61011c565b346109235761090d6109083660046108d7565b6111ce565b610915610112565b8061091f8161024b565b0390f35b610118565b906020828203126109415761093e915f0161013b565b90565b61011c565b61094f90610124565b90565b9061095c90610946565b5f5260205260405f2090565b61097e906109796001915f92610952565b61052d565b90565b346109b1576109ad61099c610997366004610928565b610968565b6109a4610112565b9182918261055c565b0390f35b610118565b7f000000000000000000000000000000000000000000000000000000000000000090565b6109e3906102dc565b90565b6109ef906109da565b9052565b9190610a06905f602085019401906109e6565b565b34610a3857610a1836600461028a565b610a34610a236109b6565b610a2b610112565b918291826109f3565b0390f35b610118565b919091604081840312610a7e57610a56835f830161013b565b92602082013567ffffffffffffffff8111610a7957610a759201610156565b9091565b610120565b61011c565b34610ab257610a9c610a96366004610a3d565b91611436565b610aa4610112565b80610aae8161024b565b0390f35b610118565b5f80fd5b5f80fd5b60e01b90565b610ace906101a0565b90565b610ada81610ac5565b03610ae157565b5f80fd5b90505190610af282610ad1565b565b90602082820312610b0d57610b0a915f01610ae5565b90565b61011c565b610b1a610112565b3d5f823e3d90fd5b610b2b906102dc565b90565b5f910312610b3857565b61011c565b610b4690610124565b9052565b9190610b6481610b5d81610b69956106c1565b8095610624565b61034b565b0190565b9695939094610b9e88606095610bac95610b91610bb49a5f60808601950190610b3d565b8b830360208d0152610b4a565b9188830360408a0152610b4a565b9401906105a6565b565b9194909293610bff6020610be97f00000000000000000000000000000000000000000000000000000000000000006109da565b63d80a4c2890610bf7610112565b938492610abf565b82528180610c0f6004820161024b565b03915afa8015610cdf57610c2a915f91610cb1575b50610b22565b926302afd6e390949695919295843b15610cac575f96610c5e948894610c6993610c52610112565b9b8c9a8b998a98610abf565b885260048801610b6d565b03925af18015610ca757610c7b575b50565b610c9a905f3d8111610ca0575b610c928183610369565b810190610b2e565b5f610c78565b503d610c88565b610b12565b610abb565b610cd2915060203d8111610cd8575b610cca8183610369565b810190610af4565b5f610c24565b503d610cc0565b610b12565b610cec61174a565b610cf4610d21565b565b90565b610d0d610d08610d1292610cf6565b6102bd565b610195565b90565b610d1e90610cf9565b90565b610d32610d2d5f610d15565b6117c0565b565b610d3c610ce4565b565b5f90565b5f1c90565b60018060a01b031690565b610d5e610d6391610d42565b610d47565b90565b610d709054610d52565b90565b610d7b610d3e565b50610d855f610d66565b90565b606090565b90929192610da2610d9d82610601565b610392565b93818552602085019082840111610dbe57610dbc9261044f565b565b6105fd565b9080601f83011215610de157816020610dde93519101610d8d565b90565b61014a565b919091604081840312610e3e575f81015167ffffffffffffffff8111610e395783610e12918301610dc3565b92602082015167ffffffffffffffff8111610e3457610e319201610dc3565b90565b610120565b610120565b61011c565b610e589160208201915f8184039101526106ca565b90565b905f610ec392610e69610d88565b50610e72610d88565b50610e9c7f00000000000000000000000000000000000000000000000000000000000000006102e8565b610eb863a903a277610eac610112565b96879485938493610abf565b835260048301610e43565b03915afa8015610f03575f80939091610edc575b509190565b9050610efb9192503d805f833e610ef38183610369565b810190610de6565b91905f610ed7565b610b12565b634e487b7160e01b5f52602160045260245ffd5b60021115610f2657565b610f08565b90610f3582610f1c565b565b610f4090610f2b565b90565b610f4c90610f37565b9052565b959492610f8894610f72610f809360409560608b01918b83035f8d0152610b4a565b9188830360208a0152610b4a565b940190610f43565b565b929192610fb67f00000000000000000000000000000000000000000000000000000000000000006109da565b906335ecb4c190929493600191833b1561103857610ff5610fea935f97938894610fde610112565b9a8b998a988997610abf565b875260048701610f50565b03925af1801561103357611007575b50565b611026905f3d811161102c575b61101e8183610369565b810190610b2e565b5f611004565b503d611014565b610b12565b610abb565b61104561174a565b61104d6110ba565b565b61105b61106091610d42565b61050f565b90565b61106d905461104f565b90565b5f1b90565b9061108160ff91611070565b9181191691161790565b6110949061054a565b90565b90565b906110af6110aa6110b69261108b565b611097565b8254611075565b9055565b6110d76110d06110ca6002611063565b1561054a565b600261109a565b565b6110e161103d565b565b6110f4906110ef61174a565b61119e565b565b60207f6464726573730000000000000000000000000000000000000000000000000000917f4f776e61626c653a206e6577206f776e657220697320746865207a65726f20615f8201520152565b6111506026604092610446565b611159816110f6565b0190565b6111729060208101905f818303910152611143565b90565b1561117c57565b611184610112565b62461bcd60e51b81528061119a6004820161115d565b0390fd5b6111cc906111c7816111c06111ba6111b55f610d15565b6101a0565b916101a0565b1415611175565b6117c0565b565b6111d7906110e3565b565b6111e491369161062f565b90565b634e487b7160e01b5f52603260045260245ffd5b90611205826106bd565b81101561121757600160209102010190565b6111e7565b90565b90565b61123661123161123b9261121c565b6102bd565b61121f565b90565b60ff60f81b1690565b611251905161123e565b90565b60f81c90565b60ff1690565b61127461126f6112799261125a565b6102bd565b61125a565b90565b61128861128d91611254565b611260565b90565b6112a461129f6112a992610cf6565b6102bd565b61125a565b90565b90565b6112c36112be6112c8926112ac565b6102bd565b61125a565b90565b90565b6112e26112dd6112e7926112cb565b6102bd565b61125a565b90565b634e487b7160e01b5f52601160045260245ffd5b61130a6113109161125a565b9161125a565b019060ff821161131c57565b6112ea565b60f81b90565b61133b6113366113409261125a565b611321565b61123e565b90565b5f7f496e76616c6964207369676e6174757265000000000000000000000000000000910152565b6113776011602092610446565b61138081611343565b0190565b6113999060208101905f81830391015261136a565b90565b6113a58161054a565b036113ac57565b5f80fd5b905051906113bd8261139c565b565b906020828203126113d8576113d5915f016113b0565b90565b61011c565b5f7f496e76616c6964207369676e6572000000000000000000000000000000000000910152565b611411600e602092610446565b61141a816113dd565b0190565b6114339060208101905f818303910152611404565b90565b916114449061148f926111d9565b61146861146361145e836114586040611222565b906111fb565b611247565b61127c565b8061147b6114755f611290565b9161125a565b1480156116ae575b611673575b508261181f565b806114aa6114a461149f5f610d15565b6101a0565b916101a0565b14611651576114f360206114dd7f00000000000000000000000000000000000000000000000000000000000000006109da565b63d80a4c28906114eb610112565b938492610abf565b825281806115036004820161024b565b03915afa801561164c5761152460209161154e935f9161161f575b50610b22565b630123d0c1906115438592611537610112565b95869485938493610abf565b8352600483016105b3565b03915afa801561161a5761156a915f916115ec575b501561054a565b90816115b0575b5061158e5761158c906115876001916001610952565b61109a565b565b611596610112565b62461bcd60e51b8152806115ac6004820161141e565b0390fd5b90506115e46115de7f00000000000000000000000000000000000000000000000000000000000000006101a0565b916101a0565b14155f611571565b61160d915060203d8111611613575b6116058183610369565b8101906113bf565b5f611563565b503d6115fb565b610b12565b61163f9150833d8111611645575b6116378183610369565b810190610af4565b5f61151e565b503d61162d565b610b12565b611659610112565b62461bcd60e51b81528061166f60048201611384565b0390fd5b61168a61168f91611684601b6112ce565b906112fe565b611327565b6116a7826116a16040935f1a93611222565b906111fb565b535f611488565b50806116c36116bd60016112af565b9161125a565b14611483565b5f7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572910152565b6116fc60208092610446565b611705816116c9565b0190565b61171e9060208101905f8183039101526116f0565b90565b1561172857565b611730610112565b62461bcd60e51b81528061174660048201611709565b0390fd5b611774611755610d73565b61176e611768611763611840565b6101a0565b916101a0565b14611721565b565b9061178760018060a01b0391611070565b9181191691161790565b61179a906102dc565b90565b90565b906117b56117b06117bc92611791565b61179d565b8254611776565b9055565b6117c95f610d66565b6117d3825f6117a0565b906118076118017f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e093611791565b91611791565b91611810610112565b8061181a8161024b565b0390a3565b61183d916118359161182f610d3e565b50611878565b919091611ac7565b90565b611848610d3e565b503390565b5f90565b90565b61186861186361186d92611851565b6102bd565b61121f565b90565b5f90565b5f90565b611880610d3e565b5061188961184d565b50611893826106bd565b6118a66118a06041611854565b9161121f565b145f146118eb576118e5916118b9611870565b506118c2611870565b506118cb611874565b506020810151606060408301519201515f1a909192611c90565b91909190565b50506118f65f610d15565b90600290565b6005111561190657565b610f08565b90611915826118fc565b565b60207f7565000000000000000000000000000000000000000000000000000000000000917f45434453413a20696e76616c6964207369676e6174757265202776272076616c5f8201520152565b6119716022604092610446565b61197a81611917565b0190565b6119939060208101905f818303910152611964565b90565b60207f7565000000000000000000000000000000000000000000000000000000000000917f45434453413a20696e76616c6964207369676e6174757265202773272076616c5f8201520152565b6119f06022604092610446565b6119f981611996565b0190565b611a129060208101905f8183039101526119e3565b90565b5f7f45434453413a20696e76616c6964207369676e6174757265206c656e67746800910152565b611a49601f602092610446565b611a5281611a15565b0190565b611a6b9060208101905f818303910152611a3c565b90565b5f7f45434453413a20696e76616c6964207369676e61747572650000000000000000910152565b611aa26018602092610446565b611aab81611a6e565b0190565b611ac49060208101905f818303910152611a95565b90565b80611ada611ad45f61190b565b9161190b565b145f14611ae45750565b80611af8611af2600161190b565b9161190b565b145f14611b2157611b07610112565b62461bcd60e51b815280611b1d60048201611aaf565b0390fd5b80611b35611b2f600261190b565b9161190b565b145f14611b5e57611b44610112565b62461bcd60e51b815280611b5a60048201611a56565b0390fd5b80611b72611b6c600361190b565b9161190b565b145f14611b9b57611b81610112565b62461bcd60e51b815280611b97600482016119fd565b0390fd5b611bae611ba8600461190b565b9161190b565b14611bb557565b611bbd610112565b62461bcd60e51b815280611bd36004820161197e565b0390fd5b611beb611be6611bf09261121f565b6102bd565b61121f565b90565b611bff611c0491610d42565b611bd7565b90565b90565b611c1e611c19611c2392611c07565b6102bd565b61121f565b90565b90565b611c3d611c38611c4292611c26565b6102bd565b61125a565b90565b611c4e9061125a565b9052565b611c87611c8e94611c7d606094989795611c73608086019a5f870190610b3d565b6020850190611c45565b6040830190610b3d565b0190610b3d565b565b929190611c9b610d3e565b50611ca461184d565b50611cae83611bf3565b611ce0611cda7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0611c0a565b9161121f565b11611da15780611cf9611cf3601b6112ce565b9161125a565b141580611d85575b611d7257611d205f936020959293611d17610112565b94859485611c52565b838052039060015afa15611d6d57611d385f51611070565b80611d53611d4d611d485f610d15565b6101a0565b916101a0565b14611d5d57905f90565b50611d675f610d15565b90600190565b610b12565b50505050611d7f5f610d15565b90600490565b5080611d9a611d94601c611c29565b9161125a565b1415611d01565b50505050611dae5f610d15565b9060039056fea164736f6c634300081e000a", + ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_espressoTEEVerifier\",\"type\":\"address\",\"internalType\":\"contractIEspressoTEEVerifier\"},{\"name\":\"_teeBatcher\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_nonTeeBatcher\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"activeIsTee\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"authenticateBatchInfo\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"_signature\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"decodeAttestationTbs\",\"inputs\":[{\"name\":\"attestation\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"espressoTEEVerifier\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractIEspressoTEEVerifier\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"nitroValidator\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractINitroValidator\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"nonTeeBatcher\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"registerSigner\",\"inputs\":[{\"name\":\"attestationTbs\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"signature\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"registerSignerWithoutAttestationVerification\",\"inputs\":[{\"name\":\"pcr0Hash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"attestationTbs\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"signature\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"enclaveAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"switchBatcher\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"teeBatcher\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"validBatchInfo\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false}]", + Bin: "0x6101006040523461007b5761001e610015610197565b9291909161045e565b610026610080565b611dc1610668823960805181818161088001526115ba015260a0518161075e015260c0518181816109b801528181610bc501528181610f9201526114b9015260e05181818161029b0152610e780152611dc190f35b610086565b60405190565b5f80fd5b601f801991011690565b634e487b7160e01b5f52604160045260245ffd5b906100b29061008a565b810190811060018060401b038211176100ca57604052565b610094565b906100e26100db610080565b92836100a8565b565b5f80fd5b60018060a01b031690565b6100fc906100e8565b90565b610108906100f3565b90565b610114816100ff565b0361011b57565b5f80fd5b9050519061012c8261010b565b565b610137816100f3565b0361013e57565b5f80fd5b9050519061014f8261012e565b565b60808183031261019257610167825f830161011f565b9261018f6101788460208501610142565b936101868160408601610142565b93606001610142565b90565b6100e4565b6101b5612429803803806101aa816100cf565b928339810190610151565b90919293565b90565b90565b6101d56101d06101da926101bb565b6101be565b6100e8565b90565b6101e6906101c1565b90565b60209181520190565b60207f6368657200000000000000000000000000000000000000000000000000000000917f426174636841757468656e74696361746f723a207a65726f20746565206261745f8201520152565b61024c60246040926101e9565b610255816101f2565b0190565b61026e9060208101905f81830391015261023f565b90565b1561027857565b610280610080565b62461bcd60e51b81528061029660048201610259565b0390fd5b60207f2062617463686572000000000000000000000000000000000000000000000000917f426174636841757468656e74696361746f723a207a65726f206e6f6e2d7465655f8201520152565b6102f460286040926101e9565b6102fd8161029a565b0190565b6103169060208101905f8183039101526102e7565b90565b1561032057565b610328610080565b62461bcd60e51b81528061033e60048201610301565b0390fd5b61034c90516100ff565b90565b61036361035e610368926100e8565b6101be565b6100e8565b90565b6103749061034f565b90565b6103809061036b565b90565b60e01b90565b610392906100f3565b90565b61039e81610389565b036103a557565b5f80fd5b905051906103b682610395565b565b906020828203126103d1576103ce915f016103a9565b90565b6100e4565b5f0190565b6103e3610080565b3d5f823e3d90fd5b6103f49061036b565b90565b6104009061034f565b90565b61040c906103f7565b90565b5f1b90565b9061042060ff9161040f565b9181191691161790565b151590565b6104389061042a565b90565b90565b9061045361044e61045a9261042f565b61043b565b8254610414565b9055565b906104ea93929161046d61056a565b6104928261048b6104856104805f6101dd565b6100f3565b916100f3565b1415610271565b6104b7836104b06104aa6104a55f6101dd565b6100f3565b916100f3565b1415610319565b60c05260805260a05260206104d46104cf60c0610342565b610377565b63d80a4c28906104e2610080565b948592610383565b825281806104fa600482016103d6565b03915afa80156105655761051c61052191610535945f91610537575b506103eb565b610403565b60e0526105306001600261043e565b6105f7565b565b610558915060203d811161055e575b61055081836100a8565b8101906103b8565b5f610516565b503d610546565b6103db565b61057a61057561065a565b6105f7565b565b5f1c90565b60018060a01b031690565b61059861059d9161057c565b610581565b90565b6105aa905461058c565b90565b906105be60018060a01b039161040f565b9181191691161790565b6105d19061036b565b90565b90565b906105ec6105e76105f3926105c8565b6105d4565b82546105ad565b9055565b6106005f6105a0565b61060a825f6105d7565b9061063e6106387f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0936105c8565b916105c8565b91610647610080565b80610651816103d6565b0390a3565b5f90565b610662610656565b50339056fe60806040526004361015610013575b610ab7565b61001d5f3561010c565b806302afd6e3146101075780631b076a4c1461010257806354fd4d50146100fd578063715018a6146100f85780637877a9ed146100f35780638da5cb5b146100ee578063a903a277146100e9578063b1bd4285146100e4578063ba58e82a146100df578063bc347f47146100da578063d909ba7c146100d5578063f2fde38b146100d0578063f81f2083146100cb578063fa14fe6d146100c65763fc619e410361000e57610a83565b610a08565b610981565b6108f5565b6108a2565b61084b565b610814565b610780565b610726565b6105c8565b610571565b6104d8565b6104a3565b610316565b610250565b60e01c90565b60405190565b5f80fd5b5f80fd5b5f80fd5b90565b61013081610124565b0361013757565b5f80fd5b9050359061014882610127565b565b5f80fd5b5f80fd5b5f80fd5b909182601f830112156101905781359167ffffffffffffffff831161018b57602001926001830284011161018657565b610152565b61014e565b61014a565b60018060a01b031690565b6101a990610195565b90565b6101b5816101a0565b036101bc57565b5f80fd5b905035906101cd826101ac565b565b9190608083820312610246576101e7815f850161013b565b92602081013567ffffffffffffffff81116102415782610208918301610156565b929093604083013567ffffffffffffffff811161023c5761022e83610239928601610156565b9390946060016101c0565b90565b610120565b610120565b61011c565b5f0190565b346102855761026f6102633660046101cf565b94939093929192610bb6565b610277610112565b806102818161024b565b0390f35b610118565b5f91031261029457565b61011c565b7f000000000000000000000000000000000000000000000000000000000000000090565b90565b6102d46102cf6102d992610195565b6102bd565b610195565b90565b6102e5906102c0565b90565b6102f1906102dc565b90565b6102fd906102e8565b9052565b9190610314905f602085019401906102f4565b565b346103465761032636600461028a565b610342610331610299565b610339610112565b91829182610301565b0390f35b610118565b601f801991011690565b634e487b7160e01b5f52604160045260245ffd5b906103739061034b565b810190811067ffffffffffffffff82111761038d57604052565b610355565b906103a561039e610112565b9283610369565b565b67ffffffffffffffff81116103c5576103c160209161034b565b0190565b610355565b906103dc6103d7836103a7565b610392565b918252565b5f7f312e302e30000000000000000000000000000000000000000000000000000000910152565b61041260056103ca565b9061041f602083016103e1565b565b610429610408565b90565b610434610421565b90565b61043f61042c565b90565b5190565b60209181520190565b90825f9392825e0152565b6104796104826020936104879361047081610442565b93848093610446565b9586910161044f565b61034b565b0190565b6104a09160208201915f81840391015261045a565b90565b346104d3576104b336600461028a565b6104cf6104be610437565b6104c6610112565b9182918261048b565b0390f35b610118565b34610506576104e836600461028a565b6104f0610d34565b6104f8610112565b806105028161024b565b0390f35b610118565b1c90565b60ff1690565b61052590600861052a930261050b565b61050f565b90565b906105389154610515565b90565b61054760025f9061052d565b90565b151590565b6105589061054a565b9052565b919061056f905f6020850194019061054f565b565b346105a15761058136600461028a565b61059d61058c61053b565b610594610112565b9182918261055c565b0390f35b610118565b6105af906101a0565b9052565b91906105c6905f602085019401906105a6565b565b346105f8576105d836600461028a565b6105f46105e3610d73565b6105eb610112565b918291826105b3565b0390f35b610118565b5f80fd5b67ffffffffffffffff811161061f5761061b60209161034b565b0190565b610355565b90825f939282370152565b9092919261064461063f82610601565b610392565b938185526020850190828401116106605761065e92610624565b565b6105fd565b9080601f83011215610683578160206106809335910161062f565b90565b61014a565b906020828203126106b8575f82013567ffffffffffffffff81116106b3576106b09201610665565b90565b610120565b61011c565b5190565b60209181520190565b6106e96106f26020936106f7936106e0816106bd565b938480936106c1565b9586910161044f565b61034b565b0190565b90916107156107239360408401908482035f8601526106ca565b9160208184039101526106ca565b90565b346107575761073e610739366004610688565b610e5b565b9061075361074a610112565b928392836106fb565b0390f35b610118565b7f000000000000000000000000000000000000000000000000000000000000000090565b346107b05761079036600461028a565b6107ac61079b61075c565b6107a3610112565b918291826105b3565b0390f35b610118565b909160408284031261080f575f82013567ffffffffffffffff811161080a57836107e0918401610156565b929093602082013567ffffffffffffffff8111610805576108019201610156565b9091565b610120565b610120565b61011c565b34610846576108306108273660046107b5565b92919091610f8a565b610838610112565b806108428161024b565b0390f35b610118565b346108795761085b36600461028a565b6108636110d9565b61086b610112565b806108758161024b565b0390f35b610118565b7f000000000000000000000000000000000000000000000000000000000000000090565b346108d2576108b236600461028a565b6108ce6108bd61087e565b6108c5610112565b918291826105b3565b0390f35b610118565b906020828203126108f0576108ed915f016101c0565b90565b61011c565b346109235761090d6109083660046108d7565b6111ce565b610915610112565b8061091f8161024b565b0390f35b610118565b906020828203126109415761093e915f0161013b565b90565b61011c565b61094f90610124565b90565b9061095c90610946565b5f5260205260405f2090565b61097e906109796001915f92610952565b61052d565b90565b346109b1576109ad61099c610997366004610928565b610968565b6109a4610112565b9182918261055c565b0390f35b610118565b7f000000000000000000000000000000000000000000000000000000000000000090565b6109e3906102dc565b90565b6109ef906109da565b9052565b9190610a06905f602085019401906109e6565b565b34610a3857610a1836600461028a565b610a34610a236109b6565b610a2b610112565b918291826109f3565b0390f35b610118565b919091604081840312610a7e57610a56835f830161013b565b92602082013567ffffffffffffffff8111610a7957610a759201610156565b9091565b610120565b61011c565b34610ab257610a9c610a96366004610a3d565b91611436565b610aa4610112565b80610aae8161024b565b0390f35b610118565b5f80fd5b5f80fd5b60e01b90565b610ace906101a0565b90565b610ada81610ac5565b03610ae157565b5f80fd5b90505190610af282610ad1565b565b90602082820312610b0d57610b0a915f01610ae5565b90565b61011c565b610b1a610112565b3d5f823e3d90fd5b610b2b906102dc565b90565b5f910312610b3857565b61011c565b610b4690610124565b9052565b9190610b6481610b5d81610b69956106c1565b8095610624565b61034b565b0190565b9695939094610b9e88606095610bac95610b91610bb49a5f60808601950190610b3d565b8b830360208d0152610b4a565b9188830360408a0152610b4a565b9401906105a6565b565b9194909293610bff6020610be97f00000000000000000000000000000000000000000000000000000000000000006109da565b63d80a4c2890610bf7610112565b938492610abf565b82528180610c0f6004820161024b565b03915afa8015610cdf57610c2a915f91610cb1575b50610b22565b926302afd6e390949695919295843b15610cac575f96610c5e948894610c6993610c52610112565b9b8c9a8b998a98610abf565b885260048801610b6d565b03925af18015610ca757610c7b575b50565b610c9a905f3d8111610ca0575b610c928183610369565b810190610b2e565b5f610c78565b503d610c88565b610b12565b610abb565b610cd2915060203d8111610cd8575b610cca8183610369565b810190610af4565b5f610c24565b503d610cc0565b610b12565b610cec61174a565b610cf4610d21565b565b90565b610d0d610d08610d1292610cf6565b6102bd565b610195565b90565b610d1e90610cf9565b90565b610d32610d2d5f610d15565b6117c0565b565b610d3c610ce4565b565b5f90565b5f1c90565b60018060a01b031690565b610d5e610d6391610d42565b610d47565b90565b610d709054610d52565b90565b610d7b610d3e565b50610d855f610d66565b90565b606090565b90929192610da2610d9d82610601565b610392565b93818552602085019082840111610dbe57610dbc9261044f565b565b6105fd565b9080601f83011215610de157816020610dde93519101610d8d565b90565b61014a565b919091604081840312610e3e575f81015167ffffffffffffffff8111610e395783610e12918301610dc3565b92602082015167ffffffffffffffff8111610e3457610e319201610dc3565b90565b610120565b610120565b61011c565b610e589160208201915f8184039101526106ca565b90565b905f610ec392610e69610d88565b50610e72610d88565b50610e9c7f00000000000000000000000000000000000000000000000000000000000000006102e8565b610eb863a903a277610eac610112565b96879485938493610abf565b835260048301610e43565b03915afa8015610f03575f80939091610edc575b509190565b9050610efb9192503d805f833e610ef38183610369565b810190610de6565b91905f610ed7565b610b12565b634e487b7160e01b5f52602160045260245ffd5b60021115610f2657565b610f08565b90610f3582610f1c565b565b610f4090610f2b565b90565b610f4c90610f37565b9052565b959492610f8894610f72610f809360409560608b01918b83035f8d0152610b4a565b9188830360208a0152610b4a565b940190610f43565b565b929192610fb67f00000000000000000000000000000000000000000000000000000000000000006109da565b906335ecb4c190929493600191833b1561103857610ff5610fea935f97938894610fde610112565b9a8b998a988997610abf565b875260048701610f50565b03925af1801561103357611007575b50565b611026905f3d811161102c575b61101e8183610369565b810190610b2e565b5f611004565b503d611014565b610b12565b610abb565b61104561174a565b61104d6110ba565b565b61105b61106091610d42565b61050f565b90565b61106d905461104f565b90565b5f1b90565b9061108160ff91611070565b9181191691161790565b6110949061054a565b90565b90565b906110af6110aa6110b69261108b565b611097565b8254611075565b9055565b6110d76110d06110ca6002611063565b1561054a565b600261109a565b565b6110e161103d565b565b6110f4906110ef61174a565b61119e565b565b60207f6464726573730000000000000000000000000000000000000000000000000000917f4f776e61626c653a206e6577206f776e657220697320746865207a65726f20615f8201520152565b6111506026604092610446565b611159816110f6565b0190565b6111729060208101905f818303910152611143565b90565b1561117c57565b611184610112565b62461bcd60e51b81528061119a6004820161115d565b0390fd5b6111cc906111c7816111c06111ba6111b55f610d15565b6101a0565b916101a0565b1415611175565b6117c0565b565b6111d7906110e3565b565b6111e491369161062f565b90565b634e487b7160e01b5f52603260045260245ffd5b90611205826106bd565b81101561121757600160209102010190565b6111e7565b90565b90565b61123661123161123b9261121c565b6102bd565b61121f565b90565b60ff60f81b1690565b611251905161123e565b90565b60f81c90565b60ff1690565b61127461126f6112799261125a565b6102bd565b61125a565b90565b61128861128d91611254565b611260565b90565b6112a461129f6112a992610cf6565b6102bd565b61125a565b90565b90565b6112c36112be6112c8926112ac565b6102bd565b61125a565b90565b90565b6112e26112dd6112e7926112cb565b6102bd565b61125a565b90565b634e487b7160e01b5f52601160045260245ffd5b61130a6113109161125a565b9161125a565b019060ff821161131c57565b6112ea565b60f81b90565b61133b6113366113409261125a565b611321565b61123e565b90565b5f7f496e76616c6964207369676e6174757265000000000000000000000000000000910152565b6113776011602092610446565b61138081611343565b0190565b6113999060208101905f81830391015261136a565b90565b6113a58161054a565b036113ac57565b5f80fd5b905051906113bd8261139c565b565b906020828203126113d8576113d5915f016113b0565b90565b61011c565b5f7f496e76616c6964207369676e6572000000000000000000000000000000000000910152565b611411600e602092610446565b61141a816113dd565b0190565b6114339060208101905f818303910152611404565b90565b916114449061148f926111d9565b61146861146361145e836114586040611222565b906111fb565b611247565b61127c565b8061147b6114755f611290565b9161125a565b1480156116ae575b611673575b508261181f565b806114aa6114a461149f5f610d15565b6101a0565b916101a0565b14611651576114f360206114dd7f00000000000000000000000000000000000000000000000000000000000000006109da565b63d80a4c28906114eb610112565b938492610abf565b825281806115036004820161024b565b03915afa801561164c5761152460209161154e935f9161161f575b50610b22565b630123d0c1906115438592611537610112565b95869485938493610abf565b8352600483016105b3565b03915afa801561161a5761156a915f916115ec575b501561054a565b90816115b0575b5061158e5761158c906115876001916001610952565b61109a565b565b611596610112565b62461bcd60e51b8152806115ac6004820161141e565b0390fd5b90506115e46115de7f00000000000000000000000000000000000000000000000000000000000000006101a0565b916101a0565b14155f611571565b61160d915060203d8111611613575b6116058183610369565b8101906113bf565b5f611563565b503d6115fb565b610b12565b61163f9150833d8111611645575b6116378183610369565b810190610af4565b5f61151e565b503d61162d565b610b12565b611659610112565b62461bcd60e51b81528061166f60048201611384565b0390fd5b61168a61168f91611684601b6112ce565b906112fe565b611327565b6116a7826116a16040935f1a93611222565b906111fb565b535f611488565b50806116c36116bd60016112af565b9161125a565b14611483565b5f7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572910152565b6116fc60208092610446565b611705816116c9565b0190565b61171e9060208101905f8183039101526116f0565b90565b1561172857565b611730610112565b62461bcd60e51b81528061174660048201611709565b0390fd5b611774611755610d73565b61176e611768611763611840565b6101a0565b916101a0565b14611721565b565b9061178760018060a01b0391611070565b9181191691161790565b61179a906102dc565b90565b90565b906117b56117b06117bc92611791565b61179d565b8254611776565b9055565b6117c95f610d66565b6117d3825f6117a0565b906118076118017f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e093611791565b91611791565b91611810610112565b8061181a8161024b565b0390a3565b61183d916118359161182f610d3e565b50611878565b919091611ac7565b90565b611848610d3e565b503390565b5f90565b90565b61186861186361186d92611851565b6102bd565b61121f565b90565b5f90565b5f90565b611880610d3e565b5061188961184d565b50611893826106bd565b6118a66118a06041611854565b9161121f565b145f146118eb576118e5916118b9611870565b506118c2611870565b506118cb611874565b506020810151606060408301519201515f1a909192611c90565b91909190565b50506118f65f610d15565b90600290565b6005111561190657565b610f08565b90611915826118fc565b565b60207f7565000000000000000000000000000000000000000000000000000000000000917f45434453413a20696e76616c6964207369676e6174757265202776272076616c5f8201520152565b6119716022604092610446565b61197a81611917565b0190565b6119939060208101905f818303910152611964565b90565b60207f7565000000000000000000000000000000000000000000000000000000000000917f45434453413a20696e76616c6964207369676e6174757265202773272076616c5f8201520152565b6119f06022604092610446565b6119f981611996565b0190565b611a129060208101905f8183039101526119e3565b90565b5f7f45434453413a20696e76616c6964207369676e6174757265206c656e67746800910152565b611a49601f602092610446565b611a5281611a15565b0190565b611a6b9060208101905f818303910152611a3c565b90565b5f7f45434453413a20696e76616c6964207369676e61747572650000000000000000910152565b611aa26018602092610446565b611aab81611a6e565b0190565b611ac49060208101905f818303910152611a95565b90565b80611ada611ad45f61190b565b9161190b565b145f14611ae45750565b80611af8611af2600161190b565b9161190b565b145f14611b2157611b07610112565b62461bcd60e51b815280611b1d60048201611aaf565b0390fd5b80611b35611b2f600261190b565b9161190b565b145f14611b5e57611b44610112565b62461bcd60e51b815280611b5a60048201611a56565b0390fd5b80611b72611b6c600361190b565b9161190b565b145f14611b9b57611b81610112565b62461bcd60e51b815280611b97600482016119fd565b0390fd5b611bae611ba8600461190b565b9161190b565b14611bb557565b611bbd610112565b62461bcd60e51b815280611bd36004820161197e565b0390fd5b611beb611be6611bf09261121f565b6102bd565b61121f565b90565b611bff611c0491610d42565b611bd7565b90565b90565b611c1e611c19611c2392611c07565b6102bd565b61121f565b90565b90565b611c3d611c38611c4292611c26565b6102bd565b61125a565b90565b611c4e9061125a565b9052565b611c87611c8e94611c7d606094989795611c73608086019a5f870190610b3d565b6020850190611c45565b6040830190610b3d565b0190610b3d565b565b929190611c9b610d3e565b50611ca461184d565b50611cae83611bf3565b611ce0611cda7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0611c0a565b9161121f565b11611da15780611cf9611cf3601b6112ce565b9161125a565b141580611d85575b611d7257611d205f936020959293611d17610112565b94859485611c52565b838052039060015afa15611d6d57611d385f51611070565b80611d53611d4d611d485f610d15565b6101a0565b916101a0565b14611d5d57905f90565b50611d675f610d15565b90600190565b610b12565b50505050611d7f5f610d15565b90600490565b5080611d9a611d94601c611c29565b9161125a565b1415611d01565b50505050611dae5f610d15565b9060039056fea164736f6c634300081c000a", } // BatchAuthenticatorABI is the input ABI used to generate the binding from. diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPSuccinctFDG.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPSuccinctFDG.s.sol new file mode 100644 index 00000000000..ea4efd26c31 --- /dev/null +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPSuccinctFDG.s.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// Libraries +import { Script } from "forge-std/Script.sol"; +import { console } from "forge-std/console.sol"; +import { GameType, Duration } from "src/dispute/lib/Types.sol"; + +// Interfaces +import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; +import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; +import { ISP1Verifier } from "src/dispute/succinct/ISP1Verifier.sol"; +import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; + +// Contracts +import { AccessManager } from "src/dispute/succinct/AccessManager.sol"; +import { OPSuccinctFaultDisputeGame } from "src/dispute/succinct/OPSuccinctFaultDisputeGame.sol"; +import { SP1MockVerifier } from "src/dispute/succinct/ISP1Verifier.sol"; + +/// @title DeployOPSuccinctFDG +/// @notice Deployment script for OPSuccinctFaultDisputeGame and related contracts. +contract DeployOPSuccinctFDG is Script { + // Storage variables to reduce stack usage + address public factoryAddr; + address public registryAddr; + address public sp1VerifierAddr; + address public accessManagerAddr; + address public gameImplAddr; + uint32 public gameTypeId; + uint256 public initialBond; + + function run() public { + _loadConfig(); + + vm.startBroadcast(); + + _deployAccessManager(); + _deployVerifier(); + _deployGame(); + + // Factory configuration is done separately via cast commands + // since it requires proxy admin privileges + console.log("Factory configuration should be done via cast with impersonation"); + + vm.stopBroadcast(); + + console.log("=== Deployment Complete ==="); + console.log("AccessManager:", accessManagerAddr); + console.log("SP1 Verifier:", sp1VerifierAddr); + console.log("Game Implementation:", gameImplAddr); + console.log("Game Type:", gameTypeId); + } + + function _loadConfig() internal { + factoryAddr = vm.envAddress("FACTORY_ADDRESS"); + registryAddr = vm.envAddress("ANCHOR_STATE_REGISTRY_ADDRESS"); + gameTypeId = uint32(vm.envOr("GAME_TYPE", uint256(42))); + initialBond = vm.envOr("INITIAL_BOND_WEI", uint256(0.001 ether)); + } + + function _deployAccessManager() internal { + AccessManager am = new AccessManager(); + accessManagerAddr = address(am); + + // Configure permissionless mode by default + if (vm.envOr("PERMISSIONLESS_MODE", true)) { + am.setProposer(address(0), true); + am.setChallenger(address(0), true); + } + } + + function _deployVerifier() internal { + if (vm.envOr("USE_SP1_MOCK_VERIFIER", true)) { + SP1MockVerifier verifier = new SP1MockVerifier(); + sp1VerifierAddr = address(verifier); + } else { + sp1VerifierAddr = vm.envAddress("VERIFIER_ADDRESS"); + } + } + + function _deployGame() internal { + uint64 maxChallenge = uint64(vm.envOr("MAX_CHALLENGE_DURATION", uint256(300))); + uint64 maxProve = uint64(vm.envOr("MAX_PROVE_DURATION", uint256(1800))); + uint256 challengerBond = vm.envOr("CHALLENGER_BOND_WEI", uint256(0.001 ether)); + + bytes32 configHash = bytes32(0); + bytes32 aggVkey = bytes32(0); + bytes32 rangeVkey = bytes32(0); + + if (!vm.envOr("USE_SP1_MOCK_VERIFIER", true)) { + configHash = vm.envBytes32("ROLLUP_CONFIG_HASH"); + aggVkey = vm.envBytes32("AGGREGATION_VKEY"); + rangeVkey = vm.envBytes32("RANGE_VKEY_COMMITMENT"); + } + + OPSuccinctFaultDisputeGame game = new OPSuccinctFaultDisputeGame( + Duration.wrap(maxChallenge), + Duration.wrap(maxProve), + IDisputeGameFactory(factoryAddr), + ISP1Verifier(sp1VerifierAddr), + configHash, + aggVkey, + rangeVkey, + challengerBond, + IAnchorStateRegistry(registryAddr), + AccessManager(accessManagerAddr) + ); + gameImplAddr = address(game); + } +} diff --git a/packages/contracts-bedrock/src/dispute/DisputeGameFactory.sol b/packages/contracts-bedrock/src/dispute/DisputeGameFactory.sol index 3453d752c44..13aaa770625 100644 --- a/packages/contracts-bedrock/src/dispute/DisputeGameFactory.sol +++ b/packages/contracts-bedrock/src/dispute/DisputeGameFactory.sol @@ -276,4 +276,15 @@ contract DisputeGameFactory is ProxyAdminOwnedBase, ReinitializableBase, Ownable initBonds[_gameType] = _initBond; emit InitBondUpdated(_gameType, _initBond); } + + /// @notice Returns the challenger bond for the given game type. + /// @dev This function is provided for compatibility with the op-succinct + /// DisputeGameFactory interface, which expects a `challengerBond(uint32)` + /// view. In this deployment, we reuse the init bond as the challenger + /// bond value. + /// @param _gameType The type of the DisputeGame as a raw uint32. + /// @return challengerBond_ The bond (in wei) associated with the game type. + function challengerBond(uint32 _gameType) external view returns (uint256 challengerBond_) { + challengerBond_ = initBonds[GameType.wrap(_gameType)]; + } } diff --git a/packages/contracts-bedrock/src/dispute/succinct/AccessManager.sol b/packages/contracts-bedrock/src/dispute/succinct/AccessManager.sol new file mode 100644 index 00000000000..cd322f6a764 --- /dev/null +++ b/packages/contracts-bedrock/src/dispute/succinct/AccessManager.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +/// @title AccessManager +/// @notice Manages permissions for dispute game proposers and challengers. +contract AccessManager is Ownable { + //////////////////////////////////////////////////////////////// + // Events // + //////////////////////////////////////////////////////////////// + + /// @notice Event emitted when proposer permissions are updated. + event ProposerPermissionUpdated(address indexed proposer, bool allowed); + + /// @notice Event emitted when challenger permissions are updated. + event ChallengerPermissionUpdated(address indexed challenger, bool allowed); + + //////////////////////////////////////////////////////////////// + // State Vars // + //////////////////////////////////////////////////////////////// + + /// @notice Tracks whitelisted proposers. + mapping(address => bool) public proposers; + + /// @notice Tracks whitelisted challengers. + mapping(address => bool) public challengers; + + /** + * @notice Allows the owner to whitelist or un-whitelist proposers. + * @param _proposer The address to set in the proposers mapping. + * @param _allowed True if whitelisting, false otherwise. + */ + function setProposer(address _proposer, bool _allowed) external onlyOwner { + proposers[_proposer] = _allowed; + emit ProposerPermissionUpdated(_proposer, _allowed); + } + + /** + * @notice Allows the owner to whitelist or un-whitelist challengers. + * @param _challenger The address to set in the challengers mapping. + * @param _allowed True if whitelisting, false otherwise. + */ + function setChallenger(address _challenger, bool _allowed) external onlyOwner { + challengers[_challenger] = _allowed; + emit ChallengerPermissionUpdated(_challenger, _allowed); + } + + /// @notice Checks if an address is allowed to propose. + /// @param _proposer The address to check. + /// @return allowed_ Whether the address is allowed to propose. + function isAllowedProposer(address _proposer) external view returns (bool allowed_) { + // If address(0) is allowed, then it's permissionless. + allowed_ = proposers[address(0)] || proposers[_proposer]; + } + + /// @notice Checks if an address is allowed to challenge. + /// @param _challenger The address to check. + /// @return allowed_ Whether the address is allowed to challenge. + function isAllowedChallenger(address _challenger) external view returns (bool allowed_) { + // If address(0) is allowed, then it's permissionless. + allowed_ = challengers[address(0)] || challengers[_challenger]; + } +} diff --git a/packages/contracts-bedrock/src/dispute/succinct/ISP1Verifier.sol b/packages/contracts-bedrock/src/dispute/succinct/ISP1Verifier.sol new file mode 100644 index 00000000000..63ce20d695a --- /dev/null +++ b/packages/contracts-bedrock/src/dispute/succinct/ISP1Verifier.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/// @title SP1 Verifier Interface +/// @author Succinct Labs +/// @notice This contract is the interface for the SP1 Verifier. +interface ISP1Verifier { + /// @notice Verifies a proof with given public values and vkey. + /// @dev It is expected that the first 4 bytes of proofBytes must match the first 4 bytes of + /// target verifier's VERIFIER_HASH. + /// @param programVKey The verification key for the RISC-V program. + /// @param publicValues The public values encoded as bytes. + /// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes. + function verifyProof(bytes32 programVKey, bytes calldata publicValues, bytes calldata proofBytes) external view; +} + +interface ISP1VerifierWithHash is ISP1Verifier { + /// @notice Returns the hash of the verifier. + function VERIFIER_HASH() external pure returns (bytes32); +} + +/// @title SP1 Mock Verifier +/// @author Succinct Labs +/// @notice A mock verifier for local testing that accepts any proof. +contract SP1MockVerifier is ISP1Verifier { + /// @notice Verifies a mock proof with given public values and vkey. + /// @dev For testing, accepts empty proofs. + function verifyProof(bytes32, bytes calldata, bytes calldata proofBytes) external pure { + assert(proofBytes.length == 0); + } +} diff --git a/packages/contracts-bedrock/src/dispute/succinct/OPSuccinctFaultDisputeGame.sol b/packages/contracts-bedrock/src/dispute/succinct/OPSuccinctFaultDisputeGame.sol new file mode 100644 index 00000000000..edfc677ad17 --- /dev/null +++ b/packages/contracts-bedrock/src/dispute/succinct/OPSuccinctFaultDisputeGame.sol @@ -0,0 +1,588 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// Libraries +import { Clone } from "@solady/utils/Clone.sol"; +import { + BondDistributionMode, + Claim, + Clock, + Duration, + GameStatus, + GameType, + Hash, + LibClock, + Timestamp +} from "src/dispute/lib/Types.sol"; +import { + AlreadyInitialized, + AnchorRootNotFound, + BadAuth, + BondTransferFailed, + ClaimAlreadyResolved, + ClockTimeExceeded, + GameNotFinalized, + GameNotInProgress, + IncorrectBondAmount, + InvalidBondDistributionMode, + NoCreditToClaim, + UnexpectedRootClaim +} from "src/dispute/lib/Errors.sol"; +import { + ClaimAlreadyChallenged, + GameNotOver, + GameOver, + IncorrectDisputeGameFactory, + InvalidParentGame, + InvalidProposalStatus, + ParentGameNotResolved +} from "src/dispute/succinct/lib/Errors.sol"; +import { AggregationOutputs } from "src/dispute/succinct/lib/Types.sol"; + +// Interfaces +import { ISemver } from "interfaces/universal/ISemver.sol"; +import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; +import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; +import { ISP1Verifier } from "src/dispute/succinct/ISP1Verifier.sol"; +import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; + +// Contracts +import { AccessManager } from "src/dispute/succinct/AccessManager.sol"; + +/// @notice Represents an output root and the L2 block number at which it was generated. +/// @custom:field root The output root hash. +/// @custom:field l2BlockNumber The L2 block number at which the output root was generated. +struct OutputRoot { + Hash root; + uint256 l2BlockNumber; +} + +/// @title OPSuccinctFaultDisputeGame +/// @notice An implementation of the `IFaultDisputeGame` interface using ZK proofs. +contract OPSuccinctFaultDisputeGame is Clone, ISemver, IDisputeGame { + //////////////////////////////////////////////////////////////// + // Enums // + //////////////////////////////////////////////////////////////// + + enum ProposalStatus { + // The initial state of a new proposal. + Unchallenged, + // A proposal that has been challenged but not yet proven. + Challenged, + // An unchallenged proposal that has been proven valid with a verified proof. + UnchallengedAndValidProofProvided, + // A challenged proposal that has been proven valid with a verified proof. + ChallengedAndValidProofProvided, + // The final state after resolution, either GameStatus.CHALLENGER_WINS or GameStatus.DEFENDER_WINS. + Resolved + } + + //////////////////////////////////////////////////////////////// + // Structs // + //////////////////////////////////////////////////////////////// + + /// @notice The `ClaimData` struct represents the data associated with a Claim. + struct ClaimData { + uint32 parentIndex; + address counteredBy; + address prover; + Claim claim; + ProposalStatus status; + Timestamp deadline; + } + + //////////////////////////////////////////////////////////////// + // Events // + //////////////////////////////////////////////////////////////// + + /// @notice Emitted when the game is challenged. + /// @param challenger The address of the challenger. + event Challenged(address indexed challenger); + + /// @notice Emitted when the game is proved. + /// @param prover The address of the prover. + event Proved(address indexed prover); + + /// @notice Emitted when the game is closed. + event GameClosed(BondDistributionMode bondDistributionMode); + + //////////////////////////////////////////////////////////////// + // State Vars // + //////////////////////////////////////////////////////////////// + + /// @notice The maximum duration allowed for a challenger to challenge a game. + Duration internal immutable MAX_CHALLENGE_DURATION; + + /// @notice The maximum duration allowed for a proposer to prove against a challenge. + Duration internal immutable MAX_PROVE_DURATION; + + /// @notice The game type ID. + GameType internal immutable GAME_TYPE; + + /// @notice The dispute game factory. + IDisputeGameFactory internal immutable DISPUTE_GAME_FACTORY; + + /// @notice The SP1 verifier. + ISP1Verifier internal immutable SP1_VERIFIER; + + /// @notice The rollup config hash. + bytes32 internal immutable ROLLUP_CONFIG_HASH; + + /// @notice The vkey for the aggregation program. + bytes32 internal immutable AGGREGATION_VKEY; + + /// @notice The 32 byte commitment to the BabyBear representation of the verification key of the range SP1 program. + bytes32 internal immutable RANGE_VKEY_COMMITMENT; + + /// @notice The challenger bond for the game. + uint256 internal immutable CHALLENGER_BOND; + + /// @notice The anchor state registry. + IAnchorStateRegistry internal immutable ANCHOR_STATE_REGISTRY; + + /// @notice The access manager. + AccessManager internal immutable ACCESS_MANAGER; + + /// @notice Semantic version. + /// @custom:semver 1.0.0 + string public constant version = "1.0.0"; + + /// @notice The starting timestamp of the game. + Timestamp public createdAt; + + /// @notice The timestamp of the game's global resolution. + Timestamp public resolvedAt; + + /// @notice The current status of the game. + GameStatus public status; + + /// @notice Flag for the `initialize` function to prevent re-initialization. + bool internal initialized; + + /// @notice The claim made by the proposer. + ClaimData public claimData; + + /// @notice Credited balances for winning participants. + mapping(address => uint256) public normalModeCredit; + + /// @notice A mapping of each claimant's refund mode credit. + mapping(address => uint256) public refundModeCredit; + + /// @notice The starting output root of the game that is proven from in case of a challenge. + OutputRoot public startingOutputRoot; + + /// @notice A boolean for whether or not the game type was respected when the game was created. + bool public wasRespectedGameTypeWhenCreated; + + /// @notice The bond distribution mode of the game. + BondDistributionMode public bondDistributionMode; + + /// @param _maxChallengeDuration The maximum duration allowed for a challenger to challenge a game. + /// @param _maxProveDuration The maximum duration allowed for a proposer to prove against a challenge. + /// @param _disputeGameFactory The factory that creates the dispute games. + /// @param _sp1Verifier The address of the SP1 verifier. + /// @param _rollupConfigHash The rollup config hash for the L2 network. + /// @param _aggregationVkey The vkey for the aggregation program. + /// @param _rangeVkeyCommitment The commitment to the range vkey. + /// @param _challengerBond The bond amount that must be submitted by the challenger. + /// @param _anchorStateRegistry The anchor state registry for the L2 network. + /// @param _accessManager The access manager for proposer/challenger permissions. + constructor( + Duration _maxChallengeDuration, + Duration _maxProveDuration, + IDisputeGameFactory _disputeGameFactory, + ISP1Verifier _sp1Verifier, + bytes32 _rollupConfigHash, + bytes32 _aggregationVkey, + bytes32 _rangeVkeyCommitment, + uint256 _challengerBond, + IAnchorStateRegistry _anchorStateRegistry, + AccessManager _accessManager + ) { + // Set up initial game state. + GAME_TYPE = GameType.wrap(42); + MAX_CHALLENGE_DURATION = _maxChallengeDuration; + MAX_PROVE_DURATION = _maxProveDuration; + DISPUTE_GAME_FACTORY = _disputeGameFactory; + SP1_VERIFIER = _sp1Verifier; + ROLLUP_CONFIG_HASH = _rollupConfigHash; + AGGREGATION_VKEY = _aggregationVkey; + RANGE_VKEY_COMMITMENT = _rangeVkeyCommitment; + CHALLENGER_BOND = _challengerBond; + ANCHOR_STATE_REGISTRY = _anchorStateRegistry; + ACCESS_MANAGER = _accessManager; + } + + /// @notice Initializes the contract. + /// @dev This function may only be called once. + function initialize() external payable virtual { + // INVARIANT: The game must not have already been initialized. + if (initialized) revert AlreadyInitialized(); + + // INVARIANT: The game can only be initialized by the dispute game factory. + if (address(DISPUTE_GAME_FACTORY) != msg.sender) { + revert IncorrectDisputeGameFactory(); + } + + // INVARIANT: The proposer must be whitelisted. + if (!ACCESS_MANAGER.isAllowedProposer(gameCreator())) revert BadAuth(); + + // Revert if the calldata size is not the expected length. + assembly { + if iszero(eq(calldatasize(), 0x7E)) { + // Store the selector for `BadExtraData()` & revert + mstore(0x00, 0x9824bdab) + revert(0x1C, 0x04) + } + } + + // The first game is initialized with a parent index of uint32.max + if (parentIndex() != type(uint32).max) { + // For subsequent games, get the parent game's information + (,, IDisputeGame proxy) = DISPUTE_GAME_FACTORY.gameAtIndex(parentIndex()); + + if ( + !ANCHOR_STATE_REGISTRY.isGameRespected(proxy) || ANCHOR_STATE_REGISTRY.isGameBlacklisted(proxy) + || ANCHOR_STATE_REGISTRY.isGameRetired(proxy) + ) { + revert InvalidParentGame(); + } + + startingOutputRoot = OutputRoot({ + l2BlockNumber: OPSuccinctFaultDisputeGame(address(proxy)).l2BlockNumber(), + root: Hash.wrap(OPSuccinctFaultDisputeGame(address(proxy)).rootClaim().raw()) + }); + + // INVARIANT: The parent game must be a valid game. + if (proxy.status() == GameStatus.CHALLENGER_WINS) { + revert InvalidParentGame(); + } + } else { + // When there is no parent game, the starting output root is the anchor state for the game type. + (startingOutputRoot.root, startingOutputRoot.l2BlockNumber) = + IAnchorStateRegistry(ANCHOR_STATE_REGISTRY).anchors(GAME_TYPE); + } + + // Do not allow the game to be initialized if the root claim corresponds to a block at or before the + // configured starting block number. + if (l2BlockNumber() <= startingOutputRoot.l2BlockNumber) { + revert UnexpectedRootClaim(rootClaim()); + } + + // Set the root claim + claimData = ClaimData({ + parentIndex: parentIndex(), + counteredBy: address(0), + prover: address(0), + claim: rootClaim(), + status: ProposalStatus.Unchallenged, + deadline: Timestamp.wrap(uint64(block.timestamp + MAX_CHALLENGE_DURATION.raw())) + }); + + // Set the game as initialized. + initialized = true; + + // Deposit the bond. + refundModeCredit[gameCreator()] += msg.value; + + // Set the game's starting timestamp + createdAt = Timestamp.wrap(uint64(block.timestamp)); + + // Set whether the game type was respected when the game was created. + wasRespectedGameTypeWhenCreated = + GameType.unwrap(ANCHOR_STATE_REGISTRY.respectedGameType()) == GameType.unwrap(GAME_TYPE); + } + + /// @notice The L2 block number for which this game is proposing an output root. + function l2BlockNumber() public pure returns (uint256 l2BlockNumber_) { + l2BlockNumber_ = _getArgUint256(0x54); + } + + /// @notice The L2 sequence number (block number) for which this game is proposing an output root. + /// @dev Required by IDisputeGame interface. Returns the same value as l2BlockNumber(). + function l2SequenceNumber() public pure returns (uint256 l2SequenceNumber_) { + l2SequenceNumber_ = _getArgUint256(0x54); + } + + /// @notice The parent index of the game. + function parentIndex() public pure returns (uint32 parentIndex_) { + parentIndex_ = _getArgUint32(0x74); + } + + /// @notice Only the starting block number of the game. + function startingBlockNumber() external view returns (uint256 startingBlockNumber_) { + startingBlockNumber_ = startingOutputRoot.l2BlockNumber; + } + + /// @notice Starting output root of the game. + function startingRootHash() external view returns (Hash startingRootHash_) { + startingRootHash_ = startingOutputRoot.root; + } + + //////////////////////////////////////////////////////////////// + // `IDisputeGame` impl // + //////////////////////////////////////////////////////////////// + + /// @notice Challenges the game. + function challenge() external payable returns (ProposalStatus) { + // INVARIANT: Can only challenge a game that has not been challenged yet. + if (claimData.status != ProposalStatus.Unchallenged) { + revert ClaimAlreadyChallenged(); + } + + // INVARIANT: The challenger must be whitelisted. + if (!ACCESS_MANAGER.isAllowedChallenger(msg.sender)) revert BadAuth(); + + // INVARIANT: Cannot challenge if the game is over. + if (gameOver()) revert GameOver(); + + // If the required bond is not met, revert. + if (msg.value != CHALLENGER_BOND) revert IncorrectBondAmount(); + + // Update the counteredBy address + claimData.counteredBy = msg.sender; + + // Update the status of the proposal + claimData.status = ProposalStatus.Challenged; + + // Update the clock to the current block timestamp, which marks the start of the challenge. + claimData.deadline = Timestamp.wrap(uint64(block.timestamp + MAX_PROVE_DURATION.raw())); + + // Deposit the bond. + refundModeCredit[msg.sender] += msg.value; + + emit Challenged(claimData.counteredBy); + + return claimData.status; + } + + /// @notice Proves the game. + /// @param proofBytes The proof bytes to validate the claim. + function prove(bytes calldata proofBytes) external returns (ProposalStatus) { + // INVARIANT: Cannot prove if the game is over. + if (gameOver()) revert GameOver(); + + // Decode the public values to check the claim root + AggregationOutputs memory publicValues = AggregationOutputs({ + l1Head: Hash.unwrap(l1Head()), + l2PreRoot: Hash.unwrap(startingOutputRoot.root), + claimRoot: rootClaim().raw(), + claimBlockNum: l2BlockNumber(), + rollupConfigHash: ROLLUP_CONFIG_HASH, + rangeVkeyCommitment: RANGE_VKEY_COMMITMENT, + proverAddress: msg.sender + }); + + // Verify the proof. Reverts if the proof is invalid. + SP1_VERIFIER.verifyProof(AGGREGATION_VKEY, abi.encode(publicValues), proofBytes); + + // Update the prover address + claimData.prover = msg.sender; + + // Update the status of the proposal + if (claimData.counteredBy == address(0)) { + claimData.status = ProposalStatus.UnchallengedAndValidProofProvided; + } else { + claimData.status = ProposalStatus.ChallengedAndValidProofProvided; + } + + emit Proved(claimData.prover); + + return claimData.status; + } + + /// @notice Returns the status of the parent game. + function getParentGameStatus() private view returns (GameStatus) { + if (parentIndex() != type(uint32).max) { + (,, IDisputeGame parentGame) = DISPUTE_GAME_FACTORY.gameAtIndex(parentIndex()); + return parentGame.status(); + } else { + return GameStatus.DEFENDER_WINS; + } + } + + /// @notice Resolves the game after the clock expires. + function resolve() external returns (GameStatus) { + // INVARIANT: Resolution cannot occur unless the game has already been resolved. + if (status != GameStatus.IN_PROGRESS) revert ClaimAlreadyResolved(); + + // INVARIANT: Cannot resolve a game if the parent game has not been resolved. + GameStatus parentGameStatus = getParentGameStatus(); + if (parentGameStatus == GameStatus.IN_PROGRESS) { + revert ParentGameNotResolved(); + } + + // INVARIANT: If the parent game's claim is invalid, then the current game's claim is invalid. + if (parentGameStatus == GameStatus.CHALLENGER_WINS) { + status = GameStatus.CHALLENGER_WINS; + normalModeCredit[claimData.counteredBy] = address(this).balance; + } else { + // INVARIANT: Game must be completed either by clock expiration or valid proof. + if (!gameOver()) revert GameNotOver(); + + // Determine status based on claim status. + if (claimData.status == ProposalStatus.Unchallenged) { + status = GameStatus.DEFENDER_WINS; + normalModeCredit[gameCreator()] = address(this).balance; + } else if (claimData.status == ProposalStatus.Challenged) { + status = GameStatus.CHALLENGER_WINS; + normalModeCredit[claimData.counteredBy] = address(this).balance; + } else if (claimData.status == ProposalStatus.UnchallengedAndValidProofProvided) { + status = GameStatus.DEFENDER_WINS; + normalModeCredit[gameCreator()] = address(this).balance; + } else if (claimData.status == ProposalStatus.ChallengedAndValidProofProvided) { + status = GameStatus.DEFENDER_WINS; + + if (claimData.prover == gameCreator()) { + normalModeCredit[claimData.prover] = address(this).balance; + } else { + normalModeCredit[claimData.prover] = CHALLENGER_BOND; + normalModeCredit[gameCreator()] = address(this).balance - CHALLENGER_BOND; + } + } else { + revert InvalidProposalStatus(); + } + } + + // Mark the game as resolved. + claimData.status = ProposalStatus.Resolved; + resolvedAt = Timestamp.wrap(uint64(block.timestamp)); + emit Resolved(status); + + return status; + } + + /// @notice Claim the credit belonging to the recipient address. + /// @param _recipient The owner and recipient of the credit. + function claimCredit(address _recipient) external { + closeGame(); + + uint256 recipientCredit; + if (bondDistributionMode == BondDistributionMode.REFUND) { + recipientCredit = refundModeCredit[_recipient]; + } else if (bondDistributionMode == BondDistributionMode.NORMAL) { + recipientCredit = normalModeCredit[_recipient]; + } else { + revert InvalidBondDistributionMode(); + } + + if (recipientCredit == 0) revert NoCreditToClaim(); + + refundModeCredit[_recipient] = 0; + normalModeCredit[_recipient] = 0; + + (bool success,) = _recipient.call{ value: recipientCredit }(hex""); + if (!success) revert BondTransferFailed(); + } + + /// @notice Closes out the game and determines the bond distribution mode. + function closeGame() public { + if (bondDistributionMode == BondDistributionMode.REFUND || bondDistributionMode == BondDistributionMode.NORMAL) + { + return; + } else if (bondDistributionMode != BondDistributionMode.UNDECIDED) { + revert InvalidBondDistributionMode(); + } + + bool finalized = ANCHOR_STATE_REGISTRY.isGameFinalized(IDisputeGame(address(this))); + if (!finalized) { + revert GameNotFinalized(); + } + + try ANCHOR_STATE_REGISTRY.setAnchorState(IDisputeGame(address(this))) { } catch { } + + bool properGame = ANCHOR_STATE_REGISTRY.isGameProper(IDisputeGame(address(this))); + + if (properGame) { + bondDistributionMode = BondDistributionMode.NORMAL; + } else { + bondDistributionMode = BondDistributionMode.REFUND; + } + + emit GameClosed(bondDistributionMode); + } + + /// @notice Determines if the game is finished. + function gameOver() public view returns (bool gameOver_) { + gameOver_ = claimData.deadline.raw() < uint64(block.timestamp) || claimData.prover != address(0); + } + + /// @notice Getter for the game type. + function gameType() public view returns (GameType gameType_) { + gameType_ = GAME_TYPE; + } + + /// @notice Getter for the creator of the dispute game. + function gameCreator() public pure returns (address creator_) { + creator_ = _getArgAddress(0x00); + } + + /// @notice Getter for the root claim. + function rootClaim() public pure returns (Claim rootClaim_) { + rootClaim_ = Claim.wrap(_getArgBytes32(0x14)); + } + + /// @notice Getter for the parent hash of the L1 block when the dispute game was created. + function l1Head() public pure returns (Hash l1Head_) { + l1Head_ = Hash.wrap(_getArgBytes32(0x34)); + } + + /// @notice Getter for the extra data. + function extraData() public pure returns (bytes memory extraData_) { + extraData_ = _getArgBytes(0x54, 0x24); + } + + /// @notice Returns the game data. + function gameData() external view returns (GameType gameType_, Claim rootClaim_, bytes memory extraData_) { + gameType_ = gameType(); + rootClaim_ = rootClaim(); + extraData_ = extraData(); + } + + //////////////////////////////////////////////////////////////// + // MISC EXTERNAL // + //////////////////////////////////////////////////////////////// + + /// @notice Returns the credit balance of a given recipient. + function credit(address _recipient) external view returns (uint256 credit_) { + if (bondDistributionMode == BondDistributionMode.REFUND) { + credit_ = refundModeCredit[_recipient]; + } else { + credit_ = normalModeCredit[_recipient]; + } + } + + //////////////////////////////////////////////////////////////// + // IMMUTABLE GETTERS // + //////////////////////////////////////////////////////////////// + + /// @notice Returns the max challenge duration. + function maxChallengeDuration() external view returns (Duration maxChallengeDuration_) { + maxChallengeDuration_ = MAX_CHALLENGE_DURATION; + } + + /// @notice Returns the max prove duration. + function maxProveDuration() external view returns (Duration maxProveDuration_) { + maxProveDuration_ = MAX_PROVE_DURATION; + } + + /// @notice Returns the dispute game factory. + function disputeGameFactory() external view returns (IDisputeGameFactory disputeGameFactory_) { + disputeGameFactory_ = DISPUTE_GAME_FACTORY; + } + + /// @notice Returns the challenger bond amount. + function challengerBond() external view returns (uint256 challengerBond_) { + challengerBond_ = CHALLENGER_BOND; + } + + /// @notice Returns the anchor state registry contract. + function anchorStateRegistry() external view returns (IAnchorStateRegistry registry_) { + registry_ = ANCHOR_STATE_REGISTRY; + } + + /// @notice Returns the access manager contract. + function accessManager() external view returns (AccessManager accessManager_) { + accessManager_ = ACCESS_MANAGER; + } +} diff --git a/packages/contracts-bedrock/src/dispute/succinct/lib/Errors.sol b/packages/contracts-bedrock/src/dispute/succinct/lib/Errors.sol new file mode 100644 index 00000000000..aff50bc119f --- /dev/null +++ b/packages/contracts-bedrock/src/dispute/succinct/lib/Errors.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +//////////////////////////////////////////////////////////////// +// `OPSuccinctFaultDisputeGame` Errors // +//////////////////////////////////////////////////////////////// + +/// @notice Thrown when the claim has already been challenged. +error ClaimAlreadyChallenged(); + +/// @notice Thrown when the game type of the parent game does not match the current game. +error UnexpectedGameType(); + +/// @notice Thrown when the parent game is invalid. +error InvalidParentGame(); + +/// @notice Thrown when the parent game is not resolved. +error ParentGameNotResolved(); + +/// @notice Thrown when the game is over. +error GameOver(); + +/// @notice Thrown when the game is not over. +error GameNotOver(); + +/// @notice Thrown when the proposal status is invalid. +error InvalidProposalStatus(); + +/// @notice Thrown when the game is initialized by an incorrect factory. +error IncorrectDisputeGameFactory(); diff --git a/packages/contracts-bedrock/src/dispute/succinct/lib/Types.sol b/packages/contracts-bedrock/src/dispute/succinct/lib/Types.sol new file mode 100644 index 00000000000..f5b87f24c8f --- /dev/null +++ b/packages/contracts-bedrock/src/dispute/succinct/lib/Types.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/// @notice The public values committed to for an OP Succinct aggregation program. +struct AggregationOutputs { + bytes32 l1Head; + bytes32 l2PreRoot; + bytes32 claimRoot; + uint256 claimBlockNum; + bytes32 rollupConfigHash; + bytes32 rangeVkeyCommitment; + address proverAddress; +}