def deserialize(self, ser_key: str): """Deserialize a serialized key into self""" ser_key = base58_decode(ser_key, 82) # Checksum if sha256(sha256(ser_key[:78]))[:4] != ser_key[78:]: raise ValueError( "Wrong checksum of the extended key: {:s}".format(ser_key.hex()) ) self.level = ser_key[4] self.fingerprint = ser_key[5:9] self.index = bytes2int(ser_key[9:13]) self.chain_code = ser_key[13:45] if ser_key[:4] == MAINNET_PRIVATE: # Miss 00 and get the private key self.key = ser_key[46:78] elif ser_key[:4] == MAINNET_PUBLIC: # Get x coordinate of the public point x = bytes2int(ser_key[46:78]) # Calculate y coordinate of the public point y = ECPoint.get_secp256k1_y(x) # Choice even y if prefix = 02, else choice odd y if ser_key[45] == 2: y = ECPoint.get_secp256k1_p() - y if y % 2 != 0 else y else: y = ECPoint.get_secp256k1_p() - y if y % 2 == 0 else y self.key = ECPoint(x, y) else: raise ValueError( "Invalid serialized extended key: {:s}".format(ser_key.hex()) )
def __repr__(self): cur_tx = self.gen_transaction(to_sign=False) return \ str({ "tx_hash": sha256(sha256(cur_tx))[::-1].hex(), "tx": cur_tx.hex() })
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 privatekey_to_wif(private_key: bytes, compressed=True): """Convert a private key to WIF (str)""" compressed_flag = b"\x01" if compressed else b"" wif = b"\x80" + private_key + compressed_flag double_sha = sha256(sha256(wif)) try: return base58_encode(wif + double_sha[:4]) except Exception: raise ValueError("Invalid private key: {:s}".format( private_key.hex()))
def address_to_pubkey_hash(address: str): """Check an address checksum. Return Public Key Hash if the checksum is correct else raise ValueError. """ # calculate the public key hash t = base58_decode(address.lstrip("1"), 25) # If the checksum is correct if sha256(sha256(t[:21]))[:4] == t[21:]: return t[1:21] else: raise ValueError( "Incorrect checksum for address: {:s}".format(address))
def get_pubkey_hash(self): """Return the object's public key hash""" if self._public_key_hash is None: self._public_key_hash = \ ripemd160(sha256(self.get_public_key())) return self._public_key_hash
def entropy_to_mnemonic(self, entropy: bytes): """Calculate a mnemonic phrase from an entropy. Parameters: entropy -- a random bytes in a multiple of 32 bits (length - 128-256 bits). Return a list with mnemonic words. """ ent_len = 8 * len(entropy) # Check a multiple of 32 bits if ent_len < 32 or ent_len % 32 != 0: raise Exception("Invalid entropy length") # cs_len -- length of checksum cs_len = ent_len // 32 checksum = bytes2int(sha256(entropy)[0:2]) >> (16 - cs_len) ent_cs = (bytes2int(entropy) << cs_len) | checksum words_count = (ent_len + ent_len // 32) // 11 mnemonic = [ self._wordlist[(ent_cs >> 11 * x) & 0x7FF].strip() for x in range(words_count) ] # Reverse the list return mnemonic[::-1]
def sign_all_inputs(self): """Sign this transaction""" # For each input prepare and sign a transaction for i in self.ins.keys(): # Prepare the transaction to sign for the input = i temp_tx = self.gen_transaction(input_num=i, to_sign=True) # Get the hash hash_temp_tx = sha256(sha256(temp_tx)) # Sign the prepared transaction r, s = self.ins[i]["keys"].sign(hash_temp_tx) # Save the final script signature and it's length self.ins[i]["final_script_sig"] = \ self.get_script_sig(r, s, self.ins[i]["keys"].get_public_key()) self.ins[i]["final_script_length"] = \ struct.pack("<B", len(self.ins[i]["final_script_sig"])) return
def get_address(self, version: int = None): """Return the object's btc-address""" # If None then version is a mainnet address version = ADDRESS_PREFIX_MAINNET if version is None else version if self._address is None: # Set an address version (mainnet or testnet) t = bytes([version]) + self.get_pubkey_hash() # Add checksum t += sha256(sha256(t))[0:4] # Count leading zeros leading_zeros = 0 for _ in t: if _ == 0: leading_zeros += 1 else: break # Change leading zeros by ones and encode to base58 self._address = leading_zeros * "1" + \ base58_encode(t) return self._address
from btc.utils import sha256 from btc.keys import KeysBTC k = KeysBTC("96a69d6682a4b2eb522e896c2fa1b8ada485c472b983e27266d1d5c8c77ec374") # Check the right values at https://www.bitaddress.org # An object methods print("Private key (hex): ", k.get_private_key_hex()) print("Private key (WIF-compressed): ", k.get_private_key_wif(True)) print("Private key (WIF-uncompressed): ", k.get_private_key_wif(False)) print("Public point: ", k.get_public_point()) print("Public key (hex): ", k.get_public_key(compressed=True).hex(),) print("Is public key compressed: ", k.is_pubkey_compressed()) print("Public key HASH160: ", k.get_pubkey_hash().hex()) print("Address: ", k.get_address()) # Static methods (convertions) print("Generator point: ", k.get_generator_point()) print("Private key to WIF: ", k.privatekey_to_wif(k._private_key, compressed=False)) print("WIF to private key", k.privatekey_from_wif(k.get_private_key_wif()).hex()) print(k.address_to_pubkey_hash(k.get_address()).hex()) # Sign and verify message = sha256(b"sample") r, s = k.sign(message) print(k.verify(message, r, s))
def get_fingerprint(public_key): """Return first 4 bytes of HASH160 of the public_key""" return ripemd160(sha256(public_key))[:4]