1use console::prelude::{ConsensusVersion, Network, Result, ensure};
17
18pub const MAX_COINBASE_REWARD: u64 = 190_258_739; const V2_MAX_BLOCK_INTERVAL: i64 = 60; const V2_MIN_BLOCK_INTERVAL: i64 = 1; const SECONDS_IN_A_YEAR: u32 = 60 * 60 * 24 * 365;
30
31pub fn block_reward<N: Network>(
33 block_height: u32,
34 total_supply: u64,
35 block_time: u16,
36 time_since_last_block: i64,
37 coinbase_reward: u64,
38 transaction_fees: u64,
39) -> Result<u64> {
40 let consensus_version = N::CONSENSUS_VERSION(block_height)?;
42 if consensus_version == ConsensusVersion::V1 {
43 Ok(block_reward_v1(total_supply, block_time, coinbase_reward, transaction_fees))
44 } else {
45 Ok(block_reward_v2(total_supply, time_since_last_block, coinbase_reward, transaction_fees))
46 }
47}
48
49pub const fn block_reward_v1(total_supply: u64, block_time: u16, coinbase_reward: u64, transaction_fees: u64) -> u64 {
56 let block_height_at_year_1 = block_height_at_year(block_time, 1);
58 let annual_reward = total_supply / 20;
60 let block_reward = annual_reward / block_height_at_year_1 as u64;
62 block_reward + (coinbase_reward / 3) + transaction_fees
64}
65
66pub fn block_reward_v2(
76 total_supply: u64,
77 time_since_last_block: i64,
78 coinbase_reward: u64,
79 transaction_fees: u64,
80) -> u64 {
81 let annual_reward = total_supply / 20;
83 let time_since_last_block = time_since_last_block.clamp(V2_MIN_BLOCK_INTERVAL, V2_MAX_BLOCK_INTERVAL);
85 let block_reward = annual_reward * time_since_last_block as u64 / SECONDS_IN_A_YEAR as u64;
87 block_reward + (coinbase_reward / 3) + transaction_fees
89}
90
91pub const fn puzzle_reward(coinbase_reward: u64) -> u64 {
95 coinbase_reward.saturating_mul(2).saturating_div(3)
97}
98
99pub fn coinbase_reward<N: Network>(
101 block_height: u32,
102 block_timestamp: i64,
103 genesis_timestamp: i64,
104 starting_supply: u64,
105 anchor_time: u16,
106 anchor_height: u32,
107 block_time: u16,
108 combined_proof_target: u128,
109 cumulative_proof_target: u64,
110 coinbase_target: u64,
111) -> Result<u64> {
112 let consensus_version = N::CONSENSUS_VERSION(block_height)?;
114 if consensus_version == ConsensusVersion::V1 {
115 coinbase_reward_v1(
116 block_height,
117 starting_supply,
118 anchor_height,
119 block_time,
120 combined_proof_target,
121 cumulative_proof_target,
122 coinbase_target,
123 )
124 } else {
125 coinbase_reward_v2(
126 block_timestamp,
127 genesis_timestamp,
128 starting_supply,
129 anchor_time,
130 combined_proof_target,
131 cumulative_proof_target,
132 coinbase_target,
133 )
134 }
135}
136
137pub fn coinbase_reward_v1(
145 block_height: u32,
146 starting_supply: u64,
147 anchor_height: u32,
148 block_time: u16,
149 combined_proof_target: u128,
150 cumulative_proof_target: u64,
151 coinbase_target: u64,
152) -> Result<u64> {
153 let remaining_coinbase_target = coinbase_target.saturating_sub(cumulative_proof_target);
155 let remaining_proof_target = combined_proof_target.min(remaining_coinbase_target as u128);
157
158 let anchor_block_reward = anchor_block_reward_at_height(block_height, starting_supply, anchor_height, block_time);
160
161 let reward = anchor_block_reward.saturating_mul(remaining_proof_target).saturating_div(coinbase_target as u128);
163
164 ensure!(reward <= MAX_COINBASE_REWARD as u128, "Coinbase reward ({reward}) exceeds maximum {MAX_COINBASE_REWARD}");
166
167 Ok(u64::try_from(reward).expect("Coinbase reward exceeds u64::MAX"))
170}
171
172pub fn coinbase_reward_v2(
180 block_timestamp: i64,
181 genesis_timestamp: i64,
182 starting_supply: u64,
183 anchor_time: u16,
184 combined_proof_target: u128,
185 cumulative_proof_target: u64,
186 coinbase_target: u64,
187) -> Result<u64> {
188 let remaining_coinbase_target = coinbase_target.saturating_sub(cumulative_proof_target);
190 let remaining_proof_target = combined_proof_target.min(remaining_coinbase_target as u128);
192
193 let anchor_block_reward =
195 anchor_block_reward_at_timestamp(block_timestamp, genesis_timestamp, starting_supply, anchor_time);
196
197 let reward = anchor_block_reward.saturating_mul(remaining_proof_target).saturating_div(coinbase_target as u128);
199
200 ensure!(reward <= MAX_COINBASE_REWARD as u128, "Coinbase reward ({reward}) exceeds maximum {MAX_COINBASE_REWARD}");
202
203 Ok(u64::try_from(reward).expect("Coinbase reward exceeds u64::MAX"))
206}
207
208fn anchor_block_reward_at_height(block_height: u32, starting_supply: u64, anchor_height: u32, block_time: u16) -> u128 {
218 const fn block_reward_at_height(height: u32, starting_supply: u64, anchor_height: u32, block_time: u16) -> u128 {
220 let block_height_at_year_10 = block_height_at_year(block_time, 10) as u128;
222 let num_remaining_blocks_to_year_10 = block_height_at_year_10.saturating_sub(height as u128);
224 let numerator = 2 * starting_supply as u128 * anchor_height as u128 * num_remaining_blocks_to_year_10;
226 let denominator = block_height_at_year_10 * (block_height_at_year_10 + 1);
228 numerator / denominator
230 }
231
232 let block_height_at_year_9 = block_height_at_year(block_time, 9);
234 let reward_at_year_9 = block_reward_at_height(block_height_at_year_9, starting_supply, anchor_height, block_time);
236 let reward_at_block_height = block_reward_at_height(block_height, starting_supply, anchor_height, block_time);
238 reward_at_block_height.max(reward_at_year_9)
240}
241
242fn anchor_block_reward_at_timestamp(
254 block_timestamp: i64,
255 genesis_timestamp: i64,
256 starting_supply: u64,
257 anchor_time: u16,
258) -> u128 {
259 const fn block_reward_at_timestamp(
261 block_timestamp: i64,
262 genesis_timestamp: i64,
263 starting_supply: u64,
264 anchor_time: u16,
265 ) -> u128 {
266 let timestamp_at_year_10 = timestamp_at_year(genesis_timestamp, 10) as u128;
268 let number_of_seconds_in_10_years = (SECONDS_IN_A_YEAR as u128).saturating_mul(10);
270 let num_remaining_seconds_to_year_10 = timestamp_at_year_10.saturating_sub(block_timestamp as u128);
272
273 let numerator =
278 2 * starting_supply as u128 * anchor_time.saturating_div(10) as u128 * num_remaining_seconds_to_year_10;
279 let denominator = number_of_seconds_in_10_years * (number_of_seconds_in_10_years.saturating_div(10) + 1);
281 numerator / denominator
283 }
284
285 let timestamp_at_year_9 = timestamp_at_year(genesis_timestamp, 9);
287 let reward_at_year_9 =
289 block_reward_at_timestamp(timestamp_at_year_9, genesis_timestamp, starting_supply, anchor_time);
290 let reward_at_block_timestamp =
292 block_reward_at_timestamp(block_timestamp, genesis_timestamp, starting_supply, anchor_time);
293 reward_at_block_timestamp.max(reward_at_year_9)
295}
296
297const fn timestamp_at_year(genesis_timestamp: i64, num_years: u32) -> i64 {
300 let seconds_elapsed = SECONDS_IN_A_YEAR.saturating_mul(num_years);
302 genesis_timestamp.saturating_add(seconds_elapsed as i64)
304}
305
306const fn block_height_at_year(block_time: u16, num_years: u32) -> u32 {
308 let block_height_at_year_1 = SECONDS_IN_A_YEAR / block_time as u32;
310 block_height_at_year_1 * num_years
312}
313
314pub fn coinbase_target(
316 previous_target: u64,
317 previous_block_timestamp: i64,
318 block_timestamp: i64,
319 anchor_time: u16,
320 num_blocks_per_epoch: u32,
321 genesis_target: u64,
322) -> Result<u64> {
323 let half_life = num_blocks_per_epoch.saturating_div(2).saturating_mul(anchor_time as u32);
325 let candidate_target =
327 retarget(previous_target, previous_block_timestamp, block_timestamp, anchor_time, half_life, true)?;
328 Ok(candidate_target.max(genesis_target))
330}
331
332pub fn proof_target(coinbase_target: u64, genesis_proof_target: u64, max_solutions_as_power_of_two: u8) -> u64 {
334 coinbase_target
335 .checked_shr(max_solutions_as_power_of_two as u32)
336 .map(|target| target.saturating_add(1))
337 .unwrap_or(genesis_proof_target)
338}
339
340fn retarget(
348 previous_target: u64,
349 previous_block_timestamp: i64,
350 block_timestamp: i64,
351 anchor_time: u16,
352 half_life: u32,
353 is_inverse: bool,
354) -> Result<u64> {
355 let block_time_elapsed = block_timestamp.saturating_sub(previous_block_timestamp).max(1);
358 let mut drift = block_time_elapsed.saturating_sub(anchor_time as i64);
360
361 if drift == 0 {
363 return Ok(previous_target);
364 }
365
366 if is_inverse {
368 drift *= -1;
369 }
370
371 const RBITS: u32 = 16;
373 const RADIX: u128 = 1 << RBITS;
374
375 let (integral, fractional) = {
377 let exponent = (RADIX as i128).saturating_mul(drift as i128) / half_life as i128;
379
380 let integral = exponent >> RBITS;
382 let fractional = (exponent - (integral << RBITS)) as u128;
383 ensure!(fractional < RADIX, "Fractional part is not within the fixed point size");
384 ensure!(exponent == (integral * (RADIX as i128) + fractional as i128), "Exponent is decomposed incorrectly");
385
386 (integral, fractional)
387 };
388
389 let fractional_multiplier = RADIX
392 + ((195_766_423_245_049_u128 * fractional
393 + 971_821_376_u128 * fractional.pow(2)
394 + 5_127_u128 * fractional.pow(3)
395 + 2_u128.pow(RBITS * 3 - 1))
396 >> (RBITS * 3));
397
398 let candidate_target = (previous_target as u128).saturating_mul(fractional_multiplier);
402
403 let shifts = integral - RBITS as i128;
406 let mut candidate_target = if shifts < 0 {
407 match candidate_target.checked_shr(u32::try_from(-shifts)?) {
408 Some(target) => core::cmp::max(target, 1),
409 None => 1,
410 }
411 } else {
412 match candidate_target.checked_shl(u32::try_from(shifts)?) {
413 Some(target) => core::cmp::max(target, 1),
414 None => u64::MAX as u128,
415 }
416 };
417
418 candidate_target = core::cmp::min(candidate_target, u64::MAX as u128);
420
421 ensure!(candidate_target.checked_shr(64) == Some(0), "The target has overflowed");
423 Ok(u64::try_from(candidate_target)?)
425}
426
427pub fn to_next_targets<N: Network>(
443 latest_cumulative_proof_target: u128,
444 combined_proof_target: u128,
445 latest_coinbase_target: u64,
446 latest_cumulative_weight: u128,
447 last_coinbase_target: u64,
448 last_coinbase_timestamp: i64,
449 next_timestamp: i64,
450) -> Result<(u64, u64, u128, u128, u64, i64)> {
451 let latest_coinbase_threshold = latest_coinbase_target.saturating_div(2) as u128;
453 let next_cumulative_proof_target = latest_cumulative_proof_target.saturating_add(combined_proof_target);
455 let is_coinbase_threshold_reached = next_cumulative_proof_target >= latest_coinbase_threshold;
457 let next_coinbase_target = coinbase_target(
459 last_coinbase_target,
460 last_coinbase_timestamp,
461 next_timestamp,
462 N::ANCHOR_TIME,
463 N::NUM_BLOCKS_PER_EPOCH,
464 N::GENESIS_COINBASE_TARGET,
465 )?;
466 let next_proof_target =
468 proof_target(next_coinbase_target, N::GENESIS_PROOF_TARGET, N::MAX_SOLUTIONS_AS_POWER_OF_TWO);
469
470 let next_cumulative_proof_target = match is_coinbase_threshold_reached {
472 true => 0,
473 false => next_cumulative_proof_target,
474 };
475
476 let next_cumulative_weight = latest_cumulative_weight.saturating_add(combined_proof_target);
478
479 let (next_last_coinbase_target, next_last_coinbase_timestamp) = match is_coinbase_threshold_reached {
481 true => (next_coinbase_target, next_timestamp),
482 false => (last_coinbase_target, last_coinbase_timestamp),
483 };
484
485 Ok((
486 next_coinbase_target,
487 next_proof_target,
488 next_cumulative_proof_target,
489 next_cumulative_weight,
490 next_last_coinbase_target,
491 next_last_coinbase_timestamp,
492 ))
493}
494
495#[cfg(test)]
496mod tests {
497 use super::*;
498 use console::network::{MainnetV0, TestnetV0, prelude::*};
499
500 type CurrentNetwork = MainnetV0;
501
502 const ITERATIONS: u32 = 1000;
503
504 const EXPECTED_ANCHOR_BLOCK_REWARD_AT_BLOCK_1: u128 = MAX_COINBASE_REWARD as u128;
505 const EXPECTED_STAKING_REWARD: u64 = 23_782_343;
506 const EXPECTED_COINBASE_REWARD_AT_BLOCK_1: u64 = MAX_COINBASE_REWARD;
507 const EXPECTED_MAX_STAKING_REWARD: u64 = 142_694_063;
508
509 #[test]
510 fn test_anchor_block_reward_v1() {
511 let reward_at_block_1 = anchor_block_reward_at_height(
513 1,
514 CurrentNetwork::STARTING_SUPPLY,
515 CurrentNetwork::ANCHOR_HEIGHT,
516 CurrentNetwork::BLOCK_TIME,
517 );
518 assert_eq!(reward_at_block_1, EXPECTED_ANCHOR_BLOCK_REWARD_AT_BLOCK_1);
519
520 fn check_reward_at_year(year: u32, expected_reward: u128) {
522 let reward_at_year = anchor_block_reward_at_height(
523 block_height_at_year(CurrentNetwork::BLOCK_TIME, year),
524 CurrentNetwork::STARTING_SUPPLY,
525 CurrentNetwork::ANCHOR_HEIGHT,
526 CurrentNetwork::BLOCK_TIME,
527 );
528 assert_eq!(reward_at_year, expected_reward);
529 }
530
531 check_reward_at_year(1, 171_232_871);
533 check_reward_at_year(2, 152_206_996);
534 check_reward_at_year(3, 133_181_122);
535 check_reward_at_year(4, 114_155_247);
536 check_reward_at_year(5, 95_129_372);
537 check_reward_at_year(6, 76_103_498);
538 check_reward_at_year(7, 57_077_623);
539 check_reward_at_year(8, 38_051_749);
540 check_reward_at_year(9, 19_025_874);
541 check_reward_at_year(10, 19_025_874);
542 check_reward_at_year(11, 19_025_874);
543 check_reward_at_year(12, 19_025_874);
544 check_reward_at_year(13, 19_025_874);
545 check_reward_at_year(14, 19_025_874);
546 check_reward_at_year(15, 19_025_874);
547
548 let block_height_at_year_9 = block_height_at_year(CurrentNetwork::BLOCK_TIME, 9);
550
551 let mut previous_reward = reward_at_block_1;
553 let anchor_height = CurrentNetwork::ANCHOR_HEIGHT as usize;
554 for height in (2..block_height_at_year_9).step_by(anchor_height).skip(1) {
555 let reward = anchor_block_reward_at_height(
556 height,
557 CurrentNetwork::STARTING_SUPPLY,
558 CurrentNetwork::ANCHOR_HEIGHT,
559 CurrentNetwork::BLOCK_TIME,
560 );
561 assert!(reward < previous_reward, "Failed on block height {height}");
562 previous_reward = reward;
563 }
564
565 for height in block_height_at_year_9..(block_height_at_year_9 + ITERATIONS) {
567 let reward = anchor_block_reward_at_height(
568 height,
569 CurrentNetwork::STARTING_SUPPLY,
570 CurrentNetwork::ANCHOR_HEIGHT,
571 CurrentNetwork::BLOCK_TIME,
572 );
573 assert_eq!(reward, 19_025_874);
574 }
575 }
576
577 #[test]
578 fn test_anchor_block_reward_v2() {
579 let reward_at_block_1 = anchor_block_reward_at_timestamp(
581 CurrentNetwork::GENESIS_TIMESTAMP + CurrentNetwork::BLOCK_TIME as i64,
582 CurrentNetwork::GENESIS_TIMESTAMP,
583 CurrentNetwork::STARTING_SUPPLY,
584 CurrentNetwork::ANCHOR_TIME,
585 );
586 assert_eq!(reward_at_block_1, EXPECTED_ANCHOR_BLOCK_REWARD_AT_BLOCK_1);
587
588 fn check_reward_at_year(year: u32, expected_reward: u128) {
590 let reward_at_year = anchor_block_reward_at_timestamp(
591 timestamp_at_year(CurrentNetwork::GENESIS_TIMESTAMP, year),
592 CurrentNetwork::GENESIS_TIMESTAMP,
593 CurrentNetwork::STARTING_SUPPLY,
594 CurrentNetwork::ANCHOR_TIME,
595 );
596 assert_eq!(reward_at_year, expected_reward);
597 }
598
599 check_reward_at_year(1, 171_232_871);
601 check_reward_at_year(2, 152_206_996);
602 check_reward_at_year(3, 133_181_122);
603 check_reward_at_year(4, 114_155_247);
604 check_reward_at_year(5, 95_129_372);
605 check_reward_at_year(6, 76_103_498);
606 check_reward_at_year(7, 57_077_623);
607 check_reward_at_year(8, 38_051_749);
608 check_reward_at_year(9, 19_025_874);
609 check_reward_at_year(10, 19_025_874);
610 check_reward_at_year(11, 19_025_874);
611 check_reward_at_year(12, 19_025_874);
612 check_reward_at_year(13, 19_025_874);
613 check_reward_at_year(14, 19_025_874);
614 check_reward_at_year(15, 19_025_874);
615
616 let timestamp_at_year_9 = timestamp_at_year(CurrentNetwork::GENESIS_TIMESTAMP, 9);
618
619 let mut previous_reward = reward_at_block_1;
621 let anchor_time = CurrentNetwork::ANCHOR_TIME as usize;
622 for timestamp in (CurrentNetwork::GENESIS_TIMESTAMP..timestamp_at_year_9).step_by(anchor_time).skip(1) {
623 let reward = anchor_block_reward_at_timestamp(
624 timestamp,
625 CurrentNetwork::GENESIS_TIMESTAMP,
626 CurrentNetwork::STARTING_SUPPLY,
627 CurrentNetwork::ANCHOR_TIME,
628 );
629 assert!(reward < previous_reward, "Failed on timestamp {timestamp}");
630 previous_reward = reward;
631 }
632
633 for timestamp in timestamp_at_year_9..(timestamp_at_year_9 + ITERATIONS as i64) {
635 let reward = anchor_block_reward_at_timestamp(
636 timestamp,
637 CurrentNetwork::GENESIS_TIMESTAMP,
638 CurrentNetwork::STARTING_SUPPLY,
639 CurrentNetwork::ANCHOR_TIME,
640 );
641 assert_eq!(reward, 19_025_874);
642 }
643 }
644
645 #[test]
646 fn test_total_anchor_block_reward_v1() {
647 fn add_anchor_block_reward(total_reward: &mut u128, start_height: u32, end_height: u32) {
649 for height in start_height..end_height {
650 *total_reward += anchor_block_reward_at_height(
651 height,
652 CurrentNetwork::STARTING_SUPPLY,
653 CurrentNetwork::ANCHOR_HEIGHT,
654 CurrentNetwork::BLOCK_TIME,
655 );
656 }
657 }
658
659 let mut total_reward = 0;
661
662 let mut check_sum_of_anchor_rewards = |year: u32, expected_reward: u128| {
664 assert!(year > 0, "Year must be greater than 0");
665 let end_height = block_height_at_year(CurrentNetwork::BLOCK_TIME, year);
666 let start_height = std::cmp::max(1, block_height_at_year(CurrentNetwork::BLOCK_TIME, year - 1));
667 add_anchor_block_reward(&mut total_reward, start_height, end_height);
668 assert_eq!(total_reward, expected_reward);
669 };
670
671 check_sum_of_anchor_rewards(1, 569999799602807);
673 check_sum_of_anchor_rewards(2, 1079999791366949);
675 check_sum_of_anchor_rewards(3, 1529999785033683);
677 check_sum_of_anchor_rewards(4, 1919999780603002);
679 check_sum_of_anchor_rewards(5, 2249999778074916);
681 check_sum_of_anchor_rewards(6, 2519999777449404);
683 check_sum_of_anchor_rewards(7, 2729999778726485);
685 check_sum_of_anchor_rewards(8, 2879999781906155);
687 check_sum_of_anchor_rewards(9, 2969999786988413);
689 check_sum_of_anchor_rewards(10, 3029999783234813);
691 check_sum_of_anchor_rewards(11, 3089999779481213);
693 check_sum_of_anchor_rewards(12, 3149999775727613);
695 check_sum_of_anchor_rewards(13, 3209999771974013);
697 check_sum_of_anchor_rewards(14, 3269999768220413);
699 check_sum_of_anchor_rewards(15, 3329999764466813);
701 }
702
703 #[test]
704 fn test_total_anchor_block_reward_v2() {
705 fn add_anchor_block_reward(total_reward: &mut u128, start_timestamp: i64, end_timestamp: i64) {
707 for timestamp in (start_timestamp..end_timestamp).step_by(CurrentNetwork::BLOCK_TIME as usize) {
708 *total_reward += anchor_block_reward_at_timestamp(
709 timestamp,
710 CurrentNetwork::GENESIS_TIMESTAMP,
711 CurrentNetwork::STARTING_SUPPLY,
712 CurrentNetwork::ANCHOR_TIME,
713 );
714 }
715 }
716
717 let mut total_reward = 0;
719
720 let mut check_sum_of_anchor_rewards = |year: u32, expected_reward: u128| {
722 assert!(year > 0, "Year must be greater than 0");
723 let end_timestamp = timestamp_at_year(CurrentNetwork::GENESIS_TIMESTAMP, year);
724 let start_timestamp = std::cmp::max(
725 CurrentNetwork::GENESIS_TIMESTAMP,
726 timestamp_at_year(CurrentNetwork::GENESIS_TIMESTAMP, year - 1),
727 );
728 add_anchor_block_reward(&mut total_reward, start_timestamp, end_timestamp);
729 assert_eq!(total_reward, expected_reward);
731 };
732
733 check_sum_of_anchor_rewards(1, 569999989861552);
735 check_sum_of_anchor_rewards(2, 1079999981625694);
737 check_sum_of_anchor_rewards(3, 1529999975292428);
739 check_sum_of_anchor_rewards(4, 1919999970861747);
741 check_sum_of_anchor_rewards(5, 2249999968333661);
743 check_sum_of_anchor_rewards(6, 2519999967708149);
745 check_sum_of_anchor_rewards(7, 2729999968985230);
747 check_sum_of_anchor_rewards(8, 2879999972164900);
749 check_sum_of_anchor_rewards(9, 2969999977247158);
751 check_sum_of_anchor_rewards(10, 3029999973493558);
753 check_sum_of_anchor_rewards(11, 3089999969739958);
755 check_sum_of_anchor_rewards(12, 3149999965986358);
757 check_sum_of_anchor_rewards(13, 3209999962232758);
759 check_sum_of_anchor_rewards(14, 3269999958479158);
761 check_sum_of_anchor_rewards(15, 3329999954725558);
763 }
764
765 #[test]
766 fn test_block_reward() {
767 let mut rng = TestRng::default();
768
769 let time_since_last_block = rng.gen_range(1..=V2_MAX_BLOCK_INTERVAL);
771 let reward = block_reward::<TestnetV0>(
772 TestnetV0::CONSENSUS_HEIGHT(ConsensusVersion::V2).unwrap(),
773 TestnetV0::STARTING_SUPPLY,
774 TestnetV0::BLOCK_TIME,
775 time_since_last_block,
776 0,
777 0,
778 )
779 .unwrap();
780 let expected_reward = block_reward_v2(TestnetV0::STARTING_SUPPLY, time_since_last_block, 0, 0);
781 assert_eq!(reward, expected_reward);
782
783 for _ in 0..100 {
784 let consensus_v1_height = rng.gen_range(0..TestnetV0::CONSENSUS_HEIGHT(ConsensusVersion::V2).unwrap());
786 let consensus_v1_reward = block_reward::<TestnetV0>(
787 consensus_v1_height,
788 TestnetV0::STARTING_SUPPLY,
789 TestnetV0::BLOCK_TIME,
790 0,
791 0,
792 0,
793 )
794 .unwrap();
795 let expected_reward = block_reward_v1(TestnetV0::STARTING_SUPPLY, TestnetV0::BLOCK_TIME, 0, 0);
796 assert_eq!(consensus_v1_reward, expected_reward);
797
798 let consensus_v2_height =
800 rng.gen_range(TestnetV0::CONSENSUS_HEIGHT(ConsensusVersion::V2).unwrap()..u32::MAX);
801 let time_since_last_block = rng.gen_range(1..=V2_MAX_BLOCK_INTERVAL);
802 let consensus_v2_reward = block_reward::<TestnetV0>(
803 consensus_v2_height,
804 TestnetV0::STARTING_SUPPLY,
805 TestnetV0::BLOCK_TIME,
806 time_since_last_block,
807 0,
808 0,
809 )
810 .unwrap();
811 let expected_reward = block_reward_v2(TestnetV0::STARTING_SUPPLY, time_since_last_block, 0, 0);
812 assert_eq!(consensus_v2_reward, expected_reward);
813 }
814 }
815
816 #[test]
817 fn test_block_reward_v1() {
818 let reward = block_reward_v1(CurrentNetwork::STARTING_SUPPLY, CurrentNetwork::BLOCK_TIME, 0, 0);
819 assert_eq!(reward, EXPECTED_STAKING_REWARD);
820
821 let larger_reward = block_reward_v1(CurrentNetwork::STARTING_SUPPLY, CurrentNetwork::BLOCK_TIME + 1, 0, 0);
823 assert!(reward < larger_reward);
824
825 let smaller_reward = block_reward_v1(CurrentNetwork::STARTING_SUPPLY, CurrentNetwork::BLOCK_TIME - 1, 0, 0);
827 assert!(reward > smaller_reward);
828 }
829
830 #[test]
831 fn test_block_reward_v2() {
832 let reward = block_reward_v2(CurrentNetwork::STARTING_SUPPLY, CurrentNetwork::BLOCK_TIME as i64, 0, 0);
833 assert_eq!(reward, EXPECTED_STAKING_REWARD);
834
835 let larger_reward =
837 block_reward_v2(CurrentNetwork::STARTING_SUPPLY, CurrentNetwork::BLOCK_TIME as i64 + 1, 0, 0);
838 assert!(reward < larger_reward);
839
840 let smaller_reward =
842 block_reward_v2(CurrentNetwork::STARTING_SUPPLY, CurrentNetwork::BLOCK_TIME as i64 - 1, 0, 0);
843 assert!(reward > smaller_reward);
844
845 let max_reward = block_reward_v2(CurrentNetwork::STARTING_SUPPLY, V2_MAX_BLOCK_INTERVAL, 0, 0);
847 assert_eq!(max_reward, EXPECTED_MAX_STAKING_REWARD);
848 let equivalent_reward = block_reward_v2(CurrentNetwork::STARTING_SUPPLY, V2_MAX_BLOCK_INTERVAL + 1, 0, 0);
849 assert_eq!(max_reward, equivalent_reward);
850
851 let min_reward = block_reward_v2(CurrentNetwork::STARTING_SUPPLY, 1, 0, 0);
853 let equivalent_reward = block_reward_v2(CurrentNetwork::STARTING_SUPPLY, 0, 0, 0);
854 assert_eq!(min_reward, equivalent_reward);
855 }
856
857 #[test]
858 fn test_block_reward_v1_vs_v2() {
859 let mut rng = TestRng::default();
860
861 const TOLERANCE: f64 = 0.001; let reward_v1 = block_reward_v1(CurrentNetwork::STARTING_SUPPLY, CurrentNetwork::BLOCK_TIME, 0, 0);
866 assert_eq!(reward_v1, EXPECTED_STAKING_REWARD);
867 let reward_v2 = block_reward_v2(CurrentNetwork::STARTING_SUPPLY, CurrentNetwork::BLOCK_TIME as i64, 0, 0);
868 assert_eq!(reward_v1, reward_v2);
869
870 let shorter_time = CurrentNetwork::BLOCK_TIME / 2;
872 let smaller_reward = block_reward_v2(CurrentNetwork::STARTING_SUPPLY, shorter_time as i64, 0, 0);
873 let expected_reward = EXPECTED_STAKING_REWARD / 2;
874 assert!((smaller_reward as f64 - expected_reward as f64).abs() / expected_reward as f64 <= TOLERANCE);
875
876 let longer_time = CurrentNetwork::BLOCK_TIME * 2;
878 let larger_reward = block_reward_v2(CurrentNetwork::STARTING_SUPPLY, longer_time as i64, 0, 0);
879 let expected_reward = EXPECTED_STAKING_REWARD * 2;
880 assert!((larger_reward as f64 - expected_reward as f64).abs() / expected_reward as f64 <= TOLERANCE);
881
882 for _ in 0..10 {
883 let factor = rng.gen_range(1..10);
885
886 let shorter_time = CurrentNetwork::BLOCK_TIME / factor;
888 let time_factor: f64 = CurrentNetwork::BLOCK_TIME as f64 / shorter_time as f64;
889 let smaller_reward = block_reward_v2(CurrentNetwork::STARTING_SUPPLY, shorter_time as i64, 0, 0);
890 let expected_reward = (EXPECTED_STAKING_REWARD as f64 / time_factor) as u64;
891 assert!((smaller_reward as f64 - expected_reward as f64).abs() / expected_reward as f64 <= TOLERANCE);
892
893 let longer_time = CurrentNetwork::BLOCK_TIME * factor;
895 let time_factor: f64 = longer_time as f64 / CurrentNetwork::BLOCK_TIME as f64;
896 let larger_reward = block_reward_v2(CurrentNetwork::STARTING_SUPPLY, longer_time as i64, 0, 0);
897 let expected_reward = (EXPECTED_STAKING_REWARD as f64 * time_factor) as u64;
898 match longer_time as i64 > V2_MAX_BLOCK_INTERVAL {
899 true => assert_eq!(larger_reward, EXPECTED_MAX_STAKING_REWARD),
900 false => {
901 assert!((larger_reward as f64 - expected_reward as f64).abs() / expected_reward as f64 <= TOLERANCE)
902 }
903 }
904 }
905 }
906
907 #[test]
908 fn test_coinbase_reward() {
909 let mut rng = TestRng::default();
910
911 let block_timestamp = TestnetV0::GENESIS_TIMESTAMP.saturating_add(
913 TestnetV0::CONSENSUS_HEIGHT(ConsensusVersion::V2).unwrap().saturating_mul(TestnetV0::BLOCK_TIME as u32)
914 as i64,
915 );
916 let reward = coinbase_reward::<TestnetV0>(
917 TestnetV0::CONSENSUS_HEIGHT(ConsensusVersion::V2).unwrap(),
918 block_timestamp,
919 TestnetV0::GENESIS_TIMESTAMP,
920 TestnetV0::STARTING_SUPPLY,
921 TestnetV0::ANCHOR_TIME,
922 TestnetV0::ANCHOR_HEIGHT,
923 TestnetV0::BLOCK_TIME,
924 1,
925 0,
926 1,
927 )
928 .unwrap();
929 let expected_reward = coinbase_reward_v2(
930 block_timestamp,
931 TestnetV0::GENESIS_TIMESTAMP,
932 TestnetV0::STARTING_SUPPLY,
933 TestnetV0::ANCHOR_TIME,
934 1,
935 0,
936 1,
937 )
938 .unwrap();
939 assert_eq!(reward, expected_reward);
940
941 for _ in 0..100 {
942 let consensus_v1_height = rng.gen_range(0..TestnetV0::CONSENSUS_HEIGHT(ConsensusVersion::V2).unwrap());
944 let block_timestamp = TestnetV0::GENESIS_TIMESTAMP
945 .saturating_add(consensus_v1_height.saturating_mul(TestnetV0::BLOCK_TIME as u32) as i64);
946 let consensus_v1_reward = coinbase_reward::<TestnetV0>(
947 consensus_v1_height,
948 block_timestamp,
949 TestnetV0::GENESIS_TIMESTAMP,
950 TestnetV0::STARTING_SUPPLY,
951 TestnetV0::ANCHOR_TIME,
952 TestnetV0::ANCHOR_HEIGHT,
953 TestnetV0::BLOCK_TIME,
954 1,
955 0,
956 1,
957 )
958 .unwrap();
959 let expected_reward = coinbase_reward_v1(
960 consensus_v1_height,
961 TestnetV0::STARTING_SUPPLY,
962 TestnetV0::ANCHOR_HEIGHT,
963 TestnetV0::BLOCK_TIME,
964 1,
965 0,
966 1,
967 )
968 .unwrap();
969 assert_eq!(consensus_v1_reward, expected_reward);
970
971 let consensus_v2_height =
973 rng.gen_range(TestnetV0::CONSENSUS_HEIGHT(ConsensusVersion::V2).unwrap()..u32::MAX);
974 let block_timestamp = TestnetV0::GENESIS_TIMESTAMP
975 .saturating_add(consensus_v2_height.saturating_mul(TestnetV0::BLOCK_TIME as u32) as i64);
976 let consensus_v2_reward = coinbase_reward::<TestnetV0>(
977 consensus_v2_height,
978 block_timestamp,
979 TestnetV0::GENESIS_TIMESTAMP,
980 TestnetV0::STARTING_SUPPLY,
981 TestnetV0::ANCHOR_TIME,
982 TestnetV0::ANCHOR_HEIGHT,
983 TestnetV0::BLOCK_TIME,
984 1,
985 0,
986 1,
987 )
988 .unwrap();
989 let expected_reward = coinbase_reward_v2(
990 block_timestamp,
991 TestnetV0::GENESIS_TIMESTAMP,
992 TestnetV0::STARTING_SUPPLY,
993 TestnetV0::ANCHOR_TIME,
994 1,
995 0,
996 1,
997 )
998 .unwrap();
999 assert_eq!(consensus_v2_reward, expected_reward);
1000 }
1001 }
1002
1003 #[test]
1004 fn test_coinbase_reward_v1() {
1005 let coinbase_target: u64 = 10000;
1006 let combined_proof_target: u128 = coinbase_target as u128;
1007
1008 let reward = coinbase_reward_v1(
1009 1,
1010 CurrentNetwork::STARTING_SUPPLY,
1011 CurrentNetwork::ANCHOR_HEIGHT,
1012 CurrentNetwork::BLOCK_TIME,
1013 combined_proof_target,
1014 0,
1015 coinbase_target,
1016 )
1017 .unwrap();
1018 assert_eq!(reward, EXPECTED_COINBASE_REWARD_AT_BLOCK_1);
1019
1020 let smaller_reward = coinbase_reward_v1(
1022 1,
1023 CurrentNetwork::STARTING_SUPPLY,
1024 CurrentNetwork::ANCHOR_HEIGHT,
1025 CurrentNetwork::BLOCK_TIME,
1026 combined_proof_target / 2,
1027 0,
1028 coinbase_target,
1029 )
1030 .unwrap();
1031 assert_eq!(smaller_reward, reward / 2);
1032
1033 let smaller_reward = coinbase_reward_v1(
1035 1,
1036 CurrentNetwork::STARTING_SUPPLY,
1037 CurrentNetwork::ANCHOR_HEIGHT,
1038 CurrentNetwork::BLOCK_TIME,
1039 combined_proof_target,
1040 coinbase_target / 2,
1041 coinbase_target,
1042 )
1043 .unwrap();
1044 assert_eq!(smaller_reward, reward / 2);
1045
1046 let equivalent_reward = coinbase_reward_v1(
1048 1,
1049 CurrentNetwork::STARTING_SUPPLY,
1050 CurrentNetwork::ANCHOR_HEIGHT,
1051 CurrentNetwork::BLOCK_TIME,
1052 u128::MAX,
1053 0,
1054 coinbase_target,
1055 )
1056 .unwrap();
1057 assert_eq!(reward, equivalent_reward);
1058
1059 let zero_reward = coinbase_reward_v1(
1061 1,
1062 CurrentNetwork::STARTING_SUPPLY,
1063 CurrentNetwork::ANCHOR_HEIGHT,
1064 CurrentNetwork::BLOCK_TIME,
1065 0,
1066 0,
1067 coinbase_target,
1068 )
1069 .unwrap();
1070 assert_eq!(zero_reward, 0);
1071
1072 let zero_reward = coinbase_reward_v1(
1074 1,
1075 CurrentNetwork::STARTING_SUPPLY,
1076 CurrentNetwork::ANCHOR_HEIGHT,
1077 CurrentNetwork::BLOCK_TIME,
1078 1,
1079 coinbase_target + 1,
1080 coinbase_target,
1081 )
1082 .unwrap();
1083 assert_eq!(zero_reward, 0);
1084 }
1085
1086 #[test]
1087 fn test_coinbase_reward_v2() {
1088 let coinbase_target: u64 = 10000;
1089 let combined_proof_target: u128 = coinbase_target as u128;
1090
1091 let reward = coinbase_reward_v2(
1092 CurrentNetwork::GENESIS_TIMESTAMP + CurrentNetwork::BLOCK_TIME as i64,
1093 CurrentNetwork::GENESIS_TIMESTAMP,
1094 CurrentNetwork::STARTING_SUPPLY,
1095 CurrentNetwork::ANCHOR_TIME,
1096 combined_proof_target,
1097 0,
1098 coinbase_target,
1099 )
1100 .unwrap();
1101 assert_eq!(reward, EXPECTED_COINBASE_REWARD_AT_BLOCK_1);
1102
1103 let smaller_reward = coinbase_reward_v2(
1105 CurrentNetwork::GENESIS_TIMESTAMP + CurrentNetwork::BLOCK_TIME as i64,
1106 CurrentNetwork::GENESIS_TIMESTAMP,
1107 CurrentNetwork::STARTING_SUPPLY,
1108 CurrentNetwork::ANCHOR_TIME,
1109 combined_proof_target / 2,
1110 0,
1111 coinbase_target,
1112 )
1113 .unwrap();
1114 assert_eq!(smaller_reward, reward / 2);
1115
1116 let smaller_reward = coinbase_reward_v2(
1118 CurrentNetwork::GENESIS_TIMESTAMP + CurrentNetwork::BLOCK_TIME as i64,
1119 CurrentNetwork::GENESIS_TIMESTAMP,
1120 CurrentNetwork::STARTING_SUPPLY,
1121 CurrentNetwork::ANCHOR_TIME,
1122 combined_proof_target,
1123 coinbase_target / 2,
1124 coinbase_target,
1125 )
1126 .unwrap();
1127 assert_eq!(smaller_reward, reward / 2);
1128
1129 let equivalent_reward = coinbase_reward_v2(
1131 CurrentNetwork::GENESIS_TIMESTAMP + CurrentNetwork::BLOCK_TIME as i64,
1132 CurrentNetwork::GENESIS_TIMESTAMP,
1133 CurrentNetwork::STARTING_SUPPLY,
1134 CurrentNetwork::ANCHOR_TIME,
1135 u128::MAX,
1136 0,
1137 coinbase_target,
1138 )
1139 .unwrap();
1140 assert_eq!(reward, equivalent_reward);
1141
1142 let zero_reward = coinbase_reward_v2(
1144 CurrentNetwork::GENESIS_TIMESTAMP + CurrentNetwork::BLOCK_TIME as i64,
1145 CurrentNetwork::GENESIS_TIMESTAMP,
1146 CurrentNetwork::STARTING_SUPPLY,
1147 CurrentNetwork::ANCHOR_TIME,
1148 0,
1149 0,
1150 coinbase_target,
1151 )
1152 .unwrap();
1153 assert_eq!(zero_reward, 0);
1154
1155 let zero_reward = coinbase_reward_v2(
1157 CurrentNetwork::GENESIS_TIMESTAMP + CurrentNetwork::BLOCK_TIME as i64,
1158 CurrentNetwork::GENESIS_TIMESTAMP,
1159 CurrentNetwork::STARTING_SUPPLY,
1160 CurrentNetwork::ANCHOR_TIME,
1161 1,
1162 coinbase_target + 1,
1163 coinbase_target,
1164 )
1165 .unwrap();
1166 assert_eq!(zero_reward, 0);
1167 }
1168
1169 #[test]
1170 fn test_coinbase_reward_v1_remaining_target() {
1171 let mut rng = TestRng::default();
1172
1173 fn compute_coinbase_reward(
1174 combined_proof_target: u64,
1175 cumulative_proof_target: u64,
1176 coinbase_target: u64,
1177 ) -> u64 {
1178 coinbase_reward_v1(
1179 1,
1180 CurrentNetwork::STARTING_SUPPLY,
1181 CurrentNetwork::ANCHOR_HEIGHT,
1182 CurrentNetwork::BLOCK_TIME,
1183 combined_proof_target as u128,
1184 cumulative_proof_target,
1185 coinbase_target,
1186 )
1187 .unwrap()
1188 }
1189
1190 let coinbase_target: u64 = rng.gen_range(1_000_000..1_000_000_000_000_000);
1192 let cumulative_proof_target = coinbase_target / 2;
1193 let combined_proof_target = coinbase_target / 4;
1194 let reward = compute_coinbase_reward(combined_proof_target, cumulative_proof_target, coinbase_target);
1195
1196 for _ in 0..ITERATIONS {
1197 let equivalent_reward = compute_coinbase_reward(
1201 combined_proof_target,
1202 rng.gen_range(0..(coinbase_target - combined_proof_target)),
1203 coinbase_target,
1204 );
1205 assert_eq!(reward, equivalent_reward);
1206
1207 let lower_reward = compute_coinbase_reward(
1210 combined_proof_target,
1211 rng.gen_range((coinbase_target - combined_proof_target + 1)..coinbase_target),
1212 coinbase_target,
1213 );
1214 assert!(lower_reward < reward);
1215
1216 let larger_reward = compute_coinbase_reward(
1219 rng.gen_range(combined_proof_target + 1..u64::MAX),
1220 cumulative_proof_target,
1221 coinbase_target,
1222 );
1223 assert!(reward < larger_reward);
1224 }
1225 }
1226
1227 #[test]
1228 fn test_coinbase_reward_v2_remaining_target() {
1229 let mut rng = TestRng::default();
1230
1231 fn compute_coinbase_reward(
1232 combined_proof_target: u64,
1233 cumulative_proof_target: u64,
1234 coinbase_target: u64,
1235 ) -> u64 {
1236 coinbase_reward_v2(
1237 CurrentNetwork::GENESIS_TIMESTAMP + CurrentNetwork::BLOCK_TIME as i64,
1238 CurrentNetwork::GENESIS_TIMESTAMP,
1239 CurrentNetwork::STARTING_SUPPLY,
1240 CurrentNetwork::ANCHOR_TIME,
1241 combined_proof_target as u128,
1242 cumulative_proof_target,
1243 coinbase_target,
1244 )
1245 .unwrap()
1246 }
1247
1248 let coinbase_target: u64 = rng.gen_range(1_000_000..1_000_000_000_000_000);
1250 let cumulative_proof_target = coinbase_target / 2;
1251 let combined_proof_target = coinbase_target / 4;
1252 let reward = compute_coinbase_reward(combined_proof_target, cumulative_proof_target, coinbase_target);
1253
1254 for _ in 0..ITERATIONS {
1255 let equivalent_reward = compute_coinbase_reward(
1259 combined_proof_target,
1260 rng.gen_range(0..(coinbase_target - combined_proof_target)),
1261 coinbase_target,
1262 );
1263 assert_eq!(reward, equivalent_reward);
1264
1265 let lower_reward = compute_coinbase_reward(
1268 combined_proof_target,
1269 rng.gen_range((coinbase_target - combined_proof_target + 1)..coinbase_target),
1270 coinbase_target,
1271 );
1272 assert!(lower_reward < reward);
1273
1274 let larger_reward = compute_coinbase_reward(
1277 rng.gen_range(combined_proof_target + 1..u64::MAX),
1278 cumulative_proof_target,
1279 coinbase_target,
1280 );
1281 assert!(reward < larger_reward);
1282 }
1283 }
1284
1285 #[test]
1286 fn test_coinbase_reward_v1_up_to_year_10() {
1287 let block_height_at_year_10 = block_height_at_year(CurrentNetwork::BLOCK_TIME, 10);
1288
1289 let mut block_height = 1;
1290
1291 let mut previous_reward = coinbase_reward_v1(
1292 block_height,
1293 CurrentNetwork::STARTING_SUPPLY,
1294 CurrentNetwork::ANCHOR_HEIGHT,
1295 CurrentNetwork::BLOCK_TIME,
1296 1,
1297 0,
1298 1,
1299 )
1300 .unwrap();
1301
1302 block_height += 1;
1303
1304 let mut total_reward = previous_reward;
1305
1306 let coinbase_target = CurrentNetwork::ANCHOR_HEIGHT as u64;
1307 let mut cumulative_proof_target = 0;
1308
1309 let mut hit_500m = false;
1310 let mut hit_1b = false;
1311
1312 while block_height < block_height_at_year_10 {
1313 let reward = coinbase_reward_v1(
1314 block_height,
1315 CurrentNetwork::STARTING_SUPPLY,
1316 CurrentNetwork::ANCHOR_HEIGHT,
1317 CurrentNetwork::BLOCK_TIME,
1318 1,
1319 cumulative_proof_target,
1320 coinbase_target,
1321 )
1322 .unwrap();
1323 assert!(reward <= previous_reward);
1324
1325 total_reward += reward;
1326 previous_reward = reward;
1327 block_height += 1;
1328
1329 cumulative_proof_target = match cumulative_proof_target + 1 {
1331 cumulative_proof_target if cumulative_proof_target == coinbase_target => 0,
1332 cumulative_proof_target => cumulative_proof_target,
1333 };
1334
1335 if !hit_500m && total_reward > 500_000_000_000_000 {
1336 println!("500M credits block height is {block_height}");
1337 assert_eq!(block_height, 5_786_964, "Update me if my parameters have changed");
1338 hit_500m = true;
1339 } else if !hit_1b && total_reward > 1_000_000_000_000_000 {
1340 println!("1B credits block height is {block_height}");
1341 assert_eq!(block_height, 13_328_683, "Update me if my parameters have changed");
1342 hit_1b = true;
1343 }
1344 }
1345
1346 assert_eq!(total_reward, 1_514_999_979_651_171, "Update me if my parameters have changed");
1347 }
1348
1349 #[test]
1350 fn test_coinbase_reward_v2_up_to_year_10() {
1351 let block_height_at_year_10 = timestamp_at_year(CurrentNetwork::GENESIS_TIMESTAMP, 10);
1352
1353 let mut timestamp = CurrentNetwork::GENESIS_TIMESTAMP;
1354
1355 let mut previous_reward = coinbase_reward_v2(
1356 CurrentNetwork::GENESIS_TIMESTAMP + CurrentNetwork::BLOCK_TIME as i64,
1357 CurrentNetwork::GENESIS_TIMESTAMP,
1358 CurrentNetwork::STARTING_SUPPLY,
1359 CurrentNetwork::ANCHOR_TIME,
1360 1,
1361 0,
1362 1,
1363 )
1364 .unwrap();
1365
1366 timestamp += CurrentNetwork::BLOCK_TIME as i64;
1367
1368 let mut total_reward = previous_reward;
1369
1370 let coinbase_target = CurrentNetwork::ANCHOR_HEIGHT as u64;
1371 let mut cumulative_proof_target = 0;
1372
1373 let mut hit_500m = false;
1374 let mut hit_1b = false;
1375
1376 while timestamp < block_height_at_year_10 {
1377 let reward = coinbase_reward_v2(
1378 timestamp,
1379 CurrentNetwork::GENESIS_TIMESTAMP,
1380 CurrentNetwork::STARTING_SUPPLY,
1381 CurrentNetwork::ANCHOR_TIME,
1382 1,
1383 cumulative_proof_target,
1384 coinbase_target,
1385 )
1386 .unwrap();
1387 assert!(reward <= previous_reward);
1388
1389 total_reward += reward;
1390 previous_reward = reward;
1391 timestamp += CurrentNetwork::BLOCK_TIME as i64;
1392
1393 cumulative_proof_target = match cumulative_proof_target + 1 {
1395 cumulative_proof_target if cumulative_proof_target == coinbase_target => 0,
1396 cumulative_proof_target => cumulative_proof_target,
1397 };
1398
1399 if !hit_500m && total_reward > 500_000_000_000_000 {
1400 println!("500M credits block timestamp is {timestamp}");
1401 assert_eq!(timestamp, 1783331630, "Update me if my parameters have changed");
1402 hit_500m = true;
1403 } else if !hit_1b && total_reward > 1_000_000_000_000_000 {
1404 println!("1B credits block timestamp is {timestamp}");
1405 assert_eq!(timestamp, 1858748810, "Update me if my parameters have changed");
1406 hit_1b = true;
1407 }
1408 }
1409
1410 assert_eq!(total_reward, 1_515_000_074_780_540, "Update me if my parameters have changed");
1411 }
1412
1413 #[test]
1414 fn test_coinbase_reward_v1_after_year_10() {
1415 let mut rng = TestRng::default();
1416
1417 let block_height_at_year_10 = block_height_at_year(CurrentNetwork::BLOCK_TIME, 10);
1418
1419 let reward = coinbase_reward_v1(
1421 block_height_at_year_10,
1422 CurrentNetwork::STARTING_SUPPLY,
1423 CurrentNetwork::ANCHOR_HEIGHT,
1424 CurrentNetwork::BLOCK_TIME,
1425 1,
1426 0,
1427 1,
1428 )
1429 .unwrap();
1430 assert_eq!(reward, 19_025_874);
1431
1432 for _ in 0..ITERATIONS {
1434 let block_height: u32 = rng.gen_range(block_height_at_year_10..block_height_at_year_10 * 10);
1435 let coinbase_target = rng.gen_range(1_000_000..1_000_000_000_000_000);
1436 let cumulative_proof_target = rng.gen_range(0..coinbase_target);
1437 let combined_proof_target = rng.gen_range(0..coinbase_target as u128);
1438
1439 let anchor_reward = anchor_block_reward_at_height(
1440 block_height,
1441 CurrentNetwork::STARTING_SUPPLY,
1442 CurrentNetwork::ANCHOR_HEIGHT,
1443 CurrentNetwork::BLOCK_TIME,
1444 );
1445 assert_eq!(anchor_reward, 19_025_874);
1446
1447 let reward = coinbase_reward_v1(
1448 block_height,
1449 CurrentNetwork::STARTING_SUPPLY,
1450 CurrentNetwork::ANCHOR_HEIGHT,
1451 CurrentNetwork::BLOCK_TIME,
1452 combined_proof_target,
1453 cumulative_proof_target,
1454 coinbase_target,
1455 )
1456 .unwrap();
1457 assert!(reward <= 19_025_874);
1458 }
1459 }
1460
1461 #[test]
1462 fn test_coinbase_reward_v2_after_year_10() {
1463 let mut rng = TestRng::default();
1464
1465 let timestamp_at_year_10 = timestamp_at_year(CurrentNetwork::GENESIS_TIMESTAMP, 10);
1466
1467 let reward = coinbase_reward_v2(
1469 timestamp_at_year_10,
1470 CurrentNetwork::GENESIS_TIMESTAMP,
1471 CurrentNetwork::STARTING_SUPPLY,
1472 CurrentNetwork::ANCHOR_TIME,
1473 1,
1474 0,
1475 1,
1476 )
1477 .unwrap();
1478 assert_eq!(reward, 19_025_874);
1479
1480 for _ in 0..ITERATIONS {
1482 let timestamp: i64 = rng.gen_range(timestamp_at_year_10..timestamp_at_year_10 * 10);
1483 let coinbase_target = rng.gen_range(1_000_000..1_000_000_000_000_000);
1484 let cumulative_proof_target = rng.gen_range(0..coinbase_target);
1485 let combined_proof_target = rng.gen_range(0..coinbase_target as u128);
1486
1487 let anchor_reward = anchor_block_reward_at_timestamp(
1488 timestamp,
1489 CurrentNetwork::GENESIS_TIMESTAMP,
1490 CurrentNetwork::STARTING_SUPPLY,
1491 CurrentNetwork::ANCHOR_TIME,
1492 );
1493 assert_eq!(anchor_reward, 19_025_874);
1494
1495 let reward = coinbase_reward_v2(
1496 timestamp,
1497 CurrentNetwork::GENESIS_TIMESTAMP,
1498 CurrentNetwork::STARTING_SUPPLY,
1499 CurrentNetwork::ANCHOR_TIME,
1500 combined_proof_target,
1501 cumulative_proof_target,
1502 coinbase_target,
1503 )
1504 .unwrap();
1505 assert!(reward <= 19_025_874);
1506 }
1507 }
1508
1509 #[test]
1510 fn test_targets() {
1511 let mut rng = TestRng::default();
1512
1513 let minimum_coinbase_target: u64 = 2u64.pow(10) - 1;
1514
1515 fn test_new_targets(rng: &mut TestRng, minimum_coinbase_target: u64) {
1516 let previous_coinbase_target: u64 = rng.gen_range(minimum_coinbase_target..u64::MAX);
1517 let previous_prover_target = proof_target(
1518 previous_coinbase_target,
1519 CurrentNetwork::GENESIS_PROOF_TARGET,
1520 CurrentNetwork::MAX_SOLUTIONS_AS_POWER_OF_TWO,
1521 );
1522
1523 let previous_timestamp = rng.gen();
1524
1525 let next_timestamp = previous_timestamp + CurrentNetwork::ANCHOR_TIME as i64;
1527 let new_coinbase_target = coinbase_target(
1528 previous_coinbase_target,
1529 previous_timestamp,
1530 next_timestamp,
1531 CurrentNetwork::ANCHOR_TIME,
1532 CurrentNetwork::NUM_BLOCKS_PER_EPOCH,
1533 CurrentNetwork::GENESIS_COINBASE_TARGET,
1534 )
1535 .unwrap();
1536 let new_prover_target = proof_target(
1537 new_coinbase_target,
1538 CurrentNetwork::GENESIS_PROOF_TARGET,
1539 CurrentNetwork::MAX_SOLUTIONS_AS_POWER_OF_TWO,
1540 );
1541 assert_eq!(new_coinbase_target, previous_coinbase_target);
1542 assert_eq!(new_prover_target, previous_prover_target);
1543
1544 let new_timestamp = previous_timestamp + 2 * CurrentNetwork::ANCHOR_TIME as i64;
1546 let new_coinbase_target = coinbase_target(
1547 previous_coinbase_target,
1548 previous_timestamp,
1549 new_timestamp,
1550 CurrentNetwork::ANCHOR_TIME,
1551 CurrentNetwork::NUM_BLOCKS_PER_EPOCH,
1552 CurrentNetwork::GENESIS_COINBASE_TARGET,
1553 )
1554 .unwrap();
1555 let new_prover_target = proof_target(
1556 new_coinbase_target,
1557 CurrentNetwork::GENESIS_PROOF_TARGET,
1558 CurrentNetwork::MAX_SOLUTIONS_AS_POWER_OF_TWO,
1559 );
1560 assert!(new_coinbase_target < previous_coinbase_target);
1561 assert!(new_prover_target < previous_prover_target);
1562
1563 let next_timestamp = previous_timestamp + (CurrentNetwork::ANCHOR_TIME / 2) as i64;
1565 let new_coinbase_target = coinbase_target(
1566 previous_coinbase_target,
1567 previous_timestamp,
1568 next_timestamp,
1569 CurrentNetwork::ANCHOR_TIME,
1570 CurrentNetwork::NUM_BLOCKS_PER_EPOCH,
1571 CurrentNetwork::GENESIS_COINBASE_TARGET,
1572 )
1573 .unwrap();
1574 let new_prover_target = proof_target(
1575 new_coinbase_target,
1576 CurrentNetwork::GENESIS_PROOF_TARGET,
1577 CurrentNetwork::MAX_SOLUTIONS_AS_POWER_OF_TWO,
1578 );
1579
1580 assert!(new_coinbase_target > previous_coinbase_target);
1581 assert!(new_prover_target > previous_prover_target);
1582 }
1583
1584 for _ in 0..ITERATIONS {
1585 test_new_targets(&mut rng, minimum_coinbase_target);
1586 }
1587 }
1588
1589 #[test]
1590 fn test_target_halving() {
1591 let mut rng = TestRng::default();
1592
1593 let minimum_coinbase_target: u64 = 2u64.pow(10) - 1;
1594
1595 for _ in 0..ITERATIONS {
1596 let previous_coinbase_target: u64 = rng.gen_range(minimum_coinbase_target..u64::MAX);
1597 let previous_timestamp = rng.gen();
1598
1599 let half_life = CurrentNetwork::NUM_BLOCKS_PER_EPOCH
1600 .saturating_div(2)
1601 .saturating_mul(CurrentNetwork::ANCHOR_TIME as u32) as i64;
1602
1603 let next_timestamp = previous_timestamp + half_life;
1605 let next_coinbase_target = coinbase_target(
1606 previous_coinbase_target,
1607 previous_timestamp,
1608 next_timestamp,
1609 CurrentNetwork::ANCHOR_TIME,
1610 CurrentNetwork::NUM_BLOCKS_PER_EPOCH,
1611 CurrentNetwork::GENESIS_COINBASE_TARGET,
1612 )
1613 .unwrap();
1614
1615 assert!(next_coinbase_target > previous_coinbase_target / 2);
1616
1617 let next_timestamp = previous_timestamp + half_life + CurrentNetwork::ANCHOR_TIME as i64;
1619 let next_coinbase_target = coinbase_target(
1620 previous_coinbase_target,
1621 previous_timestamp,
1622 next_timestamp,
1623 CurrentNetwork::ANCHOR_TIME,
1624 CurrentNetwork::NUM_BLOCKS_PER_EPOCH,
1625 CurrentNetwork::GENESIS_COINBASE_TARGET,
1626 )
1627 .unwrap();
1628
1629 assert_eq!(next_coinbase_target, previous_coinbase_target / 2);
1630
1631 let next_timestamp = previous_timestamp + half_life + 2 * CurrentNetwork::ANCHOR_TIME as i64;
1633 let next_coinbase_target = coinbase_target(
1634 previous_coinbase_target,
1635 previous_timestamp,
1636 next_timestamp,
1637 CurrentNetwork::ANCHOR_TIME,
1638 CurrentNetwork::NUM_BLOCKS_PER_EPOCH,
1639 CurrentNetwork::GENESIS_COINBASE_TARGET,
1640 )
1641 .unwrap();
1642
1643 assert!(next_coinbase_target < previous_coinbase_target / 2);
1644 }
1645 }
1646
1647 #[test]
1648 fn test_target_doubling() {
1649 let mut rng = TestRng::default();
1650
1651 const ANCHOR_TIME_DELTA: i64 = 15;
1653 const EXPECTED_NUM_BLOCKS_TO_DOUBLE: u32 = 451;
1655
1656 let minimum_coinbase_target: u64 = 2u64.pow(10) - 1;
1657
1658 let initial_coinbase_target: u64 = rng.gen_range(minimum_coinbase_target..u64::MAX / 2);
1659 let initial_timestamp: i64 = rng.gen();
1660
1661 let mut previous_coinbase_target: u64 = initial_coinbase_target;
1662 let mut previous_timestamp = initial_timestamp;
1663 let mut num_blocks = 0;
1664
1665 while previous_coinbase_target < initial_coinbase_target * 2 {
1666 let next_timestamp = previous_timestamp + ANCHOR_TIME_DELTA;
1668 let next_coinbase_target = coinbase_target(
1669 previous_coinbase_target,
1670 previous_timestamp,
1671 next_timestamp,
1672 CurrentNetwork::ANCHOR_TIME,
1673 CurrentNetwork::NUM_BLOCKS_PER_EPOCH,
1674 CurrentNetwork::GENESIS_COINBASE_TARGET,
1675 )
1676 .unwrap();
1677
1678 assert!(next_coinbase_target > previous_coinbase_target);
1679
1680 previous_coinbase_target = next_coinbase_target;
1681 previous_timestamp = next_timestamp;
1682 num_blocks += 1;
1683 }
1684
1685 let seconds = previous_timestamp - initial_timestamp;
1686 println!(
1687 "For drifts of {ANCHOR_TIME_DELTA} seconds and epochs of {} blocks, doubling the coinbase target took {num_blocks} blocks. ({seconds} seconds)",
1688 CurrentNetwork::NUM_BLOCKS_PER_EPOCH,
1689 );
1690
1691 assert_eq!(EXPECTED_NUM_BLOCKS_TO_DOUBLE, num_blocks);
1692 }
1693
1694 #[test]
1695 fn test_to_next_targets_meets_threshold() {
1696 let mut rng = TestRng::default();
1697
1698 let minimum_coinbase_target: u64 = 2u64.pow(10) - 1;
1699
1700 for _ in 0..ITERATIONS {
1701 let latest_coinbase_target = rng.gen_range(minimum_coinbase_target..u64::MAX / 2);
1703 let threshold = latest_coinbase_target as u128 / 2;
1704 let last_coinbase_target = rng.gen_range(minimum_coinbase_target..latest_coinbase_target);
1705 let last_coinbase_timestamp = rng.gen_range(0..i64::MAX / 2);
1706 let next_timestamp = last_coinbase_timestamp + 100;
1707 let latest_cumulative_weight = rng.gen_range(0..u128::MAX / 2);
1708
1709 let latest_cumulative_proof_target = rng.gen_range(0..threshold);
1711 let combined_proof_target =
1712 rng.gen_range(threshold.saturating_sub(latest_cumulative_proof_target)..u128::MAX);
1713
1714 assert!(latest_cumulative_proof_target.saturating_add(combined_proof_target) >= threshold);
1715
1716 let (
1718 _,
1719 _,
1720 next_cumulative_proof_target,
1721 next_cumulative_weight,
1722 next_last_coinbase_target,
1723 next_last_coinbase_timestamp,
1724 ) = to_next_targets::<CurrentNetwork>(
1725 latest_cumulative_proof_target,
1726 combined_proof_target,
1727 latest_coinbase_target,
1728 latest_cumulative_weight,
1729 last_coinbase_target,
1730 last_coinbase_timestamp,
1731 next_timestamp,
1732 )
1733 .unwrap();
1734
1735 assert_eq!(next_cumulative_proof_target, 0);
1740 assert_ne!(next_last_coinbase_target, last_coinbase_target);
1741 assert_eq!(next_last_coinbase_timestamp, next_timestamp);
1742
1743 assert_eq!(next_cumulative_weight, latest_cumulative_weight.saturating_add(combined_proof_target));
1745 }
1746 }
1747
1748 #[test]
1749 fn test_to_next_targets_does_not_meet_threshold() {
1750 let mut rng = TestRng::default();
1751
1752 let minimum_coinbase_target: u64 = 2u64.pow(10) - 1;
1753
1754 for _ in 0..ITERATIONS {
1755 let latest_coinbase_target = rng.gen_range(minimum_coinbase_target..u64::MAX / 2);
1757 let threshold = latest_coinbase_target as u128 / 2;
1758 let last_coinbase_target = rng.gen_range(minimum_coinbase_target..latest_coinbase_target);
1759 let last_coinbase_timestamp = rng.gen_range(0..i64::MAX / 2);
1760 let next_timestamp = last_coinbase_timestamp + 100;
1761 let latest_cumulative_weight = rng.gen_range(0..u128::MAX / 2);
1762
1763 let latest_cumulative_proof_target = rng.gen_range(0..threshold);
1765 let combined_proof_target = rng.gen_range(0..threshold.saturating_sub(latest_cumulative_proof_target));
1766
1767 assert!(latest_cumulative_proof_target.saturating_add(combined_proof_target) < threshold);
1768
1769 let (
1771 _,
1772 _,
1773 next_cumulative_proof_target,
1774 next_cumulative_weight,
1775 next_last_coinbase_target,
1776 next_last_coinbase_timestamp,
1777 ) = to_next_targets::<CurrentNetwork>(
1778 latest_cumulative_proof_target,
1779 combined_proof_target,
1780 latest_coinbase_target,
1781 latest_cumulative_weight,
1782 last_coinbase_target,
1783 last_coinbase_timestamp,
1784 next_timestamp,
1785 )
1786 .unwrap();
1787
1788 assert_eq!(next_cumulative_proof_target, latest_cumulative_proof_target + combined_proof_target);
1793 assert_eq!(next_last_coinbase_target, last_coinbase_target);
1794 assert_eq!(next_last_coinbase_timestamp, last_coinbase_timestamp);
1795
1796 assert_eq!(next_cumulative_weight, latest_cumulative_weight.saturating_add(combined_proof_target));
1798 }
1799 }
1800}