def get_script_sig(r: bytes, s: bytes, public_key: bytes): """Construct an unlocking script with a sign [r, s]""" # Convert r and s to DER sig = signature_to_der(r, s) # Construct a sigScript = DER(DER(r,s)+01) + <length pubkey> + <pubkey> return (int2bytes(len(sig) + 1, 1) + sig + b"\x01" + int2bytes(len(public_key), 1) + public_key)
def point_to_publickey(point: ECPoint, compressed=True): """Convert a point to a public key""" if compressed: return (b"\x02" if point.y % 2 == 0 else b"\x03") + \ int2bytes(point.x) else: return b"\x04" + int2bytes(point.x) + int2bytes(point.y)
def serialize(self): """Return serialized format (str) of this key (xprv / xpub)""" if self.is_private(): ser_key = ( MAINNET_PRIVATE + int2bytes(self.level, 1) + self.fingerprint + int2bytes(self.index, 4) + self.chain_code + b"\x00" + self.key ) elif self.is_public(): ser_key = ( MAINNET_PUBLIC + int2bytes(self.level, 1) + self.fingerprint + int2bytes(self.index, 4) + self.chain_code + KeysBTC.point_to_publickey(self.key) ) else: raise ValueError("Invalid extended key") checksum = sha256(sha256(ser_key))[:4] return base58_encode(ser_key + checksum)
def prv_to_child(parent_prv: ExtendedKey, index: int): """Return an extended child private key. Parameters: parent_prv -- a parent private key, index -- an index of a parent private key """ cur_key = KeysBTC(parent_prv.key) ser32_index = int2bytes(index, 4) # If a hardened index the take the private key, # otherwise, take the public key. if index >= 2 ** 31: data = b"\x00" + cur_key.get_private_key() + ser32_index else: data = cur_key.get_public_key() + ser32_index child_hash = hmac_sha512(parent_prv.chain_code, data) child_hash_left = bytes2int(child_hash[:32]) k_i = (child_hash_left + cur_key.get_private_key_int()) % \ ECPoint.get_secp256k1_order() # Check the left part if child_hash_left >= ECPoint.get_secp256k1_order() or k_i == 0: raise ValueError("The resulting key is invalid") # The right part is child_hash[32:] return ExtendedKey( int2bytes(k_i), child_hash[32:], parent_prv.level + 1, # increase level index, ExtendedKey.get_fingerprint(cur_key.get_public_key()) )
def sign(self, hash: bytes): """Sign a hash with the object's keys""" private_key_int = self.get_private_key_int() N = ECPoint.get_secp256k1_order() h = bytes2int(hash) % N h = 1 if h == 0 else h # get the deterministic random integer with RFC 6979 k = random_rfc6979(hash, private_key_int, N, self.get_generator_point()) # Calculate G * k C = self.get_generator_point() * k s = ((h + C.x * private_key_int) * mod_inverse(k, N)) % N # Return r and s return int2bytes(C.x), int2bytes(s)
def get_script_p2pkh(hash: bytes): """Construct a locking pay-to-pubkey-hash script (p2pkh): OP_DUP OP_HASH160 <len of pubkey hash> <pubkey hash> OP_CHECKSIG """ return (OP_DUP + OP_HASH160 + int2bytes(len(hash), 1) + hash + OP_EQUALVERIFY + OP_CHECKSIG)
def __init__(self, private_key=None): """Construct an object with a private key (bytes or str)""" # Init private_key, if input is None then get a random BBS if private_key is None: self._private_key = int2bytes( random_bbs(ECPoint.get_secp256k1_order_len())) else: self._private_key = bytes.fromhex(private_key) \ if isinstance(private_key, str) else private_key assert bytes2int(self._private_key) > 0, "Invalid private key" self._public_point = None self._public_key = None self._public_key_hash = None self._address = None
def pub_to_child(parent_pub: ExtendedKey, index: int): """Return an extended child public key. Parameters: parent_pub -- a parent public key, index -- an index of a parent public key """ # Check if index is not a hardened key if index >= 2 ** 31: raise ValueError( "Cannot generate a child public key because " "it is a hardened key" ) ser32_index = int2bytes(index, 4) public_key = KeysBTC.point_to_publickey(parent_pub.key) data = public_key + ser32_index child_hash = hmac_sha512(parent_pub.chain_code, data) child_hash_left = bytes2int(child_hash[:32]) K_i = KeysBTC.get_generator_point() * child_hash_left + parent_pub.key # Check the left part if child_hash_left >= ECPoint.get_secp256k1_order() or \ K_i == ECPoint.infinity(): raise ValueError( "The resulting key is invalid for index {:d}".format(index) ) # The right part is child_hash[32:] return ExtendedKey( K_i, child_hash[32:], parent_pub.level + 1, # increase level index, ExtendedKey.get_fingerprint(public_key) )
def mnemonic_to_entropy(self, mnemonic: list): """Calculate an entropy from a mnemonic phrase. Parameters: mnemonic -- a list with mnemonic words in a multiple of 3 words (length - 12-24 words). Return bytes in a multiple of 32 bits (length - 128-256 bits). """ words_count = len(mnemonic) # Check number of words if words_count < 12 or words_count > 24 or words_count % 3 != 0: raise Exception("Invalid number of the words") ent_len = words_count * 11 * 32 // 33 ent_cs = 0 for x in mnemonic: try: ent_cs = (ent_cs << 11) | self._wordlist.index(x) except ValueError: raise ValueError("Invalid word: '{:s}'".format(x)) return int2bytes(ent_cs >> (ent_len // 32), ent_len // 8)
def get_script_p2sh(hash): """Construct a locking pay-to-script-hash script (p2sh): OP_HASH160 <len of script hash> <script hash> OP_EQUAL """ return (OP_HASH160 + int2bytes(len(hash), 1) + hash + OP_EQUAL)