From c3a0d52eaacfba871a5732d11776060e842122f0 Mon Sep 17 00:00:00 2001 From: Cr0wn-Gh0ul Date: Fri, 18 Feb 2022 15:03:08 -0500 Subject: [PATCH 1/7] Change MerkleProof.sol to use calldata instead of memory for proof arg --- 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 03244f488b5..7b02928efb7 100644 --- a/contracts/utils/cryptography/MerkleProof.sol +++ b/contracts/utils/cryptography/MerkleProof.sol @@ -25,7 +25,7 @@ library MerkleProof { * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify( - bytes32[] memory proof, + bytes32[] calldata proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { @@ -40,7 +40,7 @@ library MerkleProof { * * _Available since v4.4._ */ - function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { + function processProof(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; From 259651c5dfc2d027b6da0a870cb1ea94ff03a4f2 Mon Sep 17 00:00:00 2001 From: Troy Salem Date: Fri, 18 Feb 2022 17:24:04 -0500 Subject: [PATCH 2/7] Update for backwards compatibility --- contracts/utils/cryptography/MerkleProof.sol | 47 +++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/contracts/utils/cryptography/MerkleProof.sol b/contracts/utils/cryptography/MerkleProof.sol index 7b02928efb7..b22ae0d408b 100644 --- a/contracts/utils/cryptography/MerkleProof.sol +++ b/contracts/utils/cryptography/MerkleProof.sol @@ -1,3 +1,4 @@ + // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/MerkleProof.sol) @@ -25,13 +26,30 @@ library MerkleProof { * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify( - bytes32[] calldata proof, + bytes32[] memory proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { return processProof(proof, leaf) == 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. + * + * Updated to use calldata + * + */ + function verifyCall( + bytes32[] calldata proof, + bytes32 root, + bytes32 leaf + ) internal pure returns (bool) { + return processProofCall(proof, leaf) == root; + } + /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt @@ -40,7 +58,32 @@ library MerkleProof { * * _Available since v4.4._ */ - function processProof(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { + function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { + bytes32 computedHash = leaf; + for (uint256 i = 0; i < proof.length; i++) { + bytes32 proofElement = proof[i]; + if (computedHash <= proofElement) { + // Hash(current computed hash + current element of the proof) + computedHash = _efficientHash(computedHash, proofElement); + } else { + // Hash(current element of the proof + current computed hash) + computedHash = _efficientHash(proofElement, computedHash); + } + } + return computedHash; + } + + /** + * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up + * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt + * hash matches the root of the tree. When processing the proof, the pairs + * of leafs & pre-images are assumed to be sorted. + * + * Updated to use calldata + * + * _Available since v4.4._ + */ + function processProofCall(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; From d6d40bd7630f979970ac3c82441fc119e72eadf6 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 25 May 2022 10:32:46 +0200 Subject: [PATCH 3/7] add calldata version of the multiverify and multiprocess --- CHANGELOG.md | 1 + contracts/mocks/MerkleProofWrapper.sol | 29 ++++++++++++++++++++ contracts/utils/cryptography/MerkleProof.sol | 6 ++-- test/utils/cryptography/MerkleProof.test.js | 26 ++++++++++++++++++ 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d73a30d241..d5d302fb09e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * `EnumerableMap`: add new `Bytes32ToUintMap` map type. ([#3416](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3416)) * `SafeCast`: add support for many more types, using procedural code generation. ([#3245](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3245)) * `MerkleProof`: add `multiProofVerify` to prove multiple values are part of a Merkle tree. ([#3276](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3276)) + * `MerkleProof`: add calldata versions of the `processXxx` and `verifyXxx` functions to avoid copying input arrays to memory and save gas. ([#3200](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3200)) ## 4.6.0 (2022-04-26) diff --git a/contracts/mocks/MerkleProofWrapper.sol b/contracts/mocks/MerkleProofWrapper.sol index a58e69f3b0e..f5a108897c8 100644 --- a/contracts/mocks/MerkleProofWrapper.sol +++ b/contracts/mocks/MerkleProofWrapper.sol @@ -13,10 +13,22 @@ contract MerkleProofWrapper { return MerkleProof.verify(proof, root, leaf); } + function verifyCall( + bytes32[] calldata proof, + bytes32 root, + bytes32 leaf + ) public pure returns (bool) { + return MerkleProof.verifyCall(proof, root, leaf); + } + function processProof(bytes32[] memory proof, bytes32 leaf) public pure returns (bytes32) { return MerkleProof.processProof(proof, leaf); } + function processProofCall(bytes32[] calldata proof, bytes32 leaf) public pure returns (bytes32) { + return MerkleProof.processProofCall(proof, leaf); + } + function multiProofVerify( bytes32 root, bytes32[] memory leafs, @@ -26,6 +38,15 @@ contract MerkleProofWrapper { return MerkleProof.multiProofVerify(root, leafs, proofs, proofFlag); } + function multiProofVerifyCall( + bytes32 root, + bytes32[] calldata leafs, + bytes32[] calldata proofs, + bool[] calldata proofFlag + ) public pure returns (bool) { + return MerkleProof.multiProofVerifyCall(root, leafs, proofs, proofFlag); + } + function processMultiProof( bytes32[] memory leafs, bytes32[] memory proofs, @@ -33,4 +54,12 @@ contract MerkleProofWrapper { ) public pure returns (bytes32) { return MerkleProof.processMultiProof(leafs, proofs, proofFlag); } + + function processMultiProofCall( + bytes32[] calldata leafs, + bytes32[] calldata proofs, + bool[] calldata proofFlag + ) public pure returns (bytes32) { + return MerkleProof.processMultiProofCall(leafs, proofs, proofFlag); + } } diff --git a/contracts/utils/cryptography/MerkleProof.sol b/contracts/utils/cryptography/MerkleProof.sol index 7b122de511a..5c0827d086c 100644 --- a/contracts/utils/cryptography/MerkleProof.sol +++ b/contracts/utils/cryptography/MerkleProof.sol @@ -122,11 +122,10 @@ library MerkleProof { // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the merkle tree. uint256 leafsLen = leafs.length; - uint256 proofsLen = proofs.length; uint256 totalHashes = proofFlag.length; // Check proof validity. - require(leafsLen + proofsLen - 1 == totalHashes, "MerkleProof: invalid multiproof"); + require(leafsLen + proofs.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". @@ -163,11 +162,10 @@ library MerkleProof { // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the merkle tree. uint256 leafsLen = leafs.length; - uint256 proofsLen = proofs.length; uint256 totalHashes = proofFlag.length; // Check proof validity. - require(leafsLen + proofsLen - 1 == totalHashes, "MerkleProof: invalid multiproof"); + require(leafsLen + proofs.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". diff --git a/test/utils/cryptography/MerkleProof.test.js b/test/utils/cryptography/MerkleProof.test.js index 069d7922179..4742acb7da0 100644 --- a/test/utils/cryptography/MerkleProof.test.js +++ b/test/utils/cryptography/MerkleProof.test.js @@ -25,12 +25,14 @@ contract('MerkleProof', function (accounts) { const proof = merkleTree.getHexProof(leaf); expect(await this.merkleProof.verify(proof, root, leaf)).to.equal(true); + expect(await this.merkleProof.verifyCall(proof, root, leaf)).to.equal(true); // For demonstration, it is also possible to create valid proofs for certain 64-byte values *not* in elements: const noSuchLeaf = keccak256( Buffer.concat([keccak256(elements[0]), keccak256(elements[1])].sort(Buffer.compare)), ); expect(await this.merkleProof.verify(proof.slice(1), root, noSuchLeaf)).to.equal(true); + expect(await this.merkleProof.verifyCall(proof.slice(1), root, noSuchLeaf)).to.equal(true); }); it('returns false for an invalid Merkle proof', async function () { @@ -47,6 +49,7 @@ contract('MerkleProof', function (accounts) { const badProof = badMerkleTree.getHexProof(badElements[0]); expect(await this.merkleProof.verify(badProof, correctRoot, correctLeaf)).to.equal(false); + expect(await this.merkleProof.verifyCall(badProof, correctRoot, correctLeaf)).to.equal(false); }); it('returns false for a Merkle proof of invalid length', async function () { @@ -61,6 +64,7 @@ contract('MerkleProof', function (accounts) { const badProof = proof.slice(0, proof.length - 5); expect(await this.merkleProof.verify(badProof, root, leaf)).to.equal(false); + expect(await this.merkleProof.verifyCall(badProof, root, leaf)).to.equal(false); }); }); @@ -75,6 +79,7 @@ contract('MerkleProof', function (accounts) { const proofFlags = merkleTree.getProofFlags(proofLeaves, proof); expect(await this.merkleProof.multiProofVerify(root, proofLeaves, proof, proofFlags)).to.equal(true); + expect(await this.merkleProof.multiProofVerifyCall(root, proofLeaves, proof, proofFlags)).to.equal(true); }); it('returns false for an invalid Merkle multi proof', async function () { @@ -88,6 +93,7 @@ contract('MerkleProof', function (accounts) { const badProofFlags = badMerkleTree.getProofFlags(badProofLeaves, badProof); expect(await this.merkleProof.multiProofVerify(root, badProofLeaves, badProof, badProofFlags)).to.equal(false); + expect(await this.merkleProof.multiProofVerifyCall(root, badProofLeaves, badProof, badProofFlags)).to.equal(false); }); it('revert with invalid multi proof #1', async function () { @@ -107,6 +113,16 @@ contract('MerkleProof', function (accounts) { ), 'MerkleProof: invalid multiproof', ); + + await expectRevert( + this.merkleProof.multiProofVerifyCall( + root, + [ leaves[0], badLeave ], // A, E + [ leaves[1], fill, merkleTree.layers[1][1] ], + [ false, false, false ], + ), + 'MerkleProof: invalid multiproof', + ); }); it('revert with invalid multi proof #2', async function () { @@ -126,6 +142,16 @@ contract('MerkleProof', function (accounts) { ), 'reverted with panic code 0x32', ); + + await expectRevert( + this.merkleProof.multiProofVerifyCall( + root, + [ badLeave, leaves[0] ], // A, E + [ leaves[1], fill, merkleTree.layers[1][1] ], + [ false, false, false, false ], + ), + 'reverted with panic code 0x32', + ); }); }); }); From cc411e57a0565836b380df92ffd10975049cc4d8 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 25 May 2022 11:57:00 +0200 Subject: [PATCH 4/7] lix lint --- test/utils/cryptography/MerkleProof.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/utils/cryptography/MerkleProof.test.js b/test/utils/cryptography/MerkleProof.test.js index 4742acb7da0..8ea91c9ed47 100644 --- a/test/utils/cryptography/MerkleProof.test.js +++ b/test/utils/cryptography/MerkleProof.test.js @@ -93,7 +93,8 @@ contract('MerkleProof', function (accounts) { const badProofFlags = badMerkleTree.getProofFlags(badProofLeaves, badProof); expect(await this.merkleProof.multiProofVerify(root, badProofLeaves, badProof, badProofFlags)).to.equal(false); - expect(await this.merkleProof.multiProofVerifyCall(root, badProofLeaves, badProof, badProofFlags)).to.equal(false); + expect(await this.merkleProof.multiProofVerifyCall(root, badProofLeaves, badProof, badProofFlags)) + .to.equal(false); }); it('revert with invalid multi proof #1', async function () { From a020745ca5dd68c66d14662db6a786aebb12f8d0 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 25 May 2022 14:38:28 +0200 Subject: [PATCH 5/7] fix typo --- test/utils/cryptography/MerkleProof.test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/utils/cryptography/MerkleProof.test.js b/test/utils/cryptography/MerkleProof.test.js index 8ea91c9ed47..3e4ba165789 100644 --- a/test/utils/cryptography/MerkleProof.test.js +++ b/test/utils/cryptography/MerkleProof.test.js @@ -100,7 +100,7 @@ contract('MerkleProof', function (accounts) { it('revert with invalid multi proof #1', async function () { const fill = Buffer.alloc(32); // This could be anything, we are reconstructing a fake branch const leaves = ['a', 'b', 'c', 'd'].map(keccak256).sort(Buffer.compare); - const badLeave = keccak256('e'); + const badLeaf = keccak256('e'); const merkleTree = new MerkleTree(leaves, keccak256, { sort: true }); const root = merkleTree.getRoot(); @@ -108,7 +108,7 @@ contract('MerkleProof', function (accounts) { await expectRevert( this.merkleProof.multiProofVerify( root, - [ leaves[0], badLeave ], // A, E + [ leaves[0], badLeaf ], // A, E [ leaves[1], fill, merkleTree.layers[1][1] ], [ false, false, false ], ), @@ -118,7 +118,7 @@ contract('MerkleProof', function (accounts) { await expectRevert( this.merkleProof.multiProofVerifyCall( root, - [ leaves[0], badLeave ], // A, E + [ leaves[0], badLeaf ], // A, E [ leaves[1], fill, merkleTree.layers[1][1] ], [ false, false, false ], ), @@ -129,7 +129,7 @@ contract('MerkleProof', function (accounts) { it('revert with invalid multi proof #2', async function () { const fill = Buffer.alloc(32); // This could be anything, we are reconstructing a fake branch const leaves = ['a', 'b', 'c', 'd'].map(keccak256).sort(Buffer.compare); - const badLeave = keccak256('e'); + const badLeaf = keccak256('e'); const merkleTree = new MerkleTree(leaves, keccak256, { sort: true }); const root = merkleTree.getRoot(); @@ -137,7 +137,7 @@ contract('MerkleProof', function (accounts) { await expectRevert( this.merkleProof.multiProofVerify( root, - [ badLeave, leaves[0] ], // A, E + [ badLeaf, leaves[0] ], // A, E [ leaves[1], fill, merkleTree.layers[1][1] ], [ false, false, false, false ], ), @@ -147,7 +147,7 @@ contract('MerkleProof', function (accounts) { await expectRevert( this.merkleProof.multiProofVerifyCall( root, - [ badLeave, leaves[0] ], // A, E + [ badLeaf, leaves[0] ], // A, E [ leaves[1], fill, merkleTree.layers[1][1] ], [ false, false, false, false ], ), From 3b8b6ab1334cfd02340e096382522e439a064fe4 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Thu, 26 May 2022 18:56:28 -0300 Subject: [PATCH 6/7] simplify changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5d302fb09e..2301f236444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ * `EnumerableMap`: add new `Bytes32ToUintMap` map type. ([#3416](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3416)) * `SafeCast`: add support for many more types, using procedural code generation. ([#3245](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3245)) * `MerkleProof`: add `multiProofVerify` to prove multiple values are part of a Merkle tree. ([#3276](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3276)) - * `MerkleProof`: add calldata versions of the `processXxx` and `verifyXxx` functions to avoid copying input arrays to memory and save gas. ([#3200](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3200)) + * `MerkleProof`: add calldata versions of the functions to avoid copying input arrays to memory and save gas. ([#3200](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3200)) ## 4.6.0 (2022-04-26) From b86da36c4a110781bc0b0369ff458c62aa73a824 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 1 Jun 2022 20:06:46 +0200 Subject: [PATCH 7/7] make processMultiProof and multiProofVerify calldata by default --- contracts/mocks/MerkleProofWrapper.sol | 29 ++------ contracts/utils/cryptography/MerkleProof.sol | 76 +++----------------- test/utils/cryptography/MerkleProof.test.js | 31 ++------ 3 files changed, 21 insertions(+), 115 deletions(-) diff --git a/contracts/mocks/MerkleProofWrapper.sol b/contracts/mocks/MerkleProofWrapper.sol index f5a108897c8..519222613e2 100644 --- a/contracts/mocks/MerkleProofWrapper.sol +++ b/contracts/mocks/MerkleProofWrapper.sol @@ -13,53 +13,36 @@ contract MerkleProofWrapper { return MerkleProof.verify(proof, root, leaf); } - function verifyCall( + function verifyCalldata( bytes32[] calldata proof, bytes32 root, bytes32 leaf ) public pure returns (bool) { - return MerkleProof.verifyCall(proof, root, leaf); + return MerkleProof.verifyCalldata(proof, root, leaf); } function processProof(bytes32[] memory proof, bytes32 leaf) public pure returns (bytes32) { return MerkleProof.processProof(proof, leaf); } - function processProofCall(bytes32[] calldata proof, bytes32 leaf) public pure returns (bytes32) { - return MerkleProof.processProofCall(proof, leaf); + function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) public pure returns (bytes32) { + return MerkleProof.processProofCalldata(proof, leaf); } function multiProofVerify( - bytes32 root, - bytes32[] memory leafs, - bytes32[] memory proofs, - bool[] memory proofFlag - ) public pure returns (bool) { - return MerkleProof.multiProofVerify(root, leafs, proofs, proofFlag); - } - - function multiProofVerifyCall( bytes32 root, bytes32[] calldata leafs, bytes32[] calldata proofs, bool[] calldata proofFlag ) public pure returns (bool) { - return MerkleProof.multiProofVerifyCall(root, leafs, proofs, proofFlag); + return MerkleProof.multiProofVerify(root, leafs, proofs, proofFlag); } function processMultiProof( - bytes32[] memory leafs, - bytes32[] memory proofs, - bool[] memory proofFlag - ) public pure returns (bytes32) { - return MerkleProof.processMultiProof(leafs, proofs, proofFlag); - } - - function processMultiProofCall( bytes32[] calldata leafs, bytes32[] calldata proofs, bool[] calldata proofFlag ) public pure returns (bytes32) { - return MerkleProof.processMultiProofCall(leafs, proofs, proofFlag); + return MerkleProof.processMultiProof(leafs, proofs, proofFlag); } } diff --git a/contracts/utils/cryptography/MerkleProof.sol b/contracts/utils/cryptography/MerkleProof.sol index 5c0827d086c..e9e2e555cff 100644 --- a/contracts/utils/cryptography/MerkleProof.sol +++ b/contracts/utils/cryptography/MerkleProof.sol @@ -37,12 +37,12 @@ library MerkleProof { * * _Available since v4.7._ */ - function verifyCall( + function verifyCalldata( bytes32[] calldata proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { - return processProofCall(proof, leaf) == root; + return processProofCalldata(proof, leaf) == root; } /** @@ -66,7 +66,7 @@ library MerkleProof { * * _Available since v4.7._ */ - function processProofCall(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { + function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); @@ -84,25 +84,11 @@ library MerkleProof { */ function multiProofVerify( bytes32 root, - bytes32[] memory leafs, - bytes32[] memory proofs, - bool[] memory proofFlag - ) internal pure returns (bool) { - return processMultiProof(leafs, proofs, proofFlag) == root; - } - - /** - * @dev Calldata version of {multiProofVerify} - * - * _Available since v4.7._ - */ - function multiProofVerifyCall( - bytes32 root, - bytes32[] calldata leafs, + bytes32[] calldata leaves, bytes32[] calldata proofs, bool[] calldata proofFlag ) internal pure returns (bool) { - return processMultiProofCall(leafs, proofs, proofFlag) == root; + return processMultiProof(leaves, proofs, proofFlag) == root; } /** @@ -113,59 +99,19 @@ library MerkleProof { * _Available since v4.7._ */ function processMultiProof( - bytes32[] memory leafs, - bytes32[] memory proofs, - bool[] memory proofFlag - ) 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 `leafs` 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 leafsLen = leafs.length; - uint256 totalHashes = proofFlag.length; - - // Check proof validity. - require(leafsLen + proofs.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". - bytes32[] memory hashes = new bytes32[](totalHashes); - uint256 leafPos = 0; - uint256 hashPos = 0; - uint256 proofPos = 0; - // At each step, we compute the next hash using two values: - // - 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. - for (uint256 i = 0; i < totalHashes; i++) { - bytes32 a = leafPos < leafsLen ? leafs[leafPos++] : hashes[hashPos++]; - bytes32 b = proofFlag[i] ? leafPos < leafsLen ? leafs[leafPos++] : hashes[hashPos++] : proofs[proofPos++]; - hashes[i] = _hashPair(a, b); - } - - return hashes[totalHashes - 1]; - } - - /** - * @dev Calldata version of {processMultiProof} - * - * _Available since v4.7._ - */ - function processMultiProofCall( - bytes32[] calldata leafs, + bytes32[] calldata leaves, bytes32[] calldata proofs, bool[] calldata proofFlag ) 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 `leafs` array, then goes onto the + // 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 leafsLen = leafs.length; + uint256 leavesLen = leaves.length; uint256 totalHashes = proofFlag.length; // Check proof validity. - require(leafsLen + proofs.length - 1 == totalHashes, "MerkleProof: invalid multiproof"); + require(leavesLen + proofs.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". @@ -179,8 +125,8 @@ library MerkleProof { // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the // `proofs` array. for (uint256 i = 0; i < totalHashes; i++) { - bytes32 a = leafPos < leafsLen ? leafs[leafPos++] : hashes[hashPos++]; - bytes32 b = proofFlag[i] ? leafPos < leafsLen ? leafs[leafPos++] : hashes[hashPos++] : proofs[proofPos++]; + bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; + bytes32 b = proofFlag[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proofs[proofPos++]; hashes[i] = _hashPair(a, b); } diff --git a/test/utils/cryptography/MerkleProof.test.js b/test/utils/cryptography/MerkleProof.test.js index 3e4ba165789..d1be8c69520 100644 --- a/test/utils/cryptography/MerkleProof.test.js +++ b/test/utils/cryptography/MerkleProof.test.js @@ -25,14 +25,14 @@ contract('MerkleProof', function (accounts) { const proof = merkleTree.getHexProof(leaf); expect(await this.merkleProof.verify(proof, root, leaf)).to.equal(true); - expect(await this.merkleProof.verifyCall(proof, root, leaf)).to.equal(true); + expect(await this.merkleProof.verifyCalldata(proof, root, leaf)).to.equal(true); // For demonstration, it is also possible to create valid proofs for certain 64-byte values *not* in elements: const noSuchLeaf = keccak256( Buffer.concat([keccak256(elements[0]), keccak256(elements[1])].sort(Buffer.compare)), ); expect(await this.merkleProof.verify(proof.slice(1), root, noSuchLeaf)).to.equal(true); - expect(await this.merkleProof.verifyCall(proof.slice(1), root, noSuchLeaf)).to.equal(true); + expect(await this.merkleProof.verifyCalldata(proof.slice(1), root, noSuchLeaf)).to.equal(true); }); it('returns false for an invalid Merkle proof', async function () { @@ -49,7 +49,7 @@ contract('MerkleProof', function (accounts) { const badProof = badMerkleTree.getHexProof(badElements[0]); expect(await this.merkleProof.verify(badProof, correctRoot, correctLeaf)).to.equal(false); - expect(await this.merkleProof.verifyCall(badProof, correctRoot, correctLeaf)).to.equal(false); + expect(await this.merkleProof.verifyCalldata(badProof, correctRoot, correctLeaf)).to.equal(false); }); it('returns false for a Merkle proof of invalid length', async function () { @@ -64,7 +64,7 @@ contract('MerkleProof', function (accounts) { const badProof = proof.slice(0, proof.length - 5); expect(await this.merkleProof.verify(badProof, root, leaf)).to.equal(false); - expect(await this.merkleProof.verifyCall(badProof, root, leaf)).to.equal(false); + expect(await this.merkleProof.verifyCalldata(badProof, root, leaf)).to.equal(false); }); }); @@ -79,7 +79,6 @@ contract('MerkleProof', function (accounts) { const proofFlags = merkleTree.getProofFlags(proofLeaves, proof); expect(await this.merkleProof.multiProofVerify(root, proofLeaves, proof, proofFlags)).to.equal(true); - expect(await this.merkleProof.multiProofVerifyCall(root, proofLeaves, proof, proofFlags)).to.equal(true); }); it('returns false for an invalid Merkle multi proof', async function () { @@ -93,8 +92,6 @@ contract('MerkleProof', function (accounts) { const badProofFlags = badMerkleTree.getProofFlags(badProofLeaves, badProof); expect(await this.merkleProof.multiProofVerify(root, badProofLeaves, badProof, badProofFlags)).to.equal(false); - expect(await this.merkleProof.multiProofVerifyCall(root, badProofLeaves, badProof, badProofFlags)) - .to.equal(false); }); it('revert with invalid multi proof #1', async function () { @@ -114,16 +111,6 @@ contract('MerkleProof', function (accounts) { ), 'MerkleProof: invalid multiproof', ); - - await expectRevert( - this.merkleProof.multiProofVerifyCall( - root, - [ leaves[0], badLeaf ], // A, E - [ leaves[1], fill, merkleTree.layers[1][1] ], - [ false, false, false ], - ), - 'MerkleProof: invalid multiproof', - ); }); it('revert with invalid multi proof #2', async function () { @@ -143,16 +130,6 @@ contract('MerkleProof', function (accounts) { ), 'reverted with panic code 0x32', ); - - await expectRevert( - this.merkleProof.multiProofVerifyCall( - root, - [ badLeaf, leaves[0] ], // A, E - [ leaves[1], fill, merkleTree.layers[1][1] ], - [ false, false, false, false ], - ), - 'reverted with panic code 0x32', - ); }); }); });