Skip to content

Instantly share code, notes, and snippets.

@Theo6890
Created April 19, 2023 01:05
Show Gist options
  • Save Theo6890/c0c25984b2611813476f45d66d31fdaa to your computer and use it in GitHub Desktop.
Save Theo6890/c0c25984b2611813476f45d66d31fdaa to your computer and use it in GitHub Desktop.
Fuzz & differential testing for Merkle Tree, passing array from foundry (solidity) test to js script. Contains JS code at the end of the file
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
import {MerkleProof} from "openzeppelin-contracts/utils/cryptography/MerkleProof.sol";
// install murky with: `forge install dmfxyz/murky --no-commit `
import {Strings2} from "murky/differential_testing/test/utils/Strings2.sol";
import {Strings} from "openzeppelin-contracts/utils/Strings.sol";
import "forge-std/Test.sol";
contract Merkle_merkleRoot is Test {
function testFuzzDifferential_merkleRoot_VerifyOZMerkleProofCanBeUsedWith_MerkleTreeJS(
bytes32[] memory leaves,
uint256 nodeIndex
) public {
vm.assume(leaves.length > 1);
vm.assume(nodeIndex < leaves.length);
// emit log_named_uint("leaves.length", leaves.length);
// emit log_named_bytes32("leaves[0]", leaves[0]);
// packing bytes[] array to bytes, to convert later to string
bytes memory packed = abi.encode(leaves);
emit log_named_bytes("packed", packed);
string[] memory cmd = new string[](4);
cmd[0] = "node";
cmd[1] = "scripts/generateMerkleRootAndProof.js";
// convert bytes to string
cmd[2] = Strings2.toHexString(packed);
cmd[3] = Strings.toString(nodeIndex);
bytes memory res = vm.ffi(cmd);
(bytes32 root, bytes32[] memory proof) = abi.decode(
res,
(bytes32, bytes32[])
);
// emit log_named_uint("proof.length", proof.length);
// emit log_named_bytes32("proof[0]", proof[0]);
assertTrue(MerkleProof.verify(proof, root, leaves[nodeIndex]));
}
}
/////////////////////////////// JS FILE ///////////////////////////////
const ethers = require('ethers');
const { MerkleTree } = require('merkletreejs');
const args = process.argv.slice(2);
if (args.length !== 2) {
console.log(`please supply the correct parameters:
leaves: the leaves of the merkle tree - transformed to bytes then bytes to string
nodeIndex: the index of the node to generate a proof for
`);
process.exit(1);
}
const coder = ethers.utils.defaultAbiCoder;
async function main(leaves, nodeIndex) {
const array = ethers.utils.arrayify(leaves);
const decodedLeaves = coder.decode(['bytes32[]'], array);
// console.log('decodedLeaves', decodedLeaves[0]);
const tree = new MerkleTree(decodedLeaves[0], ethers.utils.keccak256, {
sortPairs: true,
});
const root = tree.getHexRoot();
const proof = tree.getHexProof(decodedLeaves[0][parseInt(nodeIndex)]);
// console.log('root', root);
// console.log('proof', proof);
const coded = coder.encode(['bytes32', 'bytes32[]'], [root, proof]);
process.stdout.write(coded);
}
// Pattern recommended to be able to use async/await everywhere
// and properly handle errors.
main(args[0], args[1])
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment