def test_read_opcode(): assert read_opcode(get_stream(get_bytes([b"\x08", b"1" * 8 ]))) == (b"\x08", b"1" * 8) assert read_opcode( get_stream(get_bytes([OP_PUSHDATA1, b"\x4c", b"1" * 76]))) == (OP_PUSHDATA1, b"1" * 76) assert read_opcode(get_stream(get_bytes([OP_PUSHDATA2, pack('<H' ,256), b"1" * 256]))) == \ (OP_PUSHDATA2, b"1" * 256) p = b"1" * 65537 l = OP_PUSHDATA4 + pack('<L', 65537) + p assert read_opcode(get_stream(l)) == (OP_PUSHDATA4, p)
def test_op_push_data(): assert op_push_data("") == b"\x00" assert op_push_data(b"1234567890") == b'\n1234567890' assert op_push_data(b"1" * 75) == b'K' + b"1" * 75 assert op_push_data(b"1" * 76) != b'\x4c' + b"1" * 76 assert op_push_data(b"1" * 76) == get_bytes( [OP_PUSHDATA1, b"\x4c", b"1" * 76]) assert op_push_data(b"1" * 256) == get_bytes( [OP_PUSHDATA2, pack('<H', 256), b"1" * 256]) p = b"1" * 65537 l = op_push_data(p) == OP_PUSHDATA4 + pack('<L', 65537) + p assert l
def public_key_recovery(signature, messsage, rec_id, compressed=True, hex=True): signature = get_bytes(signature) messsage = get_bytes(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 parse_signature(sig): """ Check is valid signature encoded in DER format :param sig: signature in bytes or HEX encoded string. :return: boolean. """ # Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash] # * total-length: 1-byte length descriptor of everything that follows, # excluding the sighash byte. # * R-length: 1-byte length descriptor of the R value that follows. # * R: arbitrary-length big-endian encoded R value. It must use the shortest # possible encoding for a positive integers (which means no null bytes at # the start, except a single one when the next byte has its highest bit set). # * S-length: 1-byte length descriptor of the S value that follows. # * S: arbitrary-length big-endian encoded S value. The same rules apply. # * sighash: 1-byte value indicating what data is hashed (not part of the DER # signature) sig = get_bytes(sig) if not is_valid_signature_encoding(sig): raise ValueError("invalid signature") len_r = sig[3] r = sig[5:4 + len_r] s = sig[len_r + 6:-1] return r, s
def verify_signature(sig, pub_key, msg, encoding="hex"): """ Verify signature for message and given public key :param sig: signature in bytes or HEX encoded string. :param pub_key: public key in bytes or HEX encoded string. :param msg: message in bytes or HEX encoded string. :return: boolean. """ sig = get_bytes(sig) pub_key = get_bytes(pub_key) msg = get_bytes(msg, encoding=encoding) r = __secp256k1_ecdsa_verify__(sig, pub_key, msg) if r == 1: return True return False
def public_key_to_address(pubkey, testnet=False, p2sh_p2wpkh=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 pubkey: public key HEX or bytes string format. :param testnet: (optional) flag for testnet network, by default is False. :param p2sh_p2wpkh: (optional) flag for P2WPKH inside 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. """ pubkey = get_bytes(pubkey, encoding='hex') if p2sh_p2wpkh: if len(pubkey) != 33: raise ValueError("public key invalid") h = hash160(b'\x00\x14%s' % hash160(pubkey)) witness_version = None else: if witness_version is not None: if len(pubkey) != 33: raise ValueError("public key invalid") h = hash160(pubkey) return hash_to_address(h, testnet=testnet, script_hash=p2sh_p2wpkh, witness_version=witness_version)
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. """ address_hash = get_bytes(address_hash) if isinstance(script_type, str): try: script_type = SCRIPT_TYPES[script_type] except: script_type = "" 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 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. """ msg = get_bytes(msg) 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 create_master_xprivate_key(seed, testnet=False, base58=None, hex=None): """ Create extended private key from seed :param str,bytes key: seed HEX or bytes string. :param boolean base58: (optional) return result as base58 encoded string, by default True. :param boolean hex: (optional) return result as HEX encoded string, by default False. In case True base58 flag value will be ignored. :return: extended private key in base58, HEX or bytes string format. """ seed = get_bytes(seed) i = hmac_sha512(b"Bitcoin seed", seed) m, c = i[:32], i[32:] m_int = int.from_bytes(m, byteorder="big") if m_int <= 0 or m_int > ECDSA_SEC256K1_ORDER: # pragma: no cover return None prefix = TESTNET_XPRIVATE_KEY_PREFIX if testnet else MAINNET_XPRIVATE_KEY_PREFIX key = b''.join( [prefix, b'\x00\x00\x00\x00\x00\x00\x00\x00\x00', c, b'\x00', m]) if base58 is None and hex is None: base58 = True if base58: key = b"".join([key, double_sha256(key)[:4]]) return encode_base58(key) else: if hex is None: hex = False return key if not hex else key.hex()
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. """ s_hex = isinstance(script, str) script = get_bytes(script) sub_script = get_bytes(sub_script) stream = get_stream(script) if not sub_script: return script.hex() if s_hex else script r = b'' offset = 0 skip_until = 0 o, d = read_opcode(stream) while o: if script[offset:offset + len(sub_script)] == sub_script: skip_until = offset + len(sub_script) r += d[len(sub_script) - 1:] if d is not None else b"" if offset >= skip_until: r += o if d is not None: if o == OP_PUSHDATA1: r += bytes([len(d)]) elif o == OP_PUSHDATA2: r += pack('<H', len(d)) elif o == OP_PUSHDATA4: r += pack('<L', len(d)) r += d offset += 1 if d is not None: offset += len(d) if o == OP_PUSHDATA1: offset += 1 elif o == OP_PUSHDATA2: offset += 2 elif o == OP_PUSHDATA4: offset += 4 o, d = read_opcode(stream) return r.hex() if s_hex else r
def encode_base58(b, checksum=False): """Encode bytes to a base58-encoded string""" # Convert big-endian bytes to integer if not b: return '' b = get_bytes(b) if checksum: return __encode_base58__(b"%s%s" % (b, double_sha256(b)[:4])) return __encode_base58__(b)
def op_push_data(data): data = get_bytes(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, pack('<H', len(data)), data]) else: return b''.join([OP_PUSHDATA4, pack('<L', len(data)), data])
def get_multisig_public_keys(script, hex=False): script = get_bytes(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.hex() if hex else d) return pub_keys
def murmurhash3(seed, h, encoding = None): """ Calculate murmurhash3 from byte string :param h: byte string or HEX encoded string :param seed: seed randomization vector integer :return: hash as integer """ h = get_bytes(h, encoding=encoding) return __murmurhash3__(seed, h)
def siphash(h, v_0=0, v_1=0, encoding = None): """ Calculate siphash from byte string :param h: byte string or HEX encoded string :param v_0: randomization vector 0 64 bit integer :param v_1: randomization vector 1 64 bit integer :return: hash as 64 bit integer """ h = get_bytes(h, encoding=encoding) return __siphash__(v_0, v_1, h)
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. """ address_hash = get_bytes(address_hash, encoding='hex') 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 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. """ script = get_bytes(script) return sha256(script, hex) if witness else hash160(script, hex)
def entropy_to_mnemonic(entropy, language='english', word_list_dir=None, word_list=None, data=None): """ Convert entropy to mnemonic words string. :param str,bytes entropy: random entropy HEX encoded or bytes string. :param str language: (optional) uses word list language (chinese_simplified, chinese_traditional, english, french, italian, japanese, korean, spanish), by default is english. :param str word_list_dir: (optional) path to a directory containing a list of words, by default None (use BIP39 standard list) :param list word_list: (optional) already loaded word list, by default None :return: mnemonic words string. """ entropy = get_bytes(entropy) if len(entropy) not in [16, 20, 24, 28, 32]: raise ValueError( 'entropy length should be one of the following: [16, 20, 24, 28, 32]' ) if word_list is None: word_list = load_word_list(language, word_list_dir) elif not isinstance(word_list, list) or len(word_list) != 2048: raise TypeError("invalid word list type") i = int.from_bytes(entropy, byteorder="big") # append checksum b = math.ceil(len(entropy) * 8 / 32) if data is not None: if data > (2**b - 1): raise ValueError('embedded data bits too long') i = (i << b) | data else: i = (i << b) | (sha256(entropy)[0] >> (8 - b)) return " ".join([ word_list[i.__rshift__(((d - 1) * 11)) & 2047] for d in range(int((len(entropy) * 8 + 8) // 11), 0, -1) ])
def hmac_sha512(key, data, hex=False, encoding=None): key = get_bytes(key, encoding=encoding) data = get_bytes(data, encoding=encoding) if hex: return hmac.new(key, data, hashlib_sha512).hexdigest() return hmac.new(key, data, hashlib_sha512).digest()
def double_sha256(h, hex=False, encoding=None): h = get_bytes(h, encoding=encoding) return __double_sha256__(h).hex() if hex else __double_sha256__(h)
def public_key_to_p2sh_p2wpkh_script(pubkey, hex=False): pubkey = get_bytes(pubkey) if len(pubkey) != 33: raise ValueError("public key len invalid") r = b'\x00\x14%s' % hash160(pubkey) return r.hex() if hex else r
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. """ script = get_bytes(script) 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 ripemd160(h, hex=False, encoding=None): h = get_bytes(h, encoding=encoding) a = hashlib_new('ripemd160') a.update(h) return a.hexdigest() if hex else a.digest()
def test_delete_from_script(): assert delete_from_script([OP_1, OP_2], []) == get_bytes([OP_1, OP_2]) assert delete_from_script([OP_1, OP_2, OP_3], [OP_2]) == get_bytes([OP_1, OP_3]) assert delete_from_script([OP_3, OP_1, OP_3, OP_3, OP_4, OP_3], [OP_3]) == get_bytes([OP_1, OP_4]) assert delete_from_script([b"\x03", b"\x02\xff\x03"], [b"\x03", b"\x02\xff\x03"]) == get_bytes([]) assert delete_from_script( [b"\x03", b"\x02\xff\x03", b"\x03", b"\x02\xff\x03"], [b"\x03", b"\x02\xff\x03"]) == get_bytes([]) assert delete_from_script( [b"\x03", b"\x02\xff\x03", b"\x03", b"\x02\xff\x03"], [b"\x02"]) == get_bytes( [b"\x03", b"\x02\xff\x03", b"\x03", b"\x02\xff\x03"]) assert delete_from_script( [b"\x03", b"\x02\xff\x03", b"\x03", b"\x02\xff\x03"], [b"\xff"]) == get_bytes( [b"\x03", b"\x02\xff\x03", b"\x03", b"\x02\xff\x03"]) assert delete_from_script( [b"\x03", b"\x02\xff\x03", b"\x03", b"\x02\xff\x03"], [b"\x03"]) == get_bytes([b"\x02\xff\x03", b"\x02\xff\x03"]) assert delete_from_script([b"\x02", b"\xfe\xed", OP_1, OP_VERIFY], [b"\xfe\xed", OP_1]) == \ get_bytes([b"\x02", b"\xfe\xed", OP_1, OP_VERIFY]) assert delete_from_script([b"\x02", b"\xfe\xed", OP_1, OP_VERIFY], [b"\x02", b"\xfe\xed", OP_1]) == get_bytes( [OP_VERIFY]) s = "0302ff030302ff03" d = "0302ff03" e = "" assert delete_from_script(s, d) == e s = "0302ff030302ff03" d = "02" assert delete_from_script(s, d) == s s = "0302ff030302ff03" d = "ff" assert delete_from_script(s, d) == s s = "0302ff030302ff03" d = "03" e = "02ff0302ff03" assert delete_from_script(s, d) == e s = "02feed5169" d = "feed51" e = s assert delete_from_script(s, d) == e s = "02feed5169" d = "02feed51" e = "69" assert delete_from_script(s, d) == e # s = "516902feed5169" d = "feed51" e = s assert delete_from_script(s, d) == e s = "516902feed5169" d = "02feed51" e = "516969" assert delete_from_script(s, d) == e s = BYTE_OPCODE["OP_0"] + BYTE_OPCODE["OP_0"] + BYTE_OPCODE["OP_1"] s += BYTE_OPCODE["OP_1"] d = BYTE_OPCODE["OP_0"] + BYTE_OPCODE["OP_1"] e = d assert delete_from_script(s, d) == e s = BYTE_OPCODE["OP_0"] + BYTE_OPCODE["OP_0"] + BYTE_OPCODE["OP_1"] s += BYTE_OPCODE["OP_0"] + BYTE_OPCODE["OP_1"] + BYTE_OPCODE["OP_1"] d = BYTE_OPCODE["OP_0"] + BYTE_OPCODE["OP_1"] e = d assert delete_from_script(s, d) == e s = "0003feed" d = "03feed" e = "00" assert delete_from_script(s, d) == e s = "0003feed" d = "00" e = "03feed" assert delete_from_script(s, d) == e assert delete_from_script([OP_PUSHDATA1, pack('<B', 20),b"12345678901234567890"], "00").hex() == \ "4c143132333435363738393031323334353637383930" assert delete_from_script([OP_PUSHDATA2, pack('<H', 20),b"12345678901234567890"], "00").hex() == \ "4d14003132333435363738393031323334353637383930" assert delete_from_script([OP_PUSHDATA4, pack('<L', 20),b"12345678901234567890"], "00").hex() == \ "4e140000003132333435363738393031323334353637383930"
def is_valid_signature_encoding(sig): """ Check is valid signature encoded in DER format :param sig: signature in bytes or HEX encoded string. :return: boolean. """ # Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash] # * total-length: 1-byte length descriptor of everything that follows, # excluding the sighash byte. # * R-length: 1-byte length descriptor of the R value that follows. # * R: arbitrary-length big-endian encoded R value. It must use the shortest # possible encoding for a positive integers (which means no null bytes at # the start, except a single one when the next byte has its highest bit set). # * S-length: 1-byte length descriptor of the S value that follows. # * S: arbitrary-length big-endian encoded S value. The same rules apply. # * sighash: 1-byte value indicating what data is hashed (not part of the DER # signature) sig = get_bytes(sig) length = len(sig) # Minimum and maximum size constraints. if (length < 9) or (length > 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] # Zero-length integers are not allowed for R. if len_r == 0: return False # 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. # Zero-length integers are not allowed for S. if len_s == 0: return False if (len_r + len_s + 7) != length: return False # Check whether the R element is an integer. if sig[2] != 0x02: 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 # 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
def public_key_to_pubkey_script(key, hex=True): key = get_bytes(key) s = b"%s%s%s" % (bytes([len(key)]), key, OP_CHECKSIG) return s.hex() if hex else s
def hash160(h, hex=False, encoding=None): h = get_bytes(h, encoding=encoding) return ripemd160(sha256(h), True) if hex else ripemd160(sha256(h))
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"" } script = get_bytes(script) 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: # OP_1 -> OP_16 if script[s] >= 81 and script[s] <= 96: if not n: n = script[s] - 80 elif not m: 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 + 1:s + 3])[0] except: break elif script[s] == OPCODE["OP_PUSHDATA4"]: try: s += 4 + unpack('<L', script[s + 1:s + 5])[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 }