def sign_message(msg, private_key, hex=True): """ Sign message :param msg: message to sign bytes or HEX encoded string. :param private_key: private key (bytes, hex encoded string or WIF format) :param hex: (optional) If set to True return key in HEX format, by default is True. :return: DER encoded signature in bytes or HEX encoded string. """ if isinstance(msg, bytearray): msg = bytes(msg) if isinstance(msg, str): try: msg = bytes_from_hex(msg) except: pass if not isinstance(msg, bytes): raise TypeError("message must be a bytes or hex encoded string") if isinstance(private_key, bytearray): private_key = bytes(private_key) if isinstance(private_key, str): try: private_key = bytes_from_hex(private_key) except: if is_wif_valid(private_key): private_key = wif_to_private_key(private_key, hex=False) if not isinstance(private_key, bytes): raise TypeError("private key must be a bytes, hex encoded string or in WIF format") signature = __secp256k1_ecdsa_sign__(msg, private_key) return signature.hex() if hex else signature
def public_key_recovery(signature, messsage, rec_id, compressed=True, hex=True): if isinstance(signature, str): signature = bytes_from_hex(signature) if isinstance(messsage, str): messsage = bytes_from_hex(messsage) pub = __secp256k1_ecdsa_recover__(signature, messsage, rec_id, compressed) if isinstance(pub, int): if pub == 0: return None else: raise RuntimeError("signature recovery error %s" % pub) return pub.hex() if hex else pub
def merkle_root_from_branches(merkle_branches, coinbase_hash, hex=True): """ Calculate merkle root from merkle branches and coinbase transacton hash :param merkle_branches: list merkle branches in bytes or HEX encoded string. :param coinbase_hash: list coinbase transaction hash in bytes or HEX encoded string. :param hex: (optional) If set to True return result in HEX format, by default is True. :return: merkle root in bytes or HEX encoded string corresponding hex flag. """ merkle_root = coinbase_hash if not isinstance(coinbase_hash, str) else bytes_from_hex(coinbase_hash) for h in merkle_branches: if type(h) == str: h = bytes_from_hex(h) merkle_root = double_sha256(merkle_root + h) return bytes_from_hex(merkle_root) if not hex else merkle_root
def hash_to_script(address_hash, script_type, hex=False): """ Get public key script from hash. :param address: h in base58 or bech32 format. :param hex: (optional) If set to True return key in HEX format, by default is True. :return: public key script in HEX or bytes string. """ if isinstance(address_hash, str): address_hash = bytes_from_hex(address_hash) if not isinstance(address_hash, bytes): raise TypeError("address hash must be HEX encoded string or bytes") if script_type == 1: s = [OP_HASH160, b'\x14', address_hash, OP_EQUAL] elif script_type == 0: s = [ OP_DUP, OP_HASH160, b'\x14', address_hash, OP_EQUALVERIFY, OP_CHECKSIG ] elif script_type in (5, 6): s = [OP_0, bytes([len(address_hash)]), address_hash] else: raise ValueError("address type invalid") s = b''.join(s) return s.hex() if hex else s
def __init__(self, key, compressed=True, testnet=False): if isinstance(key, str): try: key = bytes_from_hex(key) except: key = PrivateKey(key) if isinstance(key, bytes): if len(key) == 32: key = PrivateKey(key, compressed=compressed, testnet=testnet) elif is_public_key_valid(key): public_key = key self.testnet = testnet self.compressed = True if len(key) == 33 else False else: raise TypeError("key invalid") if isinstance(key, PrivateKey): #: flag for testnet network private key (boolean) self.testnet = key.testnet #: flag for compressed type of corresponding public key (boolean) self.compressed = key.compressed public_key = private_to_public_key(key.key, compressed=key.compressed, hex=False) #: public key in bytes (bytes) self.key = public_key #: public key in HEX (string) self.hex = self.key.hex()
def hash_to_address(address_hash, testnet=False, script_hash=False, witness_version=0): """ Get address from public key/script hash. In case PUBKEY, P2PKH, P2PKH public key/script hash is SHA256+RIPEMD160, P2WSH script hash is SHA256. :param address_hash: public key hash or script hash in HEX or bytes string format. :param testnet: (optional) flag for testnet network, by default is False. :param script_hash: (optional) flag for script hash (P2SH address), by default is False. :param witness_version: (optional) witness program version, by default is 0, for legacy address format use None. :return: address in base58 or bech32 format. """ if isinstance(address_hash, str): address_hash = bytes_from_hex(address_hash) if not isinstance(address_hash, bytes): raise TypeError("address hash must be HEX encoded string or bytes") if not script_hash: if witness_version is None: if len(address_hash) != 20: raise ValueError("address hash length incorrect") if testnet: prefix = TESTNET_ADDRESS_BYTE_PREFIX else: prefix = MAINNET_ADDRESS_BYTE_PREFIX address_hash = b"%s%s" % (prefix, address_hash) address_hash += double_sha256(address_hash)[:4] return encode_base58(address_hash) else: if len(address_hash) not in (20, 32): raise ValueError("address hash length incorrect") if witness_version is None: if testnet: prefix = TESTNET_SCRIPT_ADDRESS_BYTE_PREFIX else: prefix = MAINNET_SCRIPT_ADDRESS_BYTE_PREFIX address_hash = b"%s%s" % (prefix, address_hash) address_hash += double_sha256(address_hash)[:4] return encode_base58(address_hash) if testnet: prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX hrp = TESTNET_SEGWIT_ADDRESS_PREFIX else: prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX hrp = MAINNET_SEGWIT_ADDRESS_PREFIX address_hash = b"%s%s" % (witness_version.to_bytes( 1, "big"), rebase_8_to_5(address_hash)) checksum = bech32_polymod(b"%s%s%s" % (prefix, address_hash, b"\x00" * 6)) checksum = rebase_8_to_5(checksum.to_bytes(5, "big"))[2:] return "%s1%s" % (hrp, rebase_5_to_32(address_hash + checksum).decode())
def bits_to_target(bits): """ Calculate target from bits :param bits: HEX string, bytes string or integer representation of bits. :return: integer. """ if type(bits) == str: bits = bytes_from_hex(bits) if type(bits) == bytes: return int_from_bytes(bits[1:], 'big') * (2 ** (8 * (bits[0] - 3))) else: shift = bits >> 24 target = (bits & 0xffffff) * (1 << (8 * (shift - 3))) return target
def script_to_hash(script, witness=False, hex=True): """ Encode script to hash HASH160 or SHA256 in dependency of the witness. :param script: script in bytes or HEX encoded string. :param witness: (optional) If set to True return SHA256 hash for P2WSH, by default is False. :param hex: (optional) If set to True return key in HEX format, by default is True. :param sub_script: sub_script which is necessary to remove from target script in bytes or HEX encoded string. :return: script in bytes or HEX encoded string corresponding to the format of target script. """ if isinstance(script, str): s = bytes_from_hex(script) if witness: return sha256(script, hex) else: return hash160(script, hex)
def __init__(self, key=None, compressed=True, testnet=False): if key is None: #: flag for compressed type of corresponding public key (boolean) self.compressed = compressed #: flag for testnet network private key (boolean) self.testnet = testnet #: private key in bytes (bytes) self.key = create_private_key(wif=False) #: private key in HEX (string) self.hex = self.key.hex() #: private key in WIF format (string) self.wif = private_key_to_wif(self.key, compressed, testnet) else: if isinstance(key, str): try: key = bytes_from_hex(key) except: pass if isinstance(key, bytes): if len(key) != 32: raise TypeError("private key invalid length") self.key = key self.compressed = compressed self.testnet = testnet self.hex = self.key.hex() self.wif = private_key_to_wif(self.key, compressed, testnet) return if not isinstance(key, str): raise TypeError("private key invalid") self.key = wif_to_private_key(key, hex=False) self.hex = self.key.hex() if key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): self.compressed = False else: self.compressed = True self.testnet = True if key[0] in (TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_COMPRESSED_PREFIX) else False self.wif = key
def merkle_root_from_proof(merkle_proof, tx_id, index, return_hex=True, receive_hex=True): if isinstance(merkle_proof, str): merkle_proof = bytes_from_hex(merkle_proof) if isinstance(merkle_proof, bytes): merkle_proof = [merkle_proof[y - 32:y] for y in range(32, len(merkle_proof) + 32, 32)] if receive_hex: _merkle_proof = deque() _merkle_proof_append = _merkle_proof.append for h in merkle_proof: _merkle_proof_append(s2rh(h) if isinstance(h, str) else h) merkle_proof = _merkle_proof tx_id = s2rh(tx_id) if isinstance(tx_id, str) else tx_id root = tx_id for h in merkle_proof: root = double_sha256(b"".join((h, root) if index % 2 else (root, h))) index = index // 2 if return_hex: return rh2s(root) return root
def delete_from_script(script, sub_script): """ Decode OP_CODE or subscript from script. :param script: target script in bytes or HEX encoded string. :param sub_script: sub_script which is necessary to remove from target script in bytes or HEX encoded string. :return: script in bytes or HEX encoded string corresponding to the format of target script. """ if not sub_script: return script s_hex = False if isinstance(script, str): try: script = bytes_from_hex(script) s_hex = True except: pass if isinstance(sub_script, str): try: sub_script = bytes_from_hex(sub_script) except: pass if not isinstance(script, bytes): raise TypeError("script invalid") if not isinstance(sub_script, bytes): raise TypeError("sub_script invalid") l = len(script) ls = len(sub_script) s = 0 k = 0 stack = [] stack_append = stack.append result = [] result_append = result.append while l - s > 0: if script[s] < 0x4c and script[s]: stack_append(script[s] + 1) s += script[s] + 1 elif script[s] == OPCODE["OP_PUSHDATA1"]: stack_append(1 + script[s + 1]) s += 1 + script[s + 1] elif script[s] == OPCODE["OP_PUSHDATA2"]: stack_append(2 + unpack('<H', script[s: s + 2])[0]) s += 2 + unpack('<H', script[s: s + 2])[0] elif script[s] == OPCODE["OP_PUSHDATA4"]: stack_append(4 + unpack('<L', script[s: s + 4])[0]) s += 4 + unpack('<L', script[s: s + 4])[0] else: stack_append(1) s += 1 if s - k >= ls: if script[k:s][:ls] == sub_script: if s - k > ls: result_append(script[k + ls:s]) t = 0 while t != s - k: t += stack.pop(0) k = s else: t = stack.pop(0) result_append(script[k:k + t]) k += t if script[k:s][:ls] == sub_script: if s - k > ls: result_append(script[k + ls:s]) else: result_append(script[k:k + ls]) return b''.join(result) if not s_hex else b''.join(result).hex()
def parse_script(script, segwit=True): """ Parse script and return script type, script address and required signatures count. :param script: script in bytes string or HEX encoded string format. :param segwit: (optional) If set to True recognize P2WPKH and P2WSH sripts, by default set to True. :return: dictionary: - nType - numeric script type - type - script type - addressHash - address hash in case address recognized - script - script if no address recognized - reqSigs - required signatures count """ if not script: return {"nType": 7, "type": "NON_STANDARD", "reqSigs": 0, "script": b""} if isinstance(script, str): try: script = bytes_from_hex(script) except: raise ValueError("hex encoded string required") l = len(script) if segwit: if l == 22 and script[0] == 0: return {"nType": 5, "type": "P2WPKH", "reqSigs": 1, "addressHash": script[2:]} if l == 34 and script[0] == 0: return {"nType": 6, "type": "P2WSH", "reqSigs": None, "addressHash": script[2:]} if l == 25 and \ script[:2] == b"\x76\xa9" and \ script[-2:] == b"\x88\xac": return {"nType": 0, "type": "P2PKH", "reqSigs": 1, "addressHash": script[3:-2]} if l == 23 and \ script[0] == 169 and \ script[-1] == 135: return {"nType": 1, "type": "P2SH", "reqSigs": None, "addressHash": script[2:-1]} if l == 67 and script[-1] == 172: return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])} if l == 35 and script[-1] == 172: return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])} if script[0] == OPCODE["OP_RETURN"]: if l == 1: return {"nType": 3, "type": "NULL_DATA", "reqSigs": 0, "data": b""} elif script[1] < OPCODE["OP_PUSHDATA1"]: if script[1] == l - 2: return {"nType": 3, "type": "NULL_DATA", "reqSigs": 0, "data": script[2:]} elif script[1] == OPCODE["OP_PUSHDATA1"]: if l > 2: if script[2] == l - 3 and script[2] <= 80: return {"nType": 3, "type": "NULL_DATA", "reqSigs": 0, "data": script[3:]} return {"nType": 8, "type": "NULL_DATA_NON_STANDARD", "reqSigs": 0, "script": script} if script[0] >= 81 and script[0] <= 96: if script[-1] == 174: if script[-2] >= 81 and script[-2] <= 96: if script[-2] >= script[0]: c, s = 0, 1 while l - 2 - s > 0: if script[s] < 0x4c: s += script[s] c += 1 else: c = 0 break s += 1 if c == script[-2] - 80: return {"nType": 4, "type": "MULTISIG", "reqSigs": script[0] - 80, "pubKeys": c, "script": script} s, m, n, last, req_sigs = 0, 0, 0, 0, 0 while l - s > 0: if script[s] >= 81 and script[s] <= 96: if not n: n = script[s] - 80 else: if m == 0: n, m = script[s] - 80, 0 elif n > m: n, m = script[s] - 80, 0 elif m == script[s] - 80: last = 0 if last else 2 elif script[s] < 0x4c: s += script[s] m += 1 if m > 16: n, m = 0, 0 elif script[s] == OPCODE["OP_PUSHDATA1"]: try: s += 1 + script[s + 1] except: break elif script[s] == OPCODE["OP_PUSHDATA2"]: try: s += 2 + unpack('<H', script[s: s + 2])[0] except: break elif script[s] == OPCODE["OP_PUSHDATA4"]: try: s += 4 + unpack('<L', script[s: s + 4])[0] except: break else: if script[s] == OPCODE["OP_CHECKSIG"]: req_sigs += 1 elif script[s] == OPCODE["OP_CHECKSIGVERIFY"]: req_sigs += 1 elif script[s] in (OPCODE["OP_CHECKMULTISIG"], OPCODE["OP_CHECKMULTISIGVERIFY"]): if last: req_sigs += n else: req_sigs += 20 n, m = 0, 0 if last: last -= 1 s += 1 return {"nType": 7, "type": "NON_STANDARD", "reqSigs": req_sigs, "script": script}
def decode_script(script, asm=False): """ Decode script to ASM format or to human readable OPCODES string. :param script: script in bytes string or HEX encoded string format. :param asm: (optional) If set to True decode to ASM format, by default set to False. :return: script in ASM format string or OPCODES string. """ if isinstance(script, str): try: script = bytes_from_hex(script) except: pass if not isinstance(script, bytes): raise TypeError("script invalid") l = len(script) s = 0 result = [] append = result.append try: while l - s > 0: if script[s] < 0x4c and script[s]: if asm: append("OP_PUSHBYTES[%s]" % script[s] ) append(script[s + 1:s + 1 + script[s]].hex()) else: append('[%s]' % script[s]) s += script[s] + 1 continue if script[s] == OPCODE["OP_PUSHDATA1"]: if asm: ld = script[s + 1] append("OP_PUSHDATA1[%s]" % ld) append(script[s + 2:s + 2 + ld].hex()) else: append(RAW_OPCODE[script[s]]) ld = script[s + 1] append('[%s]' % ld) s += 1 + script[s + 1] + 1 elif script[s] == OPCODE["OP_PUSHDATA2"]: if asm: ld = unpack('<H', script[s + 1: s + 3])[0] append("OP_PUSHDATA2[%s]" % ld) append(script[s + 3:s + 3 + ld].hex()) else: ld = unpack('<H', script[s + 1: s + 3])[0] append(RAW_OPCODE[script[s]]) append('[%s]' % ld) s += 2 + 1 + ld elif script[s] == OPCODE["OP_PUSHDATA4"]: if asm: ld = unpack('<L', script[s + 1: s + 5])[0] append("OP_PUSHDATA4[%s]" % ld) append(script[s + 5:s + 5 + ld].hex()) else: ld = unpack('<L', script[s + 1: s + 5])[0] append(RAW_OPCODE[script[s]]) append('[%s]' % ld) s += 5 + 1 + ld else: append(RAW_OPCODE[script[s]]) s += 1 except: append("[SCRIPT_DECODE_FAILED]") return ' '.join(result)
def public_key_to_pubkey_script(key, hex=True): if isinstance(key, str): key = bytes_from_hex(key) s = b"%s%s%s" % (bytes([len(key)]), key, OP_CHECKSIG) return s.hex() if hex else s