Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add check for duplicate transactional inputs and outputs in validators #1861

Merged
merged 1 commit into from
May 18, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions base_layer/core/src/validation/block_validators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,22 @@ impl StatelessBlockValidator {

impl StatelessValidation<Block> for StatelessBlockValidator {
/// The consensus checks that are done (in order of cheapest to verify to most expensive):
/// 1. Is there precisely one Coinbase output and is it correctly defined?
/// 1. Is the accounting correct?
/// 1. Are all inputs allowed to be spent (Are the feature flags satisfied)
/// 1. Is the block weight of the block under the prescribed limit?
/// 1. Does it contain only unique inputs and outputs?
/// 1. Where all the rules for the spent outputs followed?
/// 1. Was cut through applied in the block?
fn validate(&self, block: &Block) -> Result<(), ValidationError> {
let block_id = format!("block #{} ({})", block.header.height, block.hash().to_hex());
check_coinbase_output(block, &self.consensus_constants)?;
trace!(target: LOG_TARGET, "SV - Coinbase output is ok for {} ", &block_id);
check_block_weight(block, &self.consensus_constants)?;
trace!(target: LOG_TARGET, "SV - Block weight is ok for {} ", &block_id);
check_duplicate_transactions_inputs(block)?;
trace!(
target: LOG_TARGET,
"SV - No duplicate inputs or outputs for {} ",
&block_id
);
// Check that the inputs are are allowed to be spent
block.check_stxo_rules().map_err(BlockValidationError::from)?;
trace!(target: LOG_TARGET, "SV - Output constraints are ok for {} ", &block_id);
Expand Down Expand Up @@ -108,12 +115,18 @@ impl<B: BlockchainBackend> Validation<Block, B> for FullConsensusValidator {
trace!(target: LOG_TARGET, "FCV - Coinbase output ok for {}", &block_id);
check_block_weight(block, &self.rules.consensus_constants())?;
trace!(target: LOG_TARGET, "FCV - Block weight ok for {}", &block_id);
check_duplicate_transactions_inputs(block)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the description of the check to the validate function doc string list.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

trace!(
target: LOG_TARGET,
"FCV - No duplicate inputs or outputs for {} ",
&block_id
);
check_cut_through(block)?;
trace!(target: LOG_TARGET, "FCV - Cut-though correct for {}", &block_id);
block.check_stxo_rules().map_err(BlockValidationError::from)?;
trace!(target: LOG_TARGET, "FCV - Cut-though correct for {}", &block_id);
trace!(target: LOG_TARGET, "FCV - STxO rules correct for {}", &block_id);
check_accounting_balance(block, self.rules.clone(), &self.factories)?;
trace!(target: LOG_TARGET, "FCV - Cut-though correct for {}", &block_id);
trace!(target: LOG_TARGET, "FCV - accounting balance correct for {}", &block_id);
check_inputs_are_utxos(block, db)?;
trace!(target: LOG_TARGET, "FCV - All inputs are valid for {}", &block_id);
check_mmr_roots(block, db)?;
Expand Down Expand Up @@ -179,6 +192,26 @@ fn check_coinbase_output(block: &Block, consensus_constants: &ConsensusConstants
.map_err(ValidationError::from)
}

// This function checks for duplicate inputs and outputs. There should be no duplicate inputs or outputs in a block
fn check_duplicate_transactions_inputs(block: &Block) -> Result<(), ValidationError> {
trace!(
target: LOG_TARGET,
"Checking duplicate inputs and outputs on block with hash {}",
block.hash().to_hex()
);
for i in 1..block.body.inputs().len() {
if block.body.inputs()[i..].contains(&block.body.inputs()[i - 1]) {
return Err(ValidationError::CustomError("Duplicate Input".to_string()));
}
}
for i in 1..block.body.outputs().len() {
if block.body.outputs()[i..].contains(&block.body.outputs()[i - 1]) {
return Err(ValidationError::CustomError("Duplicate Output".to_string()));
}
}
Ok(())
}

/// This function checks that all inputs in the blocks are valid UTXO's to be spend
fn check_inputs_are_utxos<B: BlockchainBackend>(block: &Block, db: &B) -> Result<(), ValidationError> {
for utxo in block.body.inputs() {
Expand Down