Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

README.md

C03 Unconstrained Public Values

Goal

Understand why some public values must be constrained in the verifier, not just committed by the proof.

Scenario

A valid SP1 proof commits a full claim payload, including chain_id, but the guest does not know which chain this proof is for because the guest is not a chain, it's offchain code. Identify the bug in the contract and exploit it with a proof that was generated for a different chain.

What you are given

Where to write code

Use this file to implement the exploit:

It contains an exercise scaffold with TODOs. The function exerciseForeignChainProofIsAccepted() is not executed by default; rename it to start with test when you are ready to run the exercise.

Setup

Optional: build the guest ELF and regenerate the Solidity fixture library. Fixture values are written into both starter and solution at test/fixtures/UnconstrainedVerifierFixture.sol.

cd ctfs/sp1/c03-unconstrained-public-values/starter/rust
cargo prove build
cargo run -p host --release

Run starter baseline tests:

cd ctfs/sp1/c03-unconstrained-public-values/starter/foundry
forge test

Run the exercise after filling in the TODO and renaming the function to start with test:

cd ctfs/sp1/c03-unconstrained-public-values/starter/foundry
forge test --match-test testExerciseForeignChainProofIsAccepted

Run solution tests:

cd ctfs/sp1/c03-unconstrained-public-values/solution/foundry
forge test

Canonical SP1 public input envelope

The guest accepts exactly one canonical PublicValuesV1 envelope for every field except chain_id:

  • chain_id = caller supplied
  • context_hash = 0x2222...2222 (32 bytes)
  • recipient = 0x3333...3333
  • amount = 100
  • nonce = 1
  • version = 1

The host emits two valid proofs under the same program key:

  • HONEST_CHAIN_ID = 1
  • FOREIGN_CHAIN_ID = 10

Learning objectives

  • Distinguish proof validity from application-level chain binding.
  • Observe that committing chain_id is not enough if the verifier never compares it to block.chainid.
  • Reproduce an exploit where a proof intended for chain 10 is accepted on chain 1.
  • Constrain verifier-visible public values before applying any state effect.