SNARKeling Treasure Hunt

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

[H-01] Duplicate hash in ALLOWED_TREASURE_HASHES renders treasure "9" unclaimable

Author Revealed upon completion

Root + Impact

Description

  • The Noir circuit defines the validity of the treasure hunt. It contains a hardcoded array ALLOWED_TREASURE_HASHES representing the 10 valid
    physical treasures. For a claim to be valid, the prover must show they know a secret whose hash is present in this list

  • The issue is a manual entry error in the circuit's global array. Indices 8 and 9 contain the exact same hash value (which corresponds to secret
    "10"). Consequently, the unique hash for secret "9" is missing from the circuit

// circuits/src/main.nr
global ALLOWED_TREASURE_HASHES: [Field; 10] = [
// ... index 0-7 ...
8931814952839857299896840311953754931787080333405300398787637512717059406908,
-961435057317293580094826482786572873533235701183329831124091847635547871092, // @> Index 8
-961435057317293580094826482786572873533235701183329831124091847635547871092 // @> Index 9 (Duplicate)
];

Risk

Likelihood:

  • This error is static and exists in the compiled circuit artifacts. It will always trigger if a participant tries to prove knowledge of secret
    "9".

Impact:

  • Unclaimable Treasure: The participant who physically finds treasure "9" will be unable to generate a valid proof, as the circuit will fail the
    is_allowed check

  • Protocol Integrity: The game is advertised as having 10 treasures, but mathematically only 9 exist in the ZK logic

Proof of Concept

This Noir test case (added to tests.nr) proves the vulnerability by:

  1. Comparing the values at index 8 and 9 to confirm they are identical.

  2. Generating the hash for secret "9" and asserting that the is_allowed helper function returns false, meaning the circuit would reject a claim
    for this valid physical treasure

#[test]
fn prove_duplicate_and_missing_hashes() {
// 1. Prove index 8 and 9 are duplicates
assert(ALLOWED_TREASURE_HASHES[8] == ALLOWED_TREASURE_HASHES[9]);
// 2. Prove secret "9" hash is not allowed by the circuit
let hash_9 = std::hash::pedersen_hash([9]);
assert(is_allowed(hash_9) == false);
}

Recommended Mitigation

Update the ALLOWED_TREASURE_HASHES array in main.nr to include the correct hash for the 9th treasure. This ensures that all 10 treasures found in
the real world can be successfully verified by the ZK circuit

- -961435057317293580094826482786572873533235701183329831124091847635547871092,
+ -4417726114039171734934559783368726413190541565291523767661452385022043124552,

Support

FAQs

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

Give us feedback!