Skip to content

Commit

Permalink
feat: add opcode versions (#4836)
Browse files Browse the repository at this point in the history
Description
---
This adds in an opcode versioning that is allowed.
Refactors the validation to ensure transaction versioning info is check by blocks as well. 

Motivation and Context
---
We might need to add in new opcodes as hard forks. This adds the opcode version as part of the consensus code so we can track allowed opcodes. 

How Has This Been Tested?
---
Unit tests

Fixes: #4821
  • Loading branch information
SWvheerden authored Oct 25, 2022
1 parent 9edbbce commit c8abe99
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 105 deletions.
33 changes: 31 additions & 2 deletions base_layer/core/src/consensus/consensus_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use std::{

use chrono::{DateTime, Duration, Utc};
use tari_common::configuration::Network;
use tari_script::script;
use tari_script::{script, OpcodeVersion};
use tari_utilities::epoch_time::EpochTime;

use crate::{
Expand Down Expand Up @@ -101,6 +101,7 @@ pub struct ConsensusConstants {
pub struct OutputVersionRange {
pub outputs: RangeInclusive<TransactionOutputVersion>,
pub features: RangeInclusive<OutputFeaturesVersion>,
pub opcode: RangeInclusive<OpcodeVersion>,
}

/// All V0 for Inputs, Outputs + Features, Kernels
Expand All @@ -114,6 +115,7 @@ fn version_zero() -> (
let output_version_range = OutputVersionRange {
outputs: TransactionOutputVersion::V0..=TransactionOutputVersion::V0,
features: OutputFeaturesVersion::V0..=OutputFeaturesVersion::V0,
opcode: OpcodeVersion::V0..=OpcodeVersion::V0,
};

(input_version_range, output_version_range, kernel_version_range)
Expand Down Expand Up @@ -505,6 +507,11 @@ impl ConsensusConstants {
target_time: 200,
});
let (input_version_range, output_version_range, kernel_version_range) = version_zero();
let output_version_2_range = OutputVersionRange {
outputs: TransactionOutputVersion::V0..=TransactionOutputVersion::V0,
features: OutputFeaturesVersion::V0..=OutputFeaturesVersion::V0,
opcode: OpcodeVersion::V0..=OpcodeVersion::V1,
};
vec![
ConsensusConstants {
effective_from_height: 0,
Expand Down Expand Up @@ -541,12 +548,34 @@ impl ConsensusConstants {
emission_decay: &ESMERALDA_DECAY_PARAMS,
emission_tail: 800 * T,
max_randomx_seed_height: 3000,
proof_of_work: algos.clone(),
faucet_value: (10 * 4000) * T,
transaction_weight: TransactionWeight::v1(),
max_script_byte_size: 2048,
input_version_range: input_version_range.clone(),
output_version_range,
kernel_version_range: kernel_version_range.clone(),
permitted_output_types: Self::current_permitted_output_types(),
},
ConsensusConstants {
effective_from_height: 25000,
coinbase_lock_height: 6,
blockchain_version: 1,
valid_blockchain_version_range: 0..=1,
future_time_limit: 540,
difficulty_block_window: 90,
max_block_transaction_weight: 127_795,
median_timestamp_count: 11,
emission_initial: 18_462_816_327 * uT,
emission_decay: &ESMERALDA_DECAY_PARAMS,
emission_tail: 800 * T,
max_randomx_seed_height: 3000,
proof_of_work: algos,
faucet_value: (10 * 4000) * T,
transaction_weight: TransactionWeight::v1(),
max_script_byte_size: 2048,
input_version_range,
output_version_range,
output_version_range: output_version_2_range,
kernel_version_range,
permitted_output_types: Self::current_permitted_output_types(),
},
Expand Down
3 changes: 3 additions & 0 deletions base_layer/core/src/validation/block_validators/orphan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use crate::{
check_permitted_output_types,
check_sorting_and_duplicates,
check_total_burned,
validate_versions,
},
OrphanValidation,
ValidationError,
Expand Down Expand Up @@ -88,6 +89,8 @@ impl OrphanValidation for OrphanBlockValidator {
trace!(target: LOG_TARGET, "Validating {}", block_id);

let constants = self.rules.consensus_constants(height);
validate_versions(&block.body, constants)?;
trace!(target: LOG_TARGET, "SV - Block body versions are ok for {} ", &block_id);
check_block_weight(block, constants)?;
trace!(target: LOG_TARGET, "SV - Block weight is ok for {} ", &block_id);

Expand Down
72 changes: 72 additions & 0 deletions base_layer/core/src/validation/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,78 @@ pub fn check_permitted_output_types(
Ok(())
}

pub fn validate_versions(
body: &AggregateBody,
consensus_constants: &ConsensusConstants,
) -> 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));
}
}

// 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);

let valid_features_version = consensus_constants
.output_version_range()
.features
.contains(&output.features.version);
if !valid_output_version {
let msg = format!(
"Transaction output version is not allowed by consensus ({:?})",
output.version
);
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));
}
}

check_permitted_output_types(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));
}
}

Ok(())
}

#[cfg(test)]
mod test {
use tari_test_utils::unpack_enum;
Expand Down
66 changes: 2 additions & 64 deletions base_layer/core/src/validation/transaction_validators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ use tari_utilities::hex::Hex;

use crate::{
chain_storage::{BlockchainBackend, BlockchainDatabase},
consensus::ConsensusConstants,
transactions::{transaction_components::Transaction, CryptoFactories},
validation::{
helpers::{check_inputs_are_utxos, check_outputs, check_permitted_output_types, check_total_burned},
helpers::{check_inputs_are_utxos, check_outputs, check_total_burned, validate_versions},
MempoolTransactionValidation,
ValidationError,
},
Expand Down Expand Up @@ -98,67 +97,6 @@ impl<B: BlockchainBackend> TxConsensusValidator<B> {
Self { db }
}

fn validate_versions(
&self,
tx: &Transaction,
consensus_constants: &ConsensusConstants,
) -> Result<(), ValidationError> {
// validate input version
for input in tx.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));
}
}

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

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

if !valid_output_version {
let msg = format!(
"Transaction output version is not allowed by consensus ({:?})",
output.version
);
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));
}

check_permitted_output_types(consensus_constants, output)?;
}

// validate kernel version
for kernel in tx.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));
}
}

Ok(())
}

fn validate_excess_sig_not_in_db(&self, tx: &Transaction) -> Result<(), ValidationError> {
for kernel in tx.body.kernels() {
if let Some((db_kernel, header_hash)) = self.db.fetch_kernel_by_excess_sig(kernel.excess_sig.to_owned())? {
Expand Down Expand Up @@ -191,7 +129,7 @@ impl<B: BlockchainBackend> MempoolTransactionValidation for TxConsensusValidator

self.validate_excess_sig_not_in_db(tx)?;

self.validate_versions(tx, consensus_constants)
validate_versions(tx.body(), consensus_constants)
}
}

Expand Down
6 changes: 2 additions & 4 deletions base_layer/core/tests/helpers/block_malleability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,11 @@ fn check_block_changes_are_detected(field: MerkleMountainRangeField, block_mod_f

let (_, output) = blockchain.add_block(blocks.new_block("A1").child_of("GB").difficulty(1));

// we need to use input version V2 to include the "version" into the input hash
// and also the output version V2 to include the "sender_offset_public_key" into the output hash
let (txs, _) = schema_to_transaction(&[txn_schema!(
from: vec![output],
to: vec![50 * T],
input_version: TransactionInputVersion::V1,
output_version: TransactionOutputVersion::V1
input_version: TransactionInputVersion::V0,
output_version: TransactionOutputVersion::V0
)]);
blockchain.add_block(
blocks
Expand Down
2 changes: 1 addition & 1 deletion infrastructure/tari_script/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ mod serde;
mod stack;

pub use error::ScriptError;
pub use op_codes::{slice_to_boxed_hash, slice_to_hash, HashValue, Message, Opcode, ScalarValue};
pub use op_codes::{slice_to_boxed_hash, slice_to_hash, HashValue, Message, Opcode, OpcodeVersion, ScalarValue};
pub use script::TariScript;
pub use script_commitment::{ScriptCommitment, ScriptCommitmentError, ScriptCommitmentFactory};
pub use script_context::ScriptContext;
Expand Down
Loading

0 comments on commit c8abe99

Please sign in to comment.