Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 37 additions & 22 deletions bech32.py → segwit_addr.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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."""
Expand All @@ -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."""
Expand All @@ -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)
Expand All @@ -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
10 changes: 10 additions & 0 deletions utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
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.

# 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++
Expand Down Expand Up @@ -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']
Expand Down