Implement the block header time consensus rules
This commit is contained in:
parent
0bac2dafcc
commit
23e07a94cf
|
|
@ -43,6 +43,14 @@ pub enum ValidateContextError {
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
NonSequentialBlock,
|
NonSequentialBlock,
|
||||||
|
|
||||||
|
/// block.header.time is less than or equal to the median-time-past for the block
|
||||||
|
#[non_exhaustive]
|
||||||
|
TimeTooEarly,
|
||||||
|
|
||||||
|
/// block.header.time is greater than the median-time-past for the block plus 90 minutes
|
||||||
|
#[non_exhaustive]
|
||||||
|
TimeTooLate,
|
||||||
|
|
||||||
/// block.header.difficulty_threshold is not equal to the adjusted difficulty for the block
|
/// block.header.difficulty_threshold is not equal to the adjusted difficulty for the block
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
InvalidDifficultyThreshold,
|
InvalidDifficultyThreshold,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
|
use chrono::Duration;
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
block::{self, Block},
|
block::{self, Block},
|
||||||
parameters::Network,
|
parameters::Network,
|
||||||
|
|
@ -74,11 +75,11 @@ where
|
||||||
block.borrow().header.time,
|
block.borrow().header.time,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let expected_difficulty =
|
let difficulty_adjustment =
|
||||||
AdjustedDifficulty::new_from_block(&prepared.block, network, relevant_data);
|
AdjustedDifficulty::new_from_block(&prepared.block, network, relevant_data);
|
||||||
check::difficulty_threshold_is_valid(
|
check::difficulty_threshold_is_valid(
|
||||||
prepared.block.header.difficulty_threshold,
|
prepared.block.header.difficulty_threshold,
|
||||||
expected_difficulty,
|
difficulty_adjustment,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO: other contextual validation design and implementation
|
// TODO: other contextual validation design and implementation
|
||||||
|
|
@ -111,22 +112,37 @@ fn height_one_more_than_parent_height(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validate the `difficulty_threshold` from a candidate block's header, based
|
/// Validate the `time` and `difficulty_threshold` from a candidate block's
|
||||||
/// on an `expected_difficulty` for that block.
|
/// header.
|
||||||
///
|
///
|
||||||
/// Uses `expected_difficulty` to calculate the expected difficulty value, then
|
/// Uses the `difficulty_adjustment` context for the block to:
|
||||||
/// compares that value to the `difficulty_threshold`.
|
/// * check that the the `time` field is within the valid range, and
|
||||||
|
/// * check that the expected difficulty is equal to the block's
|
||||||
|
/// `difficulty_threshold`.
|
||||||
///
|
///
|
||||||
/// The check passes if the values are equal.
|
/// These checks are performed together, because the time field is used to
|
||||||
|
/// calculate the expected difficulty adjustment.
|
||||||
fn difficulty_threshold_is_valid(
|
fn difficulty_threshold_is_valid(
|
||||||
difficulty_threshold: CompactDifficulty,
|
difficulty_threshold: CompactDifficulty,
|
||||||
expected_difficulty: AdjustedDifficulty,
|
difficulty_adjustment: AdjustedDifficulty,
|
||||||
) -> Result<(), ValidateContextError> {
|
) -> Result<(), ValidateContextError> {
|
||||||
if difficulty_threshold == expected_difficulty.expected_difficulty_threshold() {
|
// Check the block header time consensus rules from the Zcash specification
|
||||||
Ok(())
|
let median_time_past = difficulty_adjustment.median_time_past();
|
||||||
} else {
|
let block_max_time_since_median = Duration::seconds(difficulty::BLOCK_MAX_TIME_SINCE_MEDIAN);
|
||||||
Err(ValidateContextError::InvalidDifficultyThreshold)
|
if difficulty_adjustment.candidate_time() <= median_time_past {
|
||||||
|
Err(ValidateContextError::TimeTooEarly)?
|
||||||
|
} else if difficulty_adjustment.candidate_time()
|
||||||
|
> median_time_past + block_max_time_since_median
|
||||||
|
{
|
||||||
|
Err(ValidateContextError::TimeTooLate)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let expected_difficulty = difficulty_adjustment.expected_difficulty_threshold();
|
||||||
|
if difficulty_threshold != expected_difficulty {
|
||||||
|
Err(ValidateContextError::InvalidDifficultyThreshold)?
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,12 @@ pub const POW_MAX_ADJUST_UP_PERCENT: i32 = 16;
|
||||||
/// `PoWMaxAdjustDown * 100` in the Zcash specification.
|
/// `PoWMaxAdjustDown * 100` in the Zcash specification.
|
||||||
pub const POW_MAX_ADJUST_DOWN_PERCENT: i32 = 32;
|
pub const POW_MAX_ADJUST_DOWN_PERCENT: i32 = 32;
|
||||||
|
|
||||||
|
/// The maximum number of seconds between the `meadian-time-past` of a block,
|
||||||
|
/// and the block's `time` field.
|
||||||
|
///
|
||||||
|
/// Part of the block header consensus rules in the Zcash specification.
|
||||||
|
pub const BLOCK_MAX_TIME_SINCE_MEDIAN: i64 = 90 * 60;
|
||||||
|
|
||||||
/// Contains the context needed to calculate the adjusted difficulty for a block.
|
/// Contains the context needed to calculate the adjusted difficulty for a block.
|
||||||
pub(super) struct AdjustedDifficulty {
|
pub(super) struct AdjustedDifficulty {
|
||||||
/// The `header.time` field from the candidate block
|
/// The `header.time` field from the candidate block
|
||||||
|
|
@ -141,6 +147,11 @@ impl AdjustedDifficulty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the candidate block's time field.
|
||||||
|
pub fn candidate_time(&self) -> DateTime<Utc> {
|
||||||
|
self.candidate_time
|
||||||
|
}
|
||||||
|
|
||||||
/// Calculate the expected `difficulty_threshold` for a candidate block, based
|
/// Calculate the expected `difficulty_threshold` for a candidate block, based
|
||||||
/// on the `candidate_time`, `candidate_height`, `network`, and the
|
/// on the `candidate_time`, `candidate_height`, `network`, and the
|
||||||
/// `difficulty_threshold`s and `time`s from the previous
|
/// `difficulty_threshold`s and `time`s from the previous
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue