Skip to content

Commit

Permalink
🔀 Add fuzzing+ffi for proofs verification
Browse files Browse the repository at this point in the history
  • Loading branch information
codyx committed Apr 12, 2023
1 parent e6fc675 commit e132847
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 23 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@

Pre-requisites:

- yarn
- Node.js

- Solidity compiler (solc)

- Foundry

Note: this library can be directly inlined in your contracts and doesn't need to be deployed separetely.
_Note: this library can be directly inlined in your contracts and doesn't need to be deployed separately as all functions visibility are internal pure._

## Quick Start

Expand Down Expand Up @@ -158,4 +157,4 @@ const numberStringToBytes32 = (numberAsString) =>

---

Herodotous Ltd
Herodotus Ltd - 2023
30 changes: 15 additions & 15 deletions helpers/off-chain-mmr.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,19 @@ async function main() {

const results = [];

if (providedHashes) {
const providedHashes = process.argv[4].split(";");
let rootHash = "";
for (let idx = 0; idx < providedHashes.length; ++idx) {
const result = await mmr.append(providedHashes[idx]);
rootHash = result.rootHash;
}
console.log(encoder.encode(["bytes32"], [rootHash]));
process.exit();
}
const elements = providedHashes
? providedHashes.split(";")
: new Array(iterations).fill(0).map((_, idx) => (idx + 1).toString());

for (let idx = 0; idx < iterations; ++idx) {
const result = await mmr.append((idx + 1).toString());
for (let idx = 0; idx < elements.length; ++idx) {
const result = await mmr.append(elements[idx]);

if (shouldGenerateProofs) {
const peaks = await mmr.getPeaks();
const proof = await mmr.getProof(result.leafIndex);
results.push({
index: result.leafIndex.toString(),
value: numberStringToBytes32((idx + 1).toString()),
value: numberStringToBytes32(elements[idx]),
proof: proof.siblingsHashes,
peaks,
pos: result.elementsCount.toString(),
Expand Down Expand Up @@ -71,7 +64,14 @@ async function main() {
process.stdout.write(outputs.join(";"));
} else {
// Print the root hashes to the standard output
console.log(encoder.encode(["bytes32[]"], [results]));
const onlySendFinalRootHash = process.argv[5] === "true";

console.log(
encoder.encode(
onlySendFinalRootHash ? ["bytes32"] : ["bytes32[]"],
onlySendFinalRootHash ? [results[results.length - 1]] : [results]
)
);
}
}

Expand All @@ -85,4 +85,4 @@ function numberStringToBytes32(numberAsString) {
return hexString;
}

main();
main().catch(console.error);
64 changes: 60 additions & 4 deletions test/StatelessMmr.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ contract StatelessMmrLib_Test is Test {
return string.concat("0x", string(hashString));
}

function testMmrLibWithFuzzing(bytes32[] memory randomBytes) public {
function testMmrLibAppendsWithFuzzing(bytes32[] memory randomBytes) public {
vm.assume(randomBytes.length > 0 && randomBytes.length <= 100);

string[] memory randomHashesStr = new string[](randomBytes.length);
Expand All @@ -740,12 +740,13 @@ contract StatelessMmrLib_Test is Test {
}
assertEq(randomHashesStr.length, randomBytes.length);

string[] memory inputs = new string[](5);
string[] memory inputs = new string[](6);
inputs[0] = "node";
inputs[1] = "./helpers/off-chain-mmr.js";
inputs[2] = "-1"; // Unused
inputs[3] = "false";
inputs[4] = randomHashesConcat; // Pass random bytes to node.js
inputs[3] = "false"; // Do not generate proofs
inputs[4] = randomHashesConcat; // Pass random hashes to node.js (elements to append)
inputs[5] = "true"; // Ask node.js to only send the final root hash
bytes memory output = vm.ffi(inputs);
bytes32 finalRootHash = abi.decode(output, (bytes32));

Expand All @@ -757,4 +758,59 @@ contract StatelessMmrLib_Test is Test {
);
assertEq(rootHash, finalRootHash);
}

function testMmrLibProofsWithFuzzing(bytes32[] memory randomBytes) public {
vm.assume(randomBytes.length > 1 && randomBytes.length <= 100);

string[] memory randomHashesStr = new string[](randomBytes.length);
bytes32[] memory randomHashes = new bytes32[](randomBytes.length);
string memory randomHashesConcat = "";
for (uint i = 0; i < randomBytes.length; ++i) {
randomHashesStr[i] = keccak256ToString(
keccak256(abi.encode(randomBytes[i]))
); // As string
randomHashes[i] = keccak256(abi.encode(randomBytes[i])); // As bytes
randomHashesConcat = string.concat(
randomHashesConcat,
randomHashesStr[i]
);
if (i + 1 != randomBytes.length) {
randomHashesConcat = string.concat(randomHashesConcat, ";");
}
}
assertEq(randomHashesStr.length, randomBytes.length);

string[] memory inputs = new string[](5);
inputs[0] = "node";
inputs[1] = "./helpers/off-chain-mmr.js";
inputs[2] = "-1"; // Unused
inputs[3] = "true"; // Ask node.js to generate proofs
inputs[4] = randomHashesConcat; // Pass random hashes to node.js (elements to append)

bytes memory output = vm.ffi(inputs);
string[] memory outputStrings = createStringArray(
randomHashesStr.length,
output,
";"
);
assertEq(outputStrings.length, randomHashesStr.length);

for (uint i = 0; i < outputStrings.length; ++i) {
string memory s = outputStrings[i];
(
uint index,
bytes32 value,
bytes32[] memory proof,
bytes32[] memory peaks,
uint pos,
bytes32 root
) = abi.decode(
hexStringToBytesMemory(substr(s, 2)),
(uint, bytes32, bytes32[], bytes32[], uint, bytes32)
);

// Verify proof
StatelessMmr.verifyProof(index, value, proof, peaks, pos, root);
}
}
}

0 comments on commit e132847

Please sign in to comment.