def deserialize(cls, bts: bytes, network='btc'): def read(n): nonlocal bts data, bts = bts[:n], bts[n:] return data net = read(4) is_private = net in get_network_attr('extended_prv', network).values() is_public = net in get_network_attr('extended_pub', network).values() assert is_public ^ is_private, f'Invalid network bytes : {bytes_to_hex(net)}' address_lookup = {val: key for key, val in ( get_network_attr('extended_prv', network) if is_private else get_network_attr('extended_pub', network)).items()} constructor = XPrv if is_private else XPub depth = bytes_to_int(read(1)) assert depth in range(256), f'Invalid depth : {depth}' fingerprint = read(4) i = bytes_to_int(read(4)) if depth == 0: i = None path = None else: ih = f'{i}' if i < 2 ** 31 else f"{i - 2 ** 31}h" path = '/'.join([constructor.root_path] + ['x' for _ in range(depth - 1)] + [ih]) code = read(32) key = read(33) key = PrivateKey(key, network=network) if is_private else PublicKey.decode(key, network=network) assert not bts, 'Leftover bytes' return constructor(key, code, depth=depth, i=i, parent=fingerprint, path=path, address_type=address_lookup[net])
def from_seed(seed: Union[bytes, str], address_type='P2PKH', network='btc') -> 'XPrv': if isinstance(seed, str): seed = hex_to_bytes(seed) assert 16 <= len(seed) <= 64, 'Seed should be between 128 and 512 bits' I = hmac.new(key=b"Bitcoin seed", msg=seed, digestmod=hashlib.sha512).digest() I_L, I_R = I[:32], I[32:] if bytes_to_int(I_L) == 0 or bytes_to_int(I_L) > DefaultCurve.order: raise KeyDerivationError key, code = PrivateKey(I_L, network=network), I_R return XPrv(key, code, address_type=address_type)
def decode(key: bytes, network='btc'): # TODO Easier implementation if key.startswith(b'\x04'): # uncompressed key assert len( key) == 65, 'An uncompressed public key must be 65 bytes long' x, y = bytes_to_int(key[1:33]), bytes_to_int(key[33:]) else: # compressed key assert len( key) == 33, 'A compressed public key must be 33 bytes long' x = bytes_to_int(key[1:]) root = modsqrt(f(x), DefaultCurve.curve.p()) if key.startswith(b'\x03'): # odd root y = root if root % 2 == 1 else -root % DefaultCurve.curve.p() elif key.startswith(b'\x02'): # even root y = root if root % 2 == 0 else -root % DefaultCurve.curve.p() else: assert False, 'Wrong key format' return PublicKey(ecdsa_point_creator(x, y), network=network)
def child(self, i) -> 'XPrv': hardened = i >= 1 << 31 if hardened: I = hmac.new( key=self.code, msg=self.key_data() + int_to_bytes(i).rjust(4, b'\x00'), digestmod=hashlib.sha512 ).digest() else: I = hmac.new( key=self.code, msg=self.key.to_public().encode(compressed=True) + int_to_bytes(i).rjust(4, b'\x00'), digestmod=hashlib.sha512 ).digest() I_L, I_R = bytes_to_int(I[:32]), I[32:] key = (I_L + self.key.int()) % DefaultCurve.order if I_L >= DefaultCurve.order or key == 0: return self.child(i + 1) ret_code = I_R if hardened: path = self.path + f'/{i - 2 ** 31}h' else: path = self.path + f'/{i}' private = PrivateKey.from_int(key) private.network = self.key.network return XPrv( key=private, code=ret_code, depth=self.depth + 1, i=i, parent=self.fingerprint(), path=path, address_type=self.type.value )
def bin(self): return format(bytes_to_int(self.msg), 'b')
def int(self): return bytes_to_int(self.msg)