def deserialize_address(address): """ Deserialize cryptocurrency address. Calculate public key hash and try to determine script type and network. If one and only one network is found the 'network' dictionary item with contain this network. Same applies for the script type. If more networks and or script types are found you can find these in 'networks_p2sh' and 'networks_p2pkh'. :param address: A base-58 encoded address :type address: str :return dict: with information about this address """ try: address_bytes = change_base(address, 58, 256, 25) except EncodingError as err: raise EncodingError("Invalid address %s: %s" % (address, err)) check = address_bytes[-4:] key_hash = address_bytes[:-4] checksum = hashlib.sha256(hashlib.sha256(key_hash).digest()).digest()[0:4] assert (check == checksum), "Invalid address, checksum incorrect" address_prefix = key_hash[0:1] networks_p2pkh = network_by_value('prefix_address', address_prefix) networks_p2sh = network_by_value('prefix_address_p2sh', address_prefix) public_key_hash = key_hash[1:] script_type = '' network = '' if networks_p2pkh and not networks_p2sh: script_type = 'p2pkh' if len(networks_p2pkh) == 1: network = networks_p2pkh[0] elif networks_p2sh and not networks_p2pkh: script_type = 'p2sh' if len(networks_p2sh) == 1: network = networks_p2sh[0] return { 'address': address, 'public_key_hash': change_base(public_key_hash, 256, 16), 'public_key_hash_bytes': public_key_hash, 'prefix': address_prefix, 'network': network, 'script_type': script_type, 'networks_p2sh': networks_p2sh, 'networks_p2pkh': networks_p2pkh }
def get_key_format(key, isprivate=None): """ Determins the type (private or public), format and network key. This method does not validate if a key is valid. :param key: Any private or public key :type key: str, int, bytes, bytearray :param isprivate: Is key private or not? :type isprivate: bool :return dict: Dictionary with format, network and isprivate """ if not key: raise BKeyError("Key empty, please specify a valid key") key_format = "" networks = None if isinstance(key, (bytes, bytearray)) and len(key) in [128, 130]: key = to_hexstring(key) if not (isprivate is None or isinstance(isprivate, bool)): raise BKeyError("Attribute 'is_private' must be False or True") elif isinstance(key, numbers.Number): key_format = 'decimal' isprivate = True elif isinstance(key, (bytes, bytearray)) and len(key) in [ 33, 65 ] and key[:1] in [b'\2', b'\3']: key_format = 'bin_compressed' isprivate = False elif isinstance(key, (bytes, bytearray)) and (len(key) in [33, 65] and key[:1] == b'\4'): key_format = 'bin' isprivate = False elif isinstance( key, (bytes, bytearray)) and len(key) == 33 and key[-1:] == b'\1': key_format = 'bin_compressed' isprivate = True elif isinstance(key, (bytes, bytearray)) and len(key) == 32: key_format = 'bin' isprivate = True elif len(key) == 130 and key[:2] == '04' and not isprivate: key_format = 'public_uncompressed' isprivate = False elif len(key) == 128: key_format = 'hex' if isprivate is None: isprivate = True elif len(key) == 66 and key[:2] in ['02', '03'] and not isprivate: key_format = 'public' isprivate = False elif len(key) == 64: key_format = 'hex' if isprivate is None: isprivate = True elif len(key) == 66 and key[-2:] in ['01'] and not (isprivate is False): key_format = 'hex_compressed' isprivate = True elif len(key) == 58 and key[:2] == '6P': key_format = 'wif_protected' isprivate = True else: try: key_hex = change_base(key, 58, 16) networks = network_by_value('prefix_wif', key_hex[:2]) if networks: if key_hex[-10:-8] == '01': key_format = 'wif_compressed' else: key_format = 'wif' isprivate = True else: networks = network_by_value('prefix_hdkey_private', key_hex[:8]) if networks: key_format = 'hdkey_private' isprivate = True else: networks = network_by_value('prefix_hdkey_public', key_hex[:8]) if networks: key_format = 'hdkey_public' isprivate = False # TODO: Recognise address key and implement in Key en HDKey classs # else: # networks = network_by_value('prefix_address_p2sh', key_hex[:2]) + \ # network_by_value('prefix_address', key_hex[:2]) # if networks: # key_format = 'address' # isprivate = False except (TypeError, EncodingError): pass if not key_format: try: int(key) if 70 < len(key) < 78: key_format = 'decimal' isprivate = True except (TypeError, ValueError): pass if not key_format: raise BKeyError("Key: %s. Unrecognised key format" % key) else: return { "format": key_format, "networks": networks, "isprivate": isprivate }
def __init__(self, import_key=None, network=None, compressed=True, passphrase=''): """ Initialize a Key object. Import key can be in WIF, bytes, hexstring, etc. If a private key is imported a public key will be derived. If a public is imported the private key data will be empty. Both compressed and uncompressed key version is available, the Key.compressed boolean attribute tells if the original imported key was compressed or not. :param import_key: If specified import given private or public key. If not specified a new private key is generated. :type import_key: str, int, bytes, bytearray :param network: Bitcoin, testnet, litecoin or other network :type network: str :param compressed: Is key compressed or not, default is True :type compressed: bool :param passphrase: Optional passphrase if imported key is password protected :type passphrase: str :return: Key object """ self.public_hex = None self.public_uncompressed_hex = None self.public_compressed_hex = None self.public_byte = None self.public_uncompressed_byte = None self.public_compressed_byte = None self.private_byte = None self.private_hex = None self._x = None self._y = None self.secret = None self.compressed = compressed if not import_key: import_key = random.SystemRandom().randint(0, secp256k1_n) kf = get_key_format(import_key) self.key_format = kf["format"] network = check_network_and_key(import_key, network, kf["networks"]) self.network = Network(network) if kf['isprivate']: self.isprivate = True elif kf['isprivate'] is None: raise KeyError("Could not determine if key is private or public") else: self.isprivate = False if self.key_format == "wif_protected": # TODO: return key as byte to make more efficient import_key, self.key_format = self._bip38_decrypt( import_key, passphrase) if not self.isprivate: self.secret = None pub_key = to_hexstring(import_key) if len(pub_key) == 130: self.public_uncompressed_hex = pub_key self._x = pub_key[2:66] self._y = pub_key[66:130] self.compressed = False if int(self._y, 16) % 2: prefix = '03' else: prefix = '02' self.public_hex = prefix + self._x self.public_compressed_hex = prefix + self._x else: self.public_hex = pub_key self._x = pub_key[2:66] self.compressed = True # Calculate y from x with y=x^3 + 7 function sign = pub_key[:2] == '03' x = int(self._x, 16) ys = (x**3 + 7) % secp256k1_p y = ecdsa.numbertheory.square_root_mod_prime(ys, secp256k1_p) if y & 1 != sign: y = secp256k1_p - y self._y = change_base(y, 10, 16, 64) self.public_uncompressed_hex = '04' + self._x + self._y self.public_compressed_hex = pub_key self.public_compressed_byte = binascii.unhexlify(self.public_hex) self.public_uncompressed_byte = binascii.unhexlify( self.public_uncompressed_hex) if self.compressed: self.public_byte = self.public_compressed_byte else: self.public_byte = self.public_uncompressed_byte elif self.isprivate and self.key_format == 'decimal': self.secret = import_key self.private_hex = change_base(import_key, 10, 16, 64) self.private_byte = binascii.unhexlify(self.private_hex) elif self.isprivate: if self.key_format == 'hex': key_hex = import_key key_byte = binascii.unhexlify(key_hex) elif self.key_format == 'hex_compressed': key_hex = import_key[:-2] key_byte = binascii.unhexlify(key_hex) self.compressed = True elif self.key_format == 'bin': key_byte = import_key key_hex = to_hexstring(key_byte) elif self.key_format == 'bin_compressed': key_byte = import_key[:-1] key_hex = to_hexstring(key_byte) self.compressed = True elif self.isprivate and self.key_format in [ 'wif', 'wif_compressed' ]: # Check and remove Checksum, prefix and postfix tags key = change_base(import_key, 58, 256) checksum = key[-4:] key = key[:-4] if checksum != hashlib.sha256( hashlib.sha256(key).digest()).digest()[:4]: raise BKeyError("Invalid checksum, not a valid WIF key") found_networks = network_by_value('prefix_wif', key[0:1]) if not len(found_networks): raise BKeyError( "Unrecognised WIF private key, version byte unknown. Versionbyte: %s" % key[0:1]) if self.network.network_name not in found_networks: if len(found_networks) > 1: raise BKeyError( "More then one network found with this versionbyte, please specify network. " "Networks found: %s" % found_networks) else: _logger.warning( "Current network %s is different then the one found in key: %s" % (network, found_networks[0])) self.network = Network(found_networks[0]) if key[-1:] == b'\x01': self.compressed = True key = key[:-1] else: self.compressed = False key_byte = key[1:] key_hex = to_hexstring(key_byte) else: raise KeyError("Unknown key format %s" % self.key_format) if not (key_byte or key_hex): raise KeyError("Cannot format key in hex or byte format") self.private_hex = key_hex self.private_byte = key_byte self.secret = int(key_hex, 16) else: raise KeyError("Cannot import key. Public key format unknown") if self.isprivate and not (self.public_byte or self.public_hex): if not self.isprivate: raise KeyError("Private key has no known secret number") point = ec_point(self.secret) self._x = change_base(point.x(), 10, 16, 64) self._y = change_base(point.y(), 10, 16, 64) if point.y() % 2: prefix = '03' else: prefix = '02' self.public_compressed_hex = prefix + self._x self.public_uncompressed_hex = '04' + self._x + self._y self.public_hex = self.public_compressed_hex if self.compressed else self.public_uncompressed_hex self.public_byte = binascii.unhexlify(self.public_hex) self.public_compressed_byte = binascii.unhexlify( self.public_compressed_hex) self.public_uncompressed_byte = binascii.unhexlify( self.public_uncompressed_hex)
from bitcoinlib.networks import Network, network_by_value, network_values_for, wif_prefix_search # # Network examples # network = Network('bitcoin') print("\n=== Get all WIF prefixes ===") print("WIF Prefixes: %s" % network_values_for('prefix_wif')) print("\n=== Get all HDkey private prefixes ===") print("HDkey private prefixes: %s" % network_values_for('prefix_wif')) print("\n=== Get network(s) for WIF prefix B0 ===") print("WIF Prefixes: %s" % network_by_value('prefix_wif', 'B0')) print("\n=== Get HD key private prefix for current network ===") print("self.prefix_hdkey_private: %s" % network.wif_prefix()) print("\n=== Network parameters ===") for k in network.__dir__(): if k[:1] != '_': v = eval('network.%s' % k) if not callable(v): print("%25s: %s" % (k, v)) wif = 'Zpub74CSuvLPQxWkdW7bivQAhomXZTzbE8quAakKRg1C3x7uDcCCeh7zPp1tZrtJrscihJRASZWjZQ7nPQj1SHTn8gkzAHPZL3dC' \ 'MbMQLFwMKVV' print("\n=== Search for WIF prefix ===") print("WIF: %s" % wif)
def test_networks_prefix_bech32_network_by_value_sorted(self): self.assertEqual(network_by_value('prefix_bech32', 'ltc'), ['litecoin', 'litecoin_legacy'])
def test_networks_prefix_bech32_network_by_value(self): self.assertEqual(network_by_value('prefix_bech32', 'tb'), ['testnet'])
def test_networks_prefix_wif_network_by_value(self): self.assertEqual(network_by_value('prefix_wif', '80')[:1], ['bitcoin']) self.assertEqual(network_by_value('prefix_wif', 10), [])