Skip to content

Commit

Permalink
fix(consensus): add valid range for blockchain version
Browse files Browse the repository at this point in the history
  • Loading branch information
sdbondi committed Mar 14, 2022
1 parent 1e230e7 commit 227beec
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 9 deletions.
5 changes: 4 additions & 1 deletion base_layer/core/src/base_node/sync/header_sync/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::{
consensus::ConsensusManager,
proof_of_work::{randomx_factory::RandomXFactory, PowAlgorithm},
validation::helpers::{
check_blockchain_version,
check_header_timestamp_greater_than_median,
check_not_bad_block,
check_pow_data,
Expand Down Expand Up @@ -116,6 +117,9 @@ impl<B: BlockchainBackend + 'static> BlockHeaderSyncValidator<B> {

pub fn validate(&mut self, header: BlockHeader) -> Result<u128, BlockHeaderSyncError> {
let state = self.state();
let constants = self.consensus_rules.consensus_constants(header.height);
check_blockchain_version(&constants, header.version)?;

let expected_height = state.current_height + 1;
if header.height != expected_height {
return Err(BlockHeaderSyncError::InvalidBlockHeight {
Expand All @@ -133,7 +137,6 @@ impl<B: BlockchainBackend + 'static> BlockHeaderSyncValidator<B> {

check_header_timestamp_greater_than_median(&header, &state.timestamps)?;

let constants = self.consensus_rules.consensus_constants(header.height);
let target_difficulty = state.target_difficulties.get(header.pow_algo()).calculate(
constants.min_pow_difficulty(header.pow_algo()),
constants.max_pow_difficulty(header.pow_algo()),
Expand Down
12 changes: 12 additions & 0 deletions base_layer/core/src/consensus/consensus_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ pub struct ConsensusConstants {
coinbase_lock_height: u64,
/// Current version of the blockchain
blockchain_version: u16,
/// Current version of the blockchain
valid_blockchain_version_range: RangeInclusive<u16>,
/// The Future Time Limit (FTL) of the blockchain in seconds. This is the max allowable timestamp that is excepted.
/// We use T*N/20 where T = desired chain target time, and N = block_window
future_time_limit: u64,
Expand Down Expand Up @@ -147,6 +149,11 @@ impl ConsensusConstants {
self.blockchain_version
}

/// Returns the valid blockchain version range
pub fn valid_blockchain_version_range(&self) -> &RangeInclusive<u16> {
&self.valid_blockchain_version_range
}

/// This returns the FTL (Future Time Limit) for blocks.
/// Any block with a timestamp greater than this is rejected.
pub fn ftl(&self) -> EpochTime {
Expand Down Expand Up @@ -283,6 +290,7 @@ impl ConsensusConstants {
effective_from_height: 0,
coinbase_lock_height: 2,
blockchain_version: 1,
valid_blockchain_version_range: 0..=3,
future_time_limit: 540,
difficulty_block_window,
max_block_transaction_weight: 19500,
Expand Down Expand Up @@ -321,6 +329,7 @@ impl ConsensusConstants {
effective_from_height: 0,
coinbase_lock_height: 6,
blockchain_version: 1,
valid_blockchain_version_range: 0..=3,
future_time_limit: 540,
difficulty_block_window: 90,
max_block_transaction_weight: 19500,
Expand Down Expand Up @@ -359,6 +368,7 @@ impl ConsensusConstants {
effective_from_height: 0,
coinbase_lock_height: 6,
blockchain_version: 2,
valid_blockchain_version_range: 0..=3,
future_time_limit: 540,
difficulty_block_window: 90,
// 65536 = target_block_size / bytes_per_gram = (1024*1024) / 16
Expand Down Expand Up @@ -406,6 +416,7 @@ impl ConsensusConstants {
effective_from_height: 0,
coinbase_lock_height: 360,
blockchain_version: 2,
valid_blockchain_version_range: 0..=3,
future_time_limit: 540,
difficulty_block_window: 90,
// 65536 = target_block_size / bytes_per_gram = (1024*1024) / 16
Expand Down Expand Up @@ -450,6 +461,7 @@ impl ConsensusConstants {
effective_from_height: 0,
coinbase_lock_height: 1,
blockchain_version: 1,
valid_blockchain_version_range: 0..=0,
future_time_limit: 540,
difficulty_block_window,
max_block_transaction_weight: 19500,
Expand Down
2 changes: 2 additions & 0 deletions base_layer/core/src/validation/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ pub enum ValidationError {
ConsensusError(String),
#[error("Covenant failed to validate: {0}")]
CovenantError(#[from] CovenantError),
#[error("Invalid or unsupported blockchain version {version}")]
InvalidBlockchainVersion { version: u16 },
}

// ChainStorageError has a ValidationError variant, so to prevent a cyclic dependency we use a string representation in
Expand Down
17 changes: 10 additions & 7 deletions base_layer/core/src/validation/header_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ use tari_crypto::tari_utilities::{hash::Hashable, hex::Hex};
use crate::{
blocks::BlockHeader,
chain_storage::{fetch_headers, BlockchainBackend},
consensus::ConsensusManager,
consensus::{ConsensusConstants, ConsensusManager},
proof_of_work::AchievedTargetDifficulty,
validation::{
helpers::{
check_blockchain_version,
check_header_timestamp_greater_than_median,
check_not_bad_block,
check_pow_data,
Expand All @@ -34,18 +35,17 @@ impl HeaderValidator {
fn check_median_timestamp<B: BlockchainBackend>(
&self,
db: &B,
constants: &ConsensusConstants,
block_header: &BlockHeader,
) -> Result<(), ValidationError> {
if block_header.height == 0 {
return Ok(()); // Its the genesis block, so we dont have to check median
}

let height = block_header.height - 1;
let min_height = block_header.height.saturating_sub(
self.rules
.consensus_constants(block_header.height)
.get_median_timestamp_count() as u64,
);
let min_height = block_header
.height
.saturating_sub(constants.get_median_timestamp_count() as u64);
let timestamps = fetch_headers(db, min_height, height)?
.iter()
.map(|h| h.timestamp)
Expand All @@ -69,14 +69,17 @@ impl<TBackend: BlockchainBackend> HeaderValidation<TBackend> for HeaderValidator
header: &BlockHeader,
difficulty_calculator: &DifficultyCalculator,
) -> Result<AchievedTargetDifficulty, ValidationError> {
let constants = self.rules.consensus_constants(header.height);
check_blockchain_version(constants, header.version)?;

check_timestamp_ftl(header, &self.rules)?;
let header_id = format!("header #{} ({})", header.height, header.hash().to_hex());
trace!(
target: LOG_TARGET,
"BlockHeader validation: FTL timestamp is ok for {} ",
header_id
);
self.check_median_timestamp(backend, header)?;
self.check_median_timestamp(backend, constants, header)?;
trace!(
target: LOG_TARGET,
"BlockHeader validation: Median timestamp is ok for {} ",
Expand Down
8 changes: 8 additions & 0 deletions base_layer/core/src/validation/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,14 @@ pub fn check_maturity(height: u64, inputs: &[TransactionInput]) -> Result<(), Tr
Ok(())
}

pub fn check_blockchain_version(constants: &ConsensusConstants, version: u16) -> Result<(), ValidationError> {
if constants.valid_blockchain_version_range().contains(&version) {
Ok(())
} else {
Err(ValidationError::InvalidBlockchainVersion { version })
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
29 changes: 28 additions & 1 deletion base_layer/core/src/validation/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,17 @@ use crate::{
transaction_components::{KernelBuilder, KernelFeatures, OutputFeatures, TransactionKernel},
CryptoFactories,
},
validation::{header_iter::HeaderIter, ChainBalanceValidator, FinalHorizonStateValidation},
validation::{
header_iter::HeaderIter,
header_validator::HeaderValidator,
ChainBalanceValidator,
FinalHorizonStateValidation,
},
};

mod header_validators {
use super::*;
use crate::validation::{DifficultyCalculator, HeaderValidation, ValidationError};

#[test]
fn header_iter_empty_and_invalid_height() {
Expand Down Expand Up @@ -93,6 +99,27 @@ mod header_validators {
assert_eq!(headers[i].height, i as u64);
})
}

#[test]
fn it_validates_that_version_is_in_range() {
let consensus_manager = ConsensusManagerBuilder::new(Network::LocalNet).build();
let db = create_store_with_consensus(consensus_manager.clone());

let genesis = db.fetch_chain_header(0).unwrap();

let mut header = BlockHeader::from_previous(genesis.header());
header.version = u16::MAX;

let validator = HeaderValidator::new(consensus_manager.clone());

let difficulty_calculator = DifficultyCalculator::new(consensus_manager, Default::default());
let err = validator
.validate(&*db.db_read_access().unwrap(), &header, &difficulty_calculator)
.unwrap_err();
assert!(matches!(err, ValidationError::InvalidBlockchainVersion {
version: u16::MAX
}));
}
}

#[test]
Expand Down

0 comments on commit 227beec

Please sign in to comment.