from pybgl.functions.tools import s2rh, bytes_from_hex, int_from_bytes, rh2s
from pybgl.functions.hash import sha3_256, double_sha256, sha256
from collections import deque
from math import ceil, log
def merkle_root(tx_hash_list, return_hex=True, receive_hex=True):
return merkle_root_double_sha256(tx_hash_list, return_hex=return_hex, receive_hex=receive_hex)
[docs]def merkle_root_double_sha256(tx_hash_list, return_hex=True, receive_hex=True):
"""
Calculate merkle root from transaction hash list with double sha256
:param tx_hash_list: list of transaction hashes in bytes or HEX encoded string.
:param return_hex: (optional) If set to True return result in HEX format, by default is True.
:param receive_hex: (optional) If set to False no internal check or decode from hex to bytes, by default is True.
:return: merkle root in bytes or HEX encoded string corresponding hex flag.
"""
if receive_hex:
tx_hash_list = deque([h if isinstance(h, bytes) else s2rh(h) for h in tx_hash_list])
else:
tx_hash_list = deque(tx_hash_list)
if len(tx_hash_list) == 1:
return rh2s(tx_hash_list[0]) if return_hex else tx_hash_list[0]
while True:
new_hash_list = deque()
append = new_hash_list.append
while tx_hash_list:
h1 = tx_hash_list.popleft()
try:
h2 = tx_hash_list.popleft()
except:
h2 = h1
append(double_sha256(b"".join((h1, h2))))
if len(new_hash_list) > 1:
tx_hash_list = new_hash_list
else:
return new_hash_list[0] if not return_hex else rh2s(new_hash_list[0])
[docs]def merkle_root_sha256(tx_hash_list, return_hex=True, receive_hex=True):
"""
Calculate merkle root from transaction hash list with sha256
:param tx_hash_list: list of transaction hashes in bytes or HEX encoded string.
:param return_hex: (optional) If set to True return result in HEX format, by default is True.
:param receive_hex: (optional) If set to False no internal check or decode from hex to bytes, by default is True.
:return: merkle root in bytes or HEX encoded string corresponding hex flag.
"""
if receive_hex:
tx_hash_list = deque([h if isinstance(h, bytes) else s2rh(h) for h in tx_hash_list])
else:
tx_hash_list = deque(tx_hash_list)
if len(tx_hash_list) == 1:
return rh2s(tx_hash_list[0]) if return_hex else tx_hash_list[0]
while True:
new_hash_list = deque()
append = new_hash_list.append
while tx_hash_list:
h1 = tx_hash_list.popleft()
try:
h2 = tx_hash_list.popleft()
except:
h2 = h1
append(sha256(b"".join((h1, h2))))
if len(new_hash_list) > 1:
tx_hash_list = new_hash_list
else:
return new_hash_list[0] if not return_hex else rh2s(new_hash_list[0])
[docs]def merkle_root_sha3_256(tx_hash_list, return_hex=True, receive_hex=True):
"""
Calculate merkle root from transaction hash list with sha3 256
:param tx_hash_list: list of transaction hashes in bytes or HEX encoded string.
:param return_hex: (optional) If set to True return result in HEX format, by default is True.
:param receive_hex: (optional) If set to False no internal check or decode from hex to bytes, by default is True.
:return: merkle root in bytes or HEX encoded string corresponding hex flag.
"""
if receive_hex:
tx_hash_list = deque([h if isinstance(h, bytes) else s2rh(h) for h in tx_hash_list])
else:
tx_hash_list = deque(tx_hash_list)
if len(tx_hash_list) == 1:
return rh2s(tx_hash_list[0]) if return_hex else tx_hash_list[0]
while True:
new_hash_list = deque()
append = new_hash_list.append
while tx_hash_list:
h1 = tx_hash_list.popleft()
try:
h2 = tx_hash_list.popleft()
except:
h2 = h1
append(sha3_256(b"".join((h1, h2))))
if len(new_hash_list) > 1:
tx_hash_list = new_hash_list
else:
return new_hash_list[0] if not return_hex else rh2s(new_hash_list[0])
def merkle_tree_depth(tx_hash_count):
if not isinstance(tx_hash_count, int):
raise TypeError('hash_count must be an integer')
if tx_hash_count < 1:
raise ValueError('hash_count must be at least 1')
return ceil(log(tx_hash_count, 2))
def merkle_tree(tx_hash_list, return_hex=False, receive_hex=False):
if receive_hex:
tx_hash_deque = deque()
tx_hash_deque_append = tx_hash_deque.append
for h in tx_hash_list:
tx_hash_deque_append(h if isinstance(h, bytes) else s2rh(h))
else:
tx_hash_deque = deque(tx_hash_list)
c = merkle_tree_depth(len(tx_hash_deque))
m = {c: deque(tx_hash_deque)}
while len(tx_hash_deque) > 1:
new_deque = deque()
new_deque_append = new_deque.append
while tx_hash_deque:
h1 = tx_hash_deque.popleft()
try: h2 = tx_hash_deque.popleft()
except: h2 = h1
hs = double_sha256(b"".join((h1, h2)))
new_deque_append(hs)
tx_hash_deque = new_deque
c -= 1
m[c] = deque(tx_hash_deque)
if return_hex:
for i in m:
for k in range(len(m[i])):
m[i][k] = rh2s(m[i][k])
return m
def merkle_proof(merkle_tree, index, return_hex=True, receive_hex=False):
if receive_hex == True:
_merkle_tree = dict()
for i in merkle_tree:
_merkle_tree[i] = dict()
for k in range(len(merkle_tree[i])):
h = merkle_tree[i][k]
_merkle_tree[i][k] = s2rh(h) if isinstance(h, str) else h
merkle_tree = _merkle_tree
mp = deque()
mp_append = mp.append
c = len(merkle_tree) - 1
while c:
if index % 2:
mp_append(merkle_tree[c][index - 1])
else:
if len(merkle_tree[c]) > index + 1:
mp_append(merkle_tree[c][index + 1])
else:
mp_append(merkle_tree[c][index])
c -= 1
index = index//2
if return_hex:
return [rh2s(h) for h in mp]
else:
return mp
def merkle_root_from_proof(merkle_proof, tx_id, index, return_hex=True, receive_hex=True):
if isinstance(merkle_proof, str):
merkle_proof = bytes_from_hex(merkle_proof)
if isinstance(merkle_proof, bytes):
merkle_proof = [merkle_proof[y - 32:y] for y in range(32, len(merkle_proof) + 32, 32)]
if receive_hex:
_merkle_proof = deque()
_merkle_proof_append = _merkle_proof.append
for h in merkle_proof:
_merkle_proof_append(s2rh(h) if isinstance(h, str) else h)
merkle_proof = _merkle_proof
tx_id = s2rh(tx_id) if isinstance(tx_id, str) else tx_id
root = tx_id
for h in merkle_proof:
root = double_sha256(b"".join((h, root) if index % 2 else (root, h)))
index = index // 2
if return_hex:
return rh2s(root)
return root
[docs]def merkle_branches(tx_hash_list, hex=True):
"""
Calculate merkle branches for coinbase transacton
:param tx_hash_list: list of transaction hashes in bytes or HEX encoded string.
:param hex: (optional) If set to True return result in HEX format, by default is True.
:return: list of merkle branches in bytes or HEX encoded string corresponding hex flag.
"""
tx_hash_list = [h if isinstance(h, bytes) else s2rh(h) for h in tx_hash_list]
branches = []
if len(tx_hash_list) == 1:
return []
tx_hash_list.pop(0)
while True:
branches.append(tx_hash_list.pop(0))
new_hash_list = list()
while tx_hash_list:
h1 = tx_hash_list.pop(0)
try:
h2 = tx_hash_list.pop(0)
except:
h2 = h1
new_hash_list.append(double_sha256(h1 + h2))
if len(new_hash_list) > 1:
tx_hash_list = new_hash_list
else:
if new_hash_list:
branches.append(new_hash_list.pop(0))
return branches if not hex else [h.hex() for h in branches]
def merkle_root_from_branches(merkle_branches, coinbase_hash, hex=True):
"""
Calculate merkle root from merkle branches and coinbase transacton hash
:param merkle_branches: list merkle branches in bytes or HEX encoded string.
:param coinbase_hash: list coinbase transaction hash in bytes or HEX encoded string.
:param hex: (optional) If set to True return result in HEX format, by default is True.
:return: merkle root in bytes or HEX encoded string corresponding hex flag.
"""
merkle_root = coinbase_hash if not isinstance(coinbase_hash, str) else bytes_from_hex(coinbase_hash)
for h in merkle_branches:
if type(h) == str:
h = bytes_from_hex(h)
merkle_root = double_sha256(merkle_root + h)
return bytes_from_hex(merkle_root) if not hex else merkle_root
[docs]def bits_to_target(bits):
"""
Calculate target from bits
:param bits: HEX string, bytes string or integer representation of bits.
:return: integer.
"""
if type(bits) == str:
bits = bytes_from_hex(bits)
if type(bits) == bytes:
return int_from_bytes(bits[1:], 'big') * (2 ** (8 * (bits[0] - 3)))
else:
shift = bits >> 24
target = (bits & 0xffffff) * (1 << (8 * (shift - 3)))
return target
[docs]def target_to_difficulty(target):
"""
Calculate difficulty from target
:param target: integer.
:return: float.
"""
return 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / target
[docs]def bits_to_difficulty(bits):
"""
Calculate difficulty from bits
:param bits: HEX string, bytes string or integer representation of bits.
:return: integer.
"""
return target_to_difficulty(bits_to_target(bits))
[docs]def difficulty_to_target(difficulty):
"""
Calculate target from difficulty
:param target: integer.
:return: float.
"""
return int(0x00000000FFFF0000000000000000000000000000000000000000000000000000 / difficulty)