Skip to content

Commit 6843761

Browse files
Lucas MTlucasmt
authored andcommitted
Add highly-branching MerkleProofTest
1 parent a99bd7b commit 6843761

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

src/tests/integration/test-data/foundry-prove-all

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ LoopsTest.test_sum_100()
139139
LoopsTest.test_sum_1000()
140140
LoopsTest.testSumToN(uint256)
141141
LoopsTest.testSumToNBroken(uint256)
142+
MerkleProofTest.testValidateMerkleProof(bytes32,uint256,bytes32,bytes32,bytes32,bytes32)
142143
MethodDisambiguateTest.test_method_call()
143144
MockCallTest.testMockCall()
144145
MockCallTest.testMockCalls()

src/tests/integration/test-data/foundry-prove-skip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ LoopsTest.test_sum_100()
8484
LoopsTest.test_sum_1000()
8585
LoopsTest.testSumToN(uint256)
8686
LoopsTest.testSumToNBroken(uint256)
87+
MerkleProofTest.testValidateMerkleProof(bytes32,uint256,bytes32,bytes32,bytes32,bytes32)
8788
MockCallTest.testMockCall()
8889
MockCallTest.testMockCalls()
8990
MockCallTest.testMockCallValue()
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity =0.8.13;
3+
4+
import "forge-std/Test.sol";
5+
6+
contract MerkleProofTest is Test {
7+
8+
/**
9+
* The purpose of this test is to evaluate how well we handle branching.
10+
* When we assume that _validateMerkleProof holds, the execution splits
11+
* into 2 ** proof.length (in this case, 8) branches. We want to be able
12+
* to handle this amount of branching without losing information, so we
13+
* can still prove that it holds in the final assertTrue.
14+
*
15+
* Increase the length of the proof to evaluate scalability.
16+
*/
17+
function testValidateMerkleProof(
18+
bytes32 leaf,
19+
uint256 index,
20+
bytes32 root,
21+
bytes32 proofElement0,
22+
bytes32 proofElement1,
23+
bytes32 proofElement2
24+
) external {
25+
uint256 proofLength = 3;
26+
27+
bytes32[] memory proof = new bytes32[](proofLength);
28+
proof[0] = proofElement0;
29+
proof[1] = proofElement1;
30+
proof[2] = proofElement2;
31+
32+
vm.assume(index < 2 ** proof.length);
33+
34+
vm.assume(_validateMerkleProof(leaf, index, root, proof));
35+
36+
assertTrue(_validateMerkleProof(leaf, index, root, proof));
37+
}
38+
39+
/**
40+
* Checks that the proof is valid for a Merkle tree with the given root
41+
* where the given leaf is at the given index.
42+
*/
43+
function _validateMerkleProof(
44+
bytes32 leaf,
45+
uint256 index,
46+
bytes32 root,
47+
bytes32[] memory proof
48+
) internal pure returns (bool) {
49+
// Number of leaves is exponential on the tree depth
50+
require(index < 2 ** proof.length);
51+
52+
bytes32 hash = leaf;
53+
54+
for (uint256 i; i < proof.length; i++) {
55+
if (index % 2 == 0) {
56+
// If index is even, proof element is to the right
57+
hash = keccak256(abi.encodePacked(hash, proof[i]));
58+
} else {
59+
// If index is odd, proof element is to the left
60+
hash = keccak256(abi.encodePacked(proof[i], hash));
61+
}
62+
63+
// Go up one level in the tree
64+
index = index / 2;
65+
}
66+
67+
return hash == root;
68+
}
69+
}

0 commit comments

Comments
 (0)