def test_private_to_public_key(): priv = "ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944a4" pu = "04b635dbdc16dbdf4bb9cf5b55e7d03e514fb04dcef34208155c7d3ec88e9045f4" \ "c8cbe28702911260f2a1da099a338bed4ee98f66bb8dba8031a76ab537ff6663" pk = "03b635dbdc16dbdf4bb9cf5b55e7d03e514fb04dcef34208155c7d3ec88e9045f4" assert private_to_public_key(priv) == pk assert private_to_public_key(bytes_from_hex(priv)) == pk assert private_to_public_key(bytearray(bytes_from_hex(priv))) == pk assert private_to_public_key(priv) == pk assert private_to_public_key(priv, hex=True) == pk assert private_to_public_key(priv, hex=False).hex() == pk assert private_to_public_key(priv, compressed=False) == pu assert private_to_public_key( "L49obCXV7fGz2YRzLCSJgeZBYmGeBbKPT7xiehUeYX2S4URkPFZX", pk) assert private_to_public_key( "5KPPLXhtga99qqMceRo4Z6LXV3Kx6a9hRx3ez2U7EwP5KZfy2Wf", pu) assert private_to_public_key( "93A1vGXSGoDHotruGmgyRgtV8hgfFjgtmtuc4epcag886W9d44L", pu) with pytest.raises(ValueError): assert private_to_public_key( "ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944a411", pu) with pytest.raises(ValueError): private_to_public_key(3738) with pytest.raises(Exception): private_to_public_key( "L49obCXV7fGz2YRzLCSJgeZBYmGeBbKPT7xiehUeYX2S4URkPFZQ")
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 serialize(self, segwit=True, hex=True): """ Get serialized transaction :param bool segwit: (optional) flag for segwit representation of serialized transaction, by default True. :param bool hex: (optional) if set to True return HEX encoded string, by default True. :return str,bytes: serialized transaction in HEX or bytes. """ chunks = [] append = chunks.append append(pack('<L', self["version"])) if segwit and self["segwit"]: append(b"\x00\x01") append(int_to_var_int(len(self["vIn"]))) for i in self["vIn"]: if isinstance(self["vIn"][i]['txId'], bytes): append(self["vIn"][i]['txId']) else: append(s2rh(self["vIn"][i]['txId'])) append(pack('<L', self["vIn"][i]['vOut'])) if isinstance(self["vIn"][i]['scriptSig'], bytes): append(int_to_var_int(len(self["vIn"][i]['scriptSig']))) append(self["vIn"][i]['scriptSig']) else: append(int_to_var_int(int(len(self["vIn"][i]['scriptSig']) / 2))) append(bytes_from_hex(self["vIn"][i]['scriptSig'])) append(pack('<L', self["vIn"][i]['sequence'])) append(int_to_var_int(len(self["vOut"]))) for i in self["vOut"]: append(pack('<Q', self["vOut"][i]['value'])) if isinstance(self["vOut"][i]['scriptPubKey'], bytes): append(int_to_var_int(len(self["vOut"][i]['scriptPubKey']))) append(self["vOut"][i]['scriptPubKey']) else: append(int_to_var_int(int(len(self["vOut"][i]['scriptPubKey']) / 2))) append(bytes_from_hex(self["vOut"][i]['scriptPubKey'])) if segwit and self["segwit"]: for i in self["vIn"]: append(int_to_var_int(len(self["vIn"][i]['txInWitness']))) for w in self["vIn"][i]['txInWitness']: if isinstance(w, bytes): append(int_to_var_int(len(w))) append(w) else: append(int_to_var_int(int(len(w) / 2))) append(bytes_from_hex(w)) append(pack('<L', self['lockTime'])) tx = b''.join(chunks) return tx if not hex else tx.hex()
def merkleroot_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 merkle_root if not hex else merkle_root.hex()
def __init__(self, key, compressed=True, testnet=False): if isinstance(key, str): try: key = bytes_from_hex(key) except: if is_wif_valid(key): 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_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 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 get_stream(stream): if type(stream) != BytesIO: if type(stream) == str: stream = bytes_from_hex(stream) if type(stream) == bytes: stream = BytesIO(stream) else: raise TypeError return stream
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") raw_sig = ffi.new('secp256k1_ecdsa_signature *') signed = secp256k1_ecdsa_sign(ECDSA_CONTEXT_SIGN, raw_sig, msg, private_key, ffi.NULL, ffi.NULL) if not signed: raise RuntimeError("secp256k1 error") len_sig = 74 output = ffi.new('unsigned char[%d]' % len_sig) outputlen = ffi.new('size_t *', len_sig) res = secp256k1_ecdsa_signature_serialize_der(ECDSA_CONTEXT_SIGN, output, outputlen, raw_sig) if not res: raise RuntimeError("secp256k1 error") signature = bytes(ffi.buffer(output, outputlen[0])) return signature.hex() if hex else signature
def test_is_public_key_valid(): pu = "04b635dbdc16dbdf4bb9cf5b55e7d03e514fb04dcef34208155c7d3ec88e9045f4" + \ "c8cbe28702911260f2a1da099a338bed4ee98f66bb8dba8031a76ab537ff6663" pk = "03b635dbdc16dbdf4bb9cf5b55e7d03e514fb04dcef34208155c7d3ec88e9045f4" assert is_public_key_valid(pu) == True assert is_public_key_valid(pk) == True assert is_public_key_valid(bytes_from_hex(pk)) == True assert is_public_key_valid(bytes_from_hex(pu)) == True pu = "63qdbdc16dbdf4bb9cf45b55e7d03e514fb04dcef34208155c7d3ec88e9045f4c8c" + \ "be28702911260f2a1da099a338bed4ee98f66bb8dba8031a76ab537ff6663" pk = "02b635dbdc16dbdf455bb9cf5b55e7d03e514fb04dcef34208155c7d3ec88e9045f4" assert is_public_key_valid(pu) == False assert is_public_key_valid(pk) == False assert is_public_key_valid("8989") == False pu = "04b635dbdc16dbdf455bb9cf5b55e7d03e514fb04dcef34208155c7d3ec88e902245f4" assert is_public_key_valid(pu) == False
def test_hash_to_address(): pc = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" h = hash160(pc) s = bytes([len(bytes_from_hex(pc)) ]) + bytes_from_hex(pc) + BYTE_OPCODE["OP_CHECKSIG"] assert hash_to_address(h) == "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" assert hash_to_address( h, testnet=True) == "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" h = script_to_hash(s, witness=True, hex=hex) assert hash_to_address( h) == "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3" assert hash_to_address( h, testnet=True ) == "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7" pk = "03b635dbdc16dbdf4bb9cf5b55e7d03e514fb04dcef34208155c7d3ec88e9045f4" h = hash160(pk) assert hash_to_address( h, witness_version=None) == "1Fs2Xqrk4P2XADaJeZWykaGXJ4HEb6RyT1" assert hash_to_address( h, witness_version=None, testnet=True) == "mvNyptwisQTmwL3vN8VMaVUrA3swVCX83c" # p2wpkh inside p2sh p = "L32a8Mo1LgvjrVDbzcc3NkuUfkpoLsf2Y2oEWkV4t1KpQdFzuyff" pk = private_to_public_key(p) script = b'\x00\x14' + hash160(pk) script_hash = hash160(script) assert hash_to_address( script_hash, script_hash=1, witness_version=None) == "33am12q3Bncnn3BfvLYHczyv23Sq2Wbwjw" assert hash_to_address(script_hash, script_hash=1, witness_version=None, testnet=1) == "2Mu8y4mm4oF88yppDbUAAEwyBEPezrx7CLh" with pytest.raises(ValueError): hash_to_address(29023) with pytest.raises(ValueError): hash_to_address(h + b"33", witness_version=None) with pytest.raises(ValueError): hash_to_address(h + b"33", witness_version=0)
def add_output(self, amount, address=None, script_pub_key=None): if address is None and script_pub_key is None: raise Exception("unable to add output, address or script required") if type(amount) != int: raise TypeError("unable to add output, amount type error") if amount < 0 or amount > MAX_AMOUNT: raise Exception("unable to add output, amount value error") if script_pub_key: if isinstance(script_pub_key, str): script_pub_key = bytes_from_hex(script_pub_key) if not isinstance(script_pub_key, bytes): raise TypeError("unable to add output, script_pub_key type error") else: if type(address) == Address: address = address.address script_pub_key = address_to_script(address) k = len(self["vOut"]) self["vOut"][k] = dict() self["vOut"][k]["value"] = amount segwit = True if "segwit" in self else False s = parse_script(script_pub_key, segwit) self["vOut"][k]["nType"] = s["nType"] self["vOut"][k]["type"] = s["type"] if self["format"] == "raw": self["vOut"][k]["scriptPubKey"] = script_pub_key if self["data"] is None: if s["nType"] == 3: self["data"] = s["data"] if s["nType"] not in (3, 4, 7, 8): self["vOut"][k]["addressHash"] = s["addressHash"] self["vOut"][k]["reqSigs"] = s["reqSigs"] else: self["vOut"][k]["scriptPubKey"] = script_pub_key.hex() if self["data"] is None: if s["nType"] == 3: self["data"] = s["data"].hex() if s["nType"] not in (3, 4, 7, 8): self["vOut"][k]["addressHash"] = s["addressHash"].hex() self["vOut"][k]["reqSigs"] = s["reqSigs"] self["vOut"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key) self["vOut"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1) sh = True if self["vOut"][k]["nType"] in (1, 5) else False witness_version = None if self["vOut"][k]["nType"] < 5 else 0 if "addressHash" in self["vOut"][k]: self["vOut"][k]["address"] = hash_to_address(self["vOut"][k]["addressHash"], self["testnet"], sh, witness_version) if self.auto_commit: self.commit() return self
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) raw_sig = ffi.new('secp256k1_ecdsa_signature *') r = secp256k1_ecdsa_signature_parse_der(ECDSA_CONTEXT_SIGN, raw_sig, signature, len(signature)) if not r: raise RuntimeError("secp256k1 error") compact_sig = ffi.new('unsigned char[%d]' % 64) r = secp256k1_ecdsa_signature_serialize_compact(ECDSA_CONTEXT_VERIFY, compact_sig, raw_sig) if not r: raise RuntimeError("secp256k1 error") recover_sig = ffi.new('secp256k1_ecdsa_recoverable_signature *') t = secp256k1_ecdsa_recoverable_signature_parse_compact( ECDSA_CONTEXT_ALL, recover_sig, compact_sig, rec_id) if not r: raise RuntimeError("secp256k1 error") pubkey_ptr = ffi.new('secp256k1_pubkey *') t = secp256k1_ecdsa_recover( ECDSA_CONTEXT_ALL, pubkey_ptr, recover_sig, messsage) len_key = 33 if compressed else 65 pubkey = ffi.new('char [%d]' % len_key) outlen = ffi.new('size_t *', len_key) compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED if bytes(ffi.buffer(pubkey_ptr.data, 64)) == b"\x00" * 64: return None r = secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag) if not r: raise RuntimeError("secp256k1 error") pub = bytes(ffi.buffer(pubkey, len_key)) return pub.hex() if hex else pub
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 __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) or not is_wif_valid(key): 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 if key[0] in (TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_COMPRESSED_PREFIX): self.testnet = True else: self.testnet = False self.wif = private_key_to_wif(self.key, self.compressed, self.testnet)
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 merkle_root_from_proof(merkle_proof, tx_id, index, return_hex=True, receive_hex=True): 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 = bytes_from_hex(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 test_private_key_to_wif(): assert private_key_to_wif("ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944a4") == \ "L49obCXV7fGz2YRzLCSJgeZBYmGeBbKPT7xiehUeYX2S4URkPFZX" assert private_key_to_wif(bytes_from_hex("ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944a4")) == \ "L49obCXV7fGz2YRzLCSJgeZBYmGeBbKPT7xiehUeYX2S4URkPFZX" with pytest.raises(TypeError): private_key_to_wif( "ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944") assert private_key_to_wif("ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944a4", testnet=False, compressed=True) == \ "L49obCXV7fGz2YRzLCSJgeZBYmGeBbKPT7xiehUeYX2S4URkPFZX" assert private_key_to_wif("ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944a4", testnet=False, compressed=False) == \ "5KPPLXhtga99qqMceRo4Z6LXV3Kx6a9hRx3ez2U7EwP5KZfy2Wf" assert private_key_to_wif("ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944a4", testnet=True, compressed=True) == \ "cUWo47XLYiyFByuFicFS3y4FAza3r3R5XA7Bm7wA3dgSKDYox7h6" assert private_key_to_wif("ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944a4", testnet=True, compressed=False) == \ "93A1vGXSGoDHotruGmgyRgtV8hgfFjgtmtuc4epcag886W9d44L"
def add_input(self, tx_id=None, v_out=0, sequence=0xffffffff, script_sig=b"", tx_in_witness=None, amount=None, script_pub_key=None, address=None, private_key=None, redeem_script=None, input_verify = True): if tx_id is None: tx_id = b"\x00" * 32 v_out = 0xffffffff if (sequence != 0xffffffff or self["vIn"]) and input_verify: raise RuntimeError("invalid coinbase transaction") if isinstance(tx_id, str): tx_id = s2rh(tx_id) if not isinstance(tx_id, bytes) or len(tx_id) != 32: raise TypeError("tx_id invalid") if isinstance(script_sig, str): script_sig = bytes_from_hex(script_sig) if not isinstance(script_sig, bytes) or (len(script_sig) > 520 and input_verify): raise TypeError("script_sig invalid") if not isinstance(v_out, int) or not (v_out <= 0xffffffff and v_out >= 0): raise TypeError("v_out invalid") if not isinstance(sequence, int) or not (sequence <= 0xffffffff and sequence >= 0): raise TypeError("sequence invalid") if private_key: if not isinstance(private_key, PrivateKey): private_key = PrivateKey(private_key) if amount: if not isinstance(amount, int) or not amount >= 0 and amount <= MAX_AMOUNT: raise TypeError("amount invalid") if tx_in_witness: if not isinstance(tx_in_witness, list): raise TypeError("tx_in_witness invalid") l = 0 witness = [] for w in tx_in_witness: if isinstance(w, str): witness.append(bytes_from_hex(w) if self["format"] == "raw" else w) else: witness.append(w if self["format"] == "raw" else bytes_from_hex(w)) l += 1 + len(w) if len(w) >= 0x4c: l += 1 if len(w) > 0xff: l += 1 # witness script limit if not l <= 10000: raise TypeError("tx_in_witness invalid") if tx_id == b"\x00" * 32: if not (v_out == 0xffffffff and sequence == 0xffffffff and len(script_sig) <= 100): if input_verify: raise TypeError("coinbase tx invalid") self["coinbase"] = True # script_pub_key if script_pub_key: if isinstance(script_pub_key, str): script_pub_key = bytes_from_hex(script_pub_key) if not isinstance(script_pub_key, bytes): raise TypeError("script_pub_key tx invalid") if redeem_script: if isinstance(redeem_script, str): redeem_script = bytes_from_hex(redeem_script) if not isinstance(redeem_script, bytes): raise TypeError("redeem_script tx invalid") if address is not None: if isinstance(address, str): net = True if address_net_type(address) == 'mainnet' else False if not net != self["testnet"]: raise TypeError("address invalid") script = address_to_script(address) elif type(address) in (Address, ScriptAddress): script = address_to_script(address.address) else: raise TypeError("address invalid") if script_pub_key: if script_pub_key != script: raise Exception("address not match script") else: script_pub_key = script k = len(self["vIn"]) self["vIn"][k] = dict() self["vIn"][k]["vOut"] = v_out self["vIn"][k]["sequence"] = sequence if self["format"] == "raw": self["vIn"][k]["txId"] = tx_id self["vIn"][k]["scriptSig"] = script_sig if script_pub_key: self["vIn"][k]["scriptPubKey"] = script_pub_key if redeem_script: self["vIn"][k]["redeemScript"] = redeem_script else: self["vIn"][k]["txId"] = rh2s(tx_id) self["vIn"][k]["scriptSig"] = script_sig.hex() self["vIn"][k]["scriptSigOpcodes"] = decode_script(script_sig) self["vIn"][k]["scriptSigAsm"] = decode_script(script_sig, 1) if script_pub_key: self["vIn"][k]["scriptPubKey"] = script_pub_key.hex() self["vIn"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key) self["vIn"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1) if redeem_script: self["vIn"][k]["redeemScript"] = redeem_script.hex() self["vIn"][k]["redeemScriptOpcodes"] = decode_script(redeem_script) self["vIn"][k]["redeemScriptAsm"] = decode_script(script_pub_key, 1) if tx_in_witness: self["segwit"] = True self["vIn"][k]["txInWitness"] = witness if amount: self["vIn"][k]["value"] = amount if private_key: self["vIn"][k]["private_key"] = private_key if self.auto_commit: self.commit() return self
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 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 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, "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 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 sig_hash_segwit(self, n, amount, script_pub_key=None, sighash_type=SIGHASH_ALL, preimage=False): try: self["vIn"][n] except: raise Exception("sig_hash error, input not exist") # check script_pub_key for input if script_pub_key is not None: script_code = script_pub_key else: if "scriptPubKey" not in self["vIn"][n]: raise Exception("sig_hash error, scriptPubKey required") script_code = self["vIn"][n]["scriptPubKey"] if isinstance(script_code, str): script_code = bytes.fromhex(script_code) if not isinstance(script_code,bytes): raise Exception("sig_hash error, script_code type error") # remove opcode separators pm = bytearray() # 1. nVersion of the transaction (4-byte little endian) pm += pack('<L', self["version"]) # 2. hashPrevouts (32-byte hash) # 3. hashSequence (32-byte hash) # 4. outpoint (32-byte hash + 4-byte little endian) # 5. scriptCode of the input (serialized as scripts inside CTxOuts) # 6. value of the output spent by this input (8-byte little endian) # 7. nSequence of the input (4-byte little endian) hp = bytearray() # hash of out points hs = bytearray() # hash of sequences for i in self["vIn"]: tx_id = self["vIn"][i]["txId"] if type(tx_id) == str: tx_id = s2rh(tx_id) if not (sighash_type & SIGHASH_ANYONECANPAY): hp += b"%s%s" % (tx_id, pack('<L', self["vIn"][i]["vOut"])) if (sighash_type & 31) != SIGHASH_SINGLE and (sighash_type & 31) != SIGHASH_NONE: hs += pack('<L', self["vIn"][i]["sequence"]) if i == n: outpoint = b"%s%s" % (tx_id, pack('<L', self["vIn"][i]["vOut"])) n_sequence = pack('<L', self["vIn"][i]["sequence"]) hash_prevouts = double_sha256(hp) if hp else b'\x00' * 32 hash_sequence = double_sha256(hs) if hs else b'\x00' * 32 value = amount.to_bytes(8, 'little') # 8. hashOutputs (32-byte hash) ho = bytearray() for o in self["vOut"]: script_pub_key = self["vOut"][o]["scriptPubKey"] if type(self["vOut"][o]["scriptPubKey"]) == str: script_pub_key = bytes_from_hex(script_pub_key) if (sighash_type & 31) != SIGHASH_SINGLE and (sighash_type & 31) != SIGHASH_NONE: ho += b"%s%s%s" % (self["vOut"][o]["value"].to_bytes(8, 'little'), int_to_var_int(len(script_pub_key)), script_pub_key) elif (sighash_type & 31) == SIGHASH_SINGLE and n < len(self["vOut"]): if o == n: ho += b"%s%s%s" % (self["vOut"][o]["value"].to_bytes(8, 'little'), int_to_var_int(len(script_pub_key)), script_pub_key) hash_outputs = double_sha256(ho) if ho else b'\x00' * 32 pm += b"%s%s%s%s%s%s%s%s%s" % (hash_prevouts, hash_sequence, outpoint, script_code, value, n_sequence, hash_outputs, pack('<L', self["lockTime"]), pack('<L', sighash_type)) if not preimage: pm = double_sha256(pm) return pm if self["format"] == "raw" else pm.hex()
def sig_hash(self, n, script_pub_key=None, sighash_type=SIGHASH_ALL, preimage=False): try: self["vIn"][n] except: raise Exception("sig_hash error, input not exist") # check script_pub_key for input if script_pub_key is not None: script_code = script_pub_key else: if "scriptPubKey" not in self["vIn"][n]: raise Exception("sig_hash error, scriptPubKey required") script_code = self["vIn"][n]["scriptPubKey"] if isinstance(script_code, str): script_code = bytes.fromhex(script_code) if not isinstance(script_code,bytes): raise Exception("sig_hash error, script_code type error") # remove opcode separators if ((sighash_type & 31) == SIGHASH_SINGLE) and (n >= (len(self["vOut"]))): if self["format"] == "raw": return b'\x01%s' % (b'\x00' * 31) return rh2s(b'\x01%s' % (b'\x00' * 31)) script_code = delete_from_script(script_code, BYTE_OPCODE["OP_CODESEPARATOR"]) pm = bytearray() pm += b"%s%s" % (pack('<L', self["version"]), b'\x01' if sighash_type & SIGHASH_ANYONECANPAY else int_to_var_int(len(self["vIn"]))) for i in self["vIn"]: # skip all other inputs for SIGHASH_ANYONECANPAY case if (sighash_type & SIGHASH_ANYONECANPAY) and (n != i): continue sequence = self["vIn"][i]["sequence"] if ((sighash_type & 31) == SIGHASH_SINGLE or (sighash_type & 31) == SIGHASH_NONE) and (n != i): sequence = 0 tx_id = self["vIn"][i]["txId"] if isinstance(tx_id, str): tx_id = s2rh(tx_id) if n == i: pm += b"%s%s%s%s%s" % (tx_id, pack('<L', self["vIn"][i]["vOut"]), int_to_var_int(len(script_code)), script_code, pack('<L', sequence)) else: pm += b'%s%s\x00%s' % (tx_id, pack('<L', self["vIn"][i]["vOut"]), pack('<L', sequence)) if (sighash_type & 31) == SIGHASH_NONE: pm += b'\x00' else: if (sighash_type & 31) == SIGHASH_SINGLE: pm += int_to_var_int(n + 1) else: pm += int_to_var_int(len(self["vOut"])) if (sighash_type & 31) != SIGHASH_NONE: for i in self["vOut"]: script_pub_key = self["vOut"][i]["scriptPubKey"] if isinstance(self["vOut"][i]["scriptPubKey"], str): script_pub_key = bytes_from_hex(script_pub_key) if i > n and (sighash_type & 31) == SIGHASH_SINGLE: continue if (sighash_type & 31) == SIGHASH_SINGLE and (n != i): pm += b"%s%s" % (b'\xff' * 8, b'\x00') else: pm += b"%s%s%s" % (self["vOut"][i]["value"].to_bytes(8, 'little'), int_to_var_int(len(script_pub_key)), script_pub_key) pm += b"%s%s" % (self["lockTime"].to_bytes(4, 'little'), pack(b"<i", sighash_type)) if not preimage: pm = double_sha256(pm) return pm if self["format"] == "raw" else rh2s(pm)
def encode(self): """ change Transaction object representation to "raw" bytes format, all human readable part will be stripped. """ if type(self["txId"]) == str: self["txId"] = s2rh(self["txId"]) if "flag" in self: if type(self["flag"]) == str: self["flag"] = s2rh(self["flag"]) if type(self["hash"]) == str: self["hash"] = s2rh(self["hash"]) if type(self["rawTx"]) == str: self["rawTx"] = bytes_from_hex(self["rawTx"]) for i in self["vIn"]: if type(self["vIn"][i]["txId"]) == str: self["vIn"][i]["txId"] = s2rh(self["vIn"][i]["txId"]) if type(self["vIn"][i]["scriptSig"]) == str: self["vIn"][i]["scriptSig"] = bytes_from_hex(self["vIn"][i]["scriptSig"]) try: t = list() append = t.append for w in self["vIn"][i]["txInWitness"]: if type(w) == str: w = bytes_from_hex(w) append(w) self["vIn"][i]["txInWitness"] = t except: pass try: if type(self["vIn"][i]["addressHash"]) == str: self["vIn"][i]["addressHash"] = bytes_from_hex(self["vIn"][i]["addressHash"]) if "address" in self["vIn"][i]: del self["vIn"][i]["address"] except: pass if "scriptSigAsm" in self["vIn"][i]: del self["vIn"][i]["scriptSigAsm"] if "scriptSigOpcodes" in self["vIn"][i]: del self["vIn"][i]["scriptSigOpcodes"] if "scriptPubKeyOpcodes" in self["vIn"][i]: del self["vIn"][i]["scriptPubKeyOpcodes"] if "scriptPubKeyAsm" in self["vIn"][i]: del self["vIn"][i]["scriptPubKeyAsm"] if "scriptPubKey" in self["vIn"][i]: self["vIn"][i]["scriptPubKey"] = bytes_from_hex(self["vIn"][i]["scriptPubKey"]) if "redeemScriptOpcodes" in self["vIn"][i]: del self["vIn"][i]["redeemScriptOpcodes"] if "redeemScriptAsm" in self["vIn"][i]: del self["vIn"][i]["redeemScriptAsm"] if "redeemScript" in self["vIn"][i]: del self["vIn"][i]["redeemScript"] for i in self["vOut"]: if type(self["vOut"][i]["scriptPubKey"]) == str: self["vOut"][i]["scriptPubKey"] = bytes_from_hex(self["vOut"][i]["scriptPubKey"]) try: if type(self["vOut"][i]["addressHash"]) == str: self["vOut"][i]["addressHash"] = bytes_from_hex(self["vOut"][i]["addressHash"]) if "address" in self["vOut"][i]: del self["vOut"][i]["address"] except: pass if "scriptPubKeyOpcodes" in self["vOut"][i]: del self["vOut"][i]["scriptPubKeyOpcodes"] if "scriptPubKeyAsm" in self["vOut"][i]: del self["vOut"][i]["scriptPubKeyAsm"] if "data" in self: if type(self["data"]) == str: self["data"] = bytes_from_hex(self["data"]) self["format"] = "raw" return self