diff --git a/docs/utils/merkletreelib.md b/docs/utils/merkletreelib.md index d8025e7b38..e04334cb01 100644 --- a/docs/utils/merkletreelib.md +++ b/docs/utils/merkletreelib.md @@ -3,7 +3,17 @@ Library for generating Merkle trees. +Note: +- Leafs are NOT auto hashed. Note that some libraries hash the leafs 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. +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. +- 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. +May be relevant for differential testing between Solidity vs external libraries. @@ -132,4 +142,15 @@ function pad(bytes32[] memory leafs, bytes32 defaultFill) returns (bytes32[] memory result) ``` -Returns a copy of leafs, with the length padded to a power of 2. \ No newline at end of file +Returns a copy of leafs, with the length padded to a power of 2. + +### pad(bytes32[]) + +```solidity +function pad(bytes32[] memory leafs) + internal + pure + returns (bytes32[] memory result) +``` + +Equivalent to `pad(leafs, bytes32(0))`. \ No newline at end of file diff --git a/src/utils/MerkleTreeLib.sol b/src/utils/MerkleTreeLib.sol index e898f93709..8d673a691d 100644 --- a/src/utils/MerkleTreeLib.sol +++ b/src/utils/MerkleTreeLib.sol @@ -4,6 +4,16 @@ pragma solidity ^0.8.4; /// @notice Library for generating Merkle trees. /// @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. +/// 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. +/// 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. +/// - 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. +/// May be relevant for differential testing between Solidity vs external libraries. library MerkleTreeLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ @@ -244,4 +254,9 @@ 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)); + } } diff --git a/test/MerkleTreeLib.t.sol b/test/MerkleTreeLib.t.sol index 7006490efd..053a3a2334 100644 --- a/test/MerkleTreeLib.t.sol +++ b/test/MerkleTreeLib.t.sol @@ -90,8 +90,19 @@ contract MerkleTreeLibTest is SoladyTest { return MerkleTreeLib.build(leafs).leaf(leafIndex); } + function _maybePad(bytes32[] memory leafs) internal returns (bytes32[] memory) { + if (_randomChance(2)) { + if (_randomChance(2)) { + return leafs.pad(); + } + return leafs.pad(bytes32(_random())); + } + return leafs; + } + 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); @@ -161,12 +172,14 @@ contract MerkleTreeLibTest is SoladyTest { bool[] flags; } - function testBuildAndGetLeafsMultiProof(bytes32) public { + 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.leafs = _maybePad(t.leafs); t.leafIndices = _generateUniqueLeafIndices(t.leafs); t.tree = MerkleTreeLib.build(t.leafs); (t.proof, t.flags) = t.tree.leafsMultiProof(t.leafIndices);