merkle.py (2402B)
1 #!/usr/bin/env python 2 # 3 # Electrum -lightweight Bitcoin client 4 # Copyright (C) 2021 Ivan J. <parazyd@dyne.org> 5 # 6 # Permission is hereby granted, free of charge, to any person 7 # obtaining a copy of this software and associated documentation files 8 # (the "Software"), to deal in the Software without restriction, 9 # including without limitation the rights to use, copy, modify, merge, 10 # publish, distribute, sublicense, and/or sell copies of the Software, 11 # and to permit persons to whom the Software is furnished to do so, 12 # subject to the following conditions: 13 # 14 # The above copyright notice and this permission notice shall be 15 # included in all copies or substantial portions of the Software. 16 # 17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 # SOFTWARE. 25 """Module for calculating merkle branches""" 26 from math import ceil, log 27 28 from .crypto import sha256d 29 30 31 def branch_length(hash_count): 32 """Return the length of a merkle branch given the number of hashes""" 33 return ceil(log(hash_count, 2)) 34 35 36 def merkle_branch_and_root(hashes, index): 37 """Return a (merkle branch, merkle_root) pair given hashes, and the 38 index of one of those hashes. 39 """ 40 hashes = list(hashes) 41 if not isinstance(index, int): 42 raise TypeError('index must be an integer') 43 # This also asserts hashes is not empty 44 if not 0 <= index < len(hashes): 45 raise ValueError('index out of range') 46 length = branch_length(len(hashes)) 47 48 branch = [] 49 for _ in range(length): 50 if len(hashes) & 1: 51 hashes.append(hashes[-1]) 52 branch.append(hashes[index ^ 1]) 53 index >>= 1 54 hashes = [sha256d(hashes[n] + hashes[n+1]) 55 for n in range(0, len(hashes), 2)] 56 return branch, hashes[0] 57 58 def merkle_branch(tx_hashes, tx_pos): 59 """Return a merkle branch given hashes and the tx position""" 60 branch, _root = merkle_branch_and_root(tx_hashes, tx_pos) 61 branch = [bytes(reversed(h)).hex() for h in branch] 62 return branch