def child(self, i: int) -> 'Xprv': hardened = i >= 1 << 31 if hardened: I = hmac.new(key=self.code, msg=self.keydata() + int_to_bytes(i).rjust(4, b'\x00'), digestmod=hashlib.sha512).digest() tmp = int_to_bytes(i).rjust(4, b'\x00') 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() tmp = self.key.to_public().encode( compressed=True) + int_to_bytes(i).rjust(4, b'\x00') I_L, I_R = bytes_to_int(I[:32]), I[32:] key = (I_L + self.key.int()) % CURVE.order if I_L >= CURVE.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}' return Xprv(WitPrivateKey.from_int(key), ret_code, depth=self.depth + 1, i=i, parent=self.fingerprint(), path=path)
def child(self, i: int) -> 'Xpub': hardened = i >= 1 << 31 if hardened: raise KeyDerivationError( 'Cannot derive a hardened key from an extended public key') I = hmac.new(key=self.code, msg=self.keydata() + int_to_bytes(i).rjust(4, b'\x00'), digestmod=hashlib.sha512).digest() tmp = bytes_to_hex(self.keydata() + int_to_bytes(i).rjust(4, b'\x00')) I_L, I_R = I[:32], I[32:] key = WitPrivateKey(I_L).to_public().point + self.key.point ret_code = I_R path = self.path + f'/{i}' # TODO add point at infinity check return Xpub(WitPublicKey(key), ret_code, depth=self.depth + 1, i=i, parent=self.fingerprint(), path=path)
def pub_key(self): if self.y & 1: # odd root return PublicKey(_bytes=int_to_bytes(self.x).rjust(32, b'\x00'), compressed=3) else: # even root return PublicKey(_bytes=int_to_bytes(self.x).rjust(32, b'\x00'), compressed=2)
def encode(self, compressed=True) -> bytes: if compressed: if self.y & 1: # odd root return b'\x03' + int_to_bytes(self.x).rjust(32, b'\x00') else: # even root return b'\x02' + int_to_bytes(self.x).rjust(32, b'\x00') return b'\x04' + int_to_bytes(self.x).rjust( 32, b'\x00') + int_to_bytes(self.y).rjust(32, b'\x00')
def encode(self, compact=False): """https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#der-encoding""" r = int_to_bytes(self.r) if r[0] > 0x7f: r = b'\x00' + r s = int_to_bytes(self.s) if s[0] > 0x7f: s = b'\x00' + s len_r = int_to_bytes(len(r)) len_s = int_to_bytes(len(s)) len_sig = int_to_bytes(len(r) + len(s) + 4) if compact: return r + s return b'\x30' + len_sig + b'\x02' + len_r + r + b'\x02' + len_s + s
def decode(b58: str) -> bytes: partial_sum = 0 exponent = 0 for digit in reversed(b58): try: partial_sum += ALPHABET.index(digit) * BASE**exponent except ValueError: raise Base58DecodeError('Bad Byte') from None exponent += 1 return int_to_bytes(partial_sum)
def from_binary(cls, b): return cls(int_to_bytes(int(b, 2)))
def from_int(cls, i): return cls(int_to_bytes(i))
def from_json(cls, data: dict): _bytes, _compressed = data.values() return WitPublicKey.from_hex( bytes_to_hex((int_to_bytes(_compressed) + bytes(_bytes).rjust(32, b'\x00'))))
def pb_bytes(self) -> bytes: return pb_field(field_number=1, tag=LENGTH_DELIMITED, value=concat( [int_to_bytes(self.compressed), self._bytes]))