1212import math
1313import hashlib
1414import struct
15- from typing import Optional
15+ from typing import Optional , Union
1616
1717from bitcoinutils .constants import (
1818 DEFAULT_TX_SEQUENCE ,
@@ -141,7 +141,7 @@ def __repr__(self):
141141 return self .__str__ ()
142142
143143 @staticmethod
144- def from_raw (txinputrawhex : str , cursor : int = 0 , has_segwit : bool = False ):
144+ def from_raw (txinputrawhex : Union [ str , bytes ] , cursor : int = 0 , has_segwit : bool = False ):
145145 """
146146 Imports a TxInput from a Transaction's hexadecimal data
147147
@@ -154,7 +154,12 @@ def from_raw(txinputrawhex: str, cursor: int = 0, has_segwit: bool = False):
154154 has_segwit : boolean
155155 Is the Tx Input segwit or not
156156 """
157- txinputraw = h_to_b (txinputrawhex )
157+ if isinstance (txinputrawhex , str ):
158+ txinputraw = h_to_b (txinputrawhex )
159+ elif isinstance (txinputrawhex , bytes ):
160+ txinputraw = txinputrawhex
161+ else :
162+ raise TypeError ("Input must be a hexadecimal string or bytes" )
158163
159164 # Unpack transaction ID (hash) in bytes and output index
160165 txid , vout = struct .unpack_from ('<32sI' , txinputraw , cursor )
@@ -286,7 +291,7 @@ def to_bytes(self) -> bytes:
286291 return data
287292
288293 @staticmethod
289- def from_raw (txoutputrawhex : str , cursor : int = 0 , has_segwit : bool = False ):
294+ def from_raw (txoutputrawhex : Union [ str , bytes ] , cursor : int = 0 , has_segwit : bool = False ):
290295 """
291296 Imports a TxOutput from a Transaction's hexadecimal data
292297
@@ -299,7 +304,13 @@ def from_raw(txoutputrawhex: str, cursor: int = 0, has_segwit: bool = False):
299304 has_segwit : boolean
300305 Is the Tx Output segwit or not
301306 """
302- txoutputraw = h_to_b (txoutputrawhex )
307+ if isinstance (txoutputrawhex , str ):
308+ txoutputraw = h_to_b (txoutputrawhex )
309+ elif isinstance (txoutputrawhex , bytes ):
310+ txoutputraw = txoutputrawhex
311+ else :
312+ raise TypeError ("Input must be a hexadecimal string or bytes" )
313+
303314
304315 # Unpack the amount of the TxOutput directly in bytes
305316 amount_format = "<Q" # Little-endian unsigned long long (8 bytes)
@@ -526,7 +537,7 @@ def __init__(
526537 self .version = version
527538
528539 @staticmethod
529- def from_raw (rawtxhex : str ):
540+ def from_raw (rawtxhex : Union [ str , bytes ] ):
530541 """
531542 Imports a Transaction from hexadecimal data.
532543
@@ -535,7 +546,13 @@ def from_raw(rawtxhex: str):
535546 rawtxhex : string (hex)
536547 The hexadecimal raw string of the Transaction.
537548 """
538- rawtx = h_to_b (rawtxhex )
549+ if isinstance (rawtxhex , str ):
550+ rawtx = h_to_b (rawtxhex )
551+ elif isinstance (rawtxhex , bytes ):
552+ rawtx = rawtxhex
553+ else :
554+ raise TypeError ("Input must be a hexadecimal string or bytes" )
555+
539556
540557 # Read version (4 bytes)
541558 version = rawtx [0 :4 ]
@@ -567,9 +584,11 @@ def from_raw(rawtxhex: str):
567584 output , cursor = TxOutput .from_raw (rawtx .hex (), cursor , has_segwit )
568585 outputs .append (output )
569586
570- # Handle witnesses if SegWit is enabled
587+ # Handle witnesses if SegWit is enabled and if they are present i.e. if
588+ # remaining payload length is greater than last tx field length (locktime)
589+ has_witness_field = True if len (rawtx ) - cursor > 4 else False
571590 witnesses = []
572- if has_segwit :
591+ if has_segwit and has_witness_field :
573592 for _ in range (n_inputs ):
574593 n_items , size = parse_compact_size (rawtx [cursor :])
575594 cursor += size
@@ -652,12 +671,13 @@ def get_transaction_digest(
652671 for txin in tmp_tx .inputs :
653672 txin .script_sig = Script ([])
654673
655- # If there are OP_CODESEPARATORs in the script, we need to remove them
656- # for the signature hash calculation
657- if "OP_CODESEPARATOR" in script .script :
658- # Create a new script without OP_CODESEPARATORs
659- filtered_script_elements = [element for element in script .script if element != "OP_CODESEPARATOR" ]
660- script = Script (filtered_script_elements )
674+ #
675+ # TODO Deal with (delete?) script's OP_CODESEPARATORs, if any
676+ # Very early versions of Bitcoin were using a different design for
677+ # scripts that were flawed. OP_CODESEPARATOR has no purpose currently
678+ # but we could not delete it for compatibility purposes. If it exists
679+ # in a script it needs to be removed.
680+ #
661681
662682 # the temporary transaction's scriptSig needs to be set to the
663683 # scriptPubKey of the UTXO we are trying to spend - this is required to
@@ -843,6 +863,7 @@ def get_transaction_segwit_digest(
843863 return hashlib .sha256 (hashlib .sha256 (tx_for_signing ).digest ()).digest ()
844864
845865 # TODO Update doc with TAPROOT_SIGHASH_ALL
866+ # clean prints after finishing other sighashes
846867 def get_transaction_taproot_digest (
847868 self ,
848869 txin_index : int ,
@@ -859,11 +880,11 @@ def get_transaction_taproot_digest(
859880 And: https://github.com/bitcoin/bitcoin/blob/b5f33ac1f82aea290b4653af36ac2ad1bf1cce7b/test/functional/test_framework/script.py
860881
861882 | SIGHASH types (see constants.py):
862- | TAPROOT_SIGHASH_ALL (0x00) - signs all inputs and outputs (default)
863- | SIGHASH_ALL (0x01) - signs all inputs and outputs
864- | SIGHASH_NONE (0x02) - signs all of the inputs
865- | SIGHASH_SINGLE (0x03) - signs all inputs but only txin_index output
866- | SIGHASH_ANYONECANPAY (0x80) ( only combined with one of the above)
883+ | TAPROOT_SIGHASH_ALL - signs all inputs and outputs (default)
884+ | SIGHASH_ALL - signs all inputs and outputs
885+ | SIGHASH_NONE - signs all of the inputs
886+ | SIGHASH_SINGLE - signs all inputs but only txin_index output
887+ | SIGHASH_ANYONECANPAY (only combined with one of the above)
867888 | - with ALL - signs all outputs but only txin_index input
868889 | - with NONE - signs only the txin_index input
869890 | - with SINGLE - signs txin_index input and output
@@ -886,6 +907,10 @@ def get_transaction_taproot_digest(
886907 The type of the signature hash to be created
887908 """
888909
910+ # clone transaction to modify without messing up the real transaction
911+ # tmp_tx is not really used for its to_bytes() here
912+ # TODO we could use self directly to access fields
913+ tmp_tx = Transaction .copy (self )
889914
890915 # acquiring the signature type
891916 # sign_all = sig_hash & 0x03 == SIGHASH_ALL
@@ -914,8 +939,9 @@ def get_transaction_taproot_digest(
914939
915940 # Data about the transaction
916941 if not anyone_can_pay :
942+ # print('1')
917943 # the SHA256 of the serialization of all input outpoints
918- for txin in self .inputs :
944+ for txin in tmp_tx .inputs :
919945 hash_prevouts += h_to_b (txin .txid )[::- 1 ] + struct .pack (
920946 "<I" ,
921947 txin .txout_index ,
@@ -938,13 +964,14 @@ def get_transaction_taproot_digest(
938964 tx_for_signing += hash_script_pubkeys
939965
940966 # the SHA256 of the serialization of all input nSequence
941- for txin in self .inputs :
967+ for txin in tmp_tx .inputs :
942968 hash_sequences += txin .sequence
943969 hash_sequences = hashlib .sha256 (hash_sequences ).digest ()
944970 tx_for_signing += hash_sequences
945971
946972 if not (sighash_none or sighash_single ):
947- for txout in self .outputs :
973+ # print('2')
974+ for txout in tmp_tx .outputs :
948975 amount_bytes = struct .pack ("<Q" , txout .amount )
949976 script_bytes = txout .script_pubkey .to_bytes ()
950977 hash_outputs += (
@@ -959,7 +986,8 @@ def get_transaction_taproot_digest(
959986 tx_for_signing += bytes ([spend_type ])
960987
961988 if anyone_can_pay :
962- txin = self .inputs [txin_index ]
989+ # print('3')
990+ txin = tmp_tx .inputs [txin_index ]
963991 # convert txid to big-endian first
964992 tx_for_signing += h_to_b (txin .txid )[::- 1 ] + struct .pack (
965993 "<I" ,
@@ -974,15 +1002,16 @@ def get_transaction_taproot_digest(
9741002
9751003 tx_for_signing += txin .sequence
9761004 else :
1005+ # print('4')
9771006 tx_for_signing += txin_index .to_bytes (4 , "little" )
9781007
979- if hasattr (self , 'annex' ) and self .annex is not None :
980- annex_data = self .annex [txin_index ] if isinstance (self .annex , list ) else self .annex
981- tx_for_signing += prepend_compact_size (h_to_b (annex_data ))
1008+ # TODO if annex is present it should be added here
1009+ # length of annex should use compact_size
9821010
9831011 # Data about this output
9841012 if sighash_single :
985- txout = self .outputs [txin_index ]
1013+ # print('5')
1014+ txout = tmp_tx .outputs [txin_index ]
9861015 amount_bytes = struct .pack ("<Q" , txout .amount )
9871016 script_bytes = txout .script_pubkey .to_bytes ()
9881017 hash_output = (
0 commit comments