ALLOWED_TREASURE_HASHES allows a single proof to claim two rewards, permanently locking one treasure slotThe circuit defines 10 allowed treasure hashes baked into the ALLOWED_TREASURE_HASHES constant. The is_allowed() helper checks membership only, not uniqueness, meaning only 9 unique treasures exist.
Indices 8 and 9 are identical, so the holder of the proof for the hash at index 8 can submit two valid claims — one per unique treasureHash entry in claimed mapping — collecting 20 ETH instead of 10 ETH. The 10th unique treasure slot is unreachable by any other participant, meaning one reward is either stolen or permanently locked depending on execution order. This is compounded by [H-1] — with replay protection already broken, the same proof can be submitted MAX_TREASURES times draining the full contract before the duplicate even matters.
Likelihood:
Any holder of the proof corresponding to the duplicate hash at index 8 will be able to submit two valid claims, as both index 8 and index 9 pass the is_allowed() check with the same input.
The duplicate exists statically in the compiled circuit constant, making this exploitable on every deployment without any special precondition.
Impact:
One proof holder can claim 20 ETH instead of 10 ETH, doubling their reward at the protocol's expense.
One treasure slot is permanently unclaimable by any legitimate unique participant, contradicting the circuit comment: "This allows the prover to demonstrate knowledge of a valid treasure without revealing which one it is" — only 9 distinct secrets exist, not 10.
Replace the duplicate hash at index 9 with a unique valid pedersen hash corresponding to a distinct treasure secret:
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.