Skip to content

Feat/merkle library #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 11, 2022
71 changes: 71 additions & 0 deletions contracts/src/bridge/merkle/MerkleProof.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: MIT

/**
* @authors: [@shotaronowhere]
* @reviewers: []
* @auditors: []
* @bounties: []
* @deployments: []
*/

pragma solidity ^0.8.0;

/**
* @title MerkleProof
* @author Shotaro N. - <shawtarohgn@gmail.com>
* @dev A set of funcitons to verify merkle proofs.
*/
contract MerkleProof {
/** @dev Validates membership of leaf in merkle tree with merkle proof.
* @param proof The merkle proof.
* @param leaf The leaf to validate membership in merkle tree.
* @param merkleRoot The root of the merkle tree.
*/
function validateProof(
bytes32[] memory proof,
bytes32 leaf,
bytes32 merkleRoot
) internal pure returns (bool) {
return (merkleRoot == calculateRoot(proof, leaf));
}

/** @dev Validates membership of leaf in merkle tree with merkle proof.
* @param proof The merkle proof.
* @param data The data to validate membership in merkle tree.
* @param merkleRoot The root of the merkle tree.
*/
function validateProof(
bytes32[] memory proof,
bytes memory data,
bytes32 merkleRoot
) public pure returns (bool) {
return validateProof(proof, keccak256(data), merkleRoot);
}

/** @dev Calculates merkle root from proof.
* @param proof The merkle proof.
* @param leaf The leaf to validate membership in merkle tree..
*/
function calculateRoot(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
uint256 proofLength = proof.length;
require(proofLength <= 64, "Invalid Proof");
bytes32 h = leaf;
for (uint256 i = 0; i < proofLength; i++) {
bytes32 proofElement = proof[i];
// effecient hash
if (proofElement > h)
assembly {
mstore(0x00, h)
mstore(0x20, proofElement)
h := keccak256(0x00, 0x40)
}
else
assembly {
mstore(0x00, proofElement)
mstore(0x20, h)
h := keccak256(0x00, 0x40)
}
}
return h;
}
}
128 changes: 128 additions & 0 deletions contracts/src/bridge/merkle/MerkleTreeHistory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: MIT

/**
* @authors: [@shotaronowhere]
* @reviewers: []
* @auditors: []
* @bounties: []
* @deployments: []
*/

pragma solidity ^0.8.0;

/**
* @title MerkleTreeHistory
* @author Shotaro N. - <shawtarohgn@gmail.com>
* @dev An efficient append only merkle tree with history.
*/
contract MerkleTreeHistory {
// ***************************** //
// * Storage * //
// ***************************** //

// merkle tree representation
// supports 2^64 messages.
bytes32[64] public branch;
uint256 public count;

// block number => merkle root history
mapping(uint256 => bytes32) private history;

// ************************************* //
// * State Modifiers * //
// ************************************* //

/** @dev Append data into merkle tree.
* `O(log(n))` where
* `n` is the number of leaves.
* Note: Although each insertion is O(log(n)),
* Complexity of n insertions is O(n).
* @param data The data to insert in the merkle tree.
*/
function append(bytes memory data) public {
bytes32 leaf = keccak256(data);
count += 1;
uint256 size = count;
uint256 hashBitField = (size ^ (size - 1)) & size;

for (uint256 height = 0; height < 64; height++) {
if ((hashBitField & 1) == 1) {
branch[height] = leaf;
return;
}
bytes32 node = branch[height];
// effecient hash
if (node > leaf)
assembly {
mstore(0x00, leaf)
mstore(0x20, node)
leaf := keccak256(0x00, 0x40)
}
else
assembly {
mstore(0x00, node)
mstore(0x20, leaf)
leaf := keccak256(0x00, 0x40)
}
hashBitField /= 2;
}
}

/** @dev Saves the merkle root state in history and resets.
* `O(log(n))` where
* `n` is the number of leaves.
*/
function reset() internal {
history[block.number] = getMerkleRoot();
count = 0;
}

/** @dev Gets the merkle root history
* `O(log(n))` where
* `n` is the number of leaves.
* @param blocknumber requested blocknumber.
*/
function getMerkleRootHistory(uint256 blocknumber) public view returns (bytes32) {
if (blocknumber == block.number) return getMerkleRoot();

return history[blocknumber];
}

/** @dev Gets the current merkle root.
* `O(log(n))` where
* `n` is the number of leaves.
*/
function getMerkleRoot() public view returns (bytes32) {
bytes32 node;
uint256 size = count;
uint256 height = 0;
bool isFirstHash = true;
while (size > 0) {
// avoid redundant calculation
if ((size & 1) == 1) {
if (isFirstHash) {
node = branch[height];
isFirstHash = false;
} else {
bytes32 hash = branch[height];
// effecient hash
if (hash > node)
assembly {
mstore(0x00, node)
mstore(0x20, hash)
node := keccak256(0x00, 0x40)
}
else
assembly {
mstore(0x00, hash)
mstore(0x20, node)
node := keccak256(0x00, 0x40)
}
}
}
size /= 2;
height++;
}
return node;
}
}
Loading