2019-07-12 17:09:33 +00:00
|
|
|
from eth2spec.utils.hash_function import hash
|
2019-06-28 18:16:16 +00:00
|
|
|
from math import log2
|
2019-03-18 18:51:52 +00:00
|
|
|
|
|
|
|
|
2019-05-12 21:16:17 +00:00
|
|
|
ZERO_BYTES32 = b'\x00' * 32
|
|
|
|
|
|
|
|
zerohashes = [ZERO_BYTES32]
|
2019-06-20 18:25:22 +00:00
|
|
|
for layer in range(1, 100):
|
2019-03-19 03:39:19 +00:00
|
|
|
zerohashes.append(hash(zerohashes[layer - 1] + zerohashes[layer - 1]))
|
|
|
|
|
2019-03-18 18:51:52 +00:00
|
|
|
|
2019-06-28 02:24:16 +00:00
|
|
|
def calc_merkle_tree_from_leaves(values, layer_count=32):
|
2019-03-18 18:51:52 +00:00
|
|
|
values = list(values)
|
|
|
|
tree = [values[::]]
|
2019-06-28 02:24:16 +00:00
|
|
|
for h in range(layer_count):
|
2019-03-18 18:51:52 +00:00
|
|
|
if len(values) % 2 == 1:
|
|
|
|
values.append(zerohashes[h])
|
2019-03-19 03:39:19 +00:00
|
|
|
values = [hash(values[i] + values[i + 1]) for i in range(0, len(values), 2)]
|
2019-03-18 18:51:52 +00:00
|
|
|
tree.append(values[::])
|
|
|
|
return tree
|
|
|
|
|
2019-03-19 03:39:19 +00:00
|
|
|
|
2019-06-28 02:24:16 +00:00
|
|
|
def get_merkle_root(values, pad_to=1):
|
2019-07-12 17:09:33 +00:00
|
|
|
if pad_to == 0:
|
|
|
|
return zerohashes[0]
|
2019-06-28 02:24:16 +00:00
|
|
|
layer_count = int(log2(pad_to))
|
|
|
|
if len(values) == 0:
|
|
|
|
return zerohashes[layer_count]
|
|
|
|
return calc_merkle_tree_from_leaves(values, layer_count)[-1][0]
|
2019-03-18 18:51:52 +00:00
|
|
|
|
2019-03-19 03:39:19 +00:00
|
|
|
|
2019-03-18 18:51:52 +00:00
|
|
|
def get_merkle_proof(tree, item_index):
|
|
|
|
proof = []
|
|
|
|
for i in range(32):
|
2019-03-19 03:39:19 +00:00
|
|
|
subindex = (item_index // 2**i) ^ 1
|
2019-03-18 18:51:52 +00:00
|
|
|
proof.append(tree[i][subindex] if subindex < len(tree[i]) else zerohashes[i])
|
|
|
|
return proof
|
2019-05-12 21:16:17 +00:00
|
|
|
|
|
|
|
|
2019-07-12 18:39:55 +00:00
|
|
|
def merkleize_chunks(chunks, limit=None):
|
|
|
|
# If no limit is defined, we are just merkleizing chunks (e.g. SSZ container).
|
|
|
|
if limit is None:
|
|
|
|
limit = len(chunks)
|
2019-07-12 19:23:45 +00:00
|
|
|
|
|
|
|
count = len(chunks)
|
|
|
|
# See if the input is within expected size.
|
|
|
|
# If not, a list-limit is set incorrectly, or a value is unexpectedly large.
|
|
|
|
assert count <= limit
|
|
|
|
|
2019-07-12 18:39:55 +00:00
|
|
|
if limit == 0:
|
2019-07-12 17:09:33 +00:00
|
|
|
return zerohashes[0]
|
2019-07-12 19:23:45 +00:00
|
|
|
|
2019-06-20 17:54:59 +00:00
|
|
|
depth = max(count - 1, 0).bit_length()
|
2019-07-12 18:39:55 +00:00
|
|
|
max_depth = (limit - 1).bit_length()
|
2019-06-20 17:54:59 +00:00
|
|
|
tmp = [None for _ in range(max_depth + 1)]
|
|
|
|
|
|
|
|
def merge(h, i):
|
|
|
|
j = 0
|
|
|
|
while True:
|
|
|
|
if i & (1 << j) == 0:
|
|
|
|
if i == count and j < depth:
|
2019-06-16 23:39:39 +00:00
|
|
|
h = hash(h + zerohashes[j]) # keep going if we are complementing the void to the next power of 2
|
2019-06-20 17:54:59 +00:00
|
|
|
else:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
h = hash(tmp[j] + h)
|
|
|
|
j += 1
|
|
|
|
tmp[j] = h
|
|
|
|
|
2019-06-16 23:39:39 +00:00
|
|
|
# merge in leaf by leaf.
|
2019-06-20 17:54:59 +00:00
|
|
|
for i in range(count):
|
|
|
|
merge(chunks[i], i)
|
|
|
|
|
2019-06-16 23:39:39 +00:00
|
|
|
# complement with 0 if empty, or if not the right power of 2
|
|
|
|
if 1 << depth != count:
|
|
|
|
merge(zerohashes[0], count)
|
2019-06-20 17:54:59 +00:00
|
|
|
|
2019-06-16 23:39:39 +00:00
|
|
|
# the next power of two may be smaller than the ultimate virtual size, complement with zero-hashes at each depth.
|
2019-06-20 17:54:59 +00:00
|
|
|
for j in range(depth, max_depth):
|
|
|
|
tmp[j + 1] = hash(tmp[j] + zerohashes[j])
|
|
|
|
|
|
|
|
return tmp[max_depth]
|