Skip to Content
Scalus Club is now open! Join us to get an early access to new features πŸŽ‰

Merkle Tree

A binary tree where each leaf is a hash of data and each internal node is a hash of its children. Proofs are logβ‚‚(N) hashes.

Best for: Static sets known at deploy time β€” airdrop allowlists, governance voter registries, configuration snapshots.

How It Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ root β”‚ β”‚ H(A|B) β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”‚ A β”‚ β”‚ B β”‚ β”‚ H(C|D) β”‚ β”‚ H(E|F) β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”‚ C β”‚ β”‚ D β”‚β”‚ E β”‚ β”‚ F β”‚ β”‚ H(d₁|dβ‚‚)β”‚ β”‚ H(d₃|dβ‚„)β”‚β”‚ H(dβ‚…|d₆)β”‚ β”‚ H(d₇|dβ‚ˆ)β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”΄β”€β”€β”€β” β”Œβ”€β”€β”€β”΄β”€β”€β”€β” β”Œβ”€β”€β”€β”΄β”€β”€β”€β” β”Œβ”€β”€β”€β”΄β”€β”€β”€β” d₁ dβ‚‚ d₃ dβ‚„ dβ‚… d₆ d₇ dβ‚ˆ Proof that d₃ is in the tree: [dβ‚„, C, B] Verify: D = H(d₃|dβ‚„), A = H(C|D), root = H(A|B) βœ“

To verify membership, the on-chain validator receives the element and its proof (the sibling hashes along the path to the root). It recomputes the hashes from leaf to root and checks that the result matches the known root hash.

Cost

  • Verification: logβ‚‚(N) + 1 blake2b calls
  • Proof size: 33 Γ— ceil(logβ‚‚(N)) bytes

Limitations

  • No mutations β€” the tree is built once and cannot be modified on-chain
  • Membership only β€” cannot prove that an element is not in the set

Background

Ralph Merkle described the idea in 1979 in β€œA Certified Digital Signature”. The motivation was signing many messages at once: instead of signing each message individually, hash them into a root and sign only the root.

Example: Membership Token Validator

The MembershipTokenValidatorΒ  uses a Merkle Tree to gate token minting β€” only members in a pre-built allowlist can mint. The Merkle root is baked into the script at deployment time via ParameterizedValidator[ByteString].

On-chain β€” verify the signer is in the allowlist:

import scalus.cardano.onchain.plutus.crypto.tree.MerkleTree // merkleRoot is the script parameter (baked in at deploy time) // signer.hash is the pubkeyhash of the transaction signer // proof is the sibling hashes along the path to the root MerkleTree.verifyMembership(merkleRoot, signer.hash, proof)

Off-chain β€” build the tree and generate a proof:

import scalus.crypto.tree.MerkleTree // Build the tree from a list of member pubkeyhashes val members: Seq[ByteString] = Seq(alice.hash, bob.hash, charlie.hash) val tree = MerkleTree.fromHashes(members) val root = tree.rootHash // bake this into the script parameter // Generate a membership proof for alice val proof = tree.proveMembership(alice.hash)

The AnonymousDataValidatorΒ  demonstrates another pattern: Merkle Tree for participant authorization combined with AssocMap for encrypted key-value storage.

Last updated on