Source code for bitcoin.encoding.bech32

"""Provides methods to encode/decode to/from bech32.

**Sources**
 - `Pieter Wuille reference implementation \
<https://github.com/sipa/bech32/blob/master/ref/python/segwit_addr.py>`_
"""
# Constants
ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'


def _polymod(values):
    generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
    check = 1

    for value in values:
        top = check >> 25
        check = (check & 0x1ffffff) << 5 ^ value
        for i in range(5):
            check ^= generator[i] if ((top >> i) & 1) else 0

    return check


def _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 _create_checksum(hrp, data):
    """
    Compute the checksum values given HRP and data.
    """
    values = _hrp_expand(hrp) + data
    pm = _polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
    return [(pm >> 5 * (5 - i)) & 31 for i in range(6)]


def _verify_checksum(hrp, data):
    return _polymod(_hrp_expand(hrp) + data) == 1


def _convertbits(data, frombits, tobits, padding=True):
    """
    General power-of-2 base conversion.
    """
    acc, bits = 0, 0
    res = []
    maxv = (1 << tobits) - 1
    max_acc = (1 << (frombits + tobits - 1)) - 1

    for value in data:
        if value < 0 or (value >> frombits):
            # Unknown error
            raise ValueError("Unknown error")

        acc = ((acc << frombits) | value) & max_acc
        bits += frombits
        while bits >= tobits:
            bits -= tobits
            res.append((acc >> bits) & maxv)

    if padding:
        if bits:
            res.append((acc << (tobits - bits)) & maxv)
    elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
        return None

    return res


[docs]def encode(hrp, version, program): """ Encodes bytes using bech32 convention into an human-readable string. """ if version < 0 or version > 16: raise ValueError("Version not valid") data = [version] + _convertbits(program, 8, 5) checksum = _create_checksum(hrp, data) data = data + checksum data_coded = '' for el in data: data_coded += ALPHABET[el] return hrp + '1' + data_coded
[docs]def decode(string): """ Decodes a bech32 string into a bytes object. """ hr_index = string.rfind('1') # Check position if hr_index < 1 or hr_index > len(string) - 7 or len(string) > 90: raise ValueError("Address length not correct") hrp = string[:hr_index] data = string[hr_index+1:] # Check charset for char in data: if char not in ALPHABET: raise ValueError("Character not valid to decode from bech32") data_decoded = [ALPHABET.find(el) for el in data] # Check checksum if not _verify_checksum(hrp, data_decoded): raise ValueError("Checksum not valid") # Retrieve version & checksum version = data_decoded[0] data_decoded = data_decoded[1:-6] if version > 16: raise ValueError("Version not valid") decoded = _convertbits(data_decoded, 5, 8, False) # Checks if decoded is None: raise ValueError("Address decoding failed") if version == 0 and len(decoded) != 20 and len(decoded) != 32: raise ValueError("Address decoding failed, length invalid") if len(decoded) < 2 or len(decoded) > 40: raise ValueError("Decoded length is invalid") return hrp, version, bytes(decoded)