Skip to content

Commit 7b34e8f

Browse files
Added proof verification functions to CMT and IMT (#158)
* Added proof verification functions to CMT and IMT * fixed review * Updated changelog
1 parent 7398feb commit 7b34e8f

File tree

7 files changed

+432
-12
lines changed

7 files changed

+432
-12
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
## [none]
44

5-
- Fixed `pre-release` GitHub CI workflow
5+
- Fixed `pre-release` GitHub CI workflow.
6+
- Added `verifyProof` function to the `CartesianMerkleTree` and the `IncrementalMerkleTree` libraries.
67

78
## [3.2.0]
89

contracts/libs/data-structures/CartesianMerkleTree.sol

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pragma solidity ^0.8.21;
55
* @notice Cartesian Merkle Tree Module
66
*
77
* A magnificent ZK-friendly data structure based on a Binary Search Tree + Heap + Merkle Tree. Short names: CMT, Treaple.
8-
* Possesses deterministic and idemponent properties. Can be used as a substitute for a Sparse Merkle Tree (SMT).
8+
* Possesses deterministic and idempotent properties. Can be used as a substitute for a Sparse Merkle Tree (SMT).
99
*
1010
* Gas usage for adding and removing 1,000 elements to a CMT with the keccak256 and poseidon hash functions is detailed below:
1111
*
@@ -32,6 +32,8 @@ pragma solidity ^0.8.21;
3232
*
3333
* CartesianMerkleTree.Proof memory proof = uintTreaple.getProof(100, 0);
3434
*
35+
* uintTreaple.verifyProof(proof);
36+
*
3537
* uintTreaple.getNodeByKey(100);
3638
*
3739
* uintTreaple.remove(100);
@@ -142,6 +144,21 @@ library CartesianMerkleTree {
142144
return _proof(treaple._treaple, bytes32(key_), desiredProofSize_);
143145
}
144146

147+
/**
148+
* @notice The function to verify the proof for inclusion or exclusion of a node in the CMT.
149+
* Complexity is O(log(n)), where n is the max depth of the treaple.
150+
*
151+
* @param treaple self.
152+
* @param proof_ The CMT proof struct.
153+
* @return True if the proof is valid, false otherwise.
154+
*/
155+
function verifyProof(
156+
UintCMT storage treaple,
157+
Proof memory proof_
158+
) internal view returns (bool) {
159+
return _verifyProof(treaple._treaple, proof_);
160+
}
161+
145162
/**
146163
* @notice The function to get the root of the Cartesian Merkle Tree.
147164
* Complexity is O(1).
@@ -306,6 +323,21 @@ library CartesianMerkleTree {
306323
return _proof(treaple._treaple, key_, desiredProofSize_);
307324
}
308325

326+
/**
327+
* @notice The function to verify the proof for inclusion or exclusion of a node in the CMT.
328+
* Complexity is O(log(n)), where n is the max depth of the treaple.
329+
*
330+
* @param treaple self.
331+
* @param proof_ The CMT proof struct.
332+
* @return True if the proof is valid, false otherwise.
333+
*/
334+
function verifyProof(
335+
Bytes32CMT storage treaple,
336+
Proof memory proof_
337+
) internal view returns (bool) {
338+
return _verifyProof(treaple._treaple, proof_);
339+
}
340+
309341
/**
310342
* @notice The function to get the root of the Cartesian Merkle Tree.
311343
* Complexity is O(1).
@@ -470,6 +502,21 @@ library CartesianMerkleTree {
470502
return _proof(treaple._treaple, _fromAddressToBytes32(key_), desiredProofSize_);
471503
}
472504

505+
/**
506+
* @notice The function to verify the proof for inclusion or exclusion of a node in the CMT.
507+
* Complexity is O(log(n)), where n is the max depth of the treaple.
508+
*
509+
* @param treaple self.
510+
* @param proof_ The CMT proof struct.
511+
* @return True if the proof is valid, false otherwise.
512+
*/
513+
function verifyProof(
514+
AddressCMT storage treaple,
515+
Proof memory proof_
516+
) internal view returns (bool) {
517+
return _verifyProof(treaple._treaple, proof_);
518+
}
519+
473520
/**
474521
* @notice The function to get the root of the Cartesian Merkle Tree.
475522
* Complexity is O(1).
@@ -871,6 +918,49 @@ library CartesianMerkleTree {
871918
return proof_;
872919
}
873920

921+
function _verifyProof(CMT storage treaple, Proof memory proof_) private view returns (bool) {
922+
// invalid exclusion proof
923+
if (!proof_.existence && proof_.nonExistenceKey == ZERO_HASH) {
924+
return false;
925+
}
926+
927+
bool directionBit_ = _extractDirectionBit(proof_.directionBits, 0);
928+
929+
bytes32 leftHash_ = proof_.siblings[proof_.siblingsLength - 2];
930+
bytes32 rightHash_ = proof_.siblings[proof_.siblingsLength - 1];
931+
932+
if (directionBit_) {
933+
(leftHash_, rightHash_) = (rightHash_, leftHash_);
934+
}
935+
936+
bytes32 computedHash_ = _getNodesHash(
937+
treaple,
938+
proof_.existence ? proof_.key : proof_.nonExistenceKey,
939+
leftHash_,
940+
rightHash_
941+
);
942+
943+
for (uint256 i = 2; i < proof_.siblingsLength; i += 2) {
944+
directionBit_ = _extractDirectionBit(proof_.directionBits, i);
945+
946+
leftHash_ = computedHash_;
947+
rightHash_ = proof_.siblings[proof_.siblingsLength - i - 1];
948+
949+
if (directionBit_) {
950+
(leftHash_, rightHash_) = (rightHash_, leftHash_);
951+
}
952+
953+
computedHash_ = _getNodesHash(
954+
treaple,
955+
proof_.siblings[proof_.siblingsLength - i - 2],
956+
leftHash_,
957+
rightHash_
958+
);
959+
}
960+
961+
return computedHash_ == proof_.root;
962+
}
963+
874964
function _newNode(CMT storage treaple, bytes32 key_) private returns (uint256) {
875965
uint64 nodeId_ = ++treaple.nodesCount;
876966

@@ -914,6 +1004,17 @@ library CartesianMerkleTree {
9141004
return directionBits_;
9151005
}
9161006

1007+
/**
1008+
* @dev Extracts the direction bit from the direction mask.
1009+
* @return True if the node is on the right side of the parent, false if it's on the left.
1010+
*/
1011+
function _extractDirectionBit(
1012+
uint256 directionBits_,
1013+
uint256 currentSiblingsIndex_
1014+
) private pure returns (bool) {
1015+
return (directionBits_ & (1 << (currentSiblingsIndex_ / 2))) != 0;
1016+
}
1017+
9171018
function _hashNodes(CMT storage treaple, uint256 nodeId_) private view returns (bytes32) {
9181019
Node storage node = treaple.nodes[uint64(nodeId_)];
9191020

contracts/libs/data-structures/IncrementalMerkleTree.sol

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,25 @@ library IncrementalMerkleTree {
110110
return _root(tree._tree);
111111
}
112112

113+
/**
114+
* @notice The function to verify a proof of a leaf's existence in the uint256 tree.
115+
* Complexity is O(log(n)), where n is the number of elements in the tree.
116+
*
117+
* @param tree self.
118+
* @param siblings_ The siblings of the leaf.
119+
* @param directionBits_ The direction bits of the leaf.
120+
* @param leaf_ The leaf.
121+
* @return True if the proof is valid, false otherwise.
122+
*/
123+
function verifyProof(
124+
UintIMT storage tree,
125+
bytes32[] memory siblings_,
126+
uint256 directionBits_,
127+
bytes32 leaf_
128+
) internal view returns (bool) {
129+
return _verifyProof(tree._tree, siblings_, directionBits_, leaf_);
130+
}
131+
113132
/**
114133
* @notice The function to return the height of the uint256 tree. Complexity is O(1).
115134
* @param tree self.
@@ -190,6 +209,25 @@ library IncrementalMerkleTree {
190209
return _root(tree._tree);
191210
}
192211

212+
/**
213+
* @notice The function to verify a proof of a leaf's existence in the bytes32 tree.
214+
* Complexity is O(log(n)), where n is the number of elements in the tree.
215+
*
216+
* @param tree self.
217+
* @param siblings_ The siblings of the leaf.
218+
* @param directionBits_ The direction bits of the leaf.
219+
* @param leaf_ The leaf.
220+
* @return True if the proof is valid, false otherwise.
221+
*/
222+
function verifyProof(
223+
Bytes32IMT storage tree,
224+
bytes32[] memory siblings_,
225+
uint256 directionBits_,
226+
bytes32 leaf_
227+
) internal view returns (bool) {
228+
return _verifyProof(tree._tree, siblings_, directionBits_, leaf_);
229+
}
230+
193231
/**
194232
* @notice The function to return the height of the bytes32 tree. Complexity is O(1).
195233
*/
@@ -266,6 +304,25 @@ library IncrementalMerkleTree {
266304
return _root(tree._tree);
267305
}
268306

307+
/**
308+
* @notice The function to verify a proof of a leaf's existence in the address tree.
309+
* Complexity is O(log(n)), where n is the number of elements in the tree.
310+
*
311+
* @param tree self.
312+
* @param siblings_ The siblings of the leaf.
313+
* @param directionBits_ The direction bits of the leaf.
314+
* @param leaf_ The leaf.
315+
* @return True if the proof is valid, false otherwise.
316+
*/
317+
function verifyProof(
318+
AddressIMT storage tree,
319+
bytes32[] memory siblings_,
320+
uint256 directionBits_,
321+
bytes32 leaf_
322+
) internal view returns (bool) {
323+
return _verifyProof(tree._tree, siblings_, directionBits_, leaf_);
324+
}
325+
269326
/**
270327
* @notice The function to return the height of the address tree. Complexity is O(1).
271328
*/
@@ -401,6 +458,29 @@ library IncrementalMerkleTree {
401458
return root_;
402459
}
403460

461+
function _verifyProof(
462+
IMT storage tree,
463+
bytes32[] memory siblings_,
464+
uint256 directionBits,
465+
bytes32 leaf_
466+
) private view returns (bool) {
467+
function(bytes32, bytes32) view returns (bytes32) hash2_ = tree.isCustomHasherSet
468+
? tree.hash2
469+
: _hash2;
470+
471+
bytes32 computedHash_ = leaf_;
472+
473+
for (uint256 i = 0; i < siblings_.length; ++i) {
474+
if ((directionBits >> i) & 1 != 0) {
475+
computedHash_ = hash2_(siblings_[i], computedHash_);
476+
} else {
477+
computedHash_ = hash2_(computedHash_, siblings_[i]);
478+
}
479+
}
480+
481+
return computedHash_ == _root(tree);
482+
}
483+
404484
function _height(IMT storage tree) private view returns (uint256) {
405485
return tree.branches.length;
406486
}

contracts/mock/libs/data-structures/CartesianMerkleTreeMock.sol

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,24 @@ contract CartesianMerkleTreeMock {
9696
return _addressCMT.getProof(key_, desiredProofSize_);
9797
}
9898

99+
function verifyUintProof(
100+
CartesianMerkleTree.Proof memory proof_
101+
) external view returns (bool) {
102+
return _uintCMT.verifyProof(proof_);
103+
}
104+
105+
function verifyBytes32Proof(
106+
CartesianMerkleTree.Proof memory proof_
107+
) external view returns (bool) {
108+
return _bytes32CMT.verifyProof(proof_);
109+
}
110+
111+
function verifyAddressProof(
112+
CartesianMerkleTree.Proof memory proof_
113+
) external view returns (bool) {
114+
return _addressCMT.verifyProof(proof_);
115+
}
116+
99117
function getUintRoot() external view returns (bytes32) {
100118
return _uintCMT.getRoot();
101119
}

contracts/mock/libs/data-structures/IncrementalMerkleTreeMock.sol

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,30 @@ contract IncrementalMerkleTreeMock {
6767
return _addressTree.root();
6868
}
6969

70+
function verifyUintProof(
71+
bytes32[] memory siblings_,
72+
uint256 directionBits_,
73+
bytes32 element_
74+
) external view returns (bool) {
75+
return _uintTree.verifyProof(siblings_, directionBits_, element_);
76+
}
77+
78+
function verifyBytes32Proof(
79+
bytes32[] memory siblings_,
80+
uint256 directionBits_,
81+
bytes32 element_
82+
) external view returns (bool) {
83+
return _bytes32Tree.verifyProof(siblings_, directionBits_, element_);
84+
}
85+
86+
function verifyAddressProof(
87+
bytes32[] memory siblings_,
88+
uint256 directionBits_,
89+
bytes32 element_
90+
) external view returns (bool) {
91+
return _addressTree.verifyProof(siblings_, directionBits_, element_);
92+
}
93+
7094
function getUintTreeHeight() external view returns (uint256) {
7195
return _uintTree.height();
7296
}

0 commit comments

Comments
 (0)