`circuits/src/main.nr` lines 55-66 defines the set of 10 valid treasure hashes, but indices 8 and 9 contain the same value:
```noir
global ALLOWED_TREASURE_HASHES: [Field; 10] = [
// ... 0..7 unique ...
8931814952839857299896840311953754931787080333405300398787637512717059406908,
-961435057317293580094826482786572873533235701183329831124091847635547871092, // index 8 duplicate
-961435057317293580094826482786572873533235701183329831124091847635547871092 // index 9 duplicate
];
```
Both `Prover.toml.example` and `Deploy.s.sol` (lines 25-26) show that index 8 was supposed to be `-4417726114039171734934559783368726413190541565291523767661452385022043124552` (pedersen hash of secret `"9"`). A copy-paste error left both circuit slots holding the hash of secret `"10"`, so the hash for secret `"9"` never enters the `is_allowed` set. The bug is further visible in `circuits/src/tests.nr` line 30, where the test fixture `[1,2,3,4,5,6,7,8,10,10]` was adjusted to pass the broken circuit instead of fixing it.
Likelihood: High - the defect triggers deterministically for every participant who finds physical treasure #9; it is baked into the circuit's proving key.
The hunt has 10 physical treasures but only 9 are redeemable on-chain; treasure #9 is permanently undiscoverable.
`claimsCount` can only ever reach `9`, so `withdraw()` (line 223, `require(claimsCount >= MAX_TREASURES)`) permanently reverts.
10 ETH is permanently stranded (1 unclaimed treasure * `REWARD`), recoverable only through `pause() + emergencyWithdraw()`.
The "10 distinct winners" invariant is silently violated - a finder of treasure #9 completes the physical hunt but proof generation fails.
Exploit steps:
Contract deployed with `MAX_TREASURES = 10`, funded with 100 ETH.
Participants find treasures #1-#8 and #10; each generates a valid proof (their hashes exist in `ALLOWED_TREASURE_HASHES`). Each calls `claim()` successfully; `claimsCount` reaches 9.
Last participant finds treasure #9. Off-chain proof-generation computes `pedersen_hash("9") = -4417...`, but circuit's `assert(is_allowed(treasure_hash))` on line 31 fails because `-4417...` is not in the baked-in set.
The participant has no valid proof; `claim()` cannot be submitted. `claimsCount` stays at 9 forever.
`withdraw()` reverts for everyone because `claimsCount < MAX_TREASURES (10)`. 10 ETH permanently stranded through the intended path.
Three-file consistency check confirms the defect:
```text
main.nr [8] = -961435... <-- duplicate of [9]
Prover.toml [8] = -4417... <-- intended value
Deploy.s.sol line 25 = -4417... <-- intended value
tests.nr treasures[] = [1,..,8,10,10] <-- test was adjusted, not circuit
```
Replace the duplicated hash on line 65 of `circuits/src/main.nr`:
```diff
-961435057317293580094826482786572873533235701183329831124091847635547871092,
-4417726114039171734934559783368726413190541565291523767661452385022043124552,
-961435057317293580094826482786572873533235701183329831124091847635547871092
```
Then rerun `circuits/scripts/build.sh` to regenerate the compiled circuit, `Verifier.sol`, and Foundry fixtures. After the fix, treasure #9 produces a valid proof, `claimsCount` reaches 10, and `withdraw()` unlocks normally. Also fix `tests.nr` line 30 to `[1,2,3,4,5,6,7,8,9,10]` so the test actually exercises the full hunt.
For defense-in-depth, add a Noir test asserting `ALLOWED_TREASURE_HASHES` contains 10 distinct elements (pairwise non-equal) so this copy-paste class of bug cannot reappear. Mirror the list in a Solidity constant compared against circuit output at deploy time.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.