Created
March 7, 2021 14:09
-
-
Save 10gic/66be6ed2125551bf1870e20cb2570631 to your computer and use it in GitHub Desktop.
Revisions
-
10gic created this gist
Mar 7, 2021 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,170 @@ import sys import binascii import argparse # base58 from https://github.com/keis/base58 BITCOIN_ALPHABET = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' def b58decode_int( v: bytes, alphabet: bytes = BITCOIN_ALPHABET ) -> int: """ Decode a Base58 encoded string as an integer """ v = v.rstrip() decimal = 0 for char in v: decimal = decimal * 58 + alphabet.index(char) return decimal def b58decode( v: str, alphabet: bytes = BITCOIN_ALPHABET ) -> bytes: """ Decode a Base58 encoded string """ v = v.rstrip() v = v.encode('ascii') # str -> bytes origlen = len(v) v = v.lstrip(alphabet[0:1]) newlen = len(v) acc = b58decode_int(v, alphabet=alphabet) result = [] while acc > 0: acc, mod = divmod(acc, 256) result.append(mod) return b'\0' * (origlen - newlen) + bytes(reversed(result)) """Reference implementation for Bech32 and segwit addresses.""" # https://raw.githubusercontent.com/sipa/bech32/master/ref/python/segwit_addr.py CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" def bech32_polymod(values): """Internal function that computes the Bech32 checksum.""" generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] chk = 1 for value in values: top = chk >> 25 chk = (chk & 0x1ffffff) << 5 ^ value for i in range(5): chk ^= generator[i] if ((top >> i) & 1) else 0 return chk def bech32_hrp_expand(hrp): """Expand the HRP into values for checksum computation.""" return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] def bech32_verify_checksum(hrp, data): """Verify a checksum given HRP and converted data characters.""" return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1 def bech32_decode(bech): """Validate a Bech32 string, and determine HRP and data.""" if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or (bech.lower() != bech and bech.upper() != bech)): return None, None bech = bech.lower() pos = bech.rfind('1') if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: return None, None if not all(x in CHARSET for x in bech[pos + 1:]): return None, None hrp = bech[:pos] data = [CHARSET.find(x) for x in bech[pos + 1:]] if not bech32_verify_checksum(hrp, data): return None, None return hrp, data[:-6] def convertbits(data, frombits, tobits, pad=True): """General power-of-2 base conversion.""" acc = 0 bits = 0 ret = [] maxv = (1 << tobits) - 1 max_acc = (1 << (frombits + tobits - 1)) - 1 for value in data: if value < 0 or (value >> frombits): return None acc = ((acc << frombits) | value) & max_acc bits += frombits while bits >= tobits: bits -= tobits ret.append((acc >> bits) & maxv) if pad: if bits: ret.append((acc << (tobits - bits)) & maxv) elif bits >= frombits or ((acc << (tobits - bits)) & maxv): return None return ret def decode(hrp, addr): """Decode a segwit address.""" hrpgot, data = bech32_decode(addr) if hrpgot != hrp: return None, None decoded = convertbits(data[1:], 5, 8, False) if decoded is None or len(decoded) < 2 or len(decoded) > 40: return None, None if data[0] > 16: return None, None if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: return None, None return data[0], decoded def input_args(): parser = argparse.ArgumentParser(description='Read csv file with btc address as first column' ' encodes it to hash160 and writes to stdout' ) parser.add_argument( 'csvin', metavar='csvfile', type=str, help='path to csv file with btc address in first column, - means stdin' ) a = parser.parse_args() return a def process(csvfile): """ Converts p2pkh or p2wpkh address to hash160, i.e. ripemd(sha256(pubkey)) """ if csvfile == '-': infile = sys.stdin else: infile = open(csvfile, 'r') for row in infile: first_column = row.split(',')[0].strip() if len(first_column) == 0: print('') elif first_column.startswith('1') and 26 <= len(first_column) <= 35: # P2PKH ripemd_bin = b58decode(first_column)[1:-4] ripemd_encoded = binascii.hexlify(ripemd_bin) print(ripemd_encoded.decode()) elif first_column.startswith('bc1') and len(first_column) == 42: # P2WPKH _, script_int = decode('bc', first_column.lower()) ripemd_encoded = binascii.hexlify(bytearray(script_int)) print(ripemd_encoded.decode()) else: print("btc address not supported") if infile is not sys.stdin: infile.close() if __name__ == '__main__': args = input_args() process(args.csvin)