From 3c4d0d0a77ad7eaefe5cfcbead93ee2a5bcfadbb Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Wed, 14 Jun 2017 16:34:46 -0400 Subject: [PATCH] MerkleProof library and initial stubbed out tests --- contracts/MerkleProof.sol | 38 ++++++++++++++++++++++ test/MerkleProof.js | 54 ++++++++++++++++++++++++++++++++ test/helpers/MerkleProofMock.sol | 12 +++++++ 3 files changed, 104 insertions(+) create mode 100644 contracts/MerkleProof.sol create mode 100644 test/MerkleProof.js create mode 100644 test/helpers/MerkleProofMock.sol diff --git a/contracts/MerkleProof.sol b/contracts/MerkleProof.sol new file mode 100644 index 000000000..89f9256c1 --- /dev/null +++ b/contracts/MerkleProof.sol @@ -0,0 +1,38 @@ +pragma solidity ^0.4.11; + +/* + * @title MerkleProof + * @dev Merkle proof verification + * @note Based on https://github.com/ameensol/merkle-tree-solidity/blob/master/src/MerkleProof.sol + */ +library MerkleProof { + /* + * @dev Verifies a Merkle proof proving the existence of a leaf in a Merkle tree. Assumes that each pair of leaves + * and each pair of pre-images is sorted. + * @param _proof Merkle proof containing sibling hashes on the branch from the leaf to the root of the Merkle tree + * @param _root Merkle root + * @param _leaf Leaf of Merkle tree + */ + function verifyProof(bytes _proof, bytes32 _root, bytes32 _leaf) constant returns (bool) { + bytes32 proofElement; + bytes32 computedHash = _leaf; + + for (uint256 i = 32; i <= _proof.length; i += 32) { + assembly { + // Load the current element of the proof + proofElement := mload(add(_proof, i)) + } + + if (computedHash < proofElement) { + // Hash(current computed hash + current element of the proof) + computedHash = sha3(computedHash, proofElement); + } else { + // Hash(current element of the proof + current computed hash) + computedHash = sha3(proofElement, computedHash); + } + } + + // Check if the computed hash (root) is equal to the provided root + return computedHash == _root; + } +} diff --git a/test/MerkleProof.js b/test/MerkleProof.js new file mode 100644 index 000000000..8ff58ec34 --- /dev/null +++ b/test/MerkleProof.js @@ -0,0 +1,54 @@ +var MerkleProofMock = artifacts.require("./helpers/MerkleProofMock.sol"); + +contract('MerkleProof', function(accounts) { + let merkleProof; + + before(async function() { + merkleProof = await MerkleProofMock.new(); + }); + + describe("verifyProof", function() { + it("should return true for a valid Merkle proof given even number of leaves", async function() { + // const elements = ["a", "b", "c", "d"].map(el => sha3(el)); + // const merkleTree = new MerkleTree(elements); + + // const root = merkleTree.getHexRoot(); + + // const proof = merkleTree.getHexProof(elements[0]); + + // const leaf = merkleTree.bufToHex(elements[0]); + + // const validProof = await merkleProof.verifyProof(proof, root, leaf); + // assert.isOk(validProof, "verifyProof did not return true for a valid proof given even number of leaves"); + }); + + it("should return true for a valid Merkle proof given odd number of leaves", async function () { + // const elements = ["a", "b", "c"].map(el => sha3(el)); + // const merkleTree = new MerkleTree(elements); + + // const root = merkleTree.getHexRoot(); + + // const proof = merkleTree.getHexProof(elements[0]); + + // const leaf = merkleTree.bufToHex(elements[0]); + + // const validProof = await merkleProof.verifyProof(proof, root, leaf); + // assert.isOk(validProof, "verifyProof did not return true for a valid proof given odd number of leaves"); + }); + + it("should return false for an invalid Merkle proof", async function() { + // const elements = ["a", "b", "c"].map(el => sha3(el)); + // const merkleTree = new MerkleTree(elements); + + // const root = merkleTree.getHexRoot(); + + // const proof = merkleTree.getHexProof(elements[0]); + // const badProof = proof.slice(0, proof.length - 32); + + // const leaf = merkleTree.bufToHex(elements[0]); + + // const validProof = await merkleProof.verifyProof(badProof, root, leaf); + // assert.isNotOk(validProof, "verifyProof did not return false for an invalid proof"); + }); + }); +}); diff --git a/test/helpers/MerkleProofMock.sol b/test/helpers/MerkleProofMock.sol new file mode 100644 index 000000000..84ccc1be3 --- /dev/null +++ b/test/helpers/MerkleProofMock.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.4.11; + +import '../../contracts/MerkleProof.sol'; + +contract MerkleProofMock { + + bool public result; + + function verifyProof(bytes _proof, bytes32 _root, bytes32 _leaf) { + result = MerkleProof.verifyProof(_proof, _root, _leaf); + } +}