SNARKeling Treasure Hunt

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

Duplicate baked treasure hash reduces the effective treasure inventory to 9 and desynchronizes scoped artifacts

Author Revealed upon completion

Duplicate baked treasure hash reduces the effective treasure inventory to 9 and desynchronizes scoped artifacts

Description

  • The protocol is documented and funded as a 10-treasure hunt: `README.md` states the circuit contains 10 valid treasure hashes, `Deploy.s.sol` documents 10 published hashes, and `TreasureHunt.sol` hardcodes `MAX_TREASURES = 10` with `REWARD = 10 ether`.

  • The Noir circuit does not actually contain 10 unique treasure hashes. `circuits/src/main.nr` repeats the same baked hash in the last two slots, while `Deploy.s.sol` still documents a different 9th hash (`-441772...`) that is absent from the circuit. This desynchronizes the circuit, deployment script, and operator-facing assumptions, and leaves one documented treasure outside the actual proof-valid set.

// circuits/src/main.nr
global ALLOWED_TREASURE_HASHES: [Field; 10] = [
// ...
@> -961435057317293580094826482786572873533235701183329831124091847635547871092,
@> -961435057317293580094826482786572873533235701183329831124091847635547871092,
];
// contracts/scripts/Deploy.s.sol
// @> documented hash that is missing from the circuit:
// -4417726114039171734934559783368726413190541565291523767661452385022043124552,

Risk

Likelihood: HIGH

  • The mismatch is already baked into the committed circuit source and mirrored by the scoped deployment script comments, so a deployment from this repository inherits the inconsistent treasure inventory by default.

  • Any operator or participant relying on the published hash list in `Deploy.s.sol` can be pointed to a treasure identifier that the circuit will never accept.

Impact: HIGH

  • One documented treasure becomes unclaimable through the intended ZK proof flow because its published hash is not present in the circuit's `is_allowed` set.

  • Protocol lifecycle assumptions drift out of sync: the system is funded and documented as a 10-treasure hunt even though the circuit only exposes 9 unique claimable treasure identifiers.

Proof of Concept

Add this test case to test file with any other stuff variable that depends, and run with forge test

string internal constant MISSING_DOCUMENTED_HASH = "-4417726114039171734934559783368726413190541565291523767661452385022043124552";
string internal constant DUPLICATE_CIRCUIT_HASH = "-961435057317293580094826482786572873533235701183329831124091847635547871092";
function testPoCMedium_DuplicateBakedHashMismatchesDocumentedTreasureInventory() public view {
string memory circuitSource = vm.readFile("circuits/src/main.nr");
string memory deployScript = vm.readFile("contracts/scripts/Deploy.s.sol");
string memory readme = vm.readFile("README.md");
assertEq(_countOccurrences(circuitSource, DUPLICATE_CIRCUIT_HASH), 2);
assertEq(_countOccurrences(circuitSource, MISSING_DOCUMENTED_HASH), 0);
assertEq(_countOccurrences(deployScript, MISSING_DOCUMENTED_HASH), 1);
assertGt(_countOccurrences(readme, "10 valid treasure hashes"), 0);
}
function _countOccurrences(string memory haystack, string memory needle) internal pure returns (uint256 count) {
bytes memory h = bytes(haystack);
bytes memory n = bytes(needle);
if (n.length == 0 || h.length < n.length) {
return 0;
}
for (uint256 i = 0; i <= h.length - n.length; i++) {
bool matchFound = true;
for (uint256 j = 0; j < n.length; j++) {
if (h[i + j] != n[j]) {
matchFound = false;
break;
}
}
if (matchFound) {
count++;
}
}
}

Recommended Mitigation

Update any mirrored treasure-hash documentation in `Deploy.s.sol` and related operational materials from the same source of truth used to generate the circuit.

- -961435057317293580094826482786572873533235701183329831124091847635547871092,
+ -4417726114039171734934559783368726413190541565291523767661452385022043124552,
-961435057317293580094826482786572873533235701183329831124091847635547871092,

Support

FAQs

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

Give us feedback!