Skip to content

Instantly share code, notes, and snippets.

@recmo
Created January 29, 2019 23:24
Show Gist options
  • Save recmo/0dbbaa26c051bea517cd3a8f1de3560a to your computer and use it in GitHub Desktop.
Save recmo/0dbbaa26c051bea517cd3a8f1de3560a to your computer and use it in GitHub Desktop.

Revisions

  1. recmo created this gist Jan 29, 2019.
    78 changes: 78 additions & 0 deletions MerkleVerifier.sol
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,78 @@
    contract MerkleVerifier {

    function hash_leaf(uint256 value)
    internal pure
    returns (bytes32 hash)
    {
    return bytes32(value);
    }

    function hash_node(bytes32 left, bytes32 right)
    internal
    returns (bytes32 hash)
    {
    assembly {
    mstore(0x00, left)
    mstore(0x20, right)
    hash := keccak256(0x00, 0x40)
    }
    return hash;
    }

    // Indices are required to be sorted highest to lowest.
    function verify(
    bytes32 root,
    uint256 depth,
    uint256[] memory indices,
    uint256[] memory values,
    bytes32[] memory decommitments
    )
    internal
    {
    require(indices.length == values.length, "LENGTH_MISMATCH");
    uint256 n = indices.length;

    // Dynamically allocate index and hash queue
    uint256[] memory tree_indices = new uint256[](n + 1);
    bytes32[] memory hashes = new bytes32[](n + 1);
    uint256 head = 0;
    uint256 tail = 0;
    uint256 di = 0;

    // Queue the leafs
    for(; tail < n; ++tail) {
    tree_indices[tail] = 2**depth + indices[tail];
    hashes[tail] = hash_leaf(values[tail]);
    }

    // Itterate the queue until we hit the root
    while (true) {
    uint256 index = tree_indices[head];
    bytes32 hash = hashes[head];
    head = (head + 1) % (n + 1);

    // Merkle root
    if (index == 1) {
    //require(hash == root, "INVALID_MERKLE_PROOF");
    return;

    // Even node, take sibbling from decommitments
    } else if (index & 1 == 0) {
    hash = hash_node(hash, decommitments[di++]);

    // Odd node with sibbling in the queue
    } else if (head != tail && tree_indices[head] == index - 1) {
    hash = hash_node(hashes[head], hash);
    head = (head + 1) % n;

    // Odd node with sibbling from decommitments
    } else {
    hash = hash_node(decommitments[di++], hash);

    }
    tree_indices[tail] = index / 2;
    hashes[tail] = hash;
    tail = (tail + 1) % (n + 1);
    }
    }
    }
    106 changes: 106 additions & 0 deletions merkle_tree.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,106 @@
    import hashlib
    import math
    from sha3 import keccak_256

    def hash_leaf(leaf_value):
    '''Convert a leaf value to a digest'''
    assert(leaf_value < 2**256)
    return leaf_value.to_bytes(32, 'big')

    def hash_node(left_hash, right_hash):
    '''Convert two digests to their Merkle node's digest'''
    return keccak_256(left_hash + right_hash).digest()

    def make_tree(leafs):
    '''Compute the Merkle tree of a list of values.
    The result is returned as a list where each value represents one hash in the
    tree. The indices in the array are as in a bbinary heap array.
    '''
    num_leafs = len(leafs)
    depth = int(math.log2(num_leafs))
    assert(num_leafs == 2**depth)
    num_nodes = 2 * num_leafs
    tree = [None] * num_nodes
    for i in range(num_leafs):
    tree[2**depth + i] = hash_leaf(leafs[i])
    for i in range(2**depth - 1, 0, -1):
    tree[i] = hash_node(tree[2*i], tree[2*i + 1])
    return tree

    def root(tree):
    return tree[1]

    def proof(tree, indices):
    '''Given a Merkle tree and a set of indices, provide a list of decommitments
    required to reconstruct the merkle root.'''
    depth = int(math.log2(len(tree))) - 1
    num_leafs = 2**depth
    num_nodes = 2*num_leafs
    known = [False] * num_nodes
    decommitment = []
    for i in indices:
    known[2**depth + i] = True
    for i in range(2**depth - 1, 0, -1):
    left = known[2*i]
    right = known[2*i + 1]
    if left and not right:
    decommitment += [tree[2*i + 1]]
    if not left and right:
    decommitment += [tree[2*i]]
    known[i] = left or right
    return decommitment

    def verify(root, depth, values, decommitment, debug_print=False):
    '''Verify a set of leafs in the Merkle tree.
    Parameters
    ------------------------
    root
    Merkle root that is commited to.
    depth
    Depth of the Merkle tree. Equal to log2(number of leafs)
    values
    Mapping leaf index => value of the values we want to decommit.
    decommitments
    List of intermediate values required for deconstruction.
    '''

    # Create a list of pairs [(tree_index, leaf_hash)] with tree_index decreasing
    queue = []
    for index in sorted(values.keys(), reverse=True):
    tree_index = 2**depth + index
    hash = hash_leaf(values[index])
    queue += [(tree_index, hash)]

    while True:
    assert(len(queue) >= 1)

    # Take the top from the queue
    (index, hash) = queue[0]
    queue = queue[1:]
    if debug_print:
    print(index, hash.hex())

    # The merkle root has tree index 1
    if index == 1:
    return hash == root

    # Even nodes get merged with a decommitment hash on the right
    elif index % 2 == 0:
    queue += [(index // 2, hash_node(hash, decommitment[0]))]
    decommitment = decommitment[1:]

    # Odd nodes can get merged with their neighbour
    elif len(queue) > 0 and queue[0][0] == index - 1:
    # Take the sibbling node from the stack
    (_, sibbling_hash) = queue[0]
    queue = queue[1:]

    # Merge the two nodes
    queue += [(index // 2, hash_node(sibbling_hash, hash))]

    # Remaining odd nodes are merged with a decommitment on the left
    else:
    # Merge with a decommitment hash on the left
    queue += [(index // 2, hash_node(decommitment[0], hash))]
    decommitment = decommitment[1:]