diff --git a/SOLUTION.md b/SOLUTION.md index 1ad1a5e..88560d6 100644 --- a/SOLUTION.md +++ b/SOLUTION.md @@ -1,6 +1,10 @@ # Design Approach -Describe the approach you took to design your block construction program, explain all the key concepts of creating a valid block. +## Check Structure of Transactions + +First of all we need to check the structure of the transaction which includes version, locktime, inputs and outputs. + + diff --git a/main.py b/main.py index e69de29..f426cd3 100644 --- a/main.py +++ b/main.py @@ -0,0 +1,4 @@ +from .python_files import structural_check + +# Run the structural check on the transactions in the mempool +structural_check.check_structure_transactions("./mempool") diff --git a/python_files/script_validation.py b/python_files/script_validation.py new file mode 100644 index 0000000..e69de29 diff --git a/python_files/structural_check.py b/python_files/structural_check.py new file mode 100644 index 0000000..650d425 --- /dev/null +++ b/python_files/structural_check.py @@ -0,0 +1,156 @@ +import json +import os + + +def check_transaction(tx_data): + + required_fields = ["version", "locktime", "vin", "vout"] + if not all(field in tx_data for field in required_fields): + return False # Missing fields + + # Basic checks for specific field types + if not isinstance(tx_data["version"], int): + return False + if not isinstance(tx_data["locktime"], int): + return False + + # Call the vin and vout checker functions + if not validate_vin(tx_data["vin"]): + return False + if not validate_vout(tx_data["vout"]): + return False + + return True # Passes basic checks + + +def validate_vin(vin): + # Validates a single vin element with specific checks + + # Check txid + if not vin.get("txid") or not isinstance(vin["txid"], str): + return False, "txid is empty or invalid type" + + # Check vout + if not vin.get("vout") or not isinstance(vin["vout"], int): + return False, "vout is empty or invalid type" + + # Check prevout + prevout = vin.get("prevout") + if not prevout: + return False, "prevout is missing" + required_prevout_fields = [ + "scriptpubkey", + "scriptpubkey_asm", + "scriptpubkey_type", + "scriptpubkey_address", + "value", + ] + for field in required_prevout_fields: + if field not in prevout: + return False, f"prevout is missing '{field}'" + + # Check scriptsig/scriptsig_asm and witness if applicable + scriptsig = vin.get("scriptsig") + scriptsig_asm = vin.get("scriptsig_asm") + + if not scriptsig and not scriptsig_asm: + # Both empty, check for witness + if not vin.get("witness"): + return ( + False, + "Both scriptsig and scriptsig_asm are empty, and witness is missing", + ) # (If both scriptsig and scriptsig_asm have data, we ignore witness) + + # Check is_coinbase + if not isinstance(vin.get("is_coinbase"), bool): + return False # is_coinbase must be true or false + + # Check sequence + if not vin.get("sequence") or not isinstance(vin["sequence"], int): + return False # sequence is empty or invalid type + + # All checks passed + return True + + +def validate_vout(vout): + # Validates a single vout element with specific checks + + # Check non-emptiness of fields + required_fields = [ + "scriptpubkey", + "scriptpubkey_asm", + "scriptpubkey_type", + "scriptpubkey_address", + ] + for field in required_fields: + if not vout.get(field) or not isinstance(vout[field], str): + return False, f"'{field}' is empty or invalid type" + + # Check value is non-empty integer + if not vout.get("value") or not isinstance(vout["value"], int): + return False, "value is empty or invalid type" + + # All checks passed + return True, "vout is valid" + + +def check_structure_transactions(mempool_folder): + + for filename in os.listdir(mempool_folder): + if filename.endswith(".json"): + filepath = os.path.join(mempool_folder, filename) + try: + with open(filepath) as f: + tx_data = json.load(f) + if check_transaction(tx_data): + print(f"Transaction: {filename} - Valid Structure") + else: + print(f"Transaction: {filename} - Missing or Invalid Fields") + except (FileNotFoundError, json.JSONDecodeError) as e: + print(f"Error processing {filename}: {e}") + + +# validate the signature script +def validate_signature_script(script): + """Validates a Bitcoin signature script. + + Args: + script: The signature script to validate. + + Returns: + True if the signature script is valid, False otherwise. + """ + + # Check if the script is empty. + if not script: + return False + + # Check if the script is a valid push data script. + if script[0] >= 0x01 and script[0] <= 0x4B: + return True + + # Check if the script is a valid opcode. + if script[0] in OP_CODES: + return True + + # Check if the script is a valid combination of push data and opcodes. + for i in range(len(script)): + if script[i] >= 0x01 and script[i] <= 0x4B: + continue + + if script[i] not in OP_CODES: + return False + + # The script is valid. + return True + + +# Example usage: + +script = "OP_DUP OP_HASH160 0x1234567890abcdef1234567890abcdef12345678 OP_EQUALVERIFY OP_CHECKSIG" + +if validate_signature_script(script): + print("The signature script is valid.") +else: + print("The signature script is invalid.")