packages/contracts-core/test/utils/proof/ProofGenerator.t.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import {ORIGIN_TREE_HEIGHT} from "../../../contracts/libs/Constants.sol";
import {MerkleTree} from "../../../contracts/libs/merkle/MerkleTree.sol";
import {ProofCutter} from "./ProofCutter.t.sol";
// TODO: move from test directory
contract ProofGenerator is ProofCutter {
/**
* @notice Store only non-"zero" values of the merkle tree
* merkleTree[0] are the leafs
* merkleTree[1] are keccak256(A, B) where A and B are leafs
* ...
* merkleTree[ORIGIN_TREE_HEIGHT][0] is the merkle root
*/
bytes32[][] internal merkleTree;
constructor() {
merkleTree = new bytes32[][](ORIGIN_TREE_HEIGHT + 1);
}
/**
* @notice Creates a Merkle Tree from an array of leafs.
*/
function createTree(bytes32[] memory leafs) external {
_clearTree();
// Copy the leafs into the tree
uint256 size = _copyLeafs(leafs);
// Go upwards the tree and construct parent layers one by one
for (uint256 d = 1; d <= ORIGIN_TREE_HEIGHT; ++d) {
size = (size + 1) / 2;
_createLayer(d, size);
}
}
/**
* @notice Returns a merkle proof for leaf with a given index.
*/
function getProof(uint256 index) external view returns (bytes32[] memory) {
bytes32[] memory proof = new bytes32[](ORIGIN_TREE_HEIGHT);
for (uint256 d = 0; d < ORIGIN_TREE_HEIGHT; ++d) {
// Get node's neighbor
if (index % 2 == 0) {
++index;
} else {
--index;
}
proof[d] = getNode(d, index);
// We need to go deeper
index = index / 2;
}
return cutProof(proof);
}
/**
* @notice Returns merkle root of the tree.
*/
function getRoot() external view returns (bytes32) {
return merkleTree[ORIGIN_TREE_HEIGHT][0];
}
/**
* @notice Returns node of the Merkle Tree given its depth and index.
*/
function getNode(uint256 depth, uint256 index) public view returns (bytes32 node) {
if (index < merkleTree[depth].length) {
node = merkleTree[depth][index];
} else {
node = bytes32(0);
}
}
/**
* @notice Clears the merkle tree.
*/
function _clearTree() internal {
if (merkleTree[0].length != 0) {
for (uint256 d = 0; d <= ORIGIN_TREE_HEIGHT; ++d) {
delete merkleTree[d];
}
}
}
/**
* @notice Copies the leafs into the leaf layer of the tree.
*/
function _copyLeafs(bytes32[] memory leafs) internal returns (uint256 size) {
size = leafs.length;
merkleTree[0] = new bytes32[](size);
for (uint256 i = 0; i < size; ++i) {
merkleTree[0][i] = leafs[i];
}
}
/**
* @notice Creates a layer of the tree using its child layer.
*/
function _createLayer(uint256 depth, uint256 size) internal {
merkleTree[depth] = new bytes32[](size);
for (uint256 i = 0; i < size; ++i) {
merkleTree[depth][i] = _hash(getNode(depth - 1, 2 * i), getNode(depth - 1, 2 * i + 1));
}
}
function _hash(bytes32 left, bytes32 right) internal pure returns (bytes32) {
if (left != 0 || right != 0) {
return keccak256(abi.encodePacked(left, right));
} else {
return 0;
}
}
}