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 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 is_wif_valid(wif): """ Check is private key in WIF format string is valid. :param wif: private key in WIF format string. :return: boolean. """ if not isinstance(wif, str): return False if wif[0] not in PRIVATE_KEY_PREFIX_LIST: return False try: h = decode_base58(wif) except: return False checksum = h[-4:] if wif[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): if len(h) != 37: return False elif len(h) != 38: return False if double_sha256(h[:-4])[:4] != checksum: return False return True
def private_to_public_key(private_key, compressed=True, hex=True): """ Get public key from private key using ECDSA secp256k1 :param private_key: private key in WIF, HEX or bytes. :param compressed: (optional) flag of public key compressed format, by default is True. In case private_key in WIF format, this flag is set in accordance with the key format specified in WIF string. :param hex: (optional) if set to True return key in HEX format, by default is True. :return: 33/65 bytes public key in HEX or bytes string. """ if not isinstance(private_key, bytes): if isinstance(private_key, bytearray): private_key = bytes(private_key) elif isinstance(private_key, str): try: if private_key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): compressed = False h = decode_base58(private_key) if double_sha256(h[:-4])[:4] != h[-4:]: raise Exception() private_key = h[1:33] except: try: private_key = bytes_from_hex(private_key) except: raise ValueError("private key HEX or WIF invalid") else: raise ValueError( "private key must be a bytes or WIF or hex encoded string") if len(private_key) != 32: raise ValueError("private key length invalid") pub = __secp256k1_ec_pubkey_create__(private_key, bool(compressed)) return pub.hex() if hex else pub
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
def wif_to_private_key(h, hex=True): """ Decode WIF private key to bytes string or HEX encoded string :param h: private key in WIF format string. :param hex: (optional) if set to True return key in HEX format, by default is True. :return: Private key HEX encoded string or raw bytes string. """ h = decode_base58(h) if double_sha256(h[:-4])[:4] != h[-4:]: raise TypeError("invalid wif key") return h[1:33].hex() if hex else h[1:33]
def address_to_hash(address, hex=True): """ Get address hash from base58 or bech32 address format. :param address: address in base58 or bech32 format. :param hex: (optional) If set to True return key in HEX format, by default is True. :return: script in HEX or bytes string. """ if address[0] in ADDRESS_PREFIX_LIST: h = decode_base58(address)[1:-4] elif address.split("1")[0] in (MAINNET_SEGWIT_ADDRESS_PREFIX, TESTNET_SEGWIT_ADDRESS_PREFIX, REGTEST_SEGWIT_ADDRESS_PREFIX): address = address.split("1")[1] h = rebase_5_to_8(rebase_32_to_5(address)[1:-6], False) else: return None return h.hex() if hex else h
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 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:]
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 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 is_address_valid(address, testnet=False, regtest=False): """ Check is address valid. :param address: address in base58 or bech32 format. :param testnet: (optional) flag for testnet network, by default is False. :return: boolean. """ print("addr",address) if not address or type(address) != str: return False if address[0] in (MAINNET_ADDRESS_PREFIX, MAINNET_SCRIPT_ADDRESS_PREFIX, TESTNET_ADDRESS_PREFIX, TESTNET_ADDRESS_PREFIX_2, TESTNET_SCRIPT_ADDRESS_PREFIX): if testnet: if address[0] not in (TESTNET_ADDRESS_PREFIX, TESTNET_ADDRESS_PREFIX_2, TESTNET_SCRIPT_ADDRESS_PREFIX): return False else: if address[0] not in (MAINNET_ADDRESS_PREFIX, MAINNET_SCRIPT_ADDRESS_PREFIX): return False h = decode_base58(address) if len(h) != 25: return False checksum = h[-4:] if sha3_256(h[:-4])[:4] != checksum: return False return True elif (address[:3] == MAINNET_SEGWIT_ADDRESS_PREFIX) \ or (address[:4] == TESTNET_SEGWIT_ADDRESS_PREFIX) or (address[:4] == REGTEST_SEGWIT_ADDRESS_PREFIX): if len(address) not in (43,44, 63, 64): return False try: prefix, payload = address.split('1') except: return False upp = True if prefix[0].isupper() else False for i in payload[1:]: if upp: if not i.isupper() or i not in base32charset_upcase: return False else: if i.isupper() or i not in base32charset: return False payload = payload.lower() prefix = prefix.lower() if testnet: if prefix != TESTNET_SEGWIT_ADDRESS_PREFIX and prefix != REGTEST_SEGWIT_ADDRESS_PREFIX: return False stripped_prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX if regtest: stripped_prefix = REGTEST_SEGWIT_ADDRESS_BYTE_PREFIX else: if prefix != MAINNET_SEGWIT_ADDRESS_PREFIX: return False stripped_prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX d = rebase_32_to_5(payload) address_hash = d[:-6] checksum = d[-6:] print("checksum", checksum) print("checksum_back", rebase_5_to_32(checksum)) checksum2 = bech32_polymod(b"%s%s%s" % (stripped_prefix, address_hash, b"\x00" * 6)) checksum2 = rebase_8_to_5(checksum2.to_bytes(5, "big"))[2:] print("checksum2",checksum2) print("checksum2_back", rebase_5_to_32(checksum2)) if checksum != checksum2: return False print("checksum2", checksum2) return True return False