From 211c2782a21c54dba844a40e346f4ac7e3bcf7a9 Mon Sep 17 00:00:00 2001 From: Hansie Odendaal Date: Tue, 26 Mar 2024 14:25:55 +0200 Subject: [PATCH] Add validation for zero conf Added validation checks to ensure an input in a block that is also an output in the same block will be accepted when syncing blocks. --- .../aggregate_body_chain_validator.rs | 54 +++++++++++++------ base_layer/core/src/validation/helpers.rs | 5 +- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs b/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs index 67c0824c20..ffb95f148b 100644 --- a/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs +++ b/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs @@ -110,27 +110,44 @@ fn validate_input_not_pruned( db: &B, ) -> Result, ValidationError> { let mut inputs: Vec = body.inputs().clone(); + let outputs: Vec = body.outputs().clone(); for input in &mut inputs { if input.is_compact() { - let output_mined_info = db - .fetch_output(&input.output_hash())? - .ok_or(ValidationError::UnknownInput)?; + let output = match db.fetch_output(&input.output_hash()) { + Ok(val) => match val { + Some(output_mined_info) => output_mined_info.output, + None => { + let input_output_hash = input.output_hash(); + if let Some(found) = outputs.iter().find(|o| o.hash() == input_output_hash) { + found.clone() + } else { + warn!( + target: LOG_TARGET, + "Input not found in database or block, commitment: {}, hash: {}", + input.commitment()?.to_hex(), input_output_hash.to_hex() + ); + return Err(ValidationError::UnknownInput); + } + }, + }, + Err(e) => return Err(ValidationError::from(e)), + }; - let rp_hash = match output_mined_info.output.proof { + let rp_hash = match output.proof { Some(proof) => proof.hash(), None => FixedHash::zero(), }; input.add_output_data( - output_mined_info.output.version, - output_mined_info.output.features, - output_mined_info.output.commitment, - output_mined_info.output.script, - output_mined_info.output.sender_offset_public_key, - output_mined_info.output.covenant, - output_mined_info.output.encrypted_data, - output_mined_info.output.metadata_signature, + output.version, + output.features, + output.commitment, + output.script, + output.sender_offset_public_key, + output.covenant, + output.encrypted_data, + output.metadata_signature, rp_hash, - output_mined_info.output.minimum_value_promise, + output.minimum_value_promise, ); } } @@ -207,11 +224,16 @@ fn check_inputs_are_utxos(db: &B, body: &AggregateBody) -> } let output_hashes = output_hashes.as_ref().unwrap(); - let output_hash = input.output_hash(); - if output_hashes.iter().any(|output| output == &output_hash) { + let input_output_hash = input.output_hash(); + if output_hashes.iter().any(|val| val == &input_output_hash) { continue; } - not_found_inputs.push(output_hash); + warn!( + target: LOG_TARGET, + "Input not found in database, commitment: {}, hash: {}", + input.commitment()?.to_hex(), input_output_hash.to_hex() + ); + not_found_inputs.push(input_output_hash); }, Err(err) => { return Err(err); diff --git a/base_layer/core/src/validation/helpers.rs b/base_layer/core/src/validation/helpers.rs index cc6706fa5d..4ceac26a70 100644 --- a/base_layer/core/src/validation/helpers.rs +++ b/base_layer/core/src/validation/helpers.rs @@ -163,7 +163,8 @@ pub fn is_all_unique_and_sorted<'a, I: IntoIterator, T: PartialOrd true } -/// This function checks that an input is a valid spendable UTXO +/// This function checks that an input is a valid spendable UTXO in the database. It cannot confirm +/// zero confermation transactions. pub fn check_input_is_utxo(db: &B, input: &TransactionInput) -> Result<(), ValidationError> { let output_hash = input.output_hash(); if let Some(utxo_hash) = db.fetch_unspent_output_hash_by_commitment(input.commitment()?)? { @@ -203,7 +204,7 @@ pub fn check_input_is_utxo(db: &B, input: &TransactionInpu warn!( target: LOG_TARGET, - "Validation failed due to input: {} which does not exist yet", input + "Input ({}, {}) does not exist in the database yet", input.commitment()?.to_hex(), output_hash.to_hex() ); Err(ValidationError::UnknownInput) }