diff --git a/contracts/utils/cryptography/MerkleProof.sol b/contracts/utils/cryptography/MerkleProof.sol index b3b4871de6c..e2ef2ef1d31 100644 --- a/contracts/utils/cryptography/MerkleProof.sol +++ b/contracts/utils/cryptography/MerkleProof.sol @@ -23,7 +23,23 @@ library MerkleProof { bytes32 root, bytes32 leaf ) internal pure returns (bool) { + (bool success, ) = verifyAndRecoverIndex(proof, root, leaf); + return success; + } + + /** + * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree + * defined by `root`. For this, a `proof` must be provided, containing + * sibling hashes on the branch from the leaf to the root of the tree. Each + * pair of leaves and each pair of pre-images are assumed to be sorted. + */ + function verifyAndRecoverIndex( + bytes32[] memory proof, + bytes32 root, + bytes32 leaf + ) internal pure returns (bool, uint256) { bytes32 computedHash = leaf; + uint256 index = 0; for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; @@ -31,6 +47,39 @@ library MerkleProof { if (computedHash <= proofElement) { // Hash(current computed hash + current element of the proof) computedHash = keccak256(abi.encodePacked(computedHash, proofElement)); + } else { + // Hash(current element of the proof + current computed hash) + computedHash = keccak256(abi.encodePacked(proofElement, computedHash)); + index |= 1 << i; + } + } + + // Check if the computed hash (root) is equal to the provided root + return (computedHash == root, index); + } + + /** + * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree + * defined by `root`. For this, a `proof` must be provided, containing + * sibling hashes on the branch from the leaf to the root of the tree. Each + * pair of leaves and each pair of pre-images are assumed to be sorted. + * Instead of assuming pair of leaves and pair of pre-images are being sorted, + * this method relies on leaf index to recover merkle path directions. + */ + function verify( + bytes32[] memory proof, + bytes32 root, + bytes32 leaf, + uint256 leafIndex + ) internal pure returns (bool) { + bytes32 computedHash = leaf; + + for (uint256 i = 0; i < proof.length; i++) { + bytes32 proofElement = proof[i]; + + if ((leafIndex >> i) & 1 == 0) { + // Hash(current computed hash + current element of the proof) + computedHash = keccak256(abi.encodePacked(computedHash, proofElement)); } else { // Hash(current element of the proof + current computed hash) computedHash = keccak256(abi.encodePacked(proofElement, computedHash));