Understand why a valid zk proof can still be unsafe.
A contract accepts SP1 Groth16 proofs and applies a state effect (credits a balance). Identify the bug in the contract and exploit it to double your balance.
starter/: vulnerable implementation.- Host: starter/rust/host/src/main.rs - generates the SP1 Groth16 proof, and emits fixture values.
- Guest: starter/rust/guest/src/main.rs - SP1 program that accepts one canonical payload and commits it as public values.
- Verifier: starter/foundry/src/ReplayableProofStarter.sol - vulnerable onchain consumer that verifies proofs.
solution/: recommended patch.- Verifier: solution/foundry/src/ReplayableProofSolution.sol fixed implementation (no peeking!).
This scenario is a Verifier only challenge, you do not need to modify the Guest or Host code.
Use this file to implement your attack:
It contains an exercise scaffold with TODOs. The function exerciseReplayAttack() is intentionally not executed by default; rename it to start with test when you are ready to run your exploit.
Optional: Build and prove (host) which will take 15-30 mins. Fixture values are already included at fixtures/ReplayableProofStarterFixture.sol.
cd ctfs/sp1/c01-replayable-proof/starter/rust
cargo run -p host --releaseRun starter tests:
cd ctfs/sp1/c01-replayable-proof/starter/foundry
forge testThe guest only accepts one canonical PublicValuesV1:
chain_id = 1context_hash = 0x2222...2222(32 bytes)recipient = 0x3333...3333amount = 100nonce = 1version = 1
- Distinguish proof validity from proof single-use semantics.
- Reproduce a replay attack using an identical
(proof, publicValues)pair. - Design replay protection tied to committed proof context.