nimiq_primitives/
policy.rs1use num_bigint::BigUint;
2use num_traits::pow;
3use parking_lot::RwLock;
4#[cfg(feature = "coin")]
5use crate::coin::Coin;
6use fixed_unsigned::types::FixedUnsigned10;
7
8
9lazy_static! {
10 pub static ref BLOCK_TARGET_MAX: FixedUnsigned10 = {
12 FixedUnsigned10::from(pow(BigUint::from(2u64), 240))
13 };
14}
15
16pub const BLOCK_TIME: u32 = 60;
18
19pub const DIFFICULTY_BLOCK_WINDOW: u32 = 120;
21
22pub const DIFFICULTY_MAX_ADJUSTMENT_FACTOR: f64 = 2f64;
24
25pub const TRANSACTION_VALIDITY_WINDOW: u32 = 120;
27
28pub const TOTAL_SUPPLY: u64 = 2_100_000_000_000_000;
30
31const INITIAL_SUPPLY: u64 = 252_000_000_000_000;
33
34const EMISSION_TAIL_START: u32 = 48_692_960;
36
37const EMISSION_TAIL_REWARD: u64 = 4000;
39
40const EMISSION_SPEED: u64 = 4_194_304;
42
43lazy_static! {
44 static ref SUPPLY_CACHE: RwLock<Vec<u64>> = RwLock::new(vec![INITIAL_SUPPLY]);
45}
46
47const SUPPLY_CACHE_INTERVAL: u32 = 5000;
48
49fn supply_after(block_height: u32) -> u64 {
50 let end_i = block_height / SUPPLY_CACHE_INTERVAL;
51 let start_i;
52 let mut supply;
53 {
54 let cache = SUPPLY_CACHE.read();
55 start_i = end_i.min(cache.len().saturating_sub(1) as u32);
56 supply = cache[start_i as usize];
57 }
58
59 {
61 let mut cache = SUPPLY_CACHE.write();
62 for i in start_i..end_i {
63 let start_height = i * SUPPLY_CACHE_INTERVAL;
64 let end_height = (i + 1) * SUPPLY_CACHE_INTERVAL;
65 supply = supply_between(supply, start_height, end_height);
66 cache.push(supply);
67 }
68 }
69
70 supply_between(supply, end_i * SUPPLY_CACHE_INTERVAL, block_height + 1)
72}
73
74fn supply_between(initial_supply: u64, start_height: u32, end_height: u32) -> u64 {
75 let mut supply = initial_supply;
76 for i in start_height..end_height {
77 supply += compute_block_reward(supply, i);
78 }
79 supply
80}
81
82fn compute_block_reward(current_supply: u64, block_height: u32) -> u64 {
83 if block_height == 0 {
84 return 0;
85 }
86
87 let remaining = TOTAL_SUPPLY - current_supply;
88 if block_height >= EMISSION_TAIL_START && remaining >= EMISSION_TAIL_REWARD {
89 return EMISSION_TAIL_REWARD;
90 }
91
92 let remainder = remaining % EMISSION_SPEED;
93 (remaining - remainder) / EMISSION_SPEED
94}
95
96#[cfg(feature = "coin")]
97pub fn block_reward_at(block_height: u32) -> Coin {
98 assert!(block_height >= 1, "block_height must be >= 1");
99 let current_supply = supply_after(block_height - 1);
100 Coin::from_u64(compute_block_reward(current_supply, block_height)).unwrap()
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn it_correctly_computes_block_reward() {
109 assert_eq!(block_reward_at(1), Coin::from_u64(440597534).unwrap());
110 assert_eq!(block_reward_at(2), Coin::from_u64(440597429).unwrap());
111 assert_eq!(block_reward_at(3), Coin::from_u64(440597324).unwrap());
112 assert_eq!(block_reward_at(1000), Coin::from_u64(440492605).unwrap());
113 assert_eq!(block_reward_at(4999), Coin::from_u64(440072823).unwrap());
114 assert_eq!(block_reward_at(5000), Coin::from_u64(440072718).unwrap());
115 assert_eq!(block_reward_at(5001), Coin::from_u64(440072613).unwrap());
116 assert_eq!(block_reward_at(5002), Coin::from_u64(440072508).unwrap());
117 assert_eq!(block_reward_at(100000), Coin::from_u64(430217207).unwrap());
118 assert_eq!(block_reward_at(10000000), Coin::from_u64(40607225).unwrap());
119 assert_eq!(block_reward_at(48692959), Coin::from_u64(4001).unwrap());
120 assert_eq!(block_reward_at(48692960), Coin::from_u64(4000).unwrap());
121 assert_eq!(block_reward_at(52888984), Coin::from_u64(4000).unwrap());
122 assert_eq!(block_reward_at(52888985), Coin::from_u64(0).unwrap());
123 }
124
125 #[test]
126 fn it_correctly_computes_supply() {
127 assert_eq!(supply_after(0), 252000000000000);
128 assert_eq!(supply_after(1), 252000440597534);
129 assert_eq!(supply_after(5000), 254201675369298);
130 assert_eq!(supply_after(52888983), 2099999999996000);
131 assert_eq!(supply_after(52888984), 2100000000000000);
132 }
133}