Implement median_timespan_bounded

This commit is contained in:
teor 2020-11-19 11:56:01 +10:00
parent 75519b0ae9
commit 054d6f0525
1 changed files with 54 additions and 11 deletions

View File

@ -1,25 +1,36 @@
//! Block difficulty adjustment calculations for contextual validation.
use chrono::{DateTime, Utc};
use chrono::{DateTime, Duration, Utc};
use primitive_types::U256;
use std::convert::TryInto;
use std::{cmp::max, cmp::min, convert::TryInto};
use zebra_chain::{
block, block::Block, parameters::Network, work::difficulty::CompactDifficulty,
block, block::Block, parameters::Network, parameters::NetworkUpgrade,
parameters::POW_AVERAGING_WINDOW, work::difficulty::CompactDifficulty,
work::difficulty::ExpandedDifficulty,
};
/// The averaging window for difficulty threshold arithmetic mean calculations.
///
/// `PoWAveragingWindow` in the Zcash specification.
pub const POW_AVERAGING_WINDOW: usize = 17;
/// The median block span for time median calculations.
///
/// `PoWMedianBlockSpan` in the Zcash specification.
pub const POW_MEDIAN_BLOCK_SPAN: usize = 11;
/// The damping factor for median timespan variance.
///
/// `PoWDampingFactor` in the Zcash specification.
pub const POW_DAMPING_FACTOR: i32 = 4;
/// The maximum upward adjustment percentage for median timespan variance.
///
/// `PoWMaxAdjustUp * 100` in the Zcash specification.
pub const POW_MAX_ADJUST_UP_PERCENT: i32 = 16;
/// The maximum downward adjustment percentage for median timespan variance.
///
/// `PoWMaxAdjustDown * 100` in the Zcash specification.
pub const POW_MAX_ADJUST_DOWN_PERCENT: i32 = 32;
/// Contains the context needed to calculate the adjusted difficulty for a block.
#[allow(dead_code)]
pub(super) struct AdjustedDifficulty {
@ -204,13 +215,45 @@ impl AdjustedDifficulty {
///
/// Note: This calculation only uses `PoWMedianBlockSpan` (11) times at the
/// start and end of the timespan times. timespan times `[11..=16]` are ignored.
fn median_timespan_bounded(&self) -> DateTime<Utc> {
fn median_timespan_bounded(&self) -> Duration {
let newer_times: [DateTime<Utc>; POW_MEDIAN_BLOCK_SPAN] = self.relevant_times
[0..POW_MEDIAN_BLOCK_SPAN]
.try_into()
.expect("relevant times is the correct length");
// TODO: do the actual calculation
AdjustedDifficulty::median_time(newer_times)
let newer_median = AdjustedDifficulty::median_time(newer_times);
let older_times: [DateTime<Utc>; POW_MEDIAN_BLOCK_SPAN] = self.relevant_times
[POW_AVERAGING_WINDOW..]
.try_into()
.expect("relevant times is the correct length");
let older_median = AdjustedDifficulty::median_time(older_times);
// `ActualTimespan` in the Zcash specification
let median_timespan = newer_median - older_median;
let averaging_window_timespan = NetworkUpgrade::averaging_window_timespan_for_height(
self.network,
self.candidate_height,
);
// This value is exact, but we need to truncate its nanoseconds component
let damped_variance = (median_timespan - averaging_window_timespan) / POW_DAMPING_FACTOR;
// num_seconds truncates negative values towards zero, matching the Zcash specification
let damped_variance = Duration::seconds(damped_variance.num_seconds());
// `ActualTimespanDamped` in the Zcash specification
let median_timespan_damped = averaging_window_timespan + damped_variance;
// `MinActualTimespan` and `MaxActualTimespan` in the Zcash spec
let min_median_timespan =
averaging_window_timespan * (100 - POW_MAX_ADJUST_UP_PERCENT) / 100;
let max_median_timespan =
averaging_window_timespan * (100 + POW_MAX_ADJUST_DOWN_PERCENT) / 100;
// `ActualTimespanBounded` in the Zcash specification
max(
min_median_timespan,
min(max_median_timespan, median_timespan_damped),
)
}
/// Calculate the median of the `median_block_span_times`: the `time`s from a