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