Skip to content

Instantly share code, notes, and snippets.

@chronakazi
Forked from maxcountryman/merkle.tree.clj
Created January 13, 2022 20:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chronakazi/834f84c61170017df755ef9291a60cd7 to your computer and use it in GitHub Desktop.
Save chronakazi/834f84c61170017df755ef9291a60cd7 to your computer and use it in GitHub Desktop.
A Clojure Merkle tree utilizing SHA-256.
(ns merkle.tree
(:import [java.security MessageDigest]))
(defn sha-256-digest [bs]
(.digest
(doto (MessageDigest/getInstance "SHA-256")
(.update bs))))
(def double-sha-256 (comp sha-256-digest sha-256-digest))
(defn node
"Given a label and optionally a left and right node, returns a list
representing a node in a binary tree."
([label]
(node label nil nil))
([label left right]
(cons label (list left right))))
(defn tree-levels
"Given a previous level, constructs the next level until it reaches a root."
[previous]
(let [level
(->> previous
(partition 2)
(map (fn [[left right]]
(let [combined-hash
(->> (map first [left right])
(mapcat seq)
byte-array
double-sha-256)]
(node combined-hash left right)))))]
(if (= (count level) 1)
level
(lazy-cat (tree-levels level) level))))
(defn tree
"Given a collection, constructs a Merkle tree using doubled SHA-256. The tree
is represented as a list of nodes. See `node`."
([coll]
(tree coll (fn [c] (.getBytes (str c)))))
([coll get-bytes]
;; Construct the leaf nodes and pass them into the `tree-levels` function.
(->>
(if (even? (count coll))
coll
(conj coll (last coll)))
(map get-bytes)
(map double-sha-256)
(map node)
tree-levels)))
(comment
(tree (into [] "foo")))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment