Source code for pybgl.functions.bip32

from struct import pack
from pybgl.functions.key import private_to_public_key, private_key_to_wif
from pybgl.functions.hash import hmac_sha512, double_sha256, hash160
from pybgl.functions.encode import encode_base58, decode_base58
from pybgl.constants import *
from pybgl.crypto import __secp256k1_ec_pubkey_tweak_add__
from pybgl.functions.tools import get_bytes

[docs]def create_master_xprivate_key(seed, testnet=False, base58=None, hex=None): """ Create extended private key from seed :param seed: seed HEX or bytes string. :param testnet: (optional) flag for testnet network, by default is False. :param base58: (optional) return result as base58 encoded string, by default is True. :param hex: (optional) return result as HEX encoded string, by default is False. In case True base58 flag value will be ignored. :return: extended private key in base58, HEX or bytes string format. """ seed = get_bytes(seed) i = hmac_sha512(b"Bitcoin seed", seed) m, c = i[:32], i[32:] m_int = int.from_bytes(m, byteorder="big") if m_int <= 0 or m_int > ECDSA_SEC256K1_ORDER: # pragma: no cover return None prefix = TESTNET_XPRIVATE_KEY_PREFIX if testnet else MAINNET_XPRIVATE_KEY_PREFIX key = b''.join([prefix, b'\x00\x00\x00\x00\x00\x00\x00\x00\x00', c, b'\x00', m]) if base58 is None and hex is None: base58 = True if base58: key = b"".join([key, double_sha256(key)[:4]]) return encode_base58(key) else: if hex is None: hex = False return key if not hex else key.hex()
[docs]def xprivate_to_xpublic_key(xprivate_key, base58=True, hex=False): """ Get extended public key from extended private key using ECDSA secp256k1 :param xprivate_key: extended private key in base58, HEX or bytes string. :param base58: (optional) return result as base58 encoded string, by default is True. :param hex: (optional) return result as HEX encoded string, by default is False. In case True base58 flag value will be ignored. :return: extended public key in base58, HEX or bytes string format. """ if isinstance(xprivate_key, str): try: if len(xprivate_key) == 156: xprivate_key = bytes.fromhex(xprivate_key) else: xprivate_key = decode_base58(xprivate_key, checksum=True) except: raise ValueError("invalid extended private key") if not isinstance(xprivate_key, bytes): raise TypeError("extended private key should be base58 string or bytes") if xprivate_key[:4] == TESTNET_XPRIVATE_KEY_PREFIX: prefix = TESTNET_XPUBLIC_KEY_PREFIX elif xprivate_key[:4] == MAINNET_XPRIVATE_KEY_PREFIX: prefix = MAINNET_XPUBLIC_KEY_PREFIX else: raise ValueError("invalid extended private key") key = b"".join([prefix, xprivate_key[4:45], private_to_public_key(xprivate_key[46:], hex=False)]) if hex: return key.hex() elif base58: key = b"".join([key, double_sha256(key)[:4]]) return encode_base58(key) else: return key
def decode_path(path, sub_path=False): path = path.split('/') if not sub_path: if path[0] != 'm': raise ValueError("invalid path") r = [] for k in path if sub_path else path[1:]: if k[-1] == "'": k = int(k[:-1]) + HARDENED_KEY else: k = int(k) r.append(k) return r
[docs]def derive_xkey(xkey, path, base58=None, hex=None): """ Child Key derivation for extended private/public keys :param xkey: extended private/public in base58, HEX or bytes string format. :param path: list of derivation path levels. For hardened derivation use HARDENED_KEY flag. :param base58: (optional) return result as base58 encoded string, by default is True. :param hex: (optional) return result as HEX encoded string, by default is False. In case True base58 flag value will be ignored. :return: extended child private/public key in base58, HEX or bytes string format. """ if isinstance(path, str): path = decode_path(path) if isinstance(xkey, str): xkey = decode_base58(xkey, checksum=True) if xkey[:4] in [MAINNET_XPRIVATE_KEY_PREFIX, TESTNET_XPRIVATE_KEY_PREFIX]: for i in path: xkey = derive_child_xprivate_key(xkey, i) elif xkey[:4] in [MAINNET_XPUBLIC_KEY_PREFIX, TESTNET_XPUBLIC_KEY_PREFIX]: for i in path: xkey = derive_child_xpublic_key(xkey, i) else: raise ValueError("invalid extended key") if base58 is None and hex is None: base58 = True hex = False if hex: return xkey.hex() elif base58: return encode_base58(xkey, checksum=True) else: return xkey
def derive_child_xprivate_key(xprivate_key, i): c = xprivate_key[13:45] k = xprivate_key[45:] depth = xprivate_key[4] + 1 if depth > 255: raise ValueError("path depth should be <= 255") # pragma: no cover pub = private_to_public_key(k[1:], hex=False) fingerprint = hash160(pub)[:4] s = hmac_sha512(c, b"%s%s" % (k if i >= HARDENED_KEY else pub, pack(">L", i))) p_int = int.from_bytes(s[:32],byteorder='big') if p_int >= ECDSA_SEC256K1_ORDER: # pragma: no cover return None k_int = (int.from_bytes(k[1:], byteorder='big') + p_int) % ECDSA_SEC256K1_ORDER if not k_int: # pragma: no cover return None key = int.to_bytes(k_int, byteorder = "big", length=32) return b"".join([xprivate_key[:4], bytes([depth]), fingerprint, pack(">L", i), s[32:], b'\x00', key]) def derive_child_xpublic_key(xpublic_key, i): c = xpublic_key[13:45] k = xpublic_key[45:] fingerprint = hash160(k)[:4] depth = xpublic_key[4] + 1 if depth > 255: raise ValueError("path depth should be <= 255") # pragma: no cover if i >= HARDENED_KEY: raise ValueError("derivation from extended public key impossible") s = hmac_sha512(c, k + pack(">L", i)) if int.from_bytes(s[:32], byteorder='big') >= ECDSA_SEC256K1_ORDER: # pragma: no cover return None pk = __secp256k1_ec_pubkey_tweak_add__(k, s[:32]) if isinstance(pk, int): # pragma: no cover raise RuntimeError("pubkey_tweak_add error %s" %pk) return b"".join([xpublic_key[:4], bytes([depth]), fingerprint, pack(">L", i), s[32:], pk])
[docs]def public_from_xpublic_key(xpublic_key, hex=True): """ Get public key from extended public key :param xpublic_key: extended public in base58, HEX or bytes string format. :param base58: (optional) return result as base58 encoded string, by default is True. :param hex: (optional) return result as HEX encoded string, by default is False. In case True base58 flag value will be ignored. :return: public key in HEX or bytes string format. """ if isinstance(xpublic_key, str): if len(xpublic_key) == 156: xpublic_key = bytes.fromhex(xpublic_key) else: xpublic_key = decode_base58(xpublic_key, checksum=True) if not isinstance(xpublic_key, bytes): raise TypeError("xpublic_key should be HEX, Base58 or bytes string") if xpublic_key[:4] not in [MAINNET_XPUBLIC_KEY_PREFIX, TESTNET_XPUBLIC_KEY_PREFIX]: raise ValueError("invalid extended public key") return xpublic_key[45:].hex() if hex else xpublic_key[45:]
[docs]def private_from_xprivate_key(xprivate_key, wif=True, hex=False): """ Get private key from extended private key :param xprivate_key: extended private in base58, HEX or bytes string format. :param wif: (optional) return result as WIF format, by default is True. :param hex: (optional) return result as HEX encoded string, by default is False. In case True WIF flag value will be ignored. :return: private key in HEX or bytes string format. """ if isinstance(xprivate_key, str): if len(xprivate_key) == 156: xprivate_key = bytes.fromhex(xprivate_key) else: xprivate_key = decode_base58(xprivate_key, checksum=True) if not isinstance(xprivate_key, bytes): raise TypeError("xprivate_key should be HEX, Base58 or bytes string") prefix = xprivate_key[:4] if prefix in (MAINNET_XPRIVATE_KEY_PREFIX, MAINNET_M44_XPRIVATE_KEY_PREFIX, MAINNET_M49_XPRIVATE_KEY_PREFIX, MAINNET_M84_XPRIVATE_KEY_PREFIX): testnet = False elif prefix in (TESTNET_XPRIVATE_KEY_PREFIX, TESTNET_M44_XPRIVATE_KEY_PREFIX, TESTNET_M49_XPRIVATE_KEY_PREFIX, TESTNET_M84_XPRIVATE_KEY_PREFIX): testnet = True else: raise ValueError("invalid extended private key") if hex: return xprivate_key[46:].hex() elif wif: return private_key_to_wif(xprivate_key[46:], testnet=testnet) return xprivate_key[46:].hex() if hex else xprivate_key[46:]
def is_xprivate_key_valid(key): """ Check the extended private key is valid according to BIP-0032. :param key: extended private key in BASE58, HEX or bytes string format. :return: boolean. """ if isinstance(key, str): try: key = decode_base58(key, checksum=True) except: try: key = bytes.fromhex(key) except: pass if not isinstance(key, bytes) or len(key)!=78: return False if key[:4] not in [MAINNET_XPRIVATE_KEY_PREFIX, TESTNET_XPRIVATE_KEY_PREFIX, MAINNET_M49_XPRIVATE_KEY_PREFIX, TESTNET_M49_XPRIVATE_KEY_PREFIX, MAINNET_M84_XPRIVATE_KEY_PREFIX, TESTNET_M84_XPRIVATE_KEY_PREFIX]: return False return True def is_xpublic_key_valid(key): """ Check the extended private key is valid according to BIP-0032. :param key: extended private key in BASE58, HEX or bytes string format. :return: boolean. """ if isinstance(key, str): try: key = decode_base58(key, verify_checksum = True) except: try: key = bytes.fromhex(key) except: pass if not isinstance(key, bytes) or len(key)!=78: return False if key[:4] not in [MAINNET_XPUBLIC_KEY_PREFIX, TESTNET_XPUBLIC_KEY_PREFIX, MAINNET_M49_XPUBLIC_KEY_PREFIX, TESTNET_M49_XPUBLIC_KEY_PREFIX, MAINNET_M84_XPUBLIC_KEY_PREFIX, TESTNET_M84_XPUBLIC_KEY_PREFIX]: return False return True def path_xkey_to_bip32_xkey(key, base58=True, hex=False): if isinstance(key, str): try: key = decode_base58(key, verify_checksum = True) except: try: key = bytes.fromhex(key) except: pass if not isinstance(key, bytes) or len(key)!=78: raise ValueError("invalid extended key") if key[:4] in (MAINNET_XPUBLIC_KEY_PREFIX, TESTNET_XPUBLIC_KEY_PREFIX, MAINNET_XPRIVATE_KEY_PREFIX, TESTNET_XPRIVATE_KEY_PREFIX): pass elif key[:4] in (MAINNET_M49_XPUBLIC_KEY_PREFIX, MAINNET_M84_XPUBLIC_KEY_PREFIX): key = MAINNET_XPUBLIC_KEY_PREFIX + key[4:] elif key[:4] in (TESTNET_M49_XPUBLIC_KEY_PREFIX, TESTNET_M84_XPUBLIC_KEY_PREFIX): key = TESTNET_XPUBLIC_KEY_PREFIX + key[4:] elif key[:4] in (MAINNET_M49_XPRIVATE_KEY_PREFIX, MAINNET_M84_XPRIVATE_KEY_PREFIX): key = MAINNET_XPRIVATE_KEY_PREFIX + key[4:] elif key[:4] in (TESTNET_M49_XPRIVATE_KEY_PREFIX, TESTNET_M84_XPRIVATE_KEY_PREFIX): key = TESTNET_XPRIVATE_KEY_PREFIX + key[4:] else: raise ValueError("invalid extended key") if hex: return key.hex() elif base58: return encode_base58(key, checksum=True) else: return key def bip32_xkey_to_path_xkey(key, path_type, base58=True, hex=False): if path_type not in ("BIP44", "BIP49", "BIP84"): raise ValueError("unsupported path type %s" % path_type) if isinstance(key, str): try: key = decode_base58(key, verify_checksum = True) except: try: key = bytes.fromhex(key) except: pass if not isinstance(key, bytes) or len(key) != 78: raise ValueError("invalid key") if key[:4] == TESTNET_XPRIVATE_KEY_PREFIX: if path_type == "BIP44": key = TESTNET_M44_XPRIVATE_KEY_PREFIX + key[4:] elif path_type == "BIP49": key = TESTNET_M49_XPRIVATE_KEY_PREFIX + key[4:] else: key = TESTNET_M84_XPRIVATE_KEY_PREFIX + key[4:] elif key[:4] == MAINNET_XPRIVATE_KEY_PREFIX: if path_type == "BIP44": key = MAINNET_M44_XPRIVATE_KEY_PREFIX + key[4:] elif path_type == "BIP49": key = MAINNET_M49_XPRIVATE_KEY_PREFIX + key[4:] else: key = MAINNET_M84_XPRIVATE_KEY_PREFIX + key[4:] elif key[:4] == TESTNET_XPUBLIC_KEY_PREFIX: if path_type == "BIP44": key = TESTNET_M44_XPUBLIC_KEY_PREFIX + key[4:] elif path_type == "BIP49": key = TESTNET_M49_XPUBLIC_KEY_PREFIX + key[4:] else: key = TESTNET_M84_XPUBLIC_KEY_PREFIX + key[4:] elif key[:4] == MAINNET_XPUBLIC_KEY_PREFIX: if path_type == "BIP44": key = MAINNET_M44_XPUBLIC_KEY_PREFIX + key[4:] elif path_type == "BIP49": key = MAINNET_M49_XPUBLIC_KEY_PREFIX + key[4:] else: key = MAINNET_M84_XPUBLIC_KEY_PREFIX + key[4:] else: raise ValueError("invalid key") if hex: return key.hex() elif base58: return encode_base58(key, checksum = True) else: return key