@@ -10,13 +10,13 @@ import {SHA256} from "../libs/SHA256.sol";
1010/// @notice A Merkle tree implementation populating a tree of variable depth from left to right
1111/// and providing on-chain Merkle proofs.
1212/// @dev This is a modified version of the OpenZeppelin `MerkleTree` and `MerkleProof` implementation.
13- /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.3 .0/contracts/utils/structs/MerkleTree.sol
14- /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.3 .0/contracts/utils/cryptography/MerkleProof.sol
13+ /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.4 .0/contracts/utils/structs/MerkleTree.sol
14+ /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.4 .0/contracts/utils/cryptography/MerkleProof.sol
1515/// @custom:security-contact security@anoma.foundation
1616library MerkleTree {
1717 struct Tree {
1818 uint256 _nextLeafIndex;
19- mapping ( uint256 level = > mapping ( uint256 index = > bytes32 node )) _nodes ;
19+ bytes32 [] _sides ;
2020 bytes32 [] _zeros;
2121 }
2222
@@ -30,7 +30,15 @@ library MerkleTree {
3030 function setup (Tree storage self ) internal returns (bytes32 initialRoot ) {
3131 initialRoot = SHA256.EMPTY_HASH;
3232
33- self._zeros.push (SHA256.EMPTY_HASH);
33+ // Store depth in the dynamic array
34+ Arrays.unsafeSetLength (self._zeros, 256 );
35+
36+ // Build each root of zero-filled subtrees
37+ bytes32 currentZero = SHA256.EMPTY_HASH;
38+ for (uint256 i = 0 ; i < 256 ; ++ i) {
39+ Arrays.unsafeAccess (self._zeros, i).value = currentZero;
40+ currentZero = SHA256.hash (currentZero, currentZero);
41+ }
3442
3543 self._nextLeafIndex = 0 ;
3644 }
@@ -47,110 +55,43 @@ library MerkleTree {
4755 // Get the next leaf index and increment it after assignment.
4856 index = self._nextLeafIndex++ ;
4957
58+ // Rebuild the branch from leaf to root.
59+ uint256 currentIndex = index;
5060 bytes32 currentLevelHash = leaf;
61+ for (uint256 i = 0 ; i < treeDepth; ++ i) {
62+ // Compute the next level hash for depth `i+1`.
63+ // Check whether the `currentIndex` node is the left or right child of its parent.
64+ if (isLeftChild (currentIndex)) {
65+ // Store the current hash as the sibling (side) for the current level.
66+ Arrays.unsafeAccess (self._sides, i).value = currentLevelHash;
5167
52- if (treeDepth == 0 ) {
53- self._nodes[0 ][0 ] = currentLevelHash;
54- } else {
55- uint256 currentIndex = index;
56-
57- // Rebuild the branch from leaf to root.
58- for (uint256 i = 0 ; i < treeDepth; ++ i) {
59- // Store the current node hash at depth `i`.
60- self._nodes[i][currentIndex] = currentLevelHash;
61-
62- // Compute the next level hash for depth `i+1`.
63- // Check whether the `currentIndex` node is the left or right child of its parent.
64- if (isLeftChild (currentIndex)) {
65- // Compute the `currentLevelHash` using the right sibling.
66- // Because we fill the tree from left to right,
67- // the right child is empty and we must use the depth `i` zero hash.
68- currentLevelHash = SHA256.hash (currentLevelHash, Arrays.unsafeAccess (self._zeros, i).value);
69- } else {
70- // Compute the `currentLevelHash` using the left sibling.
71- // Because we fill the tree from left to right,
72- // the left child is the previous node at depth `i`.
73- currentLevelHash = SHA256.hash (self._nodes[i][currentIndex - 1 ], currentLevelHash);
74- }
75-
76- currentIndex >>= 1 ;
68+ // Compute the current level hash using the right sibling, which is the zero hash of this level.
69+ currentLevelHash = SHA256.hash (currentLevelHash, Arrays.unsafeAccess (self._zeros, i).value);
70+ } else {
71+ // Compute the current level hash using the left sibling (side).
72+ currentLevelHash = SHA256.hash (Arrays.unsafeAccess (self._sides, i).value, currentLevelHash);
7773 }
74+
75+ currentIndex >>= 1 ;
7876 }
7977
8078 // Expand the tree if the capacity is reached.
8179 if (self._nextLeafIndex == capacity (self)) {
82- // Store the current hash in the current level at index 0.
83- self._nodes[treeDepth][0 ] = currentLevelHash;
84-
85- // Compute the new current level hash of the expanded tree.
86- bytes32 currentZero = Arrays.unsafeAccess (self._zeros, treeDepth).value;
80+ // Store the current level hash as the sibling (side) for the current level.
81+ self._sides.push (currentLevelHash);
8782
8883 // Compute the new current level hash.
89- currentLevelHash = SHA256.hash (currentLevelHash, currentZero);
90-
91- // Compute the next zero for the next level.
92- bytes32 nextZero = SHA256.hash (currentZero, currentZero);
93- self._zeros.push (nextZero);
84+ currentLevelHash = SHA256.hash (currentLevelHash, Arrays.unsafeAccess (self._zeros, treeDepth).value);
9485 }
9586
9687 newRoot = currentLevelHash;
9788 }
9889
99- /// @notice Computes a Merkle proof consisting of the sibling at each depth and the associated direction bit
100- /// indicating whether the sibling is left (0) or right (1) at the respective depth.
101- /// @param self The tree data structure.
102- /// @param index The index of the leaf.
103- /// @return siblings The siblings of the leaf to proof inclusion for.
104- /// @return directionBits The direction bits indicating whether the siblings are left of right.
105- function merkleProof (Tree storage self , uint256 index )
106- internal
107- view
108- returns (bytes32 [] memory siblings , uint256 directionBits )
109- {
110- uint256 treeDepth = depth (self);
111-
112- // Check whether the index exists or not.
113- if (index + 1 > self._nextLeafIndex) revert NonExistentLeafIndex (index);
114-
115- siblings = new bytes32 [](treeDepth);
116- uint256 currentIndex = index;
117- bytes32 currentSibling;
118-
119- // Iterate over the different tree levels starting at the bottom at the leaf level.
120- for (uint256 i = 0 ; i < treeDepth; ++ i) {
121- // Check if the current node the left or right child of its parent.
122- if (isLeftChild (currentIndex)) {
123- // Sibling is right.
124- currentSibling = self._nodes[i][currentIndex + 1 ];
125-
126- // Set the direction bit at position `i` to 1.
127- directionBits |= (1 << i);
128- } else {
129- // Sibling is left.
130- currentSibling = self._nodes[i][currentIndex - 1 ];
131-
132- // Leave the direction bit at position `i` as 0.
133- }
134-
135- // Check if the sibling is an empty subtree.
136- if (currentSibling == bytes32 (0 )) {
137- // The subtree node doesn't exist, so we store the zero hash instead.
138- siblings[i] = Arrays.unsafeAccess (self._zeros, i).value;
139- } else {
140- // The subtree node exists, so we store it.
141- siblings[i] = currentSibling;
142- }
143-
144- // Shift the number one bit to the right to drop the last binary digit.
145- currentIndex >>= 1 ;
146- }
147- }
148-
14990 /// @notice Returns the tree depth.
15091 /// @param self The tree data structure.
15192 /// @return treeDepth The depth of the tree.
15293 function depth (Tree storage self ) internal view returns (uint256 treeDepth ) {
153- treeDepth = self._zeros .length - 1 ;
94+ treeDepth = self._sides .length ;
15495 }
15596
15697 /// @notice Returns the number of leaves that have been added to the tree.
0 commit comments