use crate::core::block::HeaderVersion;
use crate::core::hash::Hash;
use crate::global;
use crate::pow::Difficulty;
use std::cmp::{max, min};
use std::collections::VecDeque;
pub const MWC_BASE: u64 = 1_000_000_000;
pub const MILLI_MWC: u64 = MWC_BASE / 1_000;
pub const MICRO_MWC: u64 = MILLI_MWC / 1_000;
pub const NANO_MWC: u64 = 1;
pub const BLOCK_TIME_SEC: u64 = 60;
pub fn reward(fee: u64, height: u64) -> u64 {
let block_reward = calc_mwc_block_reward(height);
block_reward.saturating_add(fee)
}
pub const GENESIS_BLOCK_REWARD: u64 = 10_000_000_000_000_000 + 41_800_000;
pub const HOUR_HEIGHT: u64 = 3600 / 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 {
45u64.saturating_sub(height / (YEAR_HEIGHT / 45))
}
fn ar_scale_damp_factor(_height: u64) -> u64 {
AR_SCALE_DAMP_FACTOR
}
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 BLOCK_INPUT_WEIGHT: u64 = 1;
pub const BLOCK_OUTPUT_WEIGHT: u64 = 21;
pub const BLOCK_KERNEL_WEIGHT: u64 = 3;
pub const TXFEE_INPUT_WEIGHT: u64 = 1;
pub const TXFEE_OUTPUT_WEIGHT: u64 = 4;
pub const TXFEE_KERNEL_WEIGHT: u64 = 1;
pub const MAX_BLOCK_WEIGHT: u64 = 40_000;
pub const TESTING_FIRST_HARD_FORK: u64 = 3;
pub const TESTING_SECOND_HARD_FORK: u64 = 6;
pub const TESTING_THIRD_HARD_FORK: u64 = 9;
pub const TESTING_HARD_FORK_INTERVAL: u64 = 3;
pub fn header_version(height: u64) -> HeaderVersion {
let chain_type = global::get_chain_type();
match chain_type {
global::ChainTypes::Mainnet | global::ChainTypes::Floonet => {
if height < get_c31_hard_fork_block_height() {
HeaderVersion(1)
} else {
HeaderVersion(2)
}
}
global::ChainTypes::AutomatedTesting | global::ChainTypes::UserTesting => {
if height < TESTING_FIRST_HARD_FORK {
HeaderVersion(1)
} else if height < TESTING_SECOND_HARD_FORK {
HeaderVersion(2)
} else if height < TESTING_THIRD_HARD_FORK {
HeaderVersion(3)
} else {
HeaderVersion(4)
}
}
}
}
pub fn valid_header_version(height: u64, version: HeaderVersion) -> bool {
let chain_type = global::get_chain_type();
match chain_type {
global::ChainTypes::Mainnet | global::ChainTypes::Floonet => {
if height < get_c31_hard_fork_block_height() {
version == HeaderVersion(1)
} else {
version == HeaderVersion(2)
}
}
global::ChainTypes::AutomatedTesting | global::ChainTypes::UserTesting => {
if height < TESTING_FIRST_HARD_FORK {
version == HeaderVersion(1)
} else if height < TESTING_SECOND_HARD_FORK {
version == HeaderVersion(2)
} else if height < TESTING_THIRD_HARD_FORK {
version == HeaderVersion(3)
} else {
version == HeaderVersion(4)
}
}
}
}
pub const DIFFICULTY_ADJUST_WINDOW: u64 = HOUR_HEIGHT;
pub const BLOCK_TIME_WINDOW: u64 = DIFFICULTY_ADJUST_WINDOW * BLOCK_TIME_SEC;
pub const CLAMP_FACTOR: u64 = 2;
pub const DIFFICULTY_DAMP_FACTOR: u64 = 3;
pub const AR_SCALE_DAMP_FACTOR: u64 = 13;
pub fn graph_weight(height: u64, edge_bits: u8) -> u64 {
if height < get_c31_hard_fork_block_height() || edge_bits <= 31 {
(2u64 << ((edge_bits as u64) - global::base_edge_bits() as u64) as u64) * (edge_bits as u64)
} else {
1
}
}
pub const MIN_DIFFICULTY: u64 = DIFFICULTY_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 height: u64,
pub hash: Option<Hash>,
pub timestamp: u64,
pub difficulty: Difficulty,
pub secondary_scaling: u32,
pub is_secondary: bool,
}
impl HeaderDifficultyInfo {
pub fn new(
height: u64,
hash: Option<Hash>,
timestamp: u64,
difficulty: Difficulty,
secondary_scaling: u32,
is_secondary: bool,
) -> HeaderDifficultyInfo {
HeaderDifficultyInfo {
height,
hash,
timestamp,
difficulty,
secondary_scaling,
is_secondary,
}
}
pub fn from_ts_diff(timestamp: u64, difficulty: Difficulty) -> HeaderDifficultyInfo {
HeaderDifficultyInfo {
height: 0,
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 {
height: 0,
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,
cache_values: &mut VecDeque<HeaderDifficultyInfo>,
) -> HeaderDifficultyInfo
where
T: IntoIterator<Item = HeaderDifficultyInfo>,
{
let diff_data = global::difficulty_data_to_vector(cursor, cache_values);
let sec_pow_scaling = secondary_pow_scaling(height, &diff_data[1..]);
let ts_delta: u64 =
diff_data[DIFFICULTY_ADJUST_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, DIFFICULTY_DAMP_FACTOR),
BLOCK_TIME_WINDOW,
CLAMP_FACTOR,
);
let difficulty = max(MIN_DIFFICULTY, diff_sum * BLOCK_TIME_SEC / adj_ts);
HeaderDifficultyInfo::from_diff_scaling(Difficulty::from_num(difficulty), sec_pow_scaling)
}
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 = DIFFICULTY_ADJUST_WINDOW * target_pct;
let adj_count = clamp(
damp(
ar_count(height, diff_data),
target_count,
ar_scale_damp_factor(height),
),
target_count,
CLAMP_FACTOR,
);
let scale = scale_sum * target_pct / max(1, adj_count);
max(MIN_AR_SCALE, scale) as u32
}
fn get_c31_hard_fork_block_height() -> u64 {
if global::get_chain_type() == global::ChainTypes::Floonet {
270_000
} else {
202_500
}
}
fn get_epoch_block_offset(epoch: u8) -> u64 {
let mut ret = get_c31_hard_fork_block_height();
if epoch >= 2 {
if global::get_chain_type() == global::ChainTypes::Floonet {
ret += DAY_HEIGHT;
} else {
ret += WEEK_HEIGHT;
}
}
let mut i = 3;
while i <= epoch {
ret += get_epoch_duration(i - 1);
i = i + 1;
}
ret
}
fn get_epoch_duration(epoch: u8) -> u64 {
match epoch {
2 => {
if global::get_chain_type() == global::ChainTypes::Floonet {
DAY_HEIGHT
} else {
120 * DAY_HEIGHT
}
}
3 => {
if global::get_chain_type() == global::ChainTypes::Floonet {
DAY_HEIGHT
} else {
60 * DAY_HEIGHT
}
}
4 => {
120 * DAY_HEIGHT
}
5 => {
180 * DAY_HEIGHT
}
6 => {
180 * DAY_HEIGHT
}
7 => {
YEAR_HEIGHT
}
8 => {
YEAR_HEIGHT
}
9 => {
6 * YEAR_HEIGHT
}
10 => {
10 * YEAR_HEIGHT
}
_ => {
876_349_148 }
}
}
fn get_epoch_reward(epoch: u8) -> u64 {
match epoch {
0 => GENESIS_BLOCK_REWARD,
1 => MWC_FIRST_GROUP_REWARD,
2 => {
600_000_000 }
3 => {
450_000_000 }
4 => {
300_000_000 }
5 => {
250_000_000 }
6 => {
200_000_000 }
7 => {
150_000_000 }
8 => {
100_000_000 }
9 => {
50_000_000 }
10 => {
25_000_000 }
11 => {
10_000_000 }
_ => {
MWC_LAST_BLOCK_REWARD }
}
}
pub const MWC_FIRST_GROUP_REWARD: u64 = 2_380_952_380;
pub const MWC_LAST_BLOCK_REWARD: u64 = 2_211_980;
pub fn calc_mwc_block_reward(height: u64) -> u64 {
if height == 0 {
return get_epoch_reward(0);
}
if height < get_epoch_block_offset(2) {
return get_epoch_reward(1);
} else if height < get_epoch_block_offset(3) {
return get_epoch_reward(2);
} else if height < get_epoch_block_offset(4) {
return get_epoch_reward(3);
} else if height < get_epoch_block_offset(5) {
return get_epoch_reward(4);
} else if height < get_epoch_block_offset(6) {
return get_epoch_reward(5);
} else if height < get_epoch_block_offset(7) {
return get_epoch_reward(6);
} else if height < get_epoch_block_offset(8) {
return get_epoch_reward(7);
} else if height < get_epoch_block_offset(9) {
return get_epoch_reward(8);
} else if height < get_epoch_block_offset(10) {
return get_epoch_reward(9);
} else if height < get_epoch_block_offset(11) {
return get_epoch_reward(10);
} else if height < get_epoch_block_offset(11) + get_epoch_duration(11) {
return get_epoch_reward(11);
} else if height == get_epoch_block_offset(11) + get_epoch_duration(11) {
return get_epoch_reward(12);
}
return 0;
}
pub fn calc_mwc_block_overage(height: u64, genesis_had_reward: bool) -> u64 {
let mut overage: u64 = get_epoch_reward(0); if !genesis_had_reward {
overage -= get_epoch_reward(0);
}
if height < get_epoch_block_offset(2) {
return overage + height * get_epoch_reward(1);
}
overage += get_epoch_reward(1) * (get_epoch_block_offset(2) - 1);
if height < get_epoch_block_offset(3) {
return overage + ((height + 1) - get_epoch_block_offset(2)) * get_epoch_reward(2);
}
overage += get_epoch_reward(2) * (get_epoch_block_offset(3) - get_epoch_block_offset(2));
if height < get_epoch_block_offset(4) {
return overage + ((height + 1) - get_epoch_block_offset(3)) * get_epoch_reward(3);
}
overage += get_epoch_reward(3) * (get_epoch_block_offset(4) - get_epoch_block_offset(3));
if height < get_epoch_block_offset(5) {
return overage + ((height + 1) - get_epoch_block_offset(4)) * get_epoch_reward(4);
}
overage += get_epoch_reward(4) * (get_epoch_block_offset(5) - get_epoch_block_offset(4));
if height < get_epoch_block_offset(6) {
return overage + ((height + 1) - get_epoch_block_offset(5)) * get_epoch_reward(5);
}
overage += get_epoch_reward(5) * (get_epoch_block_offset(6) - get_epoch_block_offset(5));
if height < get_epoch_block_offset(7) {
return overage + ((height + 1) - get_epoch_block_offset(6)) * get_epoch_reward(6);
}
overage += get_epoch_reward(6) * (get_epoch_block_offset(7) - get_epoch_block_offset(6));
if height < get_epoch_block_offset(8) {
return overage + ((height + 1) - get_epoch_block_offset(7)) * get_epoch_reward(7);
}
overage += get_epoch_reward(7) * (get_epoch_block_offset(8) - get_epoch_block_offset(7));
if height < get_epoch_block_offset(9) {
return overage + ((height + 1) - get_epoch_block_offset(8)) * get_epoch_reward(8);
}
overage += get_epoch_reward(8) * (get_epoch_block_offset(9) - get_epoch_block_offset(8));
if height < get_epoch_block_offset(10) {
return overage + ((height + 1) - get_epoch_block_offset(9)) * get_epoch_reward(9);
}
overage += get_epoch_reward(9) * (get_epoch_block_offset(10) - get_epoch_block_offset(9));
if height < get_epoch_block_offset(11) {
return overage + ((height + 1) - get_epoch_block_offset(10)) * get_epoch_reward(10);
}
overage += get_epoch_reward(10) * (get_epoch_block_offset(11) - get_epoch_block_offset(10));
if height < get_epoch_block_offset(11) + get_epoch_duration(11) {
return overage + ((height + 1) - get_epoch_block_offset(11)) * get_epoch_reward(11);
}
overage += get_epoch_reward(11) * (get_epoch_duration(11));
if height >= get_epoch_block_offset(11) + get_epoch_duration(11) {
overage += get_epoch_reward(12);
}
overage
}
#[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 * 31);
assert_eq!(graph_weight(YEAR_HEIGHT, 32), 1);
assert_eq!(graph_weight(YEAR_HEIGHT, 33), 1);
assert_eq!(graph_weight(YEAR_HEIGHT + WEEK_HEIGHT, 31), 256 * 31);
assert_eq!(graph_weight(YEAR_HEIGHT + 2 * WEEK_HEIGHT, 31), 256 * 31);
assert_eq!(graph_weight(YEAR_HEIGHT + 32 * WEEK_HEIGHT, 31), 256 * 31);
assert_eq!(graph_weight(2 * YEAR_HEIGHT, 31), 256 * 31);
assert_eq!(graph_weight(2 * YEAR_HEIGHT, 32), 1);
assert_eq!(graph_weight(2 * YEAR_HEIGHT, 33), 1);
assert_eq!(graph_weight(2 * YEAR_HEIGHT + WEEK_HEIGHT, 32), 1);
assert_eq!(graph_weight(2 * YEAR_HEIGHT + WEEK_HEIGHT, 31), 256 * 31);
assert_eq!(graph_weight(2 * YEAR_HEIGHT + 30 * WEEK_HEIGHT, 32), 1);
assert_eq!(graph_weight(2 * YEAR_HEIGHT + 31 * WEEK_HEIGHT, 32), 1);
assert_eq!(graph_weight(3 * YEAR_HEIGHT, 31), 256 * 31);
assert_eq!(graph_weight(3 * YEAR_HEIGHT, 32), 1);
assert_eq!(graph_weight(3 * YEAR_HEIGHT, 33), 1);
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 31), 256 * 31);
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 32), 1);
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 33), 1);
}
#[test]
fn test_epoch_dates() {
global::set_local_chain_type(global::ChainTypes::Mainnet);
assert_eq!(get_c31_hard_fork_block_height(), 202_500); assert_eq!(get_epoch_block_offset(2), 212_580); assert_eq!(get_epoch_block_offset(3), 385_380); assert_eq!(get_epoch_block_offset(4), 471_780); assert_eq!(get_epoch_block_offset(5), 644_580); assert_eq!(get_epoch_block_offset(6), 903_780); assert_eq!(get_epoch_block_offset(7), 1_162_980); assert_eq!(get_epoch_block_offset(8), 1_687_140); assert_eq!(get_epoch_block_offset(9), 2_211_300); assert_eq!(get_epoch_block_offset(10), 5_356_260); assert_eq!(get_epoch_block_offset(11), 10_597_860); assert_eq!(
get_epoch_block_offset(11) + get_epoch_duration(11),
886_947_008
);
}
#[test]
fn test_calc_mwc_block_reward() {
global::set_local_chain_type(global::ChainTypes::Mainnet);
assert_eq!(calc_mwc_block_reward(1), 2_380_952_380);
assert_eq!(calc_mwc_block_reward(2), 2_380_952_380);
assert_eq!(calc_mwc_block_reward(100000), 2_380_952_380);
assert_eq!(
calc_mwc_block_reward(get_c31_hard_fork_block_height() - 1),
2_380_952_380
);
assert_eq!(
calc_mwc_block_reward(get_c31_hard_fork_block_height()),
2_380_952_380
);
assert_eq!(
calc_mwc_block_reward(get_c31_hard_fork_block_height() + WEEK_HEIGHT - 1),
2_380_952_380
);
assert_eq!(
calc_mwc_block_reward(get_c31_hard_fork_block_height() + WEEK_HEIGHT),
600_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(2)),
600_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(2) - 1),
2_380_952_380
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(3)),
450_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(3) - 1),
600_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(4)),
300_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(4) - 1),
450_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(5)),
250_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(5) - 1),
300_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(6)),
200_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(6) - 1),
250_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(7)),
150_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(7) - 1),
200_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(8)),
100_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(8) - 1),
150_000_000
);
assert_eq!(calc_mwc_block_reward(get_epoch_block_offset(9)), 50_000_000);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(9) - 1),
100_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(10)),
25_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(10) - 1),
50_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(11)),
10_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(11) - 1),
25_000_000
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(11) + get_epoch_duration(11)),
MWC_LAST_BLOCK_REWARD
);
assert_eq!(
calc_mwc_block_reward(get_epoch_block_offset(11) + get_epoch_duration(11) + 1),
0
);
assert_eq!(calc_mwc_block_reward(2_100_000 * 320000000 + 200), 0); }
#[test]
fn test_calc_mwc_block_overage() {
global::set_local_chain_type(global::ChainTypes::Mainnet);
let genesis_reward: u64 = GENESIS_BLOCK_REWARD;
assert_eq!(calc_mwc_block_overage(0, true), genesis_reward); assert_eq!(calc_mwc_block_overage(0, false), 0); assert_eq!(
calc_mwc_block_overage(1, true),
genesis_reward + MWC_FIRST_GROUP_REWARD * 1
);
assert_eq!(
calc_mwc_block_overage(get_c31_hard_fork_block_height(), true),
genesis_reward + MWC_FIRST_GROUP_REWARD * get_c31_hard_fork_block_height()
);
assert_eq!(
calc_mwc_block_overage(get_c31_hard_fork_block_height() + WEEK_HEIGHT - 1, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_c31_hard_fork_block_height() + WEEK_HEIGHT - 1)
);
assert_eq!(
calc_mwc_block_overage(get_c31_hard_fork_block_height() + WEEK_HEIGHT, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_c31_hard_fork_block_height() + WEEK_HEIGHT - 1)
+ get_epoch_reward(2) * 1
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(2), true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_c31_hard_fork_block_height() + WEEK_HEIGHT - 1)
+ get_epoch_reward(2) * 1
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(3) - 1, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(3), true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ get_epoch_reward(3)
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(3) + 1, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ get_epoch_reward(3) * 2
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(4), true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ get_epoch_reward(4)
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(4) + 3, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ get_epoch_reward(4) * 4
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(5), true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ get_epoch_reward(5)
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(5) + 3, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ get_epoch_reward(5) * 4
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(6), true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ get_epoch_reward(6)
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(6) + 3, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ get_epoch_reward(6) * 4
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(7), true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ (get_epoch_block_offset(7) - get_epoch_block_offset(6)) * get_epoch_reward(6)
+ get_epoch_reward(7)
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(7) + 3, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ (get_epoch_block_offset(7) - get_epoch_block_offset(6)) * get_epoch_reward(6)
+ get_epoch_reward(7) * 4
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(8), true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ (get_epoch_block_offset(7) - get_epoch_block_offset(6)) * get_epoch_reward(6)
+ (get_epoch_block_offset(8) - get_epoch_block_offset(7)) * get_epoch_reward(7)
+ get_epoch_reward(8)
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(8) + 3, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ (get_epoch_block_offset(7) - get_epoch_block_offset(6)) * get_epoch_reward(6)
+ (get_epoch_block_offset(8) - get_epoch_block_offset(7)) * get_epoch_reward(7)
+ get_epoch_reward(8) * 4
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(9), true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ (get_epoch_block_offset(7) - get_epoch_block_offset(6)) * get_epoch_reward(6)
+ (get_epoch_block_offset(8) - get_epoch_block_offset(7)) * get_epoch_reward(7)
+ (get_epoch_block_offset(9) - get_epoch_block_offset(8)) * get_epoch_reward(8)
+ get_epoch_reward(9)
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(9) + 39, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ (get_epoch_block_offset(7) - get_epoch_block_offset(6)) * get_epoch_reward(6)
+ (get_epoch_block_offset(8) - get_epoch_block_offset(7)) * get_epoch_reward(7)
+ (get_epoch_block_offset(9) - get_epoch_block_offset(8)) * get_epoch_reward(8)
+ get_epoch_reward(9) * 40
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(10), true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ (get_epoch_block_offset(7) - get_epoch_block_offset(6)) * get_epoch_reward(6)
+ (get_epoch_block_offset(8) - get_epoch_block_offset(7)) * get_epoch_reward(7)
+ (get_epoch_block_offset(9) - get_epoch_block_offset(8)) * get_epoch_reward(8)
+ (get_epoch_block_offset(10) - get_epoch_block_offset(9)) * get_epoch_reward(9)
+ get_epoch_reward(10)
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(10) + 1, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ (get_epoch_block_offset(7) - get_epoch_block_offset(6)) * get_epoch_reward(6)
+ (get_epoch_block_offset(8) - get_epoch_block_offset(7)) * get_epoch_reward(7)
+ (get_epoch_block_offset(9) - get_epoch_block_offset(8)) * get_epoch_reward(8)
+ (get_epoch_block_offset(10) - get_epoch_block_offset(9)) * get_epoch_reward(9)
+ get_epoch_reward(10) * 2
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(11), true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ (get_epoch_block_offset(7) - get_epoch_block_offset(6)) * get_epoch_reward(6)
+ (get_epoch_block_offset(8) - get_epoch_block_offset(7)) * get_epoch_reward(7)
+ (get_epoch_block_offset(9) - get_epoch_block_offset(8)) * get_epoch_reward(8)
+ (get_epoch_block_offset(10) - get_epoch_block_offset(9)) * get_epoch_reward(9)
+ (get_epoch_block_offset(11) - get_epoch_block_offset(10)) * get_epoch_reward(10)
+ get_epoch_reward(11)
);
assert_eq!(
calc_mwc_block_overage(get_epoch_block_offset(11) + 39, true),
genesis_reward
+ MWC_FIRST_GROUP_REWARD * (get_epoch_block_offset(2) - 1)
+ (get_epoch_block_offset(3) - get_epoch_block_offset(2)) * get_epoch_reward(2)
+ (get_epoch_block_offset(4) - get_epoch_block_offset(3)) * get_epoch_reward(3)
+ (get_epoch_block_offset(5) - get_epoch_block_offset(4)) * get_epoch_reward(4)
+ (get_epoch_block_offset(6) - get_epoch_block_offset(5)) * get_epoch_reward(5)
+ (get_epoch_block_offset(7) - get_epoch_block_offset(6)) * get_epoch_reward(6)
+ (get_epoch_block_offset(8) - get_epoch_block_offset(7)) * get_epoch_reward(7)
+ (get_epoch_block_offset(9) - get_epoch_block_offset(8)) * get_epoch_reward(8)
+ (get_epoch_block_offset(10) - get_epoch_block_offset(9)) * get_epoch_reward(9)
+ (get_epoch_block_offset(11) - get_epoch_block_offset(10)) * get_epoch_reward(10)
+ get_epoch_reward(11) * 40
);
let total_blocks_reward = calc_mwc_block_overage(2_100_000_000 * 1000, true);
assert_eq!(total_blocks_reward, 20000000000000000);
}
#[test]
#[ignore]
fn test_rewards_full_cycle() {
global::set_local_chain_type(global::ChainTypes::Mainnet);
let mut total_coins: u64 = GENESIS_BLOCK_REWARD;
let mut height: u64 = 0;
let mut zero_reward_blocks = 0;
let total_blocks = get_epoch_block_offset(12);
while zero_reward_blocks < 100 {
assert_eq!(calc_mwc_block_overage(height, true), total_coins);
height += 1;
let r = calc_mwc_block_reward(height);
total_coins += r;
if r == 0 {
zero_reward_blocks += 1;
}
if height % 1000000 == 0 {
println!(
"Current height={}, reward={}, coins={}, progress={:.1}%",
height,
r,
total_coins,
height as f64 / total_blocks as f64 * 100.0
);
}
}
println!(
"Finished with height={}, reward={}, coins={}",
height,
calc_mwc_block_reward(height),
total_coins
);
assert_eq!(total_coins, 20000000000000000);
assert!(height > get_epoch_block_offset(12) + 99);
}
#[test]
fn test_last_epoch() {
global::set_local_chain_type(global::ChainTypes::Mainnet);
let mut total_coins: u64 = 19990529927788020;
let mut height: u64 = 886000000;
let mut zero_reward_blocks = 0;
while zero_reward_blocks < 100 {
assert_eq!(calc_mwc_block_overage(height, true), total_coins);
height += 1;
let r = calc_mwc_block_reward(height);
total_coins += r;
if r == 0 {
zero_reward_blocks += 1;
}
}
assert_eq!(total_coins, 20000000000000000);
assert!(height > get_epoch_block_offset(12) + 99);
}
}