'''Base58 encoding Implementations of Base58 and Base58Check encodings that are compatible with the bitcoin network. ''' # This module is based upon base58 snippets found scattered over many bitcoin # tools written in python. From what I gather the original source is from a # forum post by Gavin Andresen, so direct your praise to him. # This module adds shiny packaging and support for python3. from hashlib import sha256 from typing import Union __version__ = '2.0.1' # 58 character alphabet used BITCOIN_ALPHABET = \ b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' RIPPLE_ALPHABET = b'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz' # Retro compatibility alphabet = BITCOIN_ALPHABET def scrub_input(v: Union[str, bytes]) -> bytes: if isinstance(v, str): v = v.encode('ascii') return v def b58encode_int( i: int, default_one: bool = True, alphabet: bytes = BITCOIN_ALPHABET ) -> bytes: """ Encode an integer using Base58 """ if not i and default_one: return alphabet[0:1] string = b"" while i: i, idx = divmod(i, 58) string = alphabet[idx:idx+1] + string return string def b58encode( v: Union[str, bytes], alphabet: bytes = BITCOIN_ALPHABET ) -> bytes: """ Encode a string using Base58 """ v = scrub_input(v) nPad = len(v) v = v.lstrip(b'\0') nPad -= len(v) p, acc = 1, 0 for c in reversed(v): acc += p * c p = p << 8 result = b58encode_int(acc, default_one=False, alphabet=alphabet) return alphabet[0:1] * nPad + result def b58decode_int( v: Union[str, bytes], alphabet: bytes = BITCOIN_ALPHABET ) -> int: """ Decode a Base58 encoded string as an integer """ v = v.rstrip() v = scrub_input(v) decimal = 0 for char in v: decimal = decimal * 58 + alphabet.index(char) return decimal def b58decode( v: Union[str, bytes], alphabet: bytes = BITCOIN_ALPHABET ) -> bytes: """ Decode a Base58 encoded string """ v = v.rstrip() v = scrub_input(v) 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)) def b58encode_check( v: Union[str, bytes], alphabet: bytes = BITCOIN_ALPHABET ) -> bytes: """ Encode a string using Base58 with a 4 character checksum """ v = scrub_input(v) digest = sha256(sha256(v).digest()).digest() return b58encode(v + digest[:4], alphabet=alphabet) def b58decode_check( v: Union[str, bytes], alphabet: bytes = BITCOIN_ALPHABET ) -> bytes: '''Decode and verify the checksum of a Base58 encoded string''' result = b58decode(v, alphabet=alphabet) result, check = result[:-4], result[-4:] digest = sha256(sha256(result).digest()).digest() if check != digest[:4]: raise ValueError("Invalid checksum") return result