SNARKeling Treasure Hunt

First Flight #59
Beginner FriendlyGameFiFoundry
100 EXP
Submission Details
Impact: medium
Likelihood: high

(HIGH) Duplicate hash in `ALLOWED_TREASURE_HASHES` makes one treasure permanently unclaimable and leaves an inventory mismatch between the circuit and the deploy script.

Author Revealed upon completion

Location: circuits/src/main.nr:55-66 (duplicate at lines 64-65); circuits/src/tests.nr:30

Description

The Noir circuit bakes in 10 allowed Pedersen hashes in circuits/src/main.nr. Indexes 8 and 9 are identical:

global ALLOWED_TREASURE_HASHES: [Field; 10] = [
...
-961435057317293580094826482786572873533235701183329831124091847635547871092, // index 8 <-- duplicate
-961435057317293580094826482786572873533235701183329831124091847635547871092 // index 9 <-- duplicate
];

But circuits/Prover.toml.example and contracts/scripts/Deploy.s.sol both say the correct hash at index 8 should be:

-4417726114039171734934559783368726413190541565291523767661452385022043124552

Treasure #9 (whose hash is -4417726...) is therefore not in the allowed set of the circuit. Anyone who physically finds treasure #9 cannot generate a valid proof and cannot claim the reward. The workaround is visible in circuits/src/tests.nr:30, where the test author uses treasure 10 twice and skips 9 entirely:

let treasures: [Field; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 10, 10];

Risk

Likelihood: High any time a finder of treasure #9 tries to claim, the circuit rejects their proof.

Impact: Medium. One physical treasure is bricked; the ETH allocated to treasure #9 is stranded. The circuit still reaches MAX_TREASURES = 10 because treasure #10's hash matches both the duplicate entries

Proof of Concept

The simplest way to show the bug is to try to generate a proof for treasure #9 using its real hash (-4417726...). The circuit's is_allowed check fails:

// circuits/src/tests.nr - append this test
#[test(should_fail)]
fn test_Treasure9CannotBeProven() {
let treasure: Field = 9;
// The intended hash for treasure 9 per Prover.toml.example / Deploy.s.sol:
let treasure_hash: Field =
-4417726114039171734934559783368726413190541565291523767661452385022043124552;
let recipient: Field = 2;
main(treasure, treasure_hash, recipient);
}

Run:

cd circuits && nargo test --show-output

The test body is marked should_fail because main() will assert-fail at assert(is_allowed(treasure_hash)) proving that treasure #9's real hash is not in the circuit's allowed set.

Recommended Mitigation

Replace the duplicate at index 8 with the correct hash and regenerate the verifier via ./build.sh. Restore the test to enumerate all 10 unique treasures.

global ALLOWED_TREASURE_HASHES: [Field; 10] = [
...
8931814952839857299896840311953754931787080333405300398787637512717059406908,
- -961435057317293580094826482786572873533235701183329831124091847635547871092,
+ -4417726114039171734934559783368726413190541565291523767661452385022043124552,
-961435057317293580094826482786572873533235701183329831124091847635547871092
];
- let treasures: [Field; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 10, 10];
+ let treasures: [Field; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!