Skip to content

Commit

Permalink
Add highly-branching MerkleProofTest
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucas MT committed Nov 8, 2023
1 parent 336a05a commit ea8b76f
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/tests/integration/test-data/foundry-prove-all
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ LoopsTest.test_sum_100()
LoopsTest.test_sum_1000()
LoopsTest.testSumToN(uint256)
LoopsTest.testSumToNBroken(uint256)
MerkleProofTest.testValidateMerkleProof(bytes32,uint256,bytes32,bytes32,bytes32,bytes32)
MethodDisambiguateTest.test_method_call()
MockCallTest.testMockCall()
MockCallTest.testMockCalls()
Expand Down
1 change: 1 addition & 0 deletions src/tests/integration/test-data/foundry-prove-skip
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ LoopsTest.test_sum_100()
LoopsTest.test_sum_1000()
LoopsTest.testSumToN(uint256)
LoopsTest.testSumToNBroken(uint256)
MerkleProofTest.testValidateMerkleProof(bytes32,uint256,bytes32,bytes32,bytes32,bytes32)
MockCallTest.testMockCall()
MockCallTest.testMockCalls()
MockCallTest.testMockCallValue()
Expand Down
69 changes: 69 additions & 0 deletions src/tests/integration/test-data/foundry/test/MerkleProofTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.13;

import "forge-std/Test.sol";

contract MerkleProofTest is Test {

/**
* The purpose of this test is to evaluate how well we handle branching.
* When we assume that _validateMerkleProof holds, the execution splits
* into 2 ** proof.length (in this case, 8) branches. We want to be able
* to handle this amount of branching without losing information, so we
* can still prove that it holds in the final assertTrue.
*
* Increase the length of the proof to evaluate scalability.
*/
function testValidateMerkleProof(
bytes32 leaf,
uint256 index,
bytes32 root,
bytes32 proofElement0,
bytes32 proofElement1,
bytes32 proofElement2
) external {
uint256 proofLength = 3;

bytes32[] memory proof = new bytes32[](proofLength);
proof[0] = proofElement0;
proof[1] = proofElement1;
proof[2] = proofElement2;

vm.assume(index < 2 ** proof.length);

vm.assume(_validateMerkleProof(leaf, index, root, proof));

assertTrue(_validateMerkleProof(leaf, index, root, proof));
}

/**
* Checks that the proof is valid for a Merkle tree with the given root
* where the given leaf is at the given index.
*/
function _validateMerkleProof(
bytes32 leaf,
uint256 index,
bytes32 root,
bytes32[] memory proof
) internal pure returns (bool) {
// Number of leaves is exponential on the tree depth
require(index < 2 ** proof.length);

bytes32 hash = leaf;

for (uint256 i; i < proof.length; i++) {
if (index % 2 == 0) {
// If index is even, proof element is to the right
hash = keccak256(abi.encodePacked(hash, proof[i]));
} else {
// If index is odd, proof element is to the left
hash = keccak256(abi.encodePacked(proof[i], hash));
}

// Go up one level in the tree
index = index / 2;
}

return hash == root;
}
}

0 comments on commit ea8b76f

Please sign in to comment.