Skip to content

Commit 92ff025

Browse files
Amxxernestognw
andauthored
Add a MerkleTree builder (#3617)
Co-authored-by: Ernesto García <[email protected]>
1 parent e831429 commit 92ff025

13 files changed

+444
-46
lines changed

.changeset/odd-files-protect.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`Hashes`: A library with commonly used hash functions.

.changeset/warm-sheep-cover.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`MerkleTree`: A data structure that allows inserting elements into a merkle tree and updating its root hash.

contracts/mocks/ArraysMock.sol

+24
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ contract Uint256ArraysMock {
4848
function _reverse(uint256 a, uint256 b) private pure returns (bool) {
4949
return a > b;
5050
}
51+
52+
function unsafeSetLength(uint256 newLength) external {
53+
_array.unsafeSetLength(newLength);
54+
}
55+
56+
function length() external view returns (uint256) {
57+
return _array.length;
58+
}
5159
}
5260

5361
contract AddressArraysMock {
@@ -74,6 +82,14 @@ contract AddressArraysMock {
7482
function _reverse(address a, address b) private pure returns (bool) {
7583
return uint160(a) > uint160(b);
7684
}
85+
86+
function unsafeSetLength(uint256 newLength) external {
87+
_array.unsafeSetLength(newLength);
88+
}
89+
90+
function length() external view returns (uint256) {
91+
return _array.length;
92+
}
7793
}
7894

7995
contract Bytes32ArraysMock {
@@ -100,4 +116,12 @@ contract Bytes32ArraysMock {
100116
function _reverse(bytes32 a, bytes32 b) private pure returns (bool) {
101117
return uint256(a) > uint256(b);
102118
}
119+
120+
function unsafeSetLength(uint256 newLength) external {
121+
_array.unsafeSetLength(newLength);
122+
}
123+
124+
function length() external view returns (uint256) {
125+
return _array.length;
126+
}
103127
}

contracts/mocks/MerkleTreeMock.sol

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
import {MerkleTree} from "../utils/structs/MerkleTree.sol";
6+
7+
contract MerkleTreeMock {
8+
using MerkleTree for MerkleTree.Bytes32PushTree;
9+
10+
MerkleTree.Bytes32PushTree private _tree;
11+
12+
event LeafInserted(bytes32 leaf, uint256 index, bytes32 root);
13+
14+
function setup(uint8 _depth, bytes32 _zero) public {
15+
_tree.setup(_depth, _zero);
16+
}
17+
18+
function push(bytes32 leaf) public {
19+
(uint256 leafIndex, bytes32 currentRoot) = _tree.push(leaf);
20+
emit LeafInserted(leaf, leafIndex, currentRoot);
21+
}
22+
23+
function root() public view returns (bytes32) {
24+
return _tree.root();
25+
}
26+
27+
function depth() public view returns (uint256) {
28+
return _tree.depth();
29+
}
30+
31+
// internal state
32+
function nextLeafIndex() public view returns (uint256) {
33+
return _tree._nextLeafIndex;
34+
}
35+
36+
function sides(uint256 i) public view returns (bytes32) {
37+
return _tree._sides[i];
38+
}
39+
40+
function zeros(uint256 i) public view returns (bytes32) {
41+
return _tree._zeros[i];
42+
}
43+
}

contracts/utils/Arrays.sol

+33
Original file line numberDiff line numberDiff line change
@@ -440,4 +440,37 @@ library Arrays {
440440
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
441441
}
442442
}
443+
444+
/**
445+
* @dev Helper to set the length of an dynamic array. Directly writing to `.length` is forbidden.
446+
*
447+
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
448+
*/
449+
function unsafeSetLength(address[] storage array, uint256 len) internal {
450+
assembly {
451+
sstore(array.slot, len)
452+
}
453+
}
454+
455+
/**
456+
* @dev Helper to set the length of an dynamic array. Directly writing to `.length` is forbidden.
457+
*
458+
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
459+
*/
460+
function unsafeSetLength(bytes32[] storage array, uint256 len) internal {
461+
assembly {
462+
sstore(array.slot, len)
463+
}
464+
}
465+
466+
/**
467+
* @dev Helper to set the length of an dynamic array. Directly writing to `.length` is forbidden.
468+
*
469+
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
470+
*/
471+
function unsafeSetLength(uint256[] storage array, uint256 len) internal {
472+
assembly {
473+
sstore(array.slot, len)
474+
}
475+
}
443476
}

contracts/utils/README.adoc

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Miscellaneous contracts and libraries containing utility functions you can use t
99
* {SafeCast}: Checked downcasting functions to avoid silent truncation.
1010
* {ECDSA}, {MessageHashUtils}: Libraries for interacting with ECDSA signatures.
1111
* {SignatureChecker}: A library helper to support regular ECDSA from EOAs as well as ERC-1271 signatures for smart contracts.
12+
* {Hashes}: Commonly used hash functions.
1213
* {MerkleProof}: Functions for verifying https://en.wikipedia.org/wiki/Merkle_tree[Merkle Tree] proofs.
1314
* {EIP712}: Contract with functions to allow processing signed typed structure data according to https://eips.ethereum.org/EIPS/eip-712[EIP-712].
1415
* {ReentrancyGuard}: A modifier that can prevent reentrancy during certain functions.
@@ -20,6 +21,7 @@ Miscellaneous contracts and libraries containing utility functions you can use t
2021
* {EnumerableSet}: Like {EnumerableMap}, but for https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets]. Can be used to store privileged accounts, issued IDs, etc.
2122
* {DoubleEndedQueue}: An implementation of a https://en.wikipedia.org/wiki/Double-ended_queue[double ended queue] whose values can be removed added or remove from both sides. Useful for FIFO and LIFO structures.
2223
* {Checkpoints}: A data structure to store values mapped to an strictly increasing key. Can be used for storing and accessing values over time.
24+
* {MerkleTree}: A library with https://wikipedia.org/wiki/Merkle_Tree[Merkle Tree] data structures and helper functions.
2325
* {Create2}: Wrapper around the https://blog.openzeppelin.com/getting-the-most-out-of-create2/[`CREATE2` EVM opcode] for safe use without having to deal with low-level assembly.
2426
* {Address}: Collection of functions for overloading Solidity's https://docs.soliditylang.org/en/latest/types.html#address[`address`] type.
2527
* {Arrays}: Collection of functions that operate on https://docs.soliditylang.org/en/latest/types.html#arrays[`arrays`].
@@ -48,13 +50,15 @@ Because Solidity does not support generic types, {EnumerableMap} and {Enumerable
4850

4951
{{ECDSA}}
5052

53+
{{EIP712}}
54+
5155
{{MessageHashUtils}}
5256

5357
{{SignatureChecker}}
5458

55-
{{MerkleProof}}
59+
{{Hashes}}
5660

57-
{{EIP712}}
61+
{{MerkleProof}}
5862

5963
== Security
6064

@@ -88,6 +92,8 @@ Ethereum contracts have no native concept of an interface, so applications must
8892

8993
{{Checkpoints}}
9094

95+
{{MerkleTree}}
96+
9197
== Libraries
9298

9399
{{Create2}}
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
/**
6+
* @dev Library of standard hash functions.
7+
*/
8+
library Hashes {
9+
/**
10+
* @dev Commutative Keccak256 hash of a sorted pair of bytes32. Frequently used when working with merkle proofs.
11+
*
12+
* NOTE: Equivalent to the `standardNodeHash` in our https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
13+
*/
14+
function commutativeKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32) {
15+
return a < b ? _efficientKeccak256(a, b) : _efficientKeccak256(b, a);
16+
}
17+
18+
/**
19+
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
20+
*/
21+
function _efficientKeccak256(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
22+
/// @solidity memory-safe-assembly
23+
assembly {
24+
mstore(0x00, a)
25+
mstore(0x20, b)
26+
value := keccak256(0x00, 0x40)
27+
}
28+
}
29+
}

contracts/utils/cryptography/MerkleProof.sol

+6-23
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
pragma solidity ^0.8.20;
55

6+
import {Hashes} from "./Hashes.sol";
7+
68
/**
79
* @dev These functions deal with verification of Merkle Tree proofs.
810
*
@@ -49,7 +51,7 @@ library MerkleProof {
4951
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
5052
bytes32 computedHash = leaf;
5153
for (uint256 i = 0; i < proof.length; i++) {
52-
computedHash = _hashPair(computedHash, proof[i]);
54+
computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
5355
}
5456
return computedHash;
5557
}
@@ -60,7 +62,7 @@ library MerkleProof {
6062
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
6163
bytes32 computedHash = leaf;
6264
for (uint256 i = 0; i < proof.length; i++) {
63-
computedHash = _hashPair(computedHash, proof[i]);
65+
computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
6466
}
6567
return computedHash;
6668
}
@@ -138,7 +140,7 @@ library MerkleProof {
138140
bytes32 b = proofFlags[i]
139141
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
140142
: proof[proofPos++];
141-
hashes[i] = _hashPair(a, b);
143+
hashes[i] = Hashes.commutativeKeccak256(a, b);
142144
}
143145

144146
if (totalHashes > 0) {
@@ -194,7 +196,7 @@ library MerkleProof {
194196
bytes32 b = proofFlags[i]
195197
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
196198
: proof[proofPos++];
197-
hashes[i] = _hashPair(a, b);
199+
hashes[i] = Hashes.commutativeKeccak256(a, b);
198200
}
199201

200202
if (totalHashes > 0) {
@@ -210,23 +212,4 @@ library MerkleProof {
210212
return proof[0];
211213
}
212214
}
213-
214-
/**
215-
* @dev Sorts the pair (a, b) and hashes the result.
216-
*/
217-
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
218-
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
219-
}
220-
221-
/**
222-
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
223-
*/
224-
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
225-
/// @solidity memory-safe-assembly
226-
assembly {
227-
mstore(0x00, a)
228-
mstore(0x20, b)
229-
value := keccak256(0x00, 0x40)
230-
}
231-
}
232215
}

0 commit comments

Comments
 (0)