From 5a147e096f26e1423c4cf50553df75bd16593f4f Mon Sep 17 00:00:00 2001 From: Ian Slane Date: Tue, 30 Apr 2024 03:12:44 -0600 Subject: [PATCH] finished validation.rs --- mine-your-first-block/src/validation.rs | 74 +++++++++++++++++++------ 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/mine-your-first-block/src/validation.rs b/mine-your-first-block/src/validation.rs index 0402a32..7300b30 100644 --- a/mine-your-first-block/src/validation.rs +++ b/mine-your-first-block/src/validation.rs @@ -12,14 +12,21 @@ use crate::utils::{deserialize_tx, double_sha256, get_segwit_tx_message, get_sig /// This function will read through the mempool folder and validate the transactions before adding /// them to a transaction for processing vector pub fn process_mempool(mempool_path: &str) -> io::Result> { + // Create a vector to hold the valid transactions let mut valid_txs: Vec = Vec::new(); + + // Loop through the mempool folder for tx in fs::read_dir(mempool_path)? { + // Get the path of the transaction let tx = tx?; + // Check if the path is a file let path = tx.path(); + // If it's a file, deserialize the transaction if path.is_file() { if let Some(path_str) = path.to_str() { let mut transaction = deserialize_tx(path_str); + // Initialize the variables to hold the transaction data let mut is_valid = false; let mut txid = String::new(); let mut wtxid = None; @@ -51,6 +58,7 @@ pub fn process_mempool(mempool_path: &str) -> io::Result continue, } + // If the transaction is not valid, skip it if !is_valid { //eprintln!("Transaction is not valid: {:?}", path); } @@ -65,7 +73,7 @@ pub fn process_mempool(mempool_path: &str) -> io::Result io::Result, serialized_tx: Vec) -> Result> { - // Removing the sighash type from the signature + // Removing the sighash type from the signature to get the actual signature let signature = &signature[..signature.len()-1]; - let secp = Secp256k1::new(); + // Create a new Secp256k1 object + let secp = Secp256k1::new(); + // Create a message from the double sha256 hash of the serialized transaction let hash_array: [u8; 32] = double_sha256(serialized_tx); let message_result = Message::from_digest_slice(&hash_array).unwrap(); + + // Create a public key from the pubkey let public_key = PublicKey::from_slice(&pubkey).expect("Failed to create public key"); + + // Create a signature from the der encoded signature let signature = Signature::from_der(&signature).unwrap(); + // Verify the signature with the secp.verify_ecdsa function // Return Ok(true) if the signature is valid, Ok(false) if it's invalid match secp.verify_ecdsa(&message_result, &signature, &public_key) { Ok(_) => { @@ -123,13 +139,19 @@ pub fn p2wpkh_script_validation(transaction: &mut Transaction) -> Result<(bool, // Create a stack to hold the data let mut stack: Vec> = Vec::new(); + // Loop through the vin of the transaction for (i, vin) in transaction.vin.iter().enumerate() { + // Clearing the stack for each input stack.clear(); + // Get the signature and pubkey from the witness data let witness = vin.witness.clone().ok_or("Witness data not found")?; + // Check if the witness data has at least two elements if witness.len() < 2 { return Err("Witness data is missing elements".into()); } + + // Get the signature index 0 let signature = hex::decode(witness[0].clone())?; if signature.is_empty(){ return Err("Signature is empty".into()); @@ -138,7 +160,10 @@ pub fn p2wpkh_script_validation(transaction: &mut Transaction) -> Result<(bool, // sighash type off sig let sighash_type = signature[signature.len()-1]; + // Get the pubkey index 1 let pubkey= hex::decode(witness[1].clone())?; + + // Push the signature and pubkey onto the stack stack.push(signature); stack.push(pubkey); @@ -147,7 +172,7 @@ pub fn p2wpkh_script_validation(transaction: &mut Transaction) -> Result<(bool, let parts: Vec<&str> = script_pubkey.split_whitespace().collect(); let pubkey_hash = parts.last().unwrap(); - // Message hash + // Get the preimage for the message from the get_segwit_tx_message function let message_hash = get_segwit_tx_message( &mut transaction.clone(), i, @@ -156,9 +181,11 @@ pub fn p2wpkh_script_validation(transaction: &mut Transaction) -> Result<(bool, )?; let message_in_bytes = hex::decode(&message_hash)?; - // Now it execute like a p2pkh locking script where the pubkeyhah is pushed after ophash160 + // Now it execute like a p2pkh script let script_pubkey_asm = format!("OP_DUP OP_HASH160 OP_PUSHBYTES_20 {} OP_EQUALVERIFY OP_CHECKSIG", pubkey_hash); + // Loop through the script_pubkey_asm + // and execute the operations at each op code for op in script_pubkey_asm.split_whitespace(){ match op { "OP_DUP" => { @@ -189,7 +216,7 @@ pub fn p2wpkh_script_validation(transaction: &mut Transaction) -> Result<(bool, // Otherwise pop the last two items from the stack and compare them // if they are not equal return false, if they are just continue let stack_item1 = stack.pop().unwrap(); - // unsure why but i diregard an extra item on the stack + // unsure why but there is an extra item on the stack that is disregarded // then it compares the top two items let _stack_temp = stack.pop().unwrap(); let stack_item2 = stack.pop().unwrap(); @@ -210,7 +237,7 @@ pub fn p2wpkh_script_validation(transaction: &mut Transaction) -> Result<(bool, match verify_signature(signature.clone(), pubkey.clone(), message_in_bytes.clone()) { Ok(true) => { - // If the signature is valid, push a truthy value onto the stack to indicate success + // If the signature is valid, push a 1 onto the stack stack.push(vec![1]); }, Ok(false) => { @@ -233,30 +260,30 @@ pub fn p2wpkh_script_validation(transaction: &mut Transaction) -> Result<(bool, } _ => { // If it's not an operator, it's an ordinary data (like sig or pubkey) and push it onto the stack - // Verify !!! let data = hex::decode(op).unwrap_or_default(); // Convert hex string to bytes stack.push(data); } } } + // If the stack is not 1 or empty return false if stack.len() != 1 || stack.is_empty() { return Err(format!("Final stack validation failed for input {}", i).into()); } } - // May need to change this a bit... - // FOR WTXID + // FOR WTXID serialize for wtxid, then hash it and reverse it let serialized_validwtx = serialized_segwit_wtx(transaction); let wtx_bytes = hex::decode(serialized_validwtx.clone())?; let wtxid_be = double_sha256(wtx_bytes); let wtxid = reverse_bytes(wtxid_be.to_vec()); - // FOR TXID + // FOR TXID serialize for txid, then hash it and reverse it let serialized_validtx = serialized_segwit_tx(transaction); let tx_bytes = hex::decode(serialized_validtx).unwrap(); let txid_be = double_sha256(tx_bytes); let txid = reverse_bytes(txid_be.to_vec()); + // Return true if the transaction is valid, the wtxid and txid Ok((true, wtxid, txid)) } @@ -266,6 +293,7 @@ pub fn p2pkh_script_validation(transaction: &mut Transaction) -> Result<(bool, S // Create a stack to hold the data let mut stack: Vec> = Vec::new(); + // Loop through the vin of the transaction for (i,vin) in transaction.vin.iter().enumerate() { // Clearing the stack @@ -275,6 +303,7 @@ pub fn p2pkh_script_validation(transaction: &mut Transaction) -> Result<(bool, S let scriptsig = &vin.scriptsig; let script_pub_key = &vin.prevout.scriptpubkey_asm.clone(); + // Get the signature and pubkey from the scriptsig let (signature, pubkey) = get_signature_and_publickey_from_scriptsig(scriptsig) .map_err(|e| format!("Error getting signature and public key from scriptsig for input {}: {}", i, e))?; @@ -290,11 +319,13 @@ pub fn p2pkh_script_validation(transaction: &mut Transaction) -> Result<(bool, S let message_in_bytes = hex::decode(serialized_tx_for_message) .map_err(|_e| format!("Failed to decode the hex string for input: {}", i))?; + // Push the signature and pubkey onto the stack let decoded_signature = hex::decode(signature).map_err(|e| format!("Failed to decode signature: {}", e))?; let decoded_pubkey = hex::decode(pubkey).map_err(|e| format!("Failed to decode pubkey: {}", e))?; stack.push(decoded_signature); stack.push(decoded_pubkey); + // Loop through the script_pub_key for op in script_pub_key.split_whitespace() { match op { "OP_DUP" => { @@ -323,10 +354,10 @@ pub fn p2pkh_script_validation(transaction: &mut Transaction) -> Result<(bool, S return Err(format!("Stack underflow in OP_EQUALVERIFY for input {}", i).into()); } // Otherwise pop the last two items from the stack and compare them - // if they are not equal return false, if they are just continue + // If they are not equal return false, if they are just continue let stack_item1 = stack.pop().unwrap(); - // unsure why but i diregard an extra item on the stack - // then it compares the top two items + // Unsure why but there is an extra item on the stack that is disregarded " " + // Then it compares the top two items let _stack_temp = stack.pop().unwrap(); let stack_item2 = stack.pop().unwrap(); if stack_item1 != stack_item2 { @@ -338,7 +369,7 @@ pub fn p2pkh_script_validation(transaction: &mut Transaction) -> Result<(bool, S if stack.len() < 2 { return Err(format!("Stack underflow in OP_CHECKSIG for input {}", i).into()); } - // otherwise pop the last two items from the stack (pubkey and signature) + // Otherwise pop the last two items from the stack (pubkey and signature) // and validate the signature let pubkey = stack.pop().unwrap(); let signature = stack.pop().unwrap(); @@ -346,7 +377,7 @@ pub fn p2pkh_script_validation(transaction: &mut Transaction) -> Result<(bool, S match verify_signature(signature.clone(), pubkey.clone(), message_in_bytes.clone()) { Ok(true) => { - // If the signature is valid, push a truthy value onto the stack to indicate success + // If the signature is valid, push a 1 onto the stack stack.push(vec![1]); }, Ok(false) => { @@ -368,23 +399,25 @@ pub fn p2pkh_script_validation(transaction: &mut Transaction) -> Result<(bool, S } } _ => { - // If it's not an operator,it'a ordinary data (like sig or pubkey) and push it onto the stack - // Verify !!! + // If it's not an operator, it's ordinary data (like sig or pubkey) and push it onto the stack let data = hex::decode(op).unwrap_or_default(); // Convert hex string to bytes stack.push(data); } } } + // If the stack is not 1 or empty return false if stack.len() != 1 || stack.is_empty() { return Err(format!("Final stack validation failed for input {}", i).into()); } } + // serialize for txid, then hash it and reverse it let serialized_validtx = serialize_tx(transaction); let tx_bytes = hex::decode(serialized_validtx).unwrap(); let txid_be = double_sha256(tx_bytes); let txid = reverse_bytes(txid_be.to_vec()); + // Return true if the transaction is valid and the txid Ok((true, txid)) } @@ -392,14 +425,17 @@ pub fn p2pkh_script_validation(transaction: &mut Transaction) -> Result<(bool, S // Helper functions to weed out bad transactions. /// Function to get the tx amount so pub fn verify_tx_fee(transaction: &Transaction) -> u64 { + // Calculate the total input amount let total_input_amount: u64 = transaction.vin.iter() .map(|input| input.prevout.value) .sum(); + // Calculate the total output amount let total_output_amount: u64 = transaction.vout.iter() .map(|output| output.value) .sum(); + // Return the fee total_input_amount - total_output_amount } @@ -425,8 +461,10 @@ pub fn check_double_spending(transaction: &Transaction, mempool: &Vec bool { let target_string = "0000ffff00000000000000000000000000000000000000000000000000000000"; + // Convert the target and hash to a big int let target = U256::from_str_radix(target_string, 16).unwrap(); let hash_as_num = U256::from_str_radix(hash, 16).unwrap(); + // Return true if the hash is less than the target hash_as_num < target }