|  | 
|  | 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