mirror of
https://github.com/aljazceru/python-teos.git
synced 2025-12-17 22:24:23 +01:00
153 lines
5.8 KiB
Python
153 lines
5.8 KiB
Python
# Porting some functionality from https://github.com/sr-gi/bitcoin_tools with some modifications <3
|
|
from os import urandom
|
|
|
|
from test.simulator.utils import *
|
|
|
|
|
|
class TX:
|
|
""" Defines a class TX (transaction) that holds all the modifiable fields of a Bitcoin transaction, such as
|
|
version, number of inputs, reference to previous transactions, input and output scripts, value, etc.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.version = None
|
|
self.inputs = None
|
|
self.outputs = None
|
|
self.nLockTime = None
|
|
self.prev_tx_id = []
|
|
self.prev_out_index = []
|
|
self.scriptSig = []
|
|
self.scriptSig_len = []
|
|
self.nSequence = []
|
|
self.value = []
|
|
self.scriptPubKey = []
|
|
self.scriptPubKey_len = []
|
|
|
|
self.offset = 0
|
|
self.hex = ""
|
|
|
|
@classmethod
|
|
def deserialize(cls, hex_tx):
|
|
""" Builds a transaction object from the hexadecimal serialization format of a transaction that
|
|
could be obtained, for example, from a blockexplorer.
|
|
:param hex_tx: Hexadecimal serialized transaction.
|
|
:type hex_tx: hex str
|
|
:return: The transaction build using the provided hex serialized transaction.
|
|
:rtype: TX
|
|
"""
|
|
|
|
tx = cls()
|
|
tx.hex = hex_tx
|
|
|
|
try:
|
|
tx.version = int(change_endianness(parse_element(tx, 4)), 16)
|
|
|
|
# INPUTS
|
|
tx.inputs = int(parse_varint(tx), 16)
|
|
|
|
for i in range(tx.inputs):
|
|
tx.prev_tx_id.append(change_endianness(parse_element(tx, 32)))
|
|
tx.prev_out_index.append(int(change_endianness(parse_element(tx, 4)), 16))
|
|
# ScriptSig
|
|
tx.scriptSig_len.append(int(parse_varint(tx), 16))
|
|
tx.scriptSig.append(parse_element(tx, tx.scriptSig_len[i]))
|
|
tx.nSequence.append(int(parse_element(tx, 4), 16))
|
|
|
|
# OUTPUTS
|
|
tx.outputs = int(parse_varint(tx), 16)
|
|
|
|
for i in range(tx.outputs):
|
|
tx.value.append(int(change_endianness(parse_element(tx, 8)), 16))
|
|
# ScriptPubKey
|
|
tx.scriptPubKey_len.append(int(parse_varint(tx), 16))
|
|
tx.scriptPubKey.append(parse_element(tx, tx.scriptPubKey_len[i]))
|
|
|
|
tx.nLockTime = int(parse_element(tx, 4), 16)
|
|
|
|
if tx.offset != len(tx.hex):
|
|
# There is some error in the serialized transaction passed as input. Transaction can't be built
|
|
tx = None
|
|
else:
|
|
tx.offset = 0
|
|
|
|
except ValueError:
|
|
# If a parsing error occurs, the deserialization stops and None is returned
|
|
tx = None
|
|
|
|
return tx
|
|
|
|
def serialize(self, rtype=hex):
|
|
""" Serialize all the transaction fields arranged in the proper order, resulting in a hexadecimal string
|
|
ready to be broadcast to the network.
|
|
:param self: self
|
|
:type self: TX
|
|
:param rtype: Whether the serialized transaction is returned as a hex str or a byte array.
|
|
:type rtype: hex or bool
|
|
:return: Serialized transaction representation (hexadecimal or bin depending on rtype parameter).
|
|
:rtype: hex str / bin
|
|
"""
|
|
|
|
if rtype not in [hex, bin]:
|
|
raise Exception("Invalid return type (rtype). It should be either hex or bin.")
|
|
serialized_tx = change_endianness(int2bytes(self.version, 4)) # 4-byte version number (LE).
|
|
|
|
# INPUTS
|
|
serialized_tx += encode_varint(self.inputs) # Varint number of inputs.
|
|
|
|
for i in range(self.inputs):
|
|
serialized_tx += change_endianness(self.prev_tx_id[i]) # 32-byte hash of the previous transaction (LE).
|
|
serialized_tx += change_endianness(int2bytes(self.prev_out_index[i], 4)) # 4-byte output index (LE)
|
|
serialized_tx += encode_varint(len(self.scriptSig[i]) // 2) # Varint input script length.
|
|
# ScriptSig
|
|
serialized_tx += self.scriptSig[i] # Input script.
|
|
serialized_tx += int2bytes(self.nSequence[i], 4) # 4-byte sequence number.
|
|
|
|
# OUTPUTS
|
|
serialized_tx += encode_varint(self.outputs) # Varint number of outputs.
|
|
|
|
if self.outputs != 0:
|
|
for i in range(self.outputs):
|
|
serialized_tx += change_endianness(int2bytes(self.value[i], 8)) # 8-byte field Satoshi value (LE)
|
|
# ScriptPubKey
|
|
serialized_tx += encode_varint(len(self.scriptPubKey[i]) // 2) # Varint Output script length.
|
|
serialized_tx += self.scriptPubKey[i] # Output script.
|
|
|
|
serialized_tx += int2bytes(self.nLockTime, 4) # 4-byte lock time field
|
|
|
|
# If return type has been set to binary, the serialized transaction is converted.
|
|
if rtype is bin:
|
|
serialized_tx = unhexlify(serialized_tx)
|
|
|
|
return serialized_tx
|
|
|
|
@staticmethod
|
|
def create_dummy_transaction(prev_tx_id=None, prev_out_index=None):
|
|
tx = TX()
|
|
|
|
if prev_tx_id is None:
|
|
prev_tx_id = urandom(32).hex()
|
|
|
|
if prev_out_index is None:
|
|
prev_out_index = 0
|
|
|
|
tx.version = 1
|
|
tx.inputs = 1
|
|
tx.outputs = 1
|
|
tx.prev_tx_id = [prev_tx_id]
|
|
tx.prev_out_index = [prev_out_index]
|
|
tx.nLockTime = 0
|
|
tx.scriptSig = [
|
|
"47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860"
|
|
"a4acdd12909d831cc56cbbac4622082221a8768d1d0901"
|
|
]
|
|
tx.scriptSig_len = [77]
|
|
tx.nSequence = [4294967295]
|
|
tx.value = [5000000000]
|
|
tx.scriptPubKey = [
|
|
"4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c"
|
|
"1b7303b8a0626f1baded5c72a704f7e6cd84cac"
|
|
]
|
|
tx.scriptPubKey_len = [67]
|
|
|
|
return tx.serialize()
|