from struct import unpack from secp256k1 import ffi, lib secp256k1_ecdsa_signature_parse_der = lib.secp256k1_ecdsa_signature_parse_der secp256k1_ec_pubkey_parse = lib.secp256k1_ec_pubkey_parse secp256k1_ecdsa_verify = lib.secp256k1_ecdsa_verify secp256k1_ecdsa_sign = lib.secp256k1_ecdsa_sign secp256k1_ecdsa_signature_serialize_der = lib.secp256k1_ecdsa_signature_serialize_der secp256k1_ecdsa_signature_serialize_compact = lib.secp256k1_ecdsa_signature_serialize_compact secp256k1_ecdsa_recoverable_signature_parse_compact = lib.secp256k1_ecdsa_recoverable_signature_parse_compact secp256k1_ecdsa_recover = lib.secp256k1_ecdsa_recover secp256k1_ec_pubkey_serialize = lib.secp256k1_ec_pubkey_serialize from pybtc.opcodes import * from pybtc.constants import * from pybtc.functions.tools import bytes_from_hex, int_to_bytes, get_stream from pybtc.functions.hash import hash160, sha256 from pybtc.functions.address import hash_to_address from pybtc.functions.key import is_wif_valid, wif_to_private_key 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 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: pass assert isinstance(script, bytes) 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 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, "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(' 0: if script[s] < 0x4c and script[s]: if asm: 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"]: ld = script[s + 1] if asm: append(script[s + 1:s + 1 + ld].hex()) else: append(RAW_OPCODE[script[s]]) append('[%s]' % ld) s += 1 + script[s + 1] + 1 elif script[s] == OPCODE["OP_PUSHDATA2"]: ld = unpack(' 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('= 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 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 op_push_data(data): if len(data) <= 0x4b: return b''.join([bytes([len(data)]), data]) elif len(data) <= 0xff: return b''.join([OP_PUSHDATA1, bytes([len(data)]), data]) elif len(data) <= 0xffff: return b''.join([OP_PUSHDATA2, int_to_bytes(len(data), byteorder="little"), data]) else: return b''.join([OP_PUSHDATA4, int_to_bytes(len(data), byteorder="little"), data]) def get_multisig_public_keys(script): pub_keys = [] s = get_stream(script) o, d = read_opcode(s) while o: o, d = read_opcode(s) if d: pub_keys.append(d) return pub_keys def read_opcode(stream): read = stream.read b = read(1) if not b: return None, None if b[0] <= 0x4b: return b, read(b[0]) elif b[0] == OP_PUSHDATA1: return b, read(read(1)[0]) elif b[0] == OP_PUSHDATA2: return b, read(unpack(" 73): return False # A signature is of type 0x30 (compound). if sig[0] != 0x30: return False # Make sure the length covers the entire signature. if sig[1] != (length - 3): return False # Extract the length of the R element. len_r = sig[3] # Make sure the length of the S element is still inside the signature. if (5 + len_r) >= length: return False # Extract the length of the S element. len_s = sig[5 + len_r] # Verify that the length of the signature matches the sum of the length # of the elements. if (len_r + len_s + 7) != length: return False # Check whether the R element is an integer. if sig[2] != 0x02: return False # Zero-length integers are not allowed for R. if len_r == 0: return False # Negative numbers are not allowed for R. if sig[4] & 0x80: return False # Null bytes at the start of R are not allowed, unless R would # otherwise be interpreted as a negative number. if (len_r > 1) and (sig[4] == 0x00) and (not sig[5] & 0x80): return False # Check whether the S element is an integer. if sig[len_r + 4] != 0x02: return False # Zero-length integers are not allowed for S. if len_s == 0: return False # Negative numbers are not allowed for S. if sig[len_r + 6] & 0x80: return False # Null bytes at the start of S are not allowed, unless S would otherwise be # interpreted as a negative number. if (len_s > 1) and (sig[len_r + 6] == 0x00) and (not sig[len_r + 7] & 0x80): return False return True