Current section

Files

Jump to
merkle_fun lib merkle_fun.ex
Raw

lib/merkle_fun.ex

defmodule MerkleFun do
require Integer
def new(input) do
build_tree(input)
end
def root(tree), do: bytes_to_string(elem(tree, 0))
def print(tree) do
tree
|> Tuple.to_list()
|> Enum.reject(fn x -> x == 1 end)
|> Enum.map(&bytes_to_string/1)
end
def proof(tree, leaf) do
leaf_hash =
leaf
|> Base.decode16!(case: :mixed)
|> hash()
idx =
tree
|> Tuple.to_list()
|> Enum.find_index(fn l -> l === leaf_hash end)
_proof(tree, idx)
|> Enum.map(&bytes_to_string/1)
|> Enum.map(&add_0x/1)
end
defp _proof(_, 0), do: []
defp _proof(tree, idx) do
sibling_idx = get_sibling_idx(idx)
node = elem(tree, sibling_idx)
parent_idx = Integer.floor_div(idx - 1, 2)
[node | _proof(tree, parent_idx)]
end
defp build_tree(data) do
leaves =
data
|> Enum.map(fn x -> Base.decode16!(x, case: :mixed) |> hash() end)
|> Enum.sort()
leaves = leaves ++ add_padding_rows(leaves)
_build_tree(leaves, [])
|> List.to_tuple()
end
defp _build_tree([root], acc), do: [root | acc]
defp _build_tree(level, acc) do
new_level =
level
|> Enum.chunk_every(2)
|> Enum.map(fn
[x, 1] -> x
[x, y] -> hash(x <> y)
end)
_build_tree(new_level, level ++ acc)
end
defp hash(data), do: data |> ExKeccak.hash_256()
defp get_sibling_idx(idx) do
if Integer.is_even(idx) do
idx - 1
else
idx + 1
end
end
defp bytes_to_string(bytes), do: Base.encode16(bytes, case: :lower)
defp add_0x(s), do: "0x#{s}"
defp add_padding_rows(leaves) do
size = length(leaves)
num = :math.log2(size) |> ceil
num = 2 ** num
num = num - size
# pad with 1, uses less space
List.duplicate(1, num)
end
end