Skip to content

feat(fault-proof): add state backup/restore to challenger#848

Open
fakedev9999 wants to merge 5 commits into
mainfrom
feat/challenger-backup
Open

feat(fault-proof): add state backup/restore to challenger#848
fakedev9999 wants to merge 5 commits into
mainfrom
feat/challenger-backup

Conversation

@fakedev9999
Copy link
Copy Markdown
Member

@fakedev9999 fakedev9999 commented Mar 30, 2026

Summary

Add state backup/restore to the challenger, mirroring the proposer's backup mechanism (#756). The challenger re-syncs all games from index 0 on every restart — on chains with thousands of games this means thousands of redundant on-chain calls.

With backup enabled, the cursor and game cache persist to disk. On restart, state is restored and forward sync resumes from cursor+1 instead of 0.

Why forward sync (not backward like proposer)

The proposer uses backward sync (latest → anchor) because it only needs recent games for canonical head calculation. The challenger must monitor all IN_PROGRESS games — missing one means an invalid game goes unchallenged. Forward sync guarantees no gaps.

The problem was "cursor resets to 0 on restart", not "forward direction". Backup solves this.

What's backed up

  • cursor (U256) — highest processed factory index
  • games (Vec<Game>) — cached game metadata

On restore, sync_state() step 2 recomputes all should_attempt_to_* flags from on-chain state, so stale flags from backup are overwritten before any action.

Closes #841

Configuration

Set CHALLENGER_BACKUP_PATH in .env.challenger (optional):

CHALLENGER_BACKUP_PATH=/backup/challenger_state.json

Note: uses a distinct env var from the proposer's BACKUP_PATH to avoid conflicts when both run on the same machine.

Test plan

  • cargo check -p op-succinct-fp
  • cargo test -p op-succinct-fp --lib — 47/47 pass (includes new schema guard)
  • cargo test --test backup -p op-succinct-fp — 21/21 pass (12 proposer + 9 challenger)
  • cargo fmt / cargo clippy clean
  • Integration test with live challenger on devnet

The challenger re-syncs all games from index 0 on every restart. On
chains with high dispute game volume, this means thousands of redundant
on-chain calls before the challenger becomes operational.

Add ChallengerBackup for persisting cursor + game cache to disk. On
restart, state is restored from backup and forward sync resumes from
cursor+1 instead of 0.

Unlike the proposer, the challenger keeps forward sync (not backward)
because it must monitor ALL games without gaps — missing one means an
invalid game goes unchallenged.

Closes #841
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 30, 2026

Metric Value
Batch Start 16,293,083
Batch End 16,293,088
Witness Generation (seconds) 0
Execution Duration (seconds) 56
Total Instruction Count 756,201,119
Oracle Verify Cycles 85,627,968
Derivation Cycles 578,082,442
Block Execution Cycles 6,806,213
Blob Verification Cycles 31,185,041
Total SP1 Gas 1,095,997,488
Number of Blocks 5
Number of Transactions 5
Ethereum Gas Used 250,666
Cycles per Block 151,240,223
Cycles per Transaction 151,240,223
Transactions per Block 1
Gas Used per Block 50,133
Gas Used per Transaction 50,133
BN Pair Cycles 0
BN Add Cycles 0
BN Mul Cycles 0
KZG Eval Cycles 0
EC Recover Cycles 0
P256 Verify Cycles 0

- Use CHALLENGER_BACKUP_PATH env var (not BACKUP_PATH) to avoid
  collision with proposer when both run on same machine
- Extract save_json() helper to deduplicate atomic write logic
  between ProposerBackup and ChallengerBackup
- Use spawn_blocking for challenger backup I/O to avoid blocking
  the async runtime during fsync
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(fault-proof): Add backward sync and state backup to challenger

1 participant