pyflo/pybtc/functions/encode.py
2018-07-03 13:56:40 +04:00

137 lines
3.4 KiB
Python

from .hash import double_sha256
b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
base32charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
base32charset_upcase = "QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L"
int_base32_map = dict()
base32_int_map = dict()
for n, i in enumerate(base32charset):
int_base32_map[i] = n
base32_int_map[n] = ord(i)
for n, i in enumerate(base32charset_upcase):
int_base32_map[i] = n
def rebasebits(data, frombits, tobits, pad=True):
"""General power-of-2 base conversion."""
acc = 0
bits = 0
ret = bytearray()
maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 1
for value in data:
if value < 0 or (value >> frombits):
raise ValueError("invalid bytes")
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):
raise ValueError("invalid padding")
return ret
def rebase_5_to_8(data, pad = True):
return rebasebits(data, 5, 8, pad)
def rebase_8_to_5(data, pad = True):
return rebasebits(data, 8, 5, pad)
def rebase_32_to_5(data):
if type(data) == bytes:
data = data.decode()
b = bytearray()
try:
for i in data:
b.append(int_base32_map[i])
except:
raise Exception("Non base32 characters")
return b
def rebase_5_to_32(data, bytes = True):
r = bytearray()
for i in data:
r.append(base32_int_map[i])
return r.decode() if not bytes else r
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 ^ 1
def encode_base58(b):
"""Encode bytes to a base58-encoded string"""
# Convert big-endian bytes to integer
n= int('0x0' + b.hex(), 16)
# Divide that integer into bas58
res = []
while n > 0:
n, r = divmod(n, 58)
res.append(b58_digits[r])
res = ''.join(res[::-1])
# Encode leading zeros as base58 zeros
czero = 0
pad = 0
for c in b:
if c == czero:
pad += 1
else:
break
return b58_digits[0] * pad + res
def decode_base58(s):
"""Decode a base58-encoding string, returning bytes"""
if not s:
return b''
# Convert the string to an integer
n = 0
for c in s:
n *= 58
if c not in b58_digits:
raise Exception('Character %r is not a valid base58 character' % c)
digit = b58_digits.index(c)
n += digit
# Convert the integer to bytes
h = '%x' % n
if len(h) % 2:
h = '0' + h
res = bytes.fromhex(h)
# Add padding back.
pad = 0
for c in s[:-1]:
if c == b58_digits[0]:
pad += 1
else:
break
return b'\x00' * pad + res
def encode_base58_with_checksum(b):
return encode_base58(b"%s%s" % (b, double_sha256(b)[:4]))
def decode_base58_with_checksum(s):
b = decode_base58(s)
assert double_sha256(b[:-4])[:4] == b[-4:]
return b[:-4]