Skip to content

Reorder arguments for multiProof functions #3447

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions contracts/mocks/MerkleProofWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ contract MerkleProofWrapper {
}

function multiProofVerify(
bytes32 root,
bytes32[] calldata leafs,
bytes32[] calldata proofs,
bool[] calldata proofFlag
bool[] calldata proofFlag,
bytes32 root,
bytes32[] calldata leaves
) public pure returns (bool) {
return MerkleProof.multiProofVerify(root, leafs, proofs, proofFlag);
return MerkleProof.multiProofVerify(proofs, proofFlag, root, leaves);
}

function processMultiProof(
bytes32[] calldata leafs,
bytes32[] calldata proofs,
bool[] calldata proofFlag
bool[] calldata proofFlag,
bytes32[] calldata leaves
) public pure returns (bytes32) {
return MerkleProof.processMultiProof(leafs, proofs, proofFlag);
return MerkleProof.processMultiProof(proofs, proofFlag, leaves);
}
}
38 changes: 18 additions & 20 deletions contracts/utils/cryptography/MerkleProof.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
pragma solidity ^0.8.0;

/**
* @dev These functions deal with verification of Merkle Trees proofs.
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The proofs can be generated using the JavaScript library
* https://github.com/miguelmota/merkletreejs[merkletreejs].
Expand Down Expand Up @@ -75,43 +75,41 @@ library MerkleProof {
}

/**
* @dev Returns true if a `leafs` can be proved to be a part of a Merkle tree
* defined by `root`. For this, `proofs` for each leaf must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Then
* 'proofFlag' designates the nodes needed for the multi proof.
* @dev Returns true if the `leaves` can be proved to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* _Available since v4.7._
*/
function multiProofVerify(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] calldata leaves,
bytes32[] calldata proofs,
bool[] calldata proofFlag
bytes32[] calldata leaves
) internal pure returns (bool) {
return processMultiProof(leaves, proofs, proofFlag) == root;
return processMultiProof(proof, proofFlags, leaves) == root;
}

/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using the multi proof as `proofFlag`. A multi proof is
* valid if the final hash matches the root of the tree.
* @dev Returns the root of a tree reconstructed from `leaves` and the sibling nodes in `proof`,
* consuming from one or the other at each step according to the instructions given by
* `proofFlags`.
*
* _Available since v4.7._
*/
function processMultiProof(
bytes32[] calldata leaves,
bytes32[] calldata proofs,
bool[] calldata proofFlag
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] calldata leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the merkle tree.
uint256 leavesLen = leaves.length;
uint256 totalHashes = proofFlag.length;
uint256 totalHashes = proofFlags.length;

// Check proof validity.
require(leavesLen + proofs.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
Expand All @@ -123,10 +121,10 @@ library MerkleProof {
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
// `proofs` array.
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlag[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proofs[proofPos++];
bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
hashes[i] = _hashPair(a, b);
}

Expand All @@ -135,7 +133,7 @@ library MerkleProof {
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proofs[0];
return proof[0];
}
}

Expand Down
16 changes: 8 additions & 8 deletions test/utils/cryptography/MerkleProof.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ contract('MerkleProof', function (accounts) {
const proof = merkleTree.getMultiProof(proofLeaves);
const proofFlags = merkleTree.getProofFlags(proofLeaves, proof);

expect(await this.merkleProof.multiProofVerify(root, proofLeaves, proof, proofFlags)).to.equal(true);
expect(await this.merkleProof.multiProofVerify(proof, proofFlags, root, proofLeaves)).to.equal(true);
});

it('returns false for an invalid Merkle multi proof', async function () {
Expand All @@ -91,7 +91,7 @@ contract('MerkleProof', function (accounts) {
const badProof = badMerkleTree.getMultiProof(badProofLeaves);
const badProofFlags = badMerkleTree.getProofFlags(badProofLeaves, badProof);

expect(await this.merkleProof.multiProofVerify(root, badProofLeaves, badProof, badProofFlags)).to.equal(false);
expect(await this.merkleProof.multiProofVerify(badProof, badProofFlags, root, badProofLeaves)).to.equal(false);
});

it('revert with invalid multi proof #1', async function () {
Expand All @@ -104,10 +104,10 @@ contract('MerkleProof', function (accounts) {

await expectRevert(
this.merkleProof.multiProofVerify(
root,
[ leaves[0], badLeaf ], // A, E
[ leaves[1], fill, merkleTree.layers[1][1] ],
[ false, false, false ],
root,
[ leaves[0], badLeaf ], // A, E
),
'MerkleProof: invalid multiproof',
);
Expand All @@ -123,10 +123,10 @@ contract('MerkleProof', function (accounts) {

await expectRevert(
this.merkleProof.multiProofVerify(
root,
[ badLeaf, leaves[0] ], // A, E
[ leaves[1], fill, merkleTree.layers[1][1] ],
[ false, false, false, false ],
root,
[ badLeaf, leaves[0] ], // A, E
),
'reverted with panic code 0x32',
);
Expand All @@ -141,15 +141,15 @@ contract('MerkleProof', function (accounts) {
const proof = merkleTree.getMultiProof(proofLeaves);
const proofFlags = merkleTree.getProofFlags(proofLeaves, proof);

expect(await this.merkleProof.multiProofVerify(root, proofLeaves, proof, proofFlags)).to.equal(true);
expect(await this.merkleProof.multiProofVerify(proof, proofFlags, root, proofLeaves)).to.equal(true);
});

it('limit case: can prove empty leaves', async function () {
const leaves = ['a', 'b', 'c', 'd'].map(keccak256).sort(Buffer.compare);
const merkleTree = new MerkleTree(leaves, keccak256, { sort: true });

const root = merkleTree.getRoot();
expect(await this.merkleProof.multiProofVerify(root, [], [ root ], [])).to.equal(true);
expect(await this.merkleProof.multiProofVerify([ root ], [], root, [])).to.equal(true);
});
});
});