nimiq_primitives/
policy.rs

1use 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    /// The highest (easiest) block PoW target.
11    pub static ref BLOCK_TARGET_MAX: FixedUnsigned10  = {
12        FixedUnsigned10::from(pow(BigUint::from(2u64), 240))
13    };
14}
15
16/// Targeted block time in seconds.
17pub const BLOCK_TIME: u32 = 60;
18
19/// Number of blocks we take into account to calculate next difficulty.
20pub const DIFFICULTY_BLOCK_WINDOW: u32 = 120;
21
22/// Limits the rate at which the difficulty is adjusted min/max.
23pub const DIFFICULTY_MAX_ADJUSTMENT_FACTOR: f64 = 2f64;
24
25/// Number of blocks a transaction is valid.
26pub const TRANSACTION_VALIDITY_WINDOW: u32 = 120;
27
28/// Total supply in satoshis.
29pub const TOTAL_SUPPLY: u64 = 2_100_000_000_000_000;
30
31/// Initial supply in satoshis.
32const INITIAL_SUPPLY: u64 = 252_000_000_000_000;
33
34/// First block using constant tail emission until total supply is reached.
35const EMISSION_TAIL_START: u32 = 48_692_960;
36
37/// Constant tail emission in satoshis until total supply is reached.
38const EMISSION_TAIL_REWARD: u64 = 4000;
39
40/// Emission speed.
41const 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    // Update cache.
60    {
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    // Calculate remaining supply.
71    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}