use crate::core::block::HeaderVersion;
use crate::core::hash::Hash;
use crate::global;
use crate::pow::Difficulty;
use std::cmp::{max, min};
pub const GRIN_BASE: u64 = 1_000_000_000;
pub const MILLI_GRIN: u64 = GRIN_BASE / 1_000;
pub const MICRO_GRIN: u64 = MILLI_GRIN / 1_000;
pub const NANO_GRIN: u64 = 1;
pub const BLOCK_TIME_SEC: u64 = 60;
pub const REWARD: u64 = BLOCK_TIME_SEC * GRIN_BASE;
pub fn reward(fee: u64) -> u64 {
REWARD.saturating_add(fee)
}
pub const HOUR_SEC: u64 = 60 * 60;
pub const HOUR_HEIGHT: u64 = HOUR_SEC / BLOCK_TIME_SEC;
pub const DAY_HEIGHT: u64 = 24 * HOUR_HEIGHT;
pub const WEEK_HEIGHT: u64 = 7 * DAY_HEIGHT;
pub const YEAR_HEIGHT: u64 = 52 * WEEK_HEIGHT;
pub const COINBASE_MATURITY: u64 = DAY_HEIGHT;
pub fn secondary_pow_ratio(height: u64) -> u64 {
90u64.saturating_sub(height / (2 * YEAR_HEIGHT / 90))
}
pub const PROOFSIZE: usize = 42;
pub const DEFAULT_MIN_EDGE_BITS: u8 = 31;
pub const SECOND_POW_EDGE_BITS: u8 = 29;
pub const BASE_EDGE_BITS: u8 = 24;
pub const CUT_THROUGH_HORIZON: u32 = WEEK_HEIGHT as u32;
pub const STATE_SYNC_THRESHOLD: u32 = 2 * DAY_HEIGHT as u32;
pub const INPUT_WEIGHT: u64 = 1;
pub const OUTPUT_WEIGHT: u64 = 21;
pub const KERNEL_WEIGHT: u64 = 3;
pub const MAX_BLOCK_WEIGHT: u64 = 40_000;
pub const HARD_FORK_INTERVAL: u64 = YEAR_HEIGHT / 2;
pub const TESTNET_FIRST_HARD_FORK: u64 = 185_040;
pub const TESTNET_SECOND_HARD_FORK: u64 = 298_080;
pub const TESTNET_THIRD_HARD_FORK: u64 = 552_960;
pub const TESTNET_FOURTH_HARD_FORK: u64 = 642_240;
pub const TESTING_HARD_FORK_INTERVAL: u64 = 3;
pub fn header_version(height: u64) -> HeaderVersion {
let hf_interval = (1 + height / HARD_FORK_INTERVAL) as u16;
match global::get_chain_type() {
global::ChainTypes::Mainnet => HeaderVersion(min(5, hf_interval)),
global::ChainTypes::AutomatedTesting | global::ChainTypes::UserTesting => {
let testing_hf_interval = (1 + height / TESTING_HARD_FORK_INTERVAL) as u16;
HeaderVersion(min(5, testing_hf_interval))
}
global::ChainTypes::Testnet => {
if height < TESTNET_FIRST_HARD_FORK {
HeaderVersion(1)
} else if height < TESTNET_SECOND_HARD_FORK {
HeaderVersion(2)
} else if height < TESTNET_THIRD_HARD_FORK {
HeaderVersion(3)
} else if height < TESTNET_FOURTH_HARD_FORK {
HeaderVersion(4)
} else {
HeaderVersion(5)
}
}
}
}
pub fn valid_header_version(height: u64, version: HeaderVersion) -> bool {
version == header_version(height)
}
pub const DMA_WINDOW: u64 = HOUR_HEIGHT;
pub const WTEMA_HALF_LIFE: u64 = 4 * HOUR_SEC;
pub const BLOCK_TIME_WINDOW: u64 = DMA_WINDOW * BLOCK_TIME_SEC;
pub const CLAMP_FACTOR: u64 = 2;
pub const DMA_DAMP_FACTOR: u64 = 3;
pub const AR_SCALE_DAMP_FACTOR: u64 = 13;
pub fn graph_weight(height: u64, edge_bits: u8) -> u64 {
let mut xpr_edge_bits = edge_bits as u64;
let expiry_height = YEAR_HEIGHT;
if edge_bits == 31 && height >= expiry_height {
xpr_edge_bits = xpr_edge_bits.saturating_sub(1 + (height - expiry_height) / WEEK_HEIGHT);
}
(2u64 << (edge_bits - global::base_edge_bits()) as u64) * xpr_edge_bits
}
pub const C32_GRAPH_WEIGHT: u64 = (2u64 << (32 - BASE_EDGE_BITS) as u64) * 32;
pub const MIN_DMA_DIFFICULTY: u64 = DMA_DAMP_FACTOR;
pub const MIN_AR_SCALE: u64 = AR_SCALE_DAMP_FACTOR;
pub const UNIT_DIFFICULTY: u64 =
((2 as u64) << (SECOND_POW_EDGE_BITS - BASE_EDGE_BITS)) * (SECOND_POW_EDGE_BITS as u64);
pub const INITIAL_DIFFICULTY: u64 = 1_000_000 * UNIT_DIFFICULTY;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HeaderDifficultyInfo {
pub hash: Option<Hash>,
pub timestamp: u64,
pub difficulty: Difficulty,
pub secondary_scaling: u32,
pub is_secondary: bool,
}
impl HeaderDifficultyInfo {
pub fn new(
hash: Option<Hash>,
timestamp: u64,
difficulty: Difficulty,
secondary_scaling: u32,
is_secondary: bool,
) -> HeaderDifficultyInfo {
HeaderDifficultyInfo {
hash,
timestamp,
difficulty,
secondary_scaling,
is_secondary,
}
}
pub fn from_ts_diff(timestamp: u64, difficulty: Difficulty) -> HeaderDifficultyInfo {
HeaderDifficultyInfo {
hash: None,
timestamp,
difficulty,
secondary_scaling: global::initial_graph_weight(),
is_secondary: true,
}
}
pub fn from_diff_scaling(
difficulty: Difficulty,
secondary_scaling: u32,
) -> HeaderDifficultyInfo {
HeaderDifficultyInfo {
hash: None,
timestamp: 1,
difficulty,
secondary_scaling,
is_secondary: true,
}
}
}
pub fn damp(actual: u64, goal: u64, damp_factor: u64) -> u64 {
(actual + (damp_factor - 1) * goal) / damp_factor
}
pub fn clamp(actual: u64, goal: u64, clamp_factor: u64) -> u64 {
max(goal / clamp_factor, min(actual, goal * clamp_factor))
}
pub fn next_difficulty<T>(height: u64, cursor: T) -> HeaderDifficultyInfo
where
T: IntoIterator<Item = HeaderDifficultyInfo>,
{
if header_version(height) < HeaderVersion(5) {
next_dma_difficulty(height, cursor)
} else {
next_wtema_difficulty(height, cursor)
}
}
pub fn next_dma_difficulty<T>(height: u64, cursor: T) -> HeaderDifficultyInfo
where
T: IntoIterator<Item = HeaderDifficultyInfo>,
{
let diff_data = global::difficulty_data_to_vector(cursor);
let sec_pow_scaling = secondary_pow_scaling(height, &diff_data[1..]);
let ts_delta: u64 = diff_data[DMA_WINDOW as usize].timestamp - diff_data[0].timestamp;
let diff_sum: u64 = diff_data
.iter()
.skip(1)
.map(|dd| dd.difficulty.to_num())
.sum();
let adj_ts = clamp(
damp(ts_delta, BLOCK_TIME_WINDOW, DMA_DAMP_FACTOR),
BLOCK_TIME_WINDOW,
CLAMP_FACTOR,
);
let difficulty = max(MIN_DMA_DIFFICULTY, diff_sum * BLOCK_TIME_SEC / adj_ts);
HeaderDifficultyInfo::from_diff_scaling(Difficulty::from_num(difficulty), sec_pow_scaling)
}
pub fn next_wtema_difficulty<T>(_height: u64, cursor: T) -> HeaderDifficultyInfo
where
T: IntoIterator<Item = HeaderDifficultyInfo>,
{
let mut last_headers = cursor.into_iter();
let last_header = last_headers.next().unwrap();
let prev_header = last_headers.next().unwrap();
let last_block_time: u64 = last_header.timestamp - prev_header.timestamp;
let last_diff = last_header.difficulty.to_num();
let next_diff =
last_diff * WTEMA_HALF_LIFE / (WTEMA_HALF_LIFE - BLOCK_TIME_SEC + last_block_time);
let difficulty = max(Difficulty::min_wtema(), Difficulty::from_num(next_diff));
HeaderDifficultyInfo::from_diff_scaling(difficulty, 0) }
pub fn ar_count(_height: u64, diff_data: &[HeaderDifficultyInfo]) -> u64 {
100 * diff_data.iter().filter(|n| n.is_secondary).count() as u64
}
pub fn secondary_pow_scaling(height: u64, diff_data: &[HeaderDifficultyInfo]) -> u32 {
let scale_sum: u64 = diff_data.iter().map(|dd| dd.secondary_scaling as u64).sum();
let target_pct = secondary_pow_ratio(height);
let target_count = DMA_WINDOW * target_pct;
let adj_count = clamp(
damp(
ar_count(height, diff_data),
target_count,
AR_SCALE_DAMP_FACTOR,
),
target_count,
CLAMP_FACTOR,
);
let scale = scale_sum * target_pct / max(1, adj_count);
max(MIN_AR_SCALE, scale) as u32
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_graph_weight() {
global::set_local_chain_type(global::ChainTypes::Mainnet);
assert_eq!(graph_weight(1, 31), 256 * 31);
assert_eq!(graph_weight(1, 32), 512 * 32);
assert_eq!(graph_weight(1, 33), 1024 * 33);
assert_eq!(graph_weight(YEAR_HEIGHT, 31), 256 * 30);
assert_eq!(graph_weight(YEAR_HEIGHT, 32), 512 * 32);
assert_eq!(graph_weight(YEAR_HEIGHT, 33), 1024 * 33);
assert_eq!(graph_weight(YEAR_HEIGHT + WEEK_HEIGHT, 31), 256 * 29);
assert_eq!(graph_weight(YEAR_HEIGHT + 2 * WEEK_HEIGHT, 31), 256 * 28);
assert_eq!(graph_weight(YEAR_HEIGHT + 32 * WEEK_HEIGHT, 31), 0);
assert_eq!(graph_weight(2 * YEAR_HEIGHT, 31), 0);
assert_eq!(graph_weight(2 * YEAR_HEIGHT, 32), 512 * 32);
assert_eq!(graph_weight(2 * YEAR_HEIGHT, 33), 1024 * 33);
assert_eq!(
graph_weight(2 * YEAR_HEIGHT + WEEK_HEIGHT, 32),
C32_GRAPH_WEIGHT
);
assert_eq!(graph_weight(2 * YEAR_HEIGHT + WEEK_HEIGHT, 31), 0);
assert_eq!(
graph_weight(2 * YEAR_HEIGHT + 30 * WEEK_HEIGHT, 32),
C32_GRAPH_WEIGHT
);
assert_eq!(
graph_weight(2 * YEAR_HEIGHT + 31 * WEEK_HEIGHT, 32),
C32_GRAPH_WEIGHT
);
assert_eq!(graph_weight(3 * YEAR_HEIGHT, 31), 0);
assert_eq!(graph_weight(3 * YEAR_HEIGHT, 32), 512 * 32);
assert_eq!(graph_weight(3 * YEAR_HEIGHT, 33), 1024 * 33);
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 31), 0);
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 32), 512 * 32);
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 33), 1024 * 33);
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 33), 1024 * 33);
}
}