Created
April 19, 2023 01:05
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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