from struct import unpack import json from .tools import * from .address import PrivateKey, Address, PublicKey, ScriptAddress from binascii import hexlify, unhexlify class Transaction(dict): def __init__(self, raw_tx=None, tx_format="decoded", version=1, lockTime=0, testnet=False): assert tx_format in ("decoded", "raw") self["format"] = tx_format self["testnet"] = testnet self["segwit"] = False self["txId"] = None self["hash"] = None self["version"] = version self["size"] = 0 self["vSize"] = 0 self["bSize"] = 0 self["lockTime"] = lockTime self["vIn"] = dict() self["vOut"] = dict() self["rawTx"] = None self["blockHash"] = None self["confirmations"] = None self["time"] = None self["blockTime"] = None self["blockIndex"] = None self["coinbase"] = False self["fee"] = None self["data"] = None self["amount"] = None if raw_tx is None: return self["amount"] = 0 stream = self.get_stream(raw_tx) start = stream.tell() (self["version"],) = unpack('= 0 assert type(sequence) == int assert sequence <= 0xffffffff and sequence >= 0 assert type(script_sig) == bytes assert len(script_sig) <= 520 if private_key: if type(private_key) != PrivateKey: private_key = PrivateKey(private_key) if amount: assert type(amount) == int assert amount >= 0 and amount <= MAX_AMOUNT if tx_in_witness: assert type(tx_in_witness) == list l = 0 witness = [] for w in tx_in_witness: if type(w) == str: witness.append(unhexlify(w) if self["format"] == "raw" else w) else: witness.append(w if self["format"] == "raw" else unhexlify(w)) l += 1 + len(w) if len(w) >= 0x4c: l += 1 if len(w) > 0xff: l += 1 # witness script limit assert l <= 10000 if tx_id == b"\x00" * 32: assert v_out == 0xffffffff and sequence == 0xffffffff and len(script_sig) <= 100 self["coinbase"] = True # script_pub_key if script_pub_key: if type(script_pub_key) == str: script_pub_key = unhexlify(script_pub_key) type(script_pub_key) == bytes if address is not None: if type(address) == str: net = True if address_net_type(address) == 'mainnet' else False assert not net == self["testnet"] script = address_to_script(address) elif type(address) in (Address, ScriptAddress): assert type(address) == Address script = address_to_script(address.address) if script_pub_key: assert script_pub_key == 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 else: self["vIn"][k]["txId"] = rh2s(tx_id) self["vIn"][k]["scriptSig"] = hexlify(script_sig).decode() 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"] = hexlify(script_pub_key).decode() 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 self.__refresh__() return self def add_output(self, amount, address=None, script_pub_key=None): assert address is not None or script_pub_key is not None assert not (address is None and script_pub_key is None) assert type(amount) == int assert amount >= 0 and amount <= MAX_AMOUNT if script_pub_key: if type(script_pub_key) == str: script_pub_key = unhexlify(script_pub_key) assert type(script_pub_key) == bytes 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): self["vOut"][k]["addressHash"] = s["addressHash"] self["vOut"][k]["reqSigs"] = s["reqSigs"] else: self["vOut"][k]["scriptPubKey"] = hexlify(script_pub_key).decode() if self["data"] is None: if s["nType"] == 3: self["data"] = hexlify(s["data"]).decode() if s["nType"] not in (3, 4, 7): self["vOut"][k]["addressHash"] = hexlify(s["addressHash"]).decode() 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) self.__refresh__() return self def del_output(self, n=None): if not self["vOut"]: return self if n is None: n = len(self["vOut"]) - 1 new_out = dict() c = 0 for i in range(len(self["vOut"])): if i != n: new_out[c] = self["vOut"][i] c += 1 self["vOut"] = new_out self.__refresh__() return self def del_input(self, n): if not self["vIn"]: return self if n is None: n = len(self["vIn"]) - 1 new_in = dict() c = 0 for i in range(len(self["vIn"])): if i != n: new_in[c] = self["vIn"][i] c += 1 self["vIn"] = new_in self.__refresh__() return self def sign_input(self, n, private_key=None, script_pub_key=None, redeem_script=None, sighash_type=SIGHASH_ALL): if private_key is not None: if private_key: if type(private_key) != PrivateKey: private_key_obj = PrivateKey(private_key) public_key = PublicKey(private_key_obj).key private_key = private_key_obj.key else: if "privateKey" not in self["vIn"][n]: return self private_key = self["vIn"][n].private_key.key public_key = PublicKey(self["vIn"][n].private_key).key if redeem_script: if type(redeem_script) == str: redeem_script = unhexlify(redeem_script).decode() assert type(redeem_script) == bytes script = redeem_script else: script = script_pub_key sighash = self.sig_hash_input(n, script_pub_key=script, sighash_type=sighash_type) if type(sighash) == str: sighash = s2rh(sighash) signature = sign_message(sighash, private_key, 0) + bytes([sighash_type]) if redeem_script: if self["vIn"][n]["scriptSig"]: sig_script = self["vIn"][n]["scriptSig"] if type(sig_script) == str: sig_script = unhexlify(sig_script).decode() sig_script = bytes([len(public_key)]) + public_key + sig_script sig_script = bytes([len(signature)]) + signature + sig_script else: sig_script = bytes([len(signature)]) + signature sig_script += bytes([len(public_key)]) + public_key if len(redeem_script) <= 0x4b: sig_script += bytes([len(redeem_script)]) + redeem_script elif len(redeem_script) <= 0xff: sig_script = BYTE_OPCODE["OP_PUSHDATA1"] + bytes([len(redeem_script)]) + redeem_script elif len(redeem_script) <= 0xffff: sig_script = BYTE_OPCODE["OP_PUSHDATA2"] + bytes([len(redeem_script)]) + redeem_script else: sig_script = BYTE_OPCODE["OP_PUSHDATA4"] + bytes([len(redeem_script)]) + redeem_script else: sig_script = bytes([len(signature)]) + signature sig_script += bytes([len(public_key)]) + public_key if self["format"] == "raw": self["vIn"][n]["scriptSig"] = sig_script else: self["vIn"][n]["scriptSig"] = hexlify(sig_script).decode() self["vIn"][n]["scriptSigOpcodes"] = decode_script(sig_script) self["vIn"][n]["scriptSigAsm"] = decode_script(sig_script, 1) self.__refresh__() return self def sig_hash_input(self, n, script_pub_key=None, sighash_type=SIGHASH_ALL): # check n assert n >= 0 tx_in_count = len(self["vIn"]) if n >= tx_in_count: if self["format"] == "raw": return b'\x01' + b'\x00' * 31 else: return rh2s(b'\x01' + b'\x00' * 31) # check script_pub_key for input if script_pub_key is not None: script_code = script_pub_key else: assert "scriptPubKey" in self["vIn"][n] script_code = self["vIn"][n]["scriptPubKey"] if type(script_code) == str: script_code = unhexlify(script_code) assert type(script_code) == bytes # remove opcode separators script_code = delete_from_script(script_code, BYTE_OPCODE["OP_CODESEPARATOR"]) preimage = bytearray() if ((sighash_type & 31) == SIGHASH_SINGLE) and (n >= (len(self["vOut"]))): if self["format"] == "raw": return b'\x01' + b'\x00' * 31 else: return rh2s(b'\x01' + b'\x00' * 31) preimage += struct.pack(' n and (sighash_type & 31) == SIGHASH_SINGLE: continue if (sighash_type & 31) == SIGHASH_SINGLE and (n != i): preimage += b'\xff' * 8 + b'\x00' else: preimage += self["vOut"][i]["value"].to_bytes(8, 'little') preimage += int_to_var_int(len(script_pub_key)) + script_pub_key preimage += self["lockTime"].to_bytes(4, 'little') preimage += struct.pack(b"