from struct import unpack
import json
from .tools import *
from .address import PrivateKey, Address, PublicKey, ScriptAddress
from binascii import hexlify, unhexlify
[docs]class Transaction(dict):
"""
The class for Transaction object
:param raw_tx: (optional) raw transaction in bytes or HEX encoded string, if no raw transaction provided
well be created new empty transaction template.
:param tx_format: "raw" or "decoded" format. Raw format is mean that all transaction represented in bytes
for best performance.
Decoded transaction is represented in human readable format using base68, hex, bech32,
asm and opcodes. By default "decoded" format using.
:param int version: transaction version for new template, by default 1.
:param int lock_time: transaction lock time for new template, by default 0.
:param boolean testnet: address type for "decoded" transaction representation.
"""
def __init__(self, raw_tx=None, tx_format="decoded", version=1, lock_time=0, testnet=False):
if tx_format not in ("decoded", "raw"):
raise TypeError("tx_format error, raw or decoded allowed")
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"] = lock_time
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
sw = sw_len = 0
stream = self.get_stream(raw_tx)
start = stream.tell()
# start deserialization
self["version"] = unpack('<L', stream.read(4))[0]
n = read_var_int(stream)
if n == b'\x00':
# segwit format
sw = 1
self["flag"] = stream.read(1)
n = read_var_int(stream)
# inputs
ic = var_int_to_int(n)
for k in range(ic):
self["vIn"][k] = dict()
self["vIn"][k]["txId"] = stream.read(32)
self["vIn"][k]["vOut"] = unpack('<L', stream.read(4))[0]
self["vIn"][k]["scriptSig"] = stream.read(var_int_to_int(read_var_int(stream)))
(self["vIn"][k]["sequence"],) = unpack('<L', stream.read(4))
# outputs
for k in range(var_int_to_int(read_var_int(stream))):
self["vOut"][k] = dict()
self["vOut"][k]["value"] = unpack('<Q', stream.read(8))[0]
self["amount"] += self["vOut"][k]["value"]
self["vOut"][k]["scriptPubKey"] = stream.read(var_int_to_int(read_var_int(stream)))
s = parse_script(self["vOut"][k]["scriptPubKey"], sw)
self["vOut"][k]["nType"] = s["nType"]
self["vOut"][k]["type"] = s["type"]
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"]
# witness
if sw:
sw = stream.tell() - start
for k in range(ic):
self["vIn"][k]["txInWitness"] = [stream.read(var_int_to_int(read_var_int(stream))) \
for c in range(var_int_to_int(read_var_int(stream)))]
sw_len = stream.tell() - sw + 2
self["lockTime"] = unpack('<L', stream.read(4))[0]
end = stream.tell()
stream.seek(start)
b = stream.read(end - start)
self["rawTx"] = b
self["size"] = end - start
self["bSize"] = end - start - sw_len
self["weight"] = self["bSize"] * 3 + self["size"]
self["vSize"] = math.ceil(self["weight"] / 4)
if ic == 1 and \
self["vIn"][0]["txId"] == b'\x00' * 32 and \
self["vIn"][0]["vOut"] == 0xffffffff:
self["coinbase"] = True
else:
self["coinbase"] = False
if sw:
self["segwit"] = True
self["hash"] = double_sha256(b)
self["txId"] = double_sha256(b[:4] + b[6:sw] + b[-4:])
else:
self["segwit"] = False
self["txId"] = double_sha256(b)
self["hash"] = self["txId"]
[docs] def decode(self, testnet=None):
"""
change Transacion object representation to "decoded" human readable format
:param bool testnet: (optional) address type for "decoded" transaction representation, by default None.
if None used type from transaction property "format".
"""
if self["format"] == "decoded":
self.encode()
self["format"] = "decoded"
if testnet is not None:
self["testnet"] = testnet
if type(self["txId"]) == bytes:
self["txId"] = rh2s(self["txId"])
if "flag" in self:
if type(self["flag"]) == bytes:
self["flag"] = rh2s(self["flag"])
if type(self["hash"]) == bytes:
self["hash"] = rh2s(self["hash"])
if type(self["rawTx"]) == bytes:
self["rawTx"] = hexlify(self["rawTx"]).decode()
for i in self["vIn"]:
if type(self["vIn"][i]["txId"]) == bytes:
self["vIn"][i]["txId"] = rh2s(self["vIn"][i]["txId"])
if type(self["vIn"][i]["scriptSig"]) == bytes:
self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode()
try:
t = list()
for w in self["vIn"][i]["txInWitness"]:
if type(w) == bytes:
w = hexlify(w).decode()
t.append(w)
self["vIn"][i]["txInWitness"] = t
self["vIn"][i]["txInWitnessAsm"] = [decode_script(ws, 1) for ws in
self["vIn"][i]["txInWitness"]]
self["vIn"][i]["txInWitnessOpcodes"] = [decode_script(ws) for ws in
self["vIn"][i]["txInWitness"]]
except:
pass
try:
if type(self["vIn"][i]["addressHash"]) == bytes:
self["vIn"][i]["addressHash"] = hexlify(self["vIn"][i]["addressHash"]).decode()
sh = True if self["vIn"][i]["nType"] in (1, 5) else False
witness_version = None if self["vIn"][i]["nType"] < 5 else 0
self["vIn"][i]["address"] = hash_to_address(self["vIn"][i]["addressHash"],
self["testnet"],
sh,
witness_version)
except:
pass
if "scriptPubKey" in self["vIn"][i]:
if type(self["vIn"][i]["scriptPubKey"]) == bytes:
self["vIn"][i]["scriptPubKey"] = hexlify(self["vIn"][i]["scriptPubKey"]).decode()
self["vIn"][i]["scriptPubKeyOpcodes"] = decode_script(self["vIn"][i]["scriptPubKey"])
self["vIn"][i]["scriptPubKeyAsm"] = decode_script(self["vIn"][i]["scriptPubKey"], 1)
if "redeemScript" in self["vIn"][i]:
if type(self["vIn"][i]["redeemScript"]) == bytes:
self["vIn"][i]["redeemScript"] = hexlify(self["vIn"][i]["redeemScript"]).decode()
self["vIn"][i]["redeemScriptOpcodes"] = decode_script(self["vIn"][i]["redeemScript"])
self["vIn"][i]["redeemScriptAsm"] = decode_script(self["vIn"][i]["redeemScript"], 1)
if not self["coinbase"]:
if type(self["vIn"][i]["scriptSig"]) == bytes:
self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode()
self["vIn"][i]["scriptSigOpcodes"] = decode_script(self["vIn"][i]["scriptSig"])
self["vIn"][i]["scriptSigAsm"] = decode_script(self["vIn"][i]["scriptSig"], 1)
for i in self["vOut"]:
if type(self["vOut"][i]["scriptPubKey"]) == bytes:
self["vOut"][i]["scriptPubKey"] = hexlify(self["vOut"][i]["scriptPubKey"]).decode()
try:
if type(self["vOut"][i]["addressHash"]) == bytes:
self["vOut"][i]["addressHash"] = hexlify(self["vOut"][i]["addressHash"]).decode()
sh = True if self["vOut"][i]["nType"] in (1, 5) else False
witness_version = None if self["vOut"][i]["nType"] < 5 else 0
self["vOut"][i]["address"] = hash_to_address(self["vOut"][i]["addressHash"],
self["testnet"],
sh,
witness_version)
except:
pass
self["vOut"][i]["scriptPubKeyOpcodes"] = decode_script(self["vOut"][i]["scriptPubKey"])
self["vOut"][i]["scriptPubKeyAsm"] = decode_script(self["vOut"][i]["scriptPubKey"], 1)
if "data" in self:
if type(self["data"]) == bytes:
self["data"] = hexlify(self["data"]).decode()
return self
[docs] 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"] = unhexlify(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"] = unhexlify(self["vIn"][i]["scriptSig"])
try:
t = list()
for w in self["vIn"][i]["txInWitness"]:
if type(w) == str:
w = unhexlify(w)
t.append(w)
self["vIn"][i]["txInWitness"] = t
if "txInWitnessAsm" in self["vIn"][i]:
del self["vIn"][i]["txInWitnessAsm"]
if "txInWitnessOpcodes" in self["vIn"][i]:
del self["vIn"][i]["txInWitnessOpcodes"]
except:
pass
try:
if type(self["vIn"][i]["addressHash"]) == str:
self["vIn"][i]["addressHash"] = unhexlify(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"]
for i in self["vOut"]:
if type(self["vOut"][i]["scriptPubKey"]) == str:
self["vOut"][i]["scriptPubKey"] = unhexlify(self["vOut"][i]["scriptPubKey"])
try:
if type(self["vOut"][i]["addressHash"]) == str:
self["vOut"][i]["addressHash"] = unhexlify(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"] = unhexlify(self["data"])
self["format"] = "raw"
return self
def get_stream(self, stream):
if type(stream) != io.BytesIO:
if type(stream) == str:
stream = unhexlify(stream)
if type(stream) == bytes:
stream = io.BytesIO(stream)
else:
raise TypeError
return stream
[docs] 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 = []
chunks.append(struct.pack('<L', self["version"]))
if segwit and self["segwit"]:
chunks.append(b"\x00\x01")
chunks.append(int_to_var_int(len(self["vIn"])))
for i in self["vIn"]:
if isinstance(self["vIn"][i]['txId'], bytes):
chunks.append(self["vIn"][i]['txId'])
else:
chunks.append(s2rh(self["vIn"][i]['txId']))
chunks.append(struct.pack('<L', self["vIn"][i]['vOut']))
if isinstance(self["vIn"][i]['scriptSig'], bytes):
chunks.append(int_to_var_int(len(self["vIn"][i]['scriptSig'])))
chunks.append(self["vIn"][i]['scriptSig'])
else:
chunks.append(int_to_var_int(int(len(self["vIn"][i]['scriptSig']) / 2)))
chunks.append(unhexlify(self["vIn"][i]['scriptSig']))
chunks.append(struct.pack('<L', self["vIn"][i]['sequence']))
chunks.append(int_to_var_int(len(self["vOut"])))
for i in self["vOut"]:
chunks.append(struct.pack('<Q', self["vOut"][i]['value']))
if isinstance(self["vOut"][i]['scriptPubKey'], bytes):
chunks.append(int_to_var_int(len(self["vOut"][i]['scriptPubKey'])))
chunks.append(self["vOut"][i]['scriptPubKey'])
else:
chunks.append(int_to_var_int(int(len(self["vOut"][i]['scriptPubKey']) / 2)))
chunks.append(unhexlify(self["vOut"][i]['scriptPubKey']))
if segwit and self["segwit"]:
for i in self["vIn"]:
chunks.append(int_to_var_int(len(self["vIn"][i]['txInWitness'])))
for w in self["vIn"][i]['txInWitness']:
if isinstance(w, bytes):
chunks.append(int_to_var_int(len(w)))
chunks.append(w)
else:
chunks.append(int_to_var_int(int(len(w) / 2)))
chunks.append(unhexlify(w))
chunks.append(struct.pack('<L', self['lockTime']))
tx = b''.join(chunks)
return tx if not hex else hexlify(tx).decode()
[docs] def json(self):
"""
Get json transaction representation
"""
try:
return json.dumps(self)
except:
pass
return json.dumps(self.decode())
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):
if tx_id is None:
tx_id = b"\x00" * 32
v_out = 0xffffffff
assert v_out == 0xffffffff and sequence == 0xffffffff
assert not self["vIn"]
if type(tx_id) == str:
tx_id = s2rh(tx_id)
if type(script_sig) == str:
script_sig = unhexlify(script_sig)
assert type(tx_id) == bytes
assert len(tx_id) == 32
assert type(v_out) == int
assert v_out <= 0xffffffff and v_out >= 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('<L', self["version"])
preimage += b'\x01' if sighash_type & SIGHASH_ANYONECANPAY else int_to_var_int(tx_in_count)
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 and (n != i):
sequence = 0
if (sighash_type & 31) == SIGHASH_NONE and (n != i):
sequence = 0
tx_id = self["vIn"][i]["txId"]
if type(tx_id) == str:
tx_id = s2rh(tx_id)
input = tx_id + struct.pack('<L', self["vIn"][i]["vOut"])
if n == i:
input += int_to_var_int(len(script_code)) + script_code
input += struct.pack('<L', sequence)
else:
input += b'\x00' + struct.pack('<L', sequence)
preimage += input
if (sighash_type & 31) == SIGHASH_NONE:
preimage += b'\x00'
else:
if (sighash_type & 31) == SIGHASH_SINGLE:
preimage += int_to_var_int(n + 1)
else:
preimage += 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 type(self["vOut"][i]["scriptPubKey"]) == str:
script_pub_key = unhexlify(script_pub_key)
if i > 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"<i", sighash_type)
return double_sha256(preimage) if self["format"] == "raw" else rh2s(double_sha256(preimage))
def __refresh__(self):
if not self["vOut"] or not self["vIn"]:
return
no_segwit_view = self.serialize(segwit=False, hex=False)
self["txId"] = double_sha256(no_segwit_view)
self["rawTx"] = self.serialize(segwit=True, hex=False)
self["hash"] = double_sha256(self["rawTx"])
self["size"] = len(self["rawTx"])
self["bSize"] = len(no_segwit_view)
self["weight"] = self["bSize"] * 3 + self["size"]
self["vSize"] = math.ceil(self["weight"] / 4)
if self["format"] != "raw":
self["txId"] = rh2s(self["txId"])
self["hash"] = rh2s(self["hash"])
self["rawTx"] = hexlify(self["rawTx"]).decode()
input_sum = 0
for i in self["vIn"]:
if "value" in self["vIn"][i]:
input_sum += self["vIn"][i]["value"]
else:
input_sum = None
break
output_sum = 0
for i in self["vOut"]:
if "value" in self["vOut"][i]:
output_sum += self["vOut"][i]["value"]
else:
output_sum = None
break
self["amount"] = output_sum
if output_sum and input_sum:
self["fee"] = input_sum - output_sum
else:
self["fee"] = None