From 5b0233c1366bff59c0446a02c1aba33a5ddbc675 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Sat, 7 Aug 2021 21:48:16 +0300 Subject: [PATCH 1/4] Extend MerkleProof to support unsorted Merkle tree --- contracts/utils/cryptography/MerkleProof.sol | 32 ++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/contracts/utils/cryptography/MerkleProof.sol b/contracts/utils/cryptography/MerkleProof.sol index b3b4871de6c..49269aa13d9 100644 --- a/contracts/utils/cryptography/MerkleProof.sol +++ b/contracts/utils/cryptography/MerkleProof.sol @@ -40,4 +40,36 @@ library MerkleProof { // Check if the computed hash (root) is equal to the provided root return computedHash == root; } + + /** + * @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 leadIndex + ) internal pure returns (bool) { + bytes32 computedHash = leaf; + + for (uint256 i = 0; i < proof.length; i++) { + bytes32 proofElement = proof[i]; + + if ((leadIndex >> 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)); + } + } + + // Check if the computed hash (root) is equal to the provided root + return computedHash == root; + } } From ab803416f9f76fe6ad79f6939707d08b36afbef3 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Sun, 8 Aug 2021 00:08:05 +0300 Subject: [PATCH 2/4] Introduce verifyAndRecoverIndex --- contracts/utils/cryptography/MerkleProof.sol | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/contracts/utils/cryptography/MerkleProof.sol b/contracts/utils/cryptography/MerkleProof.sol index 49269aa13d9..34153815c9d 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, uint256 index) = 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]; @@ -34,11 +50,12 @@ library MerkleProof { } 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; + return (computedHash == root, index); } /** From c9e587eeeae9fd56715730e2f778758681f51485 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Sun, 8 Aug 2021 01:05:59 +0300 Subject: [PATCH 3/4] Fix linter --- contracts/utils/cryptography/MerkleProof.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/utils/cryptography/MerkleProof.sol b/contracts/utils/cryptography/MerkleProof.sol index 34153815c9d..0571665402d 100644 --- a/contracts/utils/cryptography/MerkleProof.sol +++ b/contracts/utils/cryptography/MerkleProof.sol @@ -23,10 +23,10 @@ library MerkleProof { bytes32 root, bytes32 leaf ) internal pure returns (bool) { - (bool success, uint256 index) = verifyAndRecoverIndex(proof, root, leaf); + (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 @@ -57,7 +57,7 @@ library MerkleProof { // 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 From d10badc299ec7727d1ddb80f11c8cff95129c0ed Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Tue, 24 Aug 2021 11:57:58 +0200 Subject: [PATCH 4/4] Fix name typo --- contracts/utils/cryptography/MerkleProof.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/utils/cryptography/MerkleProof.sol b/contracts/utils/cryptography/MerkleProof.sol index 0571665402d..e2ef2ef1d31 100644 --- a/contracts/utils/cryptography/MerkleProof.sol +++ b/contracts/utils/cryptography/MerkleProof.sol @@ -70,14 +70,14 @@ library MerkleProof { bytes32[] memory proof, bytes32 root, bytes32 leaf, - uint256 leadIndex + uint256 leafIndex ) internal pure returns (bool) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; - if ((leadIndex >> i) & 1 == 0) { + if ((leafIndex >> i) & 1 == 0) { // Hash(current computed hash + current element of the proof) computedHash = keccak256(abi.encodePacked(computedHash, proofElement)); } else {