From 18ec780153668eefbfb53752690014d71431ddd2 Mon Sep 17 00:00:00 2001 From: obamwonyi Date: Wed, 8 May 2024 15:27:44 +0100 Subject: [PATCH] fixed fetching scriptsig and witness from vout to vin --- src/__pycache__/validation.cpython-310.pyc | Bin 4870 -> 7747 bytes src/validation.py | 165 ++++++++++++++++++--- 2 files changed, 141 insertions(+), 24 deletions(-) diff --git a/src/__pycache__/validation.cpython-310.pyc b/src/__pycache__/validation.cpython-310.pyc index 621186a5429526a910c489b9333065c5aceafa16..9473ad6b647dfb1cb23be0b0b3200abf51d84e2f 100644 GIT binary patch literal 7747 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 XybLQemNvU=tL-l1Gb*RcP#pDNT^dy) diff --git a/src/validation.py b/src/validation.py index ae39e24..73e2704 100644 --- a/src/validation.py +++ b/src/validation.py @@ -1,4 +1,6 @@ import asyncio +import re +import time class ValidateTransaction: @@ -43,18 +45,72 @@ async def verify_input_script(self, prev_tx, vout, script_pubkey) -> bool: async def is_valid_output_script(self, scriptpubkey): """ Validate the script public key of the output - :param scriptpubkey: - :return: + :param scriptpubkey: The script public key to validate + :return: True if the script public key is valid, False otherwise """ + # Define a regular expression pattern to match P2PKH script public keys + p2pkh_pattern = r'^OP_DUP OP_HASH160 [0-9a-fA-F]{40} OP_EQUALVERIFY OP_CHECKSIG$' + + # Check if the script public key matches the P2PKH pattern + if re.match(p2pkh_pattern, scriptpubkey): + return True + else: + return False async def validate_transaction_amount(self, tx) -> bool: + async def validate_transaction_amount(self, tx) -> bool: + """ + Validates the transaction amount + :param tx: The transaction to validate + :return: True if the transaction amount is valid, False otherwise + """ + total_input_value = 0 + total_output_value = 0 + + # Calculate the total input value + for tx_input in tx.get("vin", []): + txid = tx_input.get("txid") + vout = tx_input.get("vout") + + # Retrieve the previous transaction output + prev_tx = await self.retrieve_transaction(txid) + if prev_tx is None: + # Previous transaction not found, skip this input + continue + + # Get the value of the previous output + prev_output = prev_tx.get("vout", [])[vout] + if prev_output: + total_input_value += prev_output.get("value", 0) + + # Calculate the total output value + for output in tx.get("vout", []): + total_output_value += output.get("value", 0) + + # Check if the total input value is greater than or equal to the total output value + return total_input_value >= total_output_value + + async def validate_script(self, prev_tx, vout, script_pubkey): """ - Validates the transaction amount - :param tx: - :return: bool + Validate the input script (scriptSig or witness) against the previous output's script public key + :param prev_tx: The previous transaction + :param vout: The index of the previous output + :param script_pubkey: The script public key of the previous output + :return: True if the input script is valid, False otherwise """ - # Implement logic to validate the transaction amount - pass + prev_input = prev_tx.get("vin", [])[0] # Assuming there's only one input + if not prev_input: + return False + + scriptsig = prev_input.get("scriptsig") + witness = prev_input.get("witness", []) + + print(f"scriptsig : {scriptsig}") + print(f"witness : {witness}") + # Validate the input script against the previous output's script public key + # ... + + return False async def get_prev_tx_output(self, txid, vout) -> (list, None): """ @@ -75,18 +131,16 @@ async def get_prev_tx_output(self, txid, vout) -> (list, None): if vout >= len(prev_output): return None, None - return prev_tx, prev_output - # Step 3: Verify that the output is unspent - # if not await self.is_unspent(prev_tx, vout): - # return None, None + if await self.is_spent(prev_tx, vout): + return None, None - # # Step 4: Retrieve the script pub key from the referred output - # script_pubkey = prev_tx.get("vout")[0].get("scriptpubkey") - # - # # Step 5: Validate the scriptSig or Witness - # if not await self.validate_script(prev_tx, vout, script_pubkey): - # return None, None + # Step 4: Retrieve the script pub key from the referred output + script_pubkey = prev_tx.get("vout")[0].get("scriptpubkey") + + # Step 5: Validate the scriptSig or Witness + if not await self.validate_script(prev_tx, vout, script_pubkey): + return None, None # Step 6: Ensure the sum of input values is greater than or equal to the sum of output values if not await self.validate_transaction_amount(prev_tx): @@ -96,18 +150,81 @@ async def get_prev_tx_output(self, txid, vout) -> (list, None): async def validate_locktime_and_sequence(self, tx): """ - Validates Transactions locktime - :param tx: - :return: + Validates the locktime and sequence fields of a transaction. + :param tx: The transaction to validate + :return: True if the locktime and sequence are valid, False otherwise + """ + locktime = tx.get("locktime") + sequence = tx.get("vin", [])[0].get("sequence", 0xFFFFFFFF) + + # Check if the transaction is a relative time-locked transaction + if sequence < 0xFFFFFFFF: + # Add logic to validate relative time-locked transactions + pass + + # Check if the transaction has a non-zero locktime + if locktime != 0: + # Check if the locktime is a block height + if locktime < 500000000: + # Add logic to validate locktime as a block height + pass + else: + # Validate locktime as a Unix timestamp + current_time = int(time.time()) + if current_time < locktime: + return False + + return True + + async def is_spent(self, tx, vout): + """ + Check if a specific output of a transaction has already been spent + :param tx: The transaction to check + :param vout: The output index to check + :return: True if the output is spent, False otherwise """ - pass + # Get the transaction ID + txid = tx.get("txid") + + # Iterate over all transactions + for other_tx in self.transaction_by_id.values(): + # Skip the transaction we're checking + if other_tx.get("txid") == txid: + continue + + # Check if any input of the other transaction spends the output we're checking + for other_input in other_tx.get("vin", []): + if other_input.get("txid") == txid and other_input.get("vout") == vout: + # Output is spent by this input + return True + + # Output is not spent + return False async def is_double_spend(self, tx): """ Check if the transaction is double spent - :param tx: - :return: + :param tx: The transaction to check + :return: True if the transaction is double spent, False otherwise """ + for tx_input in tx.get("vin", []): + txid = tx_input.get("txid") + vout = tx_input.get("vout") + + # Retrieve the previous transaction + prev_tx = await self.retrieve_transaction(txid) + if prev_tx is None: + # Previous transaction not found, assume not double spent + continue + + # Check if the referred output is already spent + if await self.is_spent(prev_tx, vout): + # Output is already spent, this is a double spend + return True + + # No double spend detected + return False + async def validate_transaction(self, tx) -> (bool, str): """ :param tx: @@ -146,7 +263,7 @@ async def validate_transaction(self, tx) -> (bool, str): if self.is_double_spend(tx): return False - + print("Everything is valid") return True # print("Transaction has valid version") tested