Skip to content

Commit

Permalink
fix(core): add txo version checks to async validator (#4852)
Browse files Browse the repository at this point in the history
Description
---
adds txo/kernel version checks to async validator

Motivation and Context
---
Version checks were missing from async validator

Ref #4836 

How Has This Been Tested?
---
Manually: Rewind a few blocks and resync
  • Loading branch information
sdbondi committed Oct 27, 2022
1 parent 5b0eb04 commit 2cf51b8
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 53 deletions.
Expand Up @@ -160,6 +160,7 @@ impl<B: BlockchainBackend + 'static> BlockValidator<B> {
.commitment
.commit_value(&total_kernel_offset, total_reward.as_u64());
let db = self.db.inner().clone();
let constants = self.rules.consensus_constants(height).clone();
task::spawn_blocking(move || {
let db = db.db_read_access()?;
let timer = Instant::now();
Expand All @@ -175,6 +176,7 @@ impl<B: BlockchainBackend + 'static> BlockValidator<B> {
return Err(ValidationError::UnsortedOrDuplicateKernel);
}

helpers::validate_kernel_version(&constants, kernel)?;
kernel.verify_signature()?;

if kernel.is_coinbase() {
Expand Down Expand Up @@ -248,6 +250,7 @@ impl<B: BlockchainBackend + 'static> BlockValidator<B> {
let db = self.db.inner().clone();
let prev_hash: [u8; 32] = header.prev_hash.as_slice().try_into().unwrap_or([0; 32]);
let height = header.height;
let constants = self.rules.consensus_constants(height).clone();
task::spawn_blocking(move || {
let timer = Instant::now();
let mut aggregate_input_key = PublicKey::default();
Expand Down Expand Up @@ -296,6 +299,8 @@ impl<B: BlockchainBackend + 'static> BlockValidator<B> {
return Err(TransactionError::InputMaturity.into());
}

helpers::validate_input_version(&constants, input)?;

match helpers::check_input_is_utxo(&*db, input) {
Err(ValidationError::UnknownInput) => {
// Check if the input spends from the current block
Expand Down Expand Up @@ -402,6 +407,7 @@ impl<B: BlockchainBackend + 'static> BlockValidator<B> {
aggregate_sender_offset = aggregate_sender_offset + &output.sender_offset_public_key;
}

helpers::validate_output_version(&constants, output)?;
helpers::check_permitted_output_types(&constants, output)?;
helpers::check_tari_script_byte_size(&output.script, max_script_size)?;
output.verify_metadata_signature()?;
Expand Down
128 changes: 77 additions & 51 deletions base_layer/core/src/validation/helpers.rs
Expand Up @@ -713,73 +713,99 @@ pub fn check_permitted_output_types(
Ok(())
}

pub fn validate_versions(
body: &AggregateBody,
pub fn validate_input_version(
consensus_constants: &ConsensusConstants,
input: &TransactionInput,
) -> Result<(), ValidationError> {
// validate input version
for input in body.inputs() {
if !consensus_constants.input_version_range().contains(&input.version) {
let msg = format!(
"Transaction input contains a version not allowed by consensus ({:?})",
input.version
);
return Err(ValidationError::ConsensusError(msg));
}
if !consensus_constants.input_version_range().contains(&input.version) {
let msg = format!(
"Transaction input contains a version not allowed by consensus ({:?})",
input.version
);
return Err(ValidationError::ConsensusError(msg));
}

// validate output version and output features version
for output in body.outputs() {
let valid_output_version = consensus_constants
.output_version_range()
.outputs
.contains(&output.version);
Ok(())
}

let valid_features_version = consensus_constants
pub fn validate_output_version(
consensus_constants: &ConsensusConstants,
output: &TransactionOutput,
) -> Result<(), ValidationError> {
let valid_output_version = consensus_constants
.output_version_range()
.outputs
.contains(&output.version);

if !valid_output_version {
let msg = format!(
"Transaction output version is not allowed by consensus ({:?})",
output.version
);
return Err(ValidationError::ConsensusError(msg));
}

let valid_features_version = consensus_constants
.output_version_range()
.features
.contains(&output.features.version);

if !valid_features_version {
let msg = format!(
"Transaction output features version is not allowed by consensus ({:?})",
output.features.version
);
return Err(ValidationError::ConsensusError(msg));
}

for opcode in output.script.as_slice() {
if !consensus_constants
.output_version_range()
.features
.contains(&output.features.version);
if !valid_output_version {
.opcode
.contains(&opcode.get_version())
{
let msg = format!(
"Transaction output version is not allowed by consensus ({:?})",
output.version
"Transaction output script opcode is not allowed by consensus ({})",
opcode
);
return Err(ValidationError::ConsensusError(msg));
}
}

if !valid_features_version {
let msg = format!(
"Transaction output features version is not allowed by consensus ({:?})",
output.features.version
);
return Err(ValidationError::ConsensusError(msg));
}
for opcode in output.script.as_slice() {
if !consensus_constants
.output_version_range()
.opcode
.contains(&opcode.get_version())
{
let msg = format!(
"Transaction output script opcode is not allowed by consensus ({})",
opcode
);
return Err(ValidationError::ConsensusError(msg));
}
}
Ok(())
}

check_permitted_output_types(consensus_constants, output)?;
pub fn validate_kernel_version(
consensus_constants: &ConsensusConstants,
kernel: &TransactionKernel,
) -> Result<(), ValidationError> {
if !consensus_constants.kernel_version_range().contains(&kernel.version) {
let msg = format!(
"Transaction kernel version is not allowed by consensus ({:?})",
kernel.version
);
return Err(ValidationError::ConsensusError(msg));
}
Ok(())
}

pub fn validate_versions(
body: &AggregateBody,
consensus_constants: &ConsensusConstants,
) -> Result<(), ValidationError> {
// validate input version
for input in body.inputs() {
validate_input_version(consensus_constants, input)?;
}

// validate output version and output features version
for output in body.outputs() {
validate_output_version(consensus_constants, output)?;
}

// validate kernel version
for kernel in body.kernels() {
if !consensus_constants.kernel_version_range().contains(&kernel.version) {
let msg = format!(
"Transaction kernel version is not allowed by consensus ({:?})",
kernel.version
);
return Err(ValidationError::ConsensusError(msg));
}
validate_kernel_version(consensus_constants, kernel)?;
}

Ok(())
Expand Down
14 changes: 12 additions & 2 deletions base_layer/core/src/validation/transaction_validators.rs
Expand Up @@ -27,7 +27,13 @@ use crate::{
chain_storage::{BlockchainBackend, BlockchainDatabase},
transactions::{transaction_components::Transaction, CryptoFactories},
validation::{
helpers::{check_inputs_are_utxos, check_outputs, check_total_burned, validate_versions},
helpers::{
check_inputs_are_utxos,
check_outputs,
check_permitted_output_types,
check_total_burned,
validate_versions,
},
MempoolTransactionValidation,
ValidationError,
},
Expand Down Expand Up @@ -129,7 +135,11 @@ impl<B: BlockchainBackend> MempoolTransactionValidation for TxConsensusValidator

self.validate_excess_sig_not_in_db(tx)?;

validate_versions(tx.body(), consensus_constants)
validate_versions(tx.body(), consensus_constants)?;
for output in tx.body.outputs() {
check_permitted_output_types(consensus_constants, output)?;
}
Ok(())
}
}

Expand Down

0 comments on commit 2cf51b8

Please sign in to comment.