From 12fac4ff54a166c4ab10317ace6b6e9a3024b8fe Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 23 Jun 2025 15:30:15 +0000 Subject: [PATCH 1/5] Refactor Merkles --- src/utils/MerkleProofLib.sol | 50 +++++++++++------------ src/utils/MerkleTreeLib.sol | 77 +++++++++++++++++++++++++----------- test/MerkleProofLib.t.sol | 4 +- test/MerkleTreeLib.t.sol | 17 ++------ 4 files changed, 84 insertions(+), 64 deletions(-) diff --git a/src/utils/MerkleProofLib.sol b/src/utils/MerkleProofLib.sol index 967eb98e2d..a2ea08ce10 100644 --- a/src/utils/MerkleProofLib.sol +++ b/src/utils/MerkleProofLib.sol @@ -74,24 +74,24 @@ library MerkleProofLib { } } - /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`, + /// @dev Returns whether all `leafs` exist in the Merkle tree with `root`, /// given `proof` and `flags`. /// /// Note: - /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` + /// - Breaking the invariant `flags.length == (leafs.length - 1) + proof.length` /// will always return false. - /// - The sum of the lengths of `proof` and `leaves` must never overflow. + /// - The sum of the lengths of `proof` and `leafs` must never overflow. /// - Any non-zero word in the `flags` array is treated as true. /// - The memory offset of `proof` must be non-zero /// (i.e. `proof` is not pointing to the scratch space). function verifyMultiProof( bytes32[] memory proof, bytes32 root, - bytes32[] memory leaves, + bytes32[] memory leafs, bool[] memory flags ) internal pure returns (bool isValid) { // Rebuilds the root by consuming and producing values on a queue. - // The queue starts with the `leaves` array, and goes into a `hashes` array. + // The queue starts with the `leafs` array, and goes into a `hashes` array. // After the process, the last element on the queue is verified // to be equal to the `root`. // @@ -101,21 +101,21 @@ library MerkleProofLib { /// @solidity memory-safe-assembly assembly { // Cache the lengths of the arrays. - let leavesLength := mload(leaves) + let leavesLength := mload(leafs) let proofLength := mload(proof) let flagsLength := mload(flags) // Advance the pointers of the arrays to point to the data. - leaves := add(0x20, leaves) + leafs := add(0x20, leafs) proof := add(0x20, proof) flags := add(0x20, flags) // If the number of flags is correct. for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} { - // For the case where `proof.length + leaves.length == 1`. + // For the case where `proof.length + leafs.length == 1`. if iszero(flagsLength) { - // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`. - isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root) + // `isValid = (proof.length == 1 ? proof[0] : leafs[0]) == root`. + isValid := eq(mload(xor(leafs, mul(xor(proof, leafs), proofLength))), root) break } @@ -124,12 +124,12 @@ library MerkleProofLib { // We can use the free memory space for the queue. // We don't need to allocate, since the queue is temporary. let hashesFront := mload(0x40) - // Copy the leaves into the hashes. + // Copy the leafs into the hashes. // Sometimes, a little memory expansion costs less than branching. // Should cost less, even with a high free memory offset of 0x7d00. leavesLength := shl(5, leavesLength) for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } { - mstore(add(hashesFront, i), mload(add(leaves, i))) + mstore(add(hashesFront, i), mload(add(leafs, i))) } // Compute the back of the hashes. let hashesBack := add(hashesFront, leavesLength) @@ -179,11 +179,11 @@ library MerkleProofLib { } } - /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`, + /// @dev Returns whether all `leafs` exist in the Merkle tree with `root`, /// given `proof` and `flags`. /// /// Note: - /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` + /// - Breaking the invariant `flags.length == (leafs.length - 1) + proof.length` /// will always return false. /// - Any non-zero word in the `flags` array is treated as true. /// - The calldata offset of `proof` must be non-zero @@ -191,11 +191,11 @@ library MerkleProofLib { function verifyMultiProofCalldata( bytes32[] calldata proof, bytes32 root, - bytes32[] calldata leaves, + bytes32[] calldata leafs, bool[] calldata flags ) internal pure returns (bool isValid) { // Rebuilds the root by consuming and producing values on a queue. - // The queue starts with the `leaves` array, and goes into a `hashes` array. + // The queue starts with the `leafs` array, and goes into a `hashes` array. // After the process, the last element on the queue is verified // to be equal to the `root`. // @@ -205,14 +205,14 @@ library MerkleProofLib { /// @solidity memory-safe-assembly assembly { // If the number of flags is correct. - for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} { - // For the case where `proof.length + leaves.length == 1`. + for {} eq(add(leafs.length, proof.length), add(flags.length, 1)) {} { + // For the case where `proof.length + leafs.length == 1`. if iszero(flags.length) { - // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`. + // `isValid = (proof.length == 1 ? proof[0] : leafs[0]) == root`. // forgefmt: disable-next-item isValid := eq( calldataload( - xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length)) + xor(leafs.offset, mul(xor(proof.offset, leafs.offset), proof.length)) ), root ) @@ -224,12 +224,12 @@ library MerkleProofLib { // We can use the free memory space for the queue. // We don't need to allocate, since the queue is temporary. let hashesFront := mload(0x40) - // Copy the leaves into the hashes. + // Copy the leafs into the hashes. // Sometimes, a little memory expansion costs less than branching. // Should cost less, even with a high free memory offset of 0x7d00. - calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length)) + calldatacopy(hashesFront, leafs.offset, shl(5, leafs.length)) // Compute the back of the hashes. - let hashesBack := add(hashesFront, shl(5, leaves.length)) + let hashesBack := add(hashesFront, shl(5, leafs.length)) // This is the end of the memory for the queue. // We recycle `flagsLength` to save on stack variables (sometimes save gas). flags.length := add(hashesBack, shl(5, flags.length)) @@ -292,10 +292,10 @@ library MerkleProofLib { } /// @dev Returns an empty calldata bytes32 array. - function emptyLeaves() internal pure returns (bytes32[] calldata leaves) { + function emptyLeafs() internal pure returns (bytes32[] calldata leafs) { /// @solidity memory-safe-assembly assembly { - leaves.length := 0 + leafs.length := 0 } } diff --git a/src/utils/MerkleTreeLib.sol b/src/utils/MerkleTreeLib.sol index 72a0850a2f..fc1b8e37e4 100644 --- a/src/utils/MerkleTreeLib.sol +++ b/src/utils/MerkleTreeLib.sol @@ -35,18 +35,18 @@ library MerkleTreeLib { /// @dev Builds and return a complete Merkle tree. /// To make it a full Merkle tree, use `build(pad(leafs))`. - function build(bytes32[] memory leafs) internal pure returns (bytes32[] memory result) { + function build(bytes32[] memory leafs) internal pure returns (bytes32[] memory tree) { /// @solidity memory-safe-assembly assembly { - result := mload(0x40) // `nodes`. + tree := mload(0x40) // `nodes`. let l := mload(leafs) if iszero(l) { mstore(0x00, 0x089aff6e) // `MerkleTreeLeafsEmpty()`. revert(0x1c, 0x04) } let n := sub(add(l, l), 1) - mstore(result, n) // `.length`. - let nodes := add(result, 0x20) + mstore(tree, n) // `.length`. + let nodes := add(tree, 0x20) let f := add(nodes, shl(5, n)) mstore(0x40, f) // Allocate memory. let e := add(0x20, shl(5, l)) @@ -71,11 +71,11 @@ library MerkleTreeLib { } /// @dev Returns the root. - function root(bytes32[] memory t) internal pure returns (bytes32 result) { + function root(bytes32[] memory tree) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { - result := mload(add(0x20, t)) - if iszero(mload(t)) { + result := mload(add(0x20, tree)) + if iszero(mload(tree)) { mstore(0x00, 0x7a856a38) // `MerkleTreeOutOfBoundsAccess()`. revert(0x1c, 0x04) } @@ -83,32 +83,63 @@ library MerkleTreeLib { } /// @dev Returns the number of leafs. - function numLeafs(bytes32[] memory t) internal pure returns (uint256) { + function numLeafs(bytes32[] memory tree) internal pure returns (uint256) { unchecked { - return t.length - (t.length >> 1); + return tree.length - (tree.length >> 1); } } /// @dev Returns the number of internal nodes. - function numInternalNodes(bytes32[] memory t) internal pure returns (uint256) { - return t.length >> 1; + function numInternalNodes(bytes32[] memory tree) internal pure returns (uint256) { + return tree.length >> 1; } /// @dev Returns the leaf at `leafIndex`. - function leaf(bytes32[] memory t, uint256 leafIndex) internal pure returns (bytes32 result) { + function leafAt(bytes32[] memory tree, uint256 leafIndex) + internal + pure + returns (bytes32 result) + { /// @solidity memory-safe-assembly assembly { - let n := mload(t) + let n := mload(tree) if iszero(lt(leafIndex, sub(n, shr(1, n)))) { mstore(0x00, 0x7a856a38) // `MerkleTreeOutOfBoundsAccess()`. revert(0x1c, 0x04) } - result := mload(add(t, shl(5, sub(n, leafIndex)))) + result := mload(add(tree, shl(5, sub(n, leafIndex)))) + } + } + + /// @dev Returns the leafs at `leafIndices`. + function leafsAt(bytes32[] memory tree, uint256[] memory leafIndices) + internal + pure + returns (bytes32[] memory result) + { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + let l := mload(leafIndices) + mstore(result, l) // `.length`. + let d := sub(leafIndices, result) + let n := mload(tree) + let o := add(result, 0x20) + for { let i := 0 } iszero(eq(i, l)) { i := add(i, 1) } { + let j := add(o, shl(5, i)) + let leafIndex := mload(add(j, d)) + if iszero(lt(leafIndex, sub(n, shr(1, n)))) { + mstore(0x00, 0x7a856a38) // `MerkleTreeOutOfBoundsAccess()`. + revert(0x1c, 0x04) + } + mstore(j, mload(add(tree, shl(5, sub(n, leafIndex))))) + } + mstore(0x40, add(o, shl(5, l))) // Allocate memory. } } /// @dev Returns the proof for the leaf at `leafIndex`. - function leafProof(bytes32[] memory t, uint256 leafIndex) + function leafProof(bytes32[] memory tree, uint256 leafIndex) internal pure returns (bytes32[] memory result) @@ -116,16 +147,16 @@ library MerkleTreeLib { uint256 nodeIndex; /// @solidity memory-safe-assembly assembly { - let n := mload(t) + let n := mload(tree) nodeIndex := sub(n, add(1, leafIndex)) if iszero(lt(leafIndex, sub(n, shr(1, n)))) { nodeIndex := not(0) } } - result = nodeProof(t, nodeIndex); + result = nodeProof(tree, nodeIndex); } /// @dev Returns the proof for the node at `nodeIndex`. /// This function can be used to prove the existence of internal nodes. - function nodeProof(bytes32[] memory t, uint256 nodeIndex) + function nodeProof(bytes32[] memory tree, uint256 nodeIndex) internal pure returns (bytes32[] memory result) @@ -133,13 +164,13 @@ library MerkleTreeLib { /// @solidity memory-safe-assembly assembly { result := mload(0x40) - if iszero(lt(nodeIndex, mload(t))) { + if iszero(lt(nodeIndex, mload(tree))) { mstore(0x00, 0x7a856a38) // `MerkleTreeOutOfBoundsAccess()`. revert(0x1c, 0x04) } let o := add(result, 0x20) for { let i := nodeIndex } i { i := shr(1, sub(i, 1)) } { - mstore(o, mload(add(t, shl(5, add(i, shl(1, and(1, i))))))) + mstore(o, mload(add(tree, shl(5, add(i, shl(1, and(1, i))))))) o := add(o, 0x20) } mstore(0x40, o) // Allocate memory. @@ -149,7 +180,7 @@ library MerkleTreeLib { /// @dev Returns proof and corresponding flags for multiple leafs. /// The `leafIndices` must be non-empty and sorted in strictly ascending order. - function leafsMultiProof(bytes32[] memory t, uint256[] memory leafIndices) + function leafsMultiProof(bytes32[] memory tree, uint256[] memory leafIndices) internal pure returns (bytes32[] memory proof, bool[] memory flags) @@ -207,13 +238,13 @@ library MerkleTreeLib { mstore(0x00, 0xe9729976) // `MerkleTreeInvalidLeafIndices()`. revert(0x1c, 0x04) } - let flagsLen, proofLen := gen(leafIndices, t, 0x00, 0x00) + let flagsLen, proofLen := gen(leafIndices, tree, 0x00, 0x00) proof := mload(0x40) mstore(proof, proofLen) flags := add(add(proof, 0x20), shl(5, proofLen)) mstore(flags, flagsLen) mstore(0x40, add(add(flags, 0x20), shl(5, flagsLen))) // Allocate memory. - flagsLen, proofLen := gen(leafIndices, t, proof, flags) + flagsLen, proofLen := gen(leafIndices, tree, proof, flags) } } diff --git a/test/MerkleProofLib.t.sol b/test/MerkleProofLib.t.sol index 835500de20..6d864dea7a 100644 --- a/test/MerkleProofLib.t.sol +++ b/test/MerkleProofLib.t.sol @@ -423,7 +423,7 @@ contract MerkleProofLibTest is SoladyTest { MerkleProofLib.verifyMultiProofCalldata( MerkleProofLib.emptyProof(), bytes32(0), - MerkleProofLib.emptyLeaves(), + MerkleProofLib.emptyLeafs(), MerkleProofLib.emptyFlags() ) ); @@ -432,7 +432,7 @@ contract MerkleProofLibTest is SoladyTest { MerkleProofLib.verifyMultiProof( MerkleProofLib.emptyProof(), bytes32(0), - MerkleProofLib.emptyLeaves(), + MerkleProofLib.emptyLeafs(), MerkleProofLib.emptyFlags() ) ); diff --git a/test/MerkleTreeLib.t.sol b/test/MerkleTreeLib.t.sol index b34adfdac0..b043d283e8 100644 --- a/test/MerkleTreeLib.t.sol +++ b/test/MerkleTreeLib.t.sol @@ -31,7 +31,7 @@ contract MerkleTreeLibTest is SoladyTest { _checkMemory(t); if (leafs.length >= 1) { uint256 i = _randomUniform() % leafs.length; - assertEq(t.leaf(i), leafs[i]); + assertEq(t.leafAt(i), leafs[i]); } } @@ -87,7 +87,7 @@ contract MerkleTreeLibTest is SoladyTest { pure returns (bytes32) { - return MerkleTreeLib.build(leafs).leaf(leafIndex); + return MerkleTreeLib.build(leafs).leafAt(leafIndex); } function _maybePad(bytes32[] memory leafs) internal returns (bytes32[] memory) { @@ -183,7 +183,7 @@ contract MerkleTreeLibTest is SoladyTest { t.leafIndices = _generateUniqueLeafIndices(t.leafs); t.tree = MerkleTreeLib.build(t.leafs); (t.proof, t.flags) = t.tree.leafsMultiProof(t.leafIndices); - t.gathered = _gatherLeafs(t.leafs, t.leafIndices); + t.gathered = t.tree.leafsAt(t.leafIndices); assertTrue(MerkleProofLib.verifyMultiProof(t.proof, t.tree.root(), t.gathered, t.flags)); } @@ -206,17 +206,6 @@ contract MerkleTreeLibTest is SoladyTest { LibSort.sort(indices); } - function _gatherLeafs(bytes32[] memory leafs, uint256[] memory indices) - internal - pure - returns (bytes32[] memory gathered) - { - gathered = new bytes32[](indices.length); - for (uint256 i; i < indices.length; ++i) { - gathered[i] = leafs[indices[i]]; - } - } - function testMultiProofRevertsForEmptyLeafs() public { vm.expectRevert(MerkleTreeLib.MerkleTreeInvalidLeafIndices.selector); this.multiProofRevertsForEmptyLeafs(); From 3c5a0fd09d6815c5aa14c877cd8cfc41bef9f729 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 23 Jun 2025 15:30:51 +0000 Subject: [PATCH 2/5] Regen docs --- docs/utils/merkleprooflib.md | 18 ++++++++--------- docs/utils/merkletreelib.md | 38 ++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/docs/utils/merkleprooflib.md b/docs/utils/merkleprooflib.md index 491d7389af..37e111fd9f 100644 --- a/docs/utils/merkleprooflib.md +++ b/docs/utils/merkleprooflib.md @@ -40,19 +40,19 @@ Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. function verifyMultiProof( bytes32[] memory proof, bytes32 root, - bytes32[] memory leaves, + bytes32[] memory leafs, bool[] memory flags ) internal pure returns (bool isValid) ``` -Returns whether all `leaves` exist in the Merkle tree with `root`, +Returns whether all `leafs` exist in the Merkle tree with `root`, given `proof` and `flags`. Note: -- Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` +- Breaking the invariant `flags.length == (leafs.length - 1) + proof.length` will always return false. -- The sum of the lengths of `proof` and `leaves` must never overflow. +- The sum of the lengths of `proof` and `leafs` must never overflow. - Any non-zero word in the `flags` array is treated as true. - The memory offset of `proof` must be non-zero (i.e. `proof` is not pointing to the scratch space). @@ -63,17 +63,17 @@ given `proof` and `flags`. function verifyMultiProofCalldata( bytes32[] calldata proof, bytes32 root, - bytes32[] calldata leaves, + bytes32[] calldata leafs, bool[] calldata flags ) internal pure returns (bool isValid) ``` -Returns whether all `leaves` exist in the Merkle tree with `root`, +Returns whether all `leafs` exist in the Merkle tree with `root`, given `proof` and `flags`. Note: -- Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` +- Breaking the invariant `flags.length == (leafs.length - 1) + proof.length` will always return false. - Any non-zero word in the `flags` array is treated as true. - The calldata offset of `proof` must be non-zero @@ -89,10 +89,10 @@ function emptyProof() internal pure returns (bytes32[] calldata proof) Returns an empty calldata bytes32 array. -### emptyLeaves() +### emptyLeafs() ```solidity -function emptyLeaves() internal pure returns (bytes32[] calldata leaves) +function emptyLeafs() internal pure returns (bytes32[] calldata leafs) ``` Returns an empty calldata bytes32 array. diff --git a/docs/utils/merkletreelib.md b/docs/utils/merkletreelib.md index e04334cb01..67bb7a2a9f 100644 --- a/docs/utils/merkletreelib.md +++ b/docs/utils/merkletreelib.md @@ -54,7 +54,7 @@ Leaf indices for multi proof must be strictly ascending and not empty. function build(bytes32[] memory leafs) internal pure - returns (bytes32[] memory result) + returns (bytes32[] memory tree) ``` Builds and return a complete Merkle tree. @@ -63,7 +63,10 @@ To make it a full Merkle tree, use `build(pad(leafs))`. ### root(bytes32[]) ```solidity -function root(bytes32[] memory t) internal pure returns (bytes32 result) +function root(bytes32[] memory tree) + internal + pure + returns (bytes32 result) ``` Returns the root. @@ -71,7 +74,7 @@ Returns the root. ### numLeafs(bytes32[]) ```solidity -function numLeafs(bytes32[] memory t) internal pure returns (uint256) +function numLeafs(bytes32[] memory tree) internal pure returns (uint256) ``` Returns the number of leafs. @@ -79,7 +82,7 @@ Returns the number of leafs. ### numInternalNodes(bytes32[]) ```solidity -function numInternalNodes(bytes32[] memory t) +function numInternalNodes(bytes32[] memory tree) internal pure returns (uint256) @@ -87,10 +90,10 @@ function numInternalNodes(bytes32[] memory t) Returns the number of internal nodes. -### leaf(bytes32[],uint256) +### leafAt(bytes32[],uint256) ```solidity -function leaf(bytes32[] memory t, uint256 leafIndex) +function leafAt(bytes32[] memory tree, uint256 leafIndex) internal pure returns (bytes32 result) @@ -98,10 +101,21 @@ function leaf(bytes32[] memory t, uint256 leafIndex) Returns the leaf at `leafIndex`. +### leafsAt(bytes32[],uint256[]) + +```solidity +function leafsAt(bytes32[] memory tree, uint256[] memory leafIndices) + internal + pure + returns (bytes32[] memory result) +``` + +Returns the leafs at `leafIndices`. + ### leafProof(bytes32[],uint256) ```solidity -function leafProof(bytes32[] memory t, uint256 leafIndex) +function leafProof(bytes32[] memory tree, uint256 leafIndex) internal pure returns (bytes32[] memory result) @@ -112,7 +126,7 @@ Returns the proof for the leaf at `leafIndex`. ### nodeProof(bytes32[],uint256) ```solidity -function nodeProof(bytes32[] memory t, uint256 nodeIndex) +function nodeProof(bytes32[] memory tree, uint256 nodeIndex) internal pure returns (bytes32[] memory result) @@ -124,10 +138,10 @@ This function can be used to prove the existence of internal nodes. ### leafsMultiProof(bytes32[],uint256[]) ```solidity -function leafsMultiProof(bytes32[] memory t, uint256[] memory leafIndices) - internal - pure - returns (bytes32[] memory proof, bool[] memory flags) +function leafsMultiProof( + bytes32[] memory tree, + uint256[] memory leafIndices +) internal pure returns (bytes32[] memory proof, bool[] memory flags) ``` Returns proof and corresponding flags for multiple leafs. From 0527a54e970f90f966705af354ff4a2172887001 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 23 Jun 2025 15:35:43 +0000 Subject: [PATCH 3/5] T --- docs/utils/merkleprooflib.md | 18 ++--- docs/utils/merkletreelib.md | 42 +++++------ src/utils/MerkleProofLib.sol | 50 +++++++------- src/utils/MerkleTreeLib.sol | 56 +++++++-------- test/MerkleProofLib.t.sol | 4 +- test/MerkleTreeLib.t.sol | 130 +++++++++++++++++------------------ 6 files changed, 150 insertions(+), 150 deletions(-) diff --git a/docs/utils/merkleprooflib.md b/docs/utils/merkleprooflib.md index 37e111fd9f..491d7389af 100644 --- a/docs/utils/merkleprooflib.md +++ b/docs/utils/merkleprooflib.md @@ -40,19 +40,19 @@ Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. function verifyMultiProof( bytes32[] memory proof, bytes32 root, - bytes32[] memory leafs, + bytes32[] memory leaves, bool[] memory flags ) internal pure returns (bool isValid) ``` -Returns whether all `leafs` exist in the Merkle tree with `root`, +Returns whether all `leaves` exist in the Merkle tree with `root`, given `proof` and `flags`. Note: -- Breaking the invariant `flags.length == (leafs.length - 1) + proof.length` +- Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` will always return false. -- The sum of the lengths of `proof` and `leafs` must never overflow. +- The sum of the lengths of `proof` and `leaves` must never overflow. - Any non-zero word in the `flags` array is treated as true. - The memory offset of `proof` must be non-zero (i.e. `proof` is not pointing to the scratch space). @@ -63,17 +63,17 @@ given `proof` and `flags`. function verifyMultiProofCalldata( bytes32[] calldata proof, bytes32 root, - bytes32[] calldata leafs, + bytes32[] calldata leaves, bool[] calldata flags ) internal pure returns (bool isValid) ``` -Returns whether all `leafs` exist in the Merkle tree with `root`, +Returns whether all `leaves` exist in the Merkle tree with `root`, given `proof` and `flags`. Note: -- Breaking the invariant `flags.length == (leafs.length - 1) + proof.length` +- Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` will always return false. - Any non-zero word in the `flags` array is treated as true. - The calldata offset of `proof` must be non-zero @@ -89,10 +89,10 @@ function emptyProof() internal pure returns (bytes32[] calldata proof) Returns an empty calldata bytes32 array. -### emptyLeafs() +### emptyLeaves() ```solidity -function emptyLeafs() internal pure returns (bytes32[] calldata leafs) +function emptyLeaves() internal pure returns (bytes32[] calldata leaves) ``` Returns an empty calldata bytes32 array. diff --git a/docs/utils/merkletreelib.md b/docs/utils/merkletreelib.md index 67bb7a2a9f..09994052b2 100644 --- a/docs/utils/merkletreelib.md +++ b/docs/utils/merkletreelib.md @@ -5,11 +5,11 @@ Library for generating Merkle trees. Note: -- Leafs are NOT auto hashed. Note that some libraries hash the leafs by default. +- Leaves are NOT auto hashed. Note that some libraries hash the leaves by default. We leave it up to you to decide if this is needed. -If your leafs are 64 bytes long, do hash them first for safety. +If your leaves are 64 bytes long, do hash them first for safety. See: https://www.rareskills.io/post/merkle-tree-second-preimage-attack -- Leafs are NOT auto globally sorted. Note that some libraries sort the leafs by default. +- Leaves are NOT auto globally sorted. Note that some libraries sort the leaves by default. - The pair hash is pair-sorted-keccak256, which works out-of-the-box with `MerkleProofLib`. - This library is NOT equivalent to OpenZeppelin or Murky. Equivalence is NOT required if you are just using this for pure Solidity testing. @@ -21,10 +21,10 @@ May be relevant for differential testing between Solidity vs external libraries. ## Custom Errors -### MerkleTreeLeafsEmpty() +### MerkleTreeLeavesEmpty() ```solidity -error MerkleTreeLeafsEmpty() +error MerkleTreeLeavesEmpty() ``` At least 1 leaf is required to build the tree. @@ -36,7 +36,7 @@ error MerkleTreeOutOfBoundsAccess() ``` Attempt to access a node with an out-of-bounds index. -Check if the tree has been built and has sufficient leafs and nodes. +Check if the tree has been built and has sufficient leaves and nodes. ### MerkleTreeInvalidLeafIndices() @@ -51,14 +51,14 @@ Leaf indices for multi proof must be strictly ascending and not empty. ### build(bytes32[]) ```solidity -function build(bytes32[] memory leafs) +function build(bytes32[] memory leaves) internal pure returns (bytes32[] memory tree) ``` Builds and return a complete Merkle tree. -To make it a full Merkle tree, use `build(pad(leafs))`. +To make it a full Merkle tree, use `build(pad(leaves))`. ### root(bytes32[]) @@ -71,13 +71,13 @@ function root(bytes32[] memory tree) Returns the root. -### numLeafs(bytes32[]) +### numLeaves(bytes32[]) ```solidity -function numLeafs(bytes32[] memory tree) internal pure returns (uint256) +function numLeaves(bytes32[] memory tree) internal pure returns (uint256) ``` -Returns the number of leafs. +Returns the number of leaves. ### numInternalNodes(bytes32[]) @@ -101,16 +101,16 @@ function leafAt(bytes32[] memory tree, uint256 leafIndex) Returns the leaf at `leafIndex`. -### leafsAt(bytes32[],uint256[]) +### leavesAt(bytes32[],uint256[]) ```solidity -function leafsAt(bytes32[] memory tree, uint256[] memory leafIndices) +function leavesAt(bytes32[] memory tree, uint256[] memory leafIndices) internal pure returns (bytes32[] memory result) ``` -Returns the leafs at `leafIndices`. +Returns the leaves at `leafIndices`. ### leafProof(bytes32[],uint256) @@ -135,36 +135,36 @@ function nodeProof(bytes32[] memory tree, uint256 nodeIndex) Returns the proof for the node at `nodeIndex`. This function can be used to prove the existence of internal nodes. -### leafsMultiProof(bytes32[],uint256[]) +### leavesMultiProof(bytes32[],uint256[]) ```solidity -function leafsMultiProof( +function leavesMultiProof( bytes32[] memory tree, uint256[] memory leafIndices ) internal pure returns (bytes32[] memory proof, bool[] memory flags) ``` -Returns proof and corresponding flags for multiple leafs. +Returns proof and corresponding flags for multiple leaves. The `leafIndices` must be non-empty and sorted in strictly ascending order. ### pad(bytes32[],bytes32) ```solidity -function pad(bytes32[] memory leafs, bytes32 defaultFill) +function pad(bytes32[] memory leaves, bytes32 defaultFill) internal pure returns (bytes32[] memory result) ``` -Returns a copy of leafs, with the length padded to a power of 2. +Returns a copy of leaves, with the length padded to a power of 2. ### pad(bytes32[]) ```solidity -function pad(bytes32[] memory leafs) +function pad(bytes32[] memory leaves) internal pure returns (bytes32[] memory result) ``` -Equivalent to `pad(leafs, bytes32(0))`. \ No newline at end of file +Equivalent to `pad(leaves, bytes32(0))`. \ No newline at end of file diff --git a/src/utils/MerkleProofLib.sol b/src/utils/MerkleProofLib.sol index a2ea08ce10..967eb98e2d 100644 --- a/src/utils/MerkleProofLib.sol +++ b/src/utils/MerkleProofLib.sol @@ -74,24 +74,24 @@ library MerkleProofLib { } } - /// @dev Returns whether all `leafs` exist in the Merkle tree with `root`, + /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`, /// given `proof` and `flags`. /// /// Note: - /// - Breaking the invariant `flags.length == (leafs.length - 1) + proof.length` + /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` /// will always return false. - /// - The sum of the lengths of `proof` and `leafs` must never overflow. + /// - The sum of the lengths of `proof` and `leaves` must never overflow. /// - Any non-zero word in the `flags` array is treated as true. /// - The memory offset of `proof` must be non-zero /// (i.e. `proof` is not pointing to the scratch space). function verifyMultiProof( bytes32[] memory proof, bytes32 root, - bytes32[] memory leafs, + bytes32[] memory leaves, bool[] memory flags ) internal pure returns (bool isValid) { // Rebuilds the root by consuming and producing values on a queue. - // The queue starts with the `leafs` array, and goes into a `hashes` array. + // The queue starts with the `leaves` array, and goes into a `hashes` array. // After the process, the last element on the queue is verified // to be equal to the `root`. // @@ -101,21 +101,21 @@ library MerkleProofLib { /// @solidity memory-safe-assembly assembly { // Cache the lengths of the arrays. - let leavesLength := mload(leafs) + let leavesLength := mload(leaves) let proofLength := mload(proof) let flagsLength := mload(flags) // Advance the pointers of the arrays to point to the data. - leafs := add(0x20, leafs) + leaves := add(0x20, leaves) proof := add(0x20, proof) flags := add(0x20, flags) // If the number of flags is correct. for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} { - // For the case where `proof.length + leafs.length == 1`. + // For the case where `proof.length + leaves.length == 1`. if iszero(flagsLength) { - // `isValid = (proof.length == 1 ? proof[0] : leafs[0]) == root`. - isValid := eq(mload(xor(leafs, mul(xor(proof, leafs), proofLength))), root) + // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`. + isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root) break } @@ -124,12 +124,12 @@ library MerkleProofLib { // We can use the free memory space for the queue. // We don't need to allocate, since the queue is temporary. let hashesFront := mload(0x40) - // Copy the leafs into the hashes. + // Copy the leaves into the hashes. // Sometimes, a little memory expansion costs less than branching. // Should cost less, even with a high free memory offset of 0x7d00. leavesLength := shl(5, leavesLength) for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } { - mstore(add(hashesFront, i), mload(add(leafs, i))) + mstore(add(hashesFront, i), mload(add(leaves, i))) } // Compute the back of the hashes. let hashesBack := add(hashesFront, leavesLength) @@ -179,11 +179,11 @@ library MerkleProofLib { } } - /// @dev Returns whether all `leafs` exist in the Merkle tree with `root`, + /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`, /// given `proof` and `flags`. /// /// Note: - /// - Breaking the invariant `flags.length == (leafs.length - 1) + proof.length` + /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` /// will always return false. /// - Any non-zero word in the `flags` array is treated as true. /// - The calldata offset of `proof` must be non-zero @@ -191,11 +191,11 @@ library MerkleProofLib { function verifyMultiProofCalldata( bytes32[] calldata proof, bytes32 root, - bytes32[] calldata leafs, + bytes32[] calldata leaves, bool[] calldata flags ) internal pure returns (bool isValid) { // Rebuilds the root by consuming and producing values on a queue. - // The queue starts with the `leafs` array, and goes into a `hashes` array. + // The queue starts with the `leaves` array, and goes into a `hashes` array. // After the process, the last element on the queue is verified // to be equal to the `root`. // @@ -205,14 +205,14 @@ library MerkleProofLib { /// @solidity memory-safe-assembly assembly { // If the number of flags is correct. - for {} eq(add(leafs.length, proof.length), add(flags.length, 1)) {} { - // For the case where `proof.length + leafs.length == 1`. + for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} { + // For the case where `proof.length + leaves.length == 1`. if iszero(flags.length) { - // `isValid = (proof.length == 1 ? proof[0] : leafs[0]) == root`. + // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`. // forgefmt: disable-next-item isValid := eq( calldataload( - xor(leafs.offset, mul(xor(proof.offset, leafs.offset), proof.length)) + xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length)) ), root ) @@ -224,12 +224,12 @@ library MerkleProofLib { // We can use the free memory space for the queue. // We don't need to allocate, since the queue is temporary. let hashesFront := mload(0x40) - // Copy the leafs into the hashes. + // Copy the leaves into the hashes. // Sometimes, a little memory expansion costs less than branching. // Should cost less, even with a high free memory offset of 0x7d00. - calldatacopy(hashesFront, leafs.offset, shl(5, leafs.length)) + calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length)) // Compute the back of the hashes. - let hashesBack := add(hashesFront, shl(5, leafs.length)) + let hashesBack := add(hashesFront, shl(5, leaves.length)) // This is the end of the memory for the queue. // We recycle `flagsLength` to save on stack variables (sometimes save gas). flags.length := add(hashesBack, shl(5, flags.length)) @@ -292,10 +292,10 @@ library MerkleProofLib { } /// @dev Returns an empty calldata bytes32 array. - function emptyLeafs() internal pure returns (bytes32[] calldata leafs) { + function emptyLeaves() internal pure returns (bytes32[] calldata leaves) { /// @solidity memory-safe-assembly assembly { - leafs.length := 0 + leaves.length := 0 } } diff --git a/src/utils/MerkleTreeLib.sol b/src/utils/MerkleTreeLib.sol index fc1b8e37e4..1ca7af1218 100644 --- a/src/utils/MerkleTreeLib.sol +++ b/src/utils/MerkleTreeLib.sol @@ -5,11 +5,11 @@ pragma solidity ^0.8.4; /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleTreeLib.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/merkle-tree/blob/master/src/core.ts) /// @dev Note: -/// - Leafs are NOT auto hashed. Note that some libraries hash the leafs by default. +/// - Leaves are NOT auto hashed. Note that some libraries hash the leaves by default. /// We leave it up to you to decide if this is needed. -/// If your leafs are 64 bytes long, do hash them first for safety. +/// If your leaves are 64 bytes long, do hash them first for safety. /// See: https://www.rareskills.io/post/merkle-tree-second-preimage-attack -/// - Leafs are NOT auto globally sorted. Note that some libraries sort the leafs by default. +/// - Leaves are NOT auto globally sorted. Note that some libraries sort the leaves by default. /// - The pair hash is pair-sorted-keccak256, which works out-of-the-box with `MerkleProofLib`. /// - This library is NOT equivalent to OpenZeppelin or Murky. /// Equivalence is NOT required if you are just using this for pure Solidity testing. @@ -20,10 +20,10 @@ library MerkleTreeLib { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev At least 1 leaf is required to build the tree. - error MerkleTreeLeafsEmpty(); + error MerkleTreeLeavesEmpty(); /// @dev Attempt to access a node with an out-of-bounds index. - /// Check if the tree has been built and has sufficient leafs and nodes. + /// Check if the tree has been built and has sufficient leaves and nodes. error MerkleTreeOutOfBoundsAccess(); /// @dev Leaf indices for multi proof must be strictly ascending and not empty. @@ -34,14 +34,14 @@ library MerkleTreeLib { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Builds and return a complete Merkle tree. - /// To make it a full Merkle tree, use `build(pad(leafs))`. - function build(bytes32[] memory leafs) internal pure returns (bytes32[] memory tree) { + /// To make it a full Merkle tree, use `build(pad(leaves))`. + function build(bytes32[] memory leaves) internal pure returns (bytes32[] memory tree) { /// @solidity memory-safe-assembly assembly { tree := mload(0x40) // `nodes`. - let l := mload(leafs) + let l := mload(leaves) if iszero(l) { - mstore(0x00, 0x089aff6e) // `MerkleTreeLeafsEmpty()`. + mstore(0x00, 0xe7171dc4) // `MerkleTreeLeavesEmpty()`. revert(0x1c, 0x04) } let n := sub(add(l, l), 1) @@ -51,7 +51,7 @@ library MerkleTreeLib { mstore(0x40, f) // Allocate memory. let e := add(0x20, shl(5, l)) for { let i := 0x20 } 1 {} { - mstore(sub(f, i), mload(add(leafs, i))) + mstore(sub(f, i), mload(add(leaves, i))) i := add(i, 0x20) if eq(i, e) { break } } @@ -82,8 +82,8 @@ library MerkleTreeLib { } } - /// @dev Returns the number of leafs. - function numLeafs(bytes32[] memory tree) internal pure returns (uint256) { + /// @dev Returns the number of leaves. + function numLeaves(bytes32[] memory tree) internal pure returns (uint256) { unchecked { return tree.length - (tree.length >> 1); } @@ -111,8 +111,8 @@ library MerkleTreeLib { } } - /// @dev Returns the leafs at `leafIndices`. - function leafsAt(bytes32[] memory tree, uint256[] memory leafIndices) + /// @dev Returns the leaves at `leafIndices`. + function leavesAt(bytes32[] memory tree, uint256[] memory leafIndices) internal pure returns (bytes32[] memory result) @@ -178,9 +178,9 @@ library MerkleTreeLib { } } - /// @dev Returns proof and corresponding flags for multiple leafs. + /// @dev Returns proof and corresponding flags for multiple leaves. /// The `leafIndices` must be non-empty and sorted in strictly ascending order. - function leafsMultiProof(bytes32[] memory tree, uint256[] memory leafIndices) + function leavesMultiProof(bytes32[] memory tree, uint256[] memory leafIndices) internal pure returns (bytes32[] memory proof, bool[] memory flags) @@ -194,7 +194,7 @@ library MerkleTreeLib { let b_ := 0 // Start index of circular buffer. for { let n_ := mload(t_) // Num nodes. - let l_ := sub(n_, shr(1, n_)) // Num leafs. + let l_ := sub(n_, shr(1, n_)) // Num leaves. let p_ := not(0) let i_ := 0 } 1 {} { @@ -248,8 +248,8 @@ library MerkleTreeLib { } } - /// @dev Returns a copy of leafs, with the length padded to a power of 2. - function pad(bytes32[] memory leafs, bytes32 defaultFill) + /// @dev Returns a copy of leaves, with the length padded to a power of 2. + function pad(bytes32[] memory leaves, bytes32 defaultFill) internal pure returns (bytes32[] memory result) @@ -257,20 +257,20 @@ library MerkleTreeLib { /// @solidity memory-safe-assembly assembly { result := mload(0x40) - let l := mload(leafs) + let l := mload(leaves) if iszero(l) { - mstore(0x00, 0x089aff6e) // `MerkleTreeLeafsEmpty()`. + mstore(0x00, 0xe7171dc4) // `MerkleTreeLeavesEmpty()`. revert(0x1c, 0x04) } let p := 1 // Padded length. for {} lt(p, l) {} { p := add(p, p) } mstore(result, p) // Store length. mstore(0x40, add(result, add(0x20, shl(5, p)))) // Allocate memory. - let d := sub(result, leafs) - let copyEnd := add(add(leafs, 0x20), shl(5, l)) - let end := add(add(leafs, 0x20), shl(5, p)) + let d := sub(result, leaves) + let copyEnd := add(add(leaves, 0x20), shl(5, l)) + let end := add(add(leaves, 0x20), shl(5, p)) mstore(0x00, defaultFill) - for { let i := add(leafs, 0x20) } 1 {} { + for { let i := add(leaves, 0x20) } 1 {} { mstore(add(i, d), mload(mul(i, lt(i, copyEnd)))) i := add(i, 0x20) if eq(i, end) { break } @@ -278,8 +278,8 @@ library MerkleTreeLib { } } - /// @dev Equivalent to `pad(leafs, bytes32(0))`. - function pad(bytes32[] memory leafs) internal pure returns (bytes32[] memory result) { - result = pad(leafs, bytes32(0)); + /// @dev Equivalent to `pad(leaves, bytes32(0))`. + function pad(bytes32[] memory leaves) internal pure returns (bytes32[] memory result) { + result = pad(leaves, bytes32(0)); } } diff --git a/test/MerkleProofLib.t.sol b/test/MerkleProofLib.t.sol index 6d864dea7a..835500de20 100644 --- a/test/MerkleProofLib.t.sol +++ b/test/MerkleProofLib.t.sol @@ -423,7 +423,7 @@ contract MerkleProofLibTest is SoladyTest { MerkleProofLib.verifyMultiProofCalldata( MerkleProofLib.emptyProof(), bytes32(0), - MerkleProofLib.emptyLeafs(), + MerkleProofLib.emptyLeaves(), MerkleProofLib.emptyFlags() ) ); @@ -432,7 +432,7 @@ contract MerkleProofLibTest is SoladyTest { MerkleProofLib.verifyMultiProof( MerkleProofLib.emptyProof(), bytes32(0), - MerkleProofLib.emptyLeafs(), + MerkleProofLib.emptyLeaves(), MerkleProofLib.emptyFlags() ) ); diff --git a/test/MerkleTreeLib.t.sol b/test/MerkleTreeLib.t.sol index b043d283e8..c12f584b22 100644 --- a/test/MerkleTreeLib.t.sol +++ b/test/MerkleTreeLib.t.sol @@ -12,48 +12,48 @@ contract MerkleTreeLibTest is SoladyTest { using MerkleTreeLib for bytes32[]; using LibPRNG for *; - function testBuildCompleteMerkleTree(bytes32[] memory leafs, bytes32 r) public { + function testBuildCompleteMerkleTree(bytes32[] memory leaves, bytes32 r) public { _maybeBrutalizeMemory(r); - if (leafs.length <= 1) { - leafs = new bytes32[](1); - leafs[0] = r; + if (leaves.length <= 1) { + leaves = new bytes32[](1); + leaves[0] = r; } - bytes32[] memory t = MerkleTreeLib.build(leafs); - assertEq(t.length, leafs.length * 2 - 1); - if (leafs.length == 1) { + bytes32[] memory t = MerkleTreeLib.build(leaves); + assertEq(t.length, leaves.length * 2 - 1); + if (leaves.length == 1) { assertEq(t[0], r); } else { assertNotEq(t[0], 0); } assertEq(t.root(), t[0]); - assertEq(leafs.length, t.numLeafs()); - assertEq(t.length, t.numLeafs() + t.numInternalNodes()); + assertEq(leaves.length, t.numLeaves()); + assertEq(t.length, t.numLeaves() + t.numInternalNodes()); _checkMemory(t); - if (leafs.length >= 1) { - uint256 i = _randomUniform() % leafs.length; - assertEq(t.leafAt(i), leafs[i]); + if (leaves.length >= 1) { + uint256 i = _randomUniform() % leaves.length; + assertEq(t.leafAt(i), leaves[i]); } } - function testPad(bytes32[] memory leafs, bytes32 defaultFill, uint256 r) public { + function testPad(bytes32[] memory leaves, bytes32 defaultFill, uint256 r) public { _maybeBrutalizeMemory(r); - if (leafs.length == 0) return; - assertEq(MerkleTreeLib.pad(leafs, defaultFill), _padOriginal(leafs, defaultFill)); + if (leaves.length == 0) return; + assertEq(MerkleTreeLib.pad(leaves, defaultFill), _padOriginal(leaves, defaultFill)); _checkMemory(); } - function _padOriginal(bytes32[] memory leafs, bytes32 defaultFill) + function _padOriginal(bytes32[] memory leaves, bytes32 defaultFill) internal pure returns (bytes32[] memory result) { unchecked { uint256 p = 1; - while (p < leafs.length) p = p << 1; + while (p < leaves.length) p = p << 1; result = new bytes32[](p); for (uint256 i; i < p; ++i) { - if (i < leafs.length) { - result[i] = leafs[i]; + if (i < leaves.length) { + result[i] = leaves[i]; } else { result[i] = defaultFill; } @@ -71,87 +71,87 @@ contract MerkleTreeLibTest is SoladyTest { if (h & 0x0f == 0) _brutalizeMemory(); } - function testBuildAndGetLeaf(bytes32[] memory leafs, uint256 leafIndex) public { - if (leafs.length == 0) return; + function testBuildAndGetLeaf(bytes32[] memory leaves, uint256 leafIndex) public { + if (leaves.length == 0) return; - if (leafIndex < leafs.length) { - assertEq(this.buildAndGetLeaf(leafs, leafIndex), leafs[leafIndex]); + if (leafIndex < leaves.length) { + assertEq(this.buildAndGetLeaf(leaves, leafIndex), leaves[leafIndex]); } else { vm.expectRevert(MerkleTreeLib.MerkleTreeOutOfBoundsAccess.selector); - this.buildAndGetLeaf(leafs, leafIndex); + this.buildAndGetLeaf(leaves, leafIndex); } } - function buildAndGetLeaf(bytes32[] memory leafs, uint256 leafIndex) + function buildAndGetLeaf(bytes32[] memory leaves, uint256 leafIndex) public pure returns (bytes32) { - return MerkleTreeLib.build(leafs).leafAt(leafIndex); + return MerkleTreeLib.build(leaves).leafAt(leafIndex); } - function _maybePad(bytes32[] memory leafs) internal returns (bytes32[] memory) { + function _maybePad(bytes32[] memory leaves) internal returns (bytes32[] memory) { if (_randomChance(2)) { if (_randomChance(2)) { - return leafs.pad(); + return leaves.pad(); } - return leafs.pad(bytes32(_random())); + return leaves.pad(bytes32(_random())); } - return leafs; + return leaves; } - function testBuildAndGetLeafProof(bytes32[] memory leafs, uint256 leafIndex) public { - if (leafs.length == 0) return _testBuildAndGetRoot(leafs); - leafs = _maybePad(leafs); - bytes32[] memory t = MerkleTreeLib.build(leafs); - if (leafIndex < leafs.length) { - bytes32[] memory proof = this.buildAndGetLeafProof(leafs, leafIndex); - assertTrue(MerkleProofLib.verify(proof, t.root(), leafs[leafIndex])); + function testBuildAndGetLeafProof(bytes32[] memory leaves, uint256 leafIndex) public { + if (leaves.length == 0) return _testBuildAndGetRoot(leaves); + leaves = _maybePad(leaves); + bytes32[] memory t = MerkleTreeLib.build(leaves); + if (leafIndex < leaves.length) { + bytes32[] memory proof = this.buildAndGetLeafProof(leaves, leafIndex); + assertTrue(MerkleProofLib.verify(proof, t.root(), leaves[leafIndex])); } else { vm.expectRevert(MerkleTreeLib.MerkleTreeOutOfBoundsAccess.selector); - this.buildAndGetLeafProof(leafs, leafIndex); + this.buildAndGetLeafProof(leaves, leafIndex); } } - function buildAndGetLeafProof(bytes32[] memory leafs, uint256 leafIndex) + function buildAndGetLeafProof(bytes32[] memory leaves, uint256 leafIndex) public pure returns (bytes32[] memory proof) { - bytes32[] memory t = MerkleTreeLib.build(leafs); + bytes32[] memory t = MerkleTreeLib.build(leaves); proof = t.leafProof(leafIndex); _checkMemory(); } - function testBuildAndGetNodeProof(bytes32[] memory leafs, uint256 nodeIndex) public { - if (leafs.length == 0) return _testBuildAndGetRoot(leafs); - bytes32[] memory t = MerkleTreeLib.build(leafs); + function testBuildAndGetNodeProof(bytes32[] memory leaves, uint256 nodeIndex) public { + if (leaves.length == 0) return _testBuildAndGetRoot(leaves); + bytes32[] memory t = MerkleTreeLib.build(leaves); if (nodeIndex < t.length) { - bytes32[] memory proof = this.buildAndGetNodeProof(leafs, nodeIndex); + bytes32[] memory proof = this.buildAndGetNodeProof(leaves, nodeIndex); assertTrue(MerkleProofLib.verify(proof, t.root(), t[nodeIndex])); } else { vm.expectRevert(MerkleTreeLib.MerkleTreeOutOfBoundsAccess.selector); - this.buildAndGetNodeProof(leafs, nodeIndex); + this.buildAndGetNodeProof(leaves, nodeIndex); } } - function buildAndGetNodeProof(bytes32[] memory leafs, uint256 nodeIndex) + function buildAndGetNodeProof(bytes32[] memory leaves, uint256 nodeIndex) public pure returns (bytes32[] memory proof) { - bytes32[] memory t = MerkleTreeLib.build(leafs); + bytes32[] memory t = MerkleTreeLib.build(leaves); proof = t.nodeProof(nodeIndex); _checkMemory(); } - function _testBuildAndGetRoot(bytes32[] memory leafs) internal { - vm.expectRevert(MerkleTreeLib.MerkleTreeLeafsEmpty.selector); - this.buildAndGetRoot(leafs); + function _testBuildAndGetRoot(bytes32[] memory leaves) internal { + vm.expectRevert(MerkleTreeLib.MerkleTreeLeavesEmpty.selector); + this.buildAndGetRoot(leaves); } - function buildAndGetRoot(bytes32[] memory leafs) public pure returns (bytes32) { - return MerkleTreeLib.build(leafs).root(); + function buildAndGetRoot(bytes32[] memory leaves) public pure returns (bytes32) { + return MerkleTreeLib.build(leaves).root(); } function testGetRootFromEmptyTree() public { @@ -164,7 +164,7 @@ contract MerkleTreeLibTest is SoladyTest { } struct TestMultiProofTemps { - bytes32[] leafs; + bytes32[] leaves; uint256[] leafIndices; bytes32[] gathered; bytes32[] tree; @@ -175,24 +175,24 @@ contract MerkleTreeLibTest is SoladyTest { function testBuildAndGetLeafsMultiProof(bytes32 r) public { _maybeBrutalizeMemory(r); TestMultiProofTemps memory t; - t.leafs = new bytes32[](_bound(_random(), 1, 128)); - for (uint256 i; i < t.leafs.length; ++i) { - t.leafs[i] = bytes32(_random()); + t.leaves = new bytes32[](_bound(_random(), 1, 128)); + for (uint256 i; i < t.leaves.length; ++i) { + t.leaves[i] = bytes32(_random()); } - t.leafs = _maybePad(t.leafs); - t.leafIndices = _generateUniqueLeafIndices(t.leafs); - t.tree = MerkleTreeLib.build(t.leafs); - (t.proof, t.flags) = t.tree.leafsMultiProof(t.leafIndices); - t.gathered = t.tree.leafsAt(t.leafIndices); + t.leaves = _maybePad(t.leaves); + t.leafIndices = _generateUniqueLeafIndices(t.leaves); + t.tree = MerkleTreeLib.build(t.leaves); + (t.proof, t.flags) = t.tree.leavesMultiProof(t.leafIndices); + t.gathered = t.tree.leavesAt(t.leafIndices); assertTrue(MerkleProofLib.verifyMultiProof(t.proof, t.tree.root(), t.gathered, t.flags)); } - function _generateUniqueLeafIndices(bytes32[] memory leafs) + function _generateUniqueLeafIndices(bytes32[] memory leaves) internal returns (uint256[] memory indices) { - indices = new uint256[](leafs.length); - for (uint256 i; i < leafs.length; ++i) { + indices = new uint256[](leaves.length); + for (uint256 i; i < leaves.length; ++i) { indices[i] = i; } LibPRNG.PRNG memory prng; @@ -212,6 +212,6 @@ contract MerkleTreeLibTest is SoladyTest { } function multiProofRevertsForEmptyLeafs() public pure { - (new bytes32[](1)).leafsMultiProof(new uint256[](0)); + (new bytes32[](1)).leavesMultiProof(new uint256[](0)); } } From 9a1970afed817f17f5834d71db5d17be94fb06ad Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 23 Jun 2025 15:42:50 +0000 Subject: [PATCH 4/5] T --- docs/utils/merkletreelib.md | 8 ++++---- src/utils/MerkleTreeLib.sol | 4 ++-- test/MerkleTreeLib.t.sol | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/utils/merkletreelib.md b/docs/utils/merkletreelib.md index 09994052b2..424bf10f36 100644 --- a/docs/utils/merkletreelib.md +++ b/docs/utils/merkletreelib.md @@ -90,10 +90,10 @@ function numInternalNodes(bytes32[] memory tree) Returns the number of internal nodes. -### leafAt(bytes32[],uint256) +### leaf(bytes32[],uint256) ```solidity -function leafAt(bytes32[] memory tree, uint256 leafIndex) +function leaf(bytes32[] memory tree, uint256 leafIndex) internal pure returns (bytes32 result) @@ -101,10 +101,10 @@ function leafAt(bytes32[] memory tree, uint256 leafIndex) Returns the leaf at `leafIndex`. -### leavesAt(bytes32[],uint256[]) +### gatherLeaves(bytes32[],uint256[]) ```solidity -function leavesAt(bytes32[] memory tree, uint256[] memory leafIndices) +function gatherLeaves(bytes32[] memory tree, uint256[] memory leafIndices) internal pure returns (bytes32[] memory result) diff --git a/src/utils/MerkleTreeLib.sol b/src/utils/MerkleTreeLib.sol index 1ca7af1218..43366d15c9 100644 --- a/src/utils/MerkleTreeLib.sol +++ b/src/utils/MerkleTreeLib.sol @@ -95,7 +95,7 @@ library MerkleTreeLib { } /// @dev Returns the leaf at `leafIndex`. - function leafAt(bytes32[] memory tree, uint256 leafIndex) + function leaf(bytes32[] memory tree, uint256 leafIndex) internal pure returns (bytes32 result) @@ -112,7 +112,7 @@ library MerkleTreeLib { } /// @dev Returns the leaves at `leafIndices`. - function leavesAt(bytes32[] memory tree, uint256[] memory leafIndices) + function gatherLeaves(bytes32[] memory tree, uint256[] memory leafIndices) internal pure returns (bytes32[] memory result) diff --git a/test/MerkleTreeLib.t.sol b/test/MerkleTreeLib.t.sol index c12f584b22..45f46d49f6 100644 --- a/test/MerkleTreeLib.t.sol +++ b/test/MerkleTreeLib.t.sol @@ -31,7 +31,7 @@ contract MerkleTreeLibTest is SoladyTest { _checkMemory(t); if (leaves.length >= 1) { uint256 i = _randomUniform() % leaves.length; - assertEq(t.leafAt(i), leaves[i]); + assertEq(t.leaf(i), leaves[i]); } } @@ -87,7 +87,7 @@ contract MerkleTreeLibTest is SoladyTest { pure returns (bytes32) { - return MerkleTreeLib.build(leaves).leafAt(leafIndex); + return MerkleTreeLib.build(leaves).leaf(leafIndex); } function _maybePad(bytes32[] memory leaves) internal returns (bytes32[] memory) { @@ -183,7 +183,7 @@ contract MerkleTreeLibTest is SoladyTest { t.leafIndices = _generateUniqueLeafIndices(t.leaves); t.tree = MerkleTreeLib.build(t.leaves); (t.proof, t.flags) = t.tree.leavesMultiProof(t.leafIndices); - t.gathered = t.tree.leavesAt(t.leafIndices); + t.gathered = t.tree.gatherLeaves(t.leafIndices); assertTrue(MerkleProofLib.verifyMultiProof(t.proof, t.tree.root(), t.gathered, t.flags)); } From 8a582fc658122450c3772003f25ba488187e5f92 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 23 Jun 2025 15:55:26 +0000 Subject: [PATCH 5/5] T --- docs/utils/merkletreelib.md | 4 ++-- src/utils/MerkleTreeLib.sol | 2 +- test/MerkleTreeLib.t.sol | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/utils/merkletreelib.md b/docs/utils/merkletreelib.md index 424bf10f36..1cae65ad48 100644 --- a/docs/utils/merkletreelib.md +++ b/docs/utils/merkletreelib.md @@ -135,10 +135,10 @@ function nodeProof(bytes32[] memory tree, uint256 nodeIndex) Returns the proof for the node at `nodeIndex`. This function can be used to prove the existence of internal nodes. -### leavesMultiProof(bytes32[],uint256[]) +### multiProofForLeaves(bytes32[],uint256[]) ```solidity -function leavesMultiProof( +function multiProofForLeaves( bytes32[] memory tree, uint256[] memory leafIndices ) internal pure returns (bytes32[] memory proof, bool[] memory flags) diff --git a/src/utils/MerkleTreeLib.sol b/src/utils/MerkleTreeLib.sol index 43366d15c9..6d12bec529 100644 --- a/src/utils/MerkleTreeLib.sol +++ b/src/utils/MerkleTreeLib.sol @@ -180,7 +180,7 @@ library MerkleTreeLib { /// @dev Returns proof and corresponding flags for multiple leaves. /// The `leafIndices` must be non-empty and sorted in strictly ascending order. - function leavesMultiProof(bytes32[] memory tree, uint256[] memory leafIndices) + function multiProofForLeaves(bytes32[] memory tree, uint256[] memory leafIndices) internal pure returns (bytes32[] memory proof, bool[] memory flags) diff --git a/test/MerkleTreeLib.t.sol b/test/MerkleTreeLib.t.sol index 45f46d49f6..bdfbf09aa3 100644 --- a/test/MerkleTreeLib.t.sol +++ b/test/MerkleTreeLib.t.sol @@ -182,7 +182,7 @@ contract MerkleTreeLibTest is SoladyTest { t.leaves = _maybePad(t.leaves); t.leafIndices = _generateUniqueLeafIndices(t.leaves); t.tree = MerkleTreeLib.build(t.leaves); - (t.proof, t.flags) = t.tree.leavesMultiProof(t.leafIndices); + (t.proof, t.flags) = t.tree.multiProofForLeaves(t.leafIndices); t.gathered = t.tree.gatherLeaves(t.leafIndices); assertTrue(MerkleProofLib.verifyMultiProof(t.proof, t.tree.root(), t.gathered, t.flags)); } @@ -212,6 +212,6 @@ contract MerkleTreeLibTest is SoladyTest { } function multiProofRevertsForEmptyLeafs() public pure { - (new bytes32[](1)).leavesMultiProof(new uint256[](0)); + (new bytes32[](1)).multiProofForLeaves(new uint256[](0)); } }