1
0
Fork 0
mirror of https://github.com/bitcoin/bips.git synced 2025-02-22 06:52:38 +01:00
bitcoin-bips/bip-0328/_base58.py
2025-02-11 12:41:17 -08:00

176 lines
4.3 KiB
Python

"""
Base 58 conversion utilities
****************************
"""
#
# base58.py
# Original source: git://github.com/joric/brutus.git
# which was forked from git://github.com/samrushing/caesure.git
#
# Distributed under the MIT/X11 software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
from binascii import hexlify, unhexlify
from typing import List
from _common import hash256
b58_digits: str = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def encode(b: bytes) -> str:
"""
Encode bytes to a base58-encoded string
:param b: Bytes to encode
:return: Base58 encoded string of ``b``
"""
# Convert big-endian bytes to integer
n: int = int('0x0' + hexlify(b).decode('utf8'), 16)
# Divide that integer into base58
temp: List[str] = []
while n > 0:
n, r = divmod(n, 58)
temp.append(b58_digits[r])
res: str = ''.join(temp[::-1])
# Encode leading zeros as base58 zeros
czero: int = 0
pad: int = 0
for c in b:
if c == czero:
pad += 1
else:
break
return b58_digits[0] * pad + res
def decode(s: str) -> bytes:
"""
Decode a base58-encoding string, returning bytes
:param s: Base48 string to decode
:return: Bytes encoded by ``s``
"""
if not s:
return b''
# Convert the string to an integer
n: int = 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: str = '%x' % n
if len(h) % 2:
h = '0' + h
res = unhexlify(h.encode('utf8'))
# 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 decode_check(s: str) -> bytes:
"""
Decode a Base58Check encoded string, returning bytes
:param s: Base58 string to decode
:return: Bytes encoded by ``s``
"""
data = decode(s)
payload = data[:-4]
checksum = data[-4:]
calc_checksum = hash256(payload)
if checksum != calc_checksum[:4]:
raise ValueError("Invalid checksum")
return payload
def encode_check(b: bytes) -> str:
checksum = hash256(b)[0:4]
data = b + checksum
return encode(data)
def get_xpub_fingerprint(s: str) -> bytes:
"""
Get the parent fingerprint from an extended public key
:param s: The extended pubkey
:return: The parent fingerprint bytes
"""
data = decode(s)
fingerprint = data[5:9]
return fingerprint
def get_xpub_fingerprint_hex(xpub: str) -> str:
"""
Get the parent fingerprint as a hex string from an extended public key
:param s: The extended pubkey
:return: The parent fingerprint as a hex string
"""
data = decode(xpub)
fingerprint = data[5:9]
return hexlify(fingerprint).decode()
def to_address(b: bytes, version: bytes) -> str:
"""
Base58 Check Encode the data with the version number.
Used to encode legacy style addresses.
:param b: The data to encode
:param version: The version number to encode with
:return: The Base58 Check Encoded string
"""
data = version + b
checksum = hash256(data)[0:4]
data += checksum
return encode(data)
def xpub_to_pub_hex(xpub: str) -> str:
"""
Get the public key as a string from the extended public key.
:param xpub: The extended pubkey
:return: The pubkey hex string
"""
data = decode(xpub)
pubkey = data[-37:-4]
return hexlify(pubkey).decode()
def xpub_to_xonly_pub_hex(xpub: str) -> str:
"""
Get the public key as a string from the extended public key.
:param xpub: The extended pubkey
:return: The pubkey hex string
"""
data = decode(xpub)
pubkey = data[-36:-4]
return hexlify(pubkey).decode()
def xpub_main_2_test(xpub: str) -> str:
"""
Convert an extended pubkey from mainnet version to testnet version.
:param xpub: The extended pubkey
:return: The extended pubkey re-encoded using testnet version bytes
"""
data = decode(xpub)
test_data = b'\x04\x35\x87\xCF' + data[4:-4]
checksum = hash256(test_data)[0:4]
return encode(test_data + checksum)