{"id":25867188,"url":"https://github.com/inferara/merkle-tree-wasm-spec","last_synced_at":"2026-05-09T21:51:54.407Z","repository":{"id":263570091,"uuid":"890812671","full_name":"Inferara/merkle-tree-wasm-spec","owner":"Inferara","description":"MerkleTree WASM formal specification","archived":false,"fork":false,"pushed_at":"2025-01-28T09:38:58.000Z","size":76,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-28T10:30:05.763Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"WebAssembly","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Inferara.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-19T08:19:21.000Z","updated_at":"2025-01-28T09:39:02.000Z","dependencies_parsed_at":"2024-11-19T09:34:12.492Z","dependency_job_id":"db7a4067-39ac-4f87-a6dc-944fec661ca2","html_url":"https://github.com/Inferara/merkle-tree-wasm-spec","commit_stats":null,"previous_names":["inferara/merkle-tree-wasm-spec"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inferara%2Fmerkle-tree-wasm-spec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inferara%2Fmerkle-tree-wasm-spec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inferara%2Fmerkle-tree-wasm-spec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inferara%2Fmerkle-tree-wasm-spec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Inferara","download_url":"https://codeload.github.com/Inferara/merkle-tree-wasm-spec/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241455016,"owners_count":19965516,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-02T03:34:20.098Z","updated_at":"2026-05-09T21:51:54.367Z","avatar_url":"https://github.com/Inferara.png","language":"WebAssembly","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MerkleTree WASM formal specification\n\nMany blockchains such as Ethereum or Polkadot utilize Merkle Trees (or variations like Merkle Patricia Tries) for efficient and secure data verification. This repository contains a WASM implementation of dependency-free Merkle tree algorithms and a formal specification on Inference WASM extension (see [merkle-tree](./merkle-tree.wat) and [specification](./specification.wat)).\n\n---\n\n### **Pseudocode Implementation**\n\n```pseudo\nclass MerkleNode:\n    left: MerkleNode\n    right: MerkleNode\n    hash: bytes\n\nfunction create_merkle_tree(data_blocks):\n    nodes = []\n    for data in data_blocks:\n        nodes.append(MerkleNode(hash=hash_function(data)))\n\n    while len(nodes) \u003e 1:\n        temp_nodes = []\n        for i in range(0, len(nodes), 2):\n            left_node = nodes[i]\n            right_node = nodes[i + 1] if i + 1 \u003c len(nodes) else nodes[i]\n            combined_hash = hash_function(left_node.hash + right_node.hash)\n            parent_node = MerkleNode(left=left_node, right=right_node, hash=combined_hash)\n            temp_nodes.append(parent_node)\n        nodes = temp_nodes\n\n    return nodes[0]  # Root of the Merkle Tree\n```\n\n#### **Explanation**\n\n- **Leaf Nodes**: Created by hashing individual data blocks.\n- **Parent Nodes**: Combine hashes of child nodes to create a new hash.\n- **Root Node**: The final node represents the root hash, summarizing all underlying data.\n\n### **Formal Properties of the Merkle Tree Algorithm**\n\nLet us define the following:\n\n- Let $H: \\\\{0,1\\\\}^* \\rightarrow \\\\{0,1\\\\}^{HashSize}$ be a cryptographic hash function.\n- Let $S = [h_1, h_2, \\ldots, h_n]$ be a sequence of hashes (leaves of the Merkle tree).\n- Let $merkleTree(S)$ be a function that constructs a Merkle tree from the sequence $S$ and returns the root hash.\n- Let $merkleChain(i,S)$ be the authentication path (chain of proofs) for the $i$-th leaf $h_i$​ in $S$.\n- Let $merkleRecon(h,i,c)$ be a function that reconstructs the root hash from a leaf hash $h$, its position $i$ and a chain of proofs $c$.\n\n---\n\n**Property 1: Collision Implication from Identical Root Hashes with Different Leaf Sets**\n\n_Statement:_\n\nThere exists an $O(n)$ - complex algorithm that takes two hash sequences $S=[h_1,h_2,…,h_n]$ and $S'=[h_1',h_2', \\ldots,h_n']$, producing collision of the hash function $H$, if $S\\neq S'$ and Merkle trees constructed from these sequences yield the same root hash. \n\n_Mathematically:_\n\nThere exists $O(n)$ - complex function $F : \\\\{0,1\\\\}^{HashSize \\times n} \\times \\\\{0,1\\\\}^{HashSize \\times n} \\rightarrow \\\\{0,1\\\\}^{HashSize \\times 2} \\times \\\\{0,1\\\\}^{HashSize \\times 2}$ such that\n\nIf $S \\neq S'$, $merkleTree(S)=merkleTree(S')$ and $F(S, S') = (x, y)$, then $H(x)=H(y)$.\n\n---\n\n**Property 2: Correctness of Proof Verification for a Valid Leaf**\n\n_Statement:_\n\nFor any $i \\in \\\\{1, 2, \\ldots, n\\\\}$, the root hash reconstructed from the $i$-th leaf hash $h_i$​ and its corresponding chain of proofs $merkleChain(i,S)$ equals the root hash of the Merkle tree built from $S$.\n\n_Mathematically:_\n\nFor all $i \\in \\\\{1, 2, \\ldots, n\\\\}$ and all $S = [h_1, h_2, \\ldots, h_n]$\n\n$\\text{merkleRecon}(h_i, i, \\text{merkleChain}(i, S)) = \\text{merkleTree}(S)$.\n\n---\n\n**Property 3: Collision Implication from Two Leaf Proofs Leading to same Root Hash from same position**\n\n_Statement:_\n\nThere exists an $O(\\log n)$ - complex algorithm that takes index $i \\in \\\\{1, 2, \\ldots, n\\\\}$, two hashes $h_i \\ne h_i'$ and two evidence chains $c, c' \\in \\\\{0,1\\\\}^{HashSize \\times \\lceil\\log_2 n\\rceil}$, producing collision of the hash function $H$, if $\\text{merkleRecon}(h_i, i, c) = \\text{merkleRecon}(h_i', i, c')$.\n\n_Mathematically:_\n\nThere exists $O(\\log n)$ - complex function $G : \\\\{1, 2, \\ldots, n\\\\} \\times \\\\{0,1\\\\}^{HashSize} \\times \\\\{0,1\\\\}^{HashSize} \\times \\\\{0,1\\\\}^{HashSize \\times \\lceil\\log_2 n\\rceil} \\times \\\\{0,1\\\\}^{HashSize \\times \\lceil\\log_2 n\\rceil} \\rightarrow \\\\{0,1\\\\}^{HashSize \\times 2} \\times \\\\{0,1\\\\}^{HashSize \\times 2}$ such that\n\nIf $\\text{merkleRecon}(h, i, c) = \\text{merkleRecon}(h', i, c')$ and $G(i, h, h', c, c') = (x, y)$ then $H(x) = H(y)$.\n\n---\n\n**Explanation:**\n\n- **Property 1** ensures that the uniqueness of the root hash depends on the uniqueness of the leaf hashes and the collision resistance of $H$. If different leaf sequences yield the same root, $H$ must have collided somewhere in the tree.\n- **Property 2** guarantees that any legitimate leaf can be authenticated against the root hash using its proof chain, maintaining the integrity of the tree structure.\n- **Property 3** asserts that if two different leaves can be used to reconstruct the same root hash from the same position with any proof chains, it implies a collision in $H$, violating the tree's integrity.\n\n## Rust implementation\n\n```rust\nfn concat_and_hash(l: Hash, r: Hash) -\u003e Hash {\n    // hashing implementation\n}\n\nfn repeat_and_hash(l: Hash) -\u003e Hash {\n    concat_and_hash(l, l)\n}\n\nfn merkle_tree(leaves: \u0026[Hash]) -\u003e Vec\u003cVec\u003cHash\u003e\u003e {\n    let mut tree = vec![leaves.to_vec()];\n\n    loop {\n        let layer = tree.last().unwrap();\n        let ll = layer.len();\n        if ll \u003c 2 {\n            break;\n        }\n        let mut nl = Vec::new();\n        for i in (1..ll).step_by(2) {\n            nl.push(concat_and_hash(layer[i - 1], layer[i]));\n        }\n        if ll % 2 != 0 {\n            nl.push(repeat_and_hash(layer[ll - 1]));\n        }\n        tree.push(nl);\n    }\n\n    tree\n}\n\nfn merkle_root(tree: \u0026[Vec\u003cHash\u003e]) -\u003e Hash {\n    tree.last().unwrap()[0]\n}\n\nfn merkle_chain(tree: \u0026[Vec\u003cHash\u003e], idx: usize) -\u003e Vec\u003cHash\u003e {\n    let mut chain = Vec::new();\n    let mut i = idx;\n\n    for layer in \u0026tree[..tree.len() - 1] {\n        let i1 = i ^ 1;\n        let i2 = if i1 \u003c layer.len() { i1 } else { i };\n        chain.push(layer[i2]);\n        i \u003e\u003e= 1;\n    }\n\n    chain\n}\n\nfn merkle_recon(idx: usize, leaf: Hash, chain: \u0026[Hash]) -\u003e Hash {\n    let mut acc = leaf;\n    let mut i = idx;\n\n    for link in chain {\n        acc = if i \u0026 1 != 0 {\n            concat_and_hash(*link, acc)\n        } else {\n            concat_and_hash(acc, *link)\n        };\n        i \u003e\u003e= 1;\n    }\n\n    acc\n}\n```\n\nThe central problem in formulating the properties of cryptographic algorithms is that they are often impossible to express as required by their applications. For example, this:\n\n$$\n\\forall D \\neq D', \\text{MerkleTree}(D) \\neq \\text{MerkleTree}(D')\n$$\n\nis clearly false. Collisions inevitably follow from the very principle of hashing, so the best we can formally assert is that finding them is _difficult_. In the case of Merkle trees, the natural benchmark of complexity is the strength of the hash function used, meaning the statement becomes:\n\n\u003e Given a collision of Merkle trees, a collision of the hash function used in their construction can be computed in polynomial time.\n\nPutting aside the complexity estimation of the solution, for now, let's try to at least formulate its existence.\n\n```rust\n// Auxiliary function that returns two arrays of hashes\n// of an undefined but equal length with undefined but\n// different contents.\nfn data_prep() -\u003e (Vec\u003cHash\u003e, Vec\u003cHash\u003e) {\n    let size = usize::MAX; // Undefined size\n    let data1 = vec![Hash::default(); size];\n    let data2 = vec![Hash::default(); size];\n    let diff = 0; // Index where data differs\n    assert!(data1[diff] != data2[diff]);\n    (data1, data2)\n}\n\n// INCORRECT!!! To compute hash collisions from collisions of trees,\n// we cannot use non-determinism, as complexity can only be adequately\n// assessed for classical algorithms. This needs to be rewritten.\nfn nondet_collision(tree1: \u0026[Vec\u003cHash\u003e], tree2: \u0026[Vec\u003cHash\u003e]) -\u003e ((Hash, Hash), (Hash, Hash)) {\n    let i = 0; // Undefined index\n    let j = 0; // Undefined index\n    assert!(tree1[i][j] == tree2[i][j]);\n    let i1 = i - 1;\n    let j1 = j \u003c\u003c 1;\n    let h11 = tree1[i1][j1];\n    let h21 = tree2[i1][j1];\n    let j2 = j1 + 1;\n    let h12 = tree1[i1][j2];\n    let h22 = tree2[i1][j2];\n    assert!(h11 != h21 || h12 != h22);\n    ((h11, h12), (h21, h22))\n}\n\n// This is much more correct. Searching for hash collisions in a Merkle\n// tree without non-determinism, inspecting each branch of the tree\n// no more than once. The thoroughness of such a search convinces,\n// provided the reader, looking at this code, can independently conclude\n// about its complexity.\nfn find_collision(tree1: \u0026[Vec\u003cHash\u003e], tree2: \u0026[Vec\u003cHash\u003e]) -\u003e ((Hash, Hash), (Hash, Hash)) {\n    let height = tree1.len();\n    assert!(height == tree2.len());\n\n    for i in 1..height {\n        let width = tree1[i].len();\n        assert!(width == tree2[i].len());\n\n        for j in 0..width {\n            if tree1[i][j] != tree2[i][j] {\n                continue;\n            }\n            let i1 = i - 1;\n            let j1 = j \u003c\u003c 1;\n            let h11 = tree1[i1][j1];\n            let h21 = tree2[i1][j1];\n            let j2 = j1 + 1;\n            let h12 = tree1[i1][j2];\n            let h22 = tree2[i1][j2];\n\n            if h11 != h21 || h12 != h22 {\n                return ((h11, h12), (h21, h22));\n            }\n        }\n    }\n\n    panic!(\"No collision found\");\n}\n\nfn collision_resistance() {\n    let (data1, data2) = data_prep();\n    let tree1 = merkle_tree(\u0026data1);\n    let tree2 = merkle_tree(\u0026data2);\n    assert!(merkle_root(\u0026tree1) == merkle_root(\u0026tree2));\n\n    // Here, for persuasiveness, it would be good to wrap the call\n    // to `find_collision` in a construct that at least verifies\n    // this is classical computation without undefined values.\n    // Even better if this construct limits the computation's complexity\n    // to a polynomial relative to the input size.\n    let ((h11, h12), (h21, h22)) = find_collision(\u0026tree1, \u0026tree2);\n\n    assert!(h11 != h21 || h12 != h22);\n    let h1 = concat_and_hash(h11, h12);\n    let h2 = concat_and_hash(h21, h22);\n    assert!(h1 == h2);\n}\n\nspec {\n    total merkle_integrity();\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finferara%2Fmerkle-tree-wasm-spec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finferara%2Fmerkle-tree-wasm-spec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finferara%2Fmerkle-tree-wasm-spec/lists"}