From c2875aae6b0206070b07e6c6bf4908523ff36c06 Mon Sep 17 00:00:00 2001 From: who-biz <37732338+who-biz@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:14:01 +0000 Subject: [PATCH] Add compatbility for bech32 (segwit) vouts --- bech32.py => segwit_addr.py | 59 +++++++++++++++++++++++-------------- utils.py | 10 +++++++ 2 files changed, 47 insertions(+), 22 deletions(-) rename bech32.py => segwit_addr.py (73%) diff --git a/bech32.py b/segwit_addr.py similarity index 73% rename from bech32.py rename to segwit_addr.py index c9621ec..44e548f 100644 --- a/bech32.py +++ b/segwit_addr.py @@ -1,4 +1,4 @@ -# Copyright (c) 2017 Pieter Wuille +# Copyright (c) 2017, 2020 Pieter Wuille # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -18,12 +18,18 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -"""Reference implementation for Bech32 and segwit addresses.""" +"""Reference implementation for Bech32/Bech32m and segwit addresses.""" -# https://raw.githubusercontent.com/sipa/bech32/master/ref/python/segwit_addr.py +import binascii +from enum import Enum -CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" +class Encoding(Enum): + """Enumeration type to list the various supported encodings.""" + BECH32 = 1 + BECH32M = 2 +CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" +BECH32M_CONST = 0x2bc830a3 def bech32_polymod(values): """Internal function that computes the Bech32 checksum.""" @@ -44,39 +50,45 @@ def bech32_hrp_expand(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_create_checksum(hrp, data): + const = bech32_polymod(bech32_hrp_expand(hrp) + data) + if const == 1: + return Encoding.BECH32 + if const == BECH32M_CONST: + return Encoding.BECH32M + return None + +def bech32_create_checksum(hrp, data, spec): """Compute the checksum values given HRP and data.""" values = bech32_hrp_expand(hrp) + data - polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1 + const = BECH32M_CONST if spec == Encoding.BECH32M else 1 + polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ const return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] -def bech32_encode(hrp, data): +def bech32_encode(hrp, data, spec): """Compute a Bech32 string given HRP and data values.""" - combined = data + bech32_create_checksum(hrp, data) + combined = data + bech32_create_checksum(hrp, data, spec) + #print combined + #convcomb = convertbits(combined,8,5,False) return hrp + '1' + ''.join([CHARSET[d] for d in combined]) - def bech32_decode(bech): - """Validate a Bech32 string, and determine HRP and data.""" + """Validate a Bech32/Bech32m 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) + return (None, None, None) bech = bech.lower() pos = bech.rfind('1') if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: - return (None, None) + return (None, None, None) if not all(x in CHARSET for x in bech[pos+1:]): - return (None, None) + return (None, 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]) - + spec = bech32_verify_checksum(hrp, data) + if spec is None: + return (None, None, None) + return (hrp, data[:-6], spec) def convertbits(data, frombits, tobits, pad=True): """General power-of-2 base conversion.""" @@ -103,7 +115,7 @@ def convertbits(data, frombits, tobits, pad=True): def decode(hrp, addr): """Decode a segwit address.""" - hrpgot, data = bech32_decode(addr) + hrpgot, data, spec = bech32_decode(addr) if hrpgot != hrp: return (None, None) decoded = convertbits(data[1:], 5, 8, False) @@ -113,12 +125,15 @@ def decode(hrp, addr): return (None, None) if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: return (None, None) + if data[0] == 0 and spec != Encoding.BECH32 or data[0] != 0 and spec != Encoding.BECH32M: + return (None, None) return (data[0], decoded) def encode(hrp, witver, witprog): """Encode a segwit address.""" - ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5)) + spec = Encoding.BECH32 if witver == 0 else Encoding.BECH32M + ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5), spec) if decode(hrp, ret) == (None, None): return None return ret diff --git a/utils.py b/utils.py index 830e3b0..b3aa28d 100644 --- a/utils.py +++ b/utils.py @@ -4,6 +4,7 @@ from binascii import hexlify, unhexlify from base58 import b58encode import sys +import segwit_addr # THIS functions are from bitcoin_tools and was only mildly changed. # Please refer to readme.md for the proper link to that library. @@ -11,6 +12,9 @@ # Fee per byte range NSPECIALSCRIPTS = 6 +# Leading segwit str +hrp = "bc" + def txout_decompress(x): """ Decompresses the Satoshi amount of a UTXO stored in the LevelDB. Code is a port from the Bitcoin Core C++ @@ -366,6 +370,12 @@ def parse_ldb(fin_name, version=0.15, types=(0, 1)): continue add = 'P2PK' yield add, out['amount'], value['height'] + elif out['out_type'] in (28, 40): + datalist = list(bytearray.fromhex(out['data'])) + witver = datalist[0] + program = datalist[2:(datalist[1]+2)] + add = segwit_addr.encode(hrp, witver, program) + yield add, out['amount'], value['height'] else: not_decoded[0] += 1 not_decoded[1] += out['amount']