iuna

iuna - experimental devnet protocol
git clone https://iuna.jhx.app/git/iuna.git
Log | Files | Refs | README | LICENSE

mine_policy.rs (3367B)


      1 use std::collections::BTreeMap;
      2 
      3 use anyhow::{Result, bail};
      4 
      5 use super::{Block, MINE_ACTIONS_PER_ANCHOR_LIMIT, Transaction};
      6 
      7 pub(super) const MINE_RETARGET_WINDOW_BLOCKS: u64 = 10;
      8 pub(super) const MINE_MAX_RETARGET_STEP_BITS: u32 = 2;
      9 pub(super) const MINE_MIN_DIFFICULTY_BITS: u32 = 10;
     10 pub(super) const MINE_MAX_ANCHOR_AGE_BLOCKS: u64 = MINE_RETARGET_WINDOW_BLOCKS;
     11 
     12 const MINE_TARGET_ACTIONS_PER_BLOCK: u64 = 1;
     13 const MINE_MAX_DIFFICULTY_BITS: u32 = 32;
     14 
     15 pub(super) fn retarget_mine_difficulty_bits(current: u32, mine_actions: u64) -> u32 {
     16     let target = MINE_RETARGET_WINDOW_BLOCKS.saturating_mul(MINE_TARGET_ACTIONS_PER_BLOCK);
     17     if target == 0 || mine_actions == target {
     18         return current.clamp(MINE_MIN_DIFFICULTY_BITS, MINE_MAX_DIFFICULTY_BITS);
     19     }
     20 
     21     let step = if mine_actions > target {
     22         floor_log2_ratio(mine_actions, target).min(MINE_MAX_RETARGET_STEP_BITS)
     23     } else if mine_actions == 0 {
     24         MINE_MAX_RETARGET_STEP_BITS
     25     } else {
     26         floor_log2_ratio(target, mine_actions).min(MINE_MAX_RETARGET_STEP_BITS)
     27     };
     28 
     29     if step == 0 {
     30         return current.clamp(MINE_MIN_DIFFICULTY_BITS, MINE_MAX_DIFFICULTY_BITS);
     31     }
     32     if mine_actions > target {
     33         current
     34             .saturating_add(step)
     35             .clamp(MINE_MIN_DIFFICULTY_BITS, MINE_MAX_DIFFICULTY_BITS)
     36     } else {
     37         current
     38             .saturating_sub(step)
     39             .clamp(MINE_MIN_DIFFICULTY_BITS, MINE_MAX_DIFFICULTY_BITS)
     40     }
     41 }
     42 
     43 fn floor_log2_ratio(numerator: u64, denominator: u64) -> u32 {
     44     if denominator == 0 || numerator <= denominator {
     45         return 0;
     46     }
     47     let mut step = 0_u32;
     48     let mut threshold = denominator;
     49     while threshold <= numerator / 2 {
     50         threshold = threshold.saturating_mul(2);
     51         step = step.saturating_add(1);
     52     }
     53     step
     54 }
     55 
     56 pub(super) fn ensure_mine_anchor_limit(_height: u64, transactions: &[Transaction]) -> Result<()> {
     57     let mut anchor_counts = BTreeMap::new();
     58     for transaction in transactions {
     59         let Some(anchor) = mine_anchor(transaction) else {
     60             continue;
     61         };
     62         let count = anchor_counts.entry(anchor).or_insert(0usize);
     63         *count += 1;
     64         if *count > MINE_ACTIONS_PER_ANCHOR_LIMIT {
     65             bail!("block exceeds mine actions per anchor limit");
     66         }
     67     }
     68     Ok(())
     69 }
     70 
     71 pub(super) fn mine_anchor(transaction: &Transaction) -> Option<&str> {
     72     match transaction {
     73         Transaction::Mine { anchor, .. } => Some(anchor.as_str()),
     74         _ => None,
     75     }
     76 }
     77 
     78 pub(super) fn mine_anchor_count_before_height(chain: &[Block], anchor: &str, height: u64) -> usize {
     79     chain
     80         .iter()
     81         .take_while(|block| block.height <= height)
     82         .map(|block| {
     83             block
     84                 .transactions
     85                 .iter()
     86                 .filter(|transaction| mine_anchor(transaction) == Some(anchor))
     87                 .count()
     88         })
     89         .sum()
     90 }
     91 
     92 #[cfg(test)]
     93 mod tests {
     94     use super::*;
     95     use crate::domain::MINE_DIFFICULTY_BITS;
     96 
     97     #[test]
     98     fn retarget_bounds_match_protocol_constants() {
     99         assert_eq!(
    100             retarget_mine_difficulty_bits(MINE_DIFFICULTY_BITS, 0),
    101             MINE_DIFFICULTY_BITS - MINE_MAX_RETARGET_STEP_BITS
    102         );
    103         assert_eq!(
    104             retarget_mine_difficulty_bits(1, 0),
    105             MINE_MIN_DIFFICULTY_BITS
    106         );
    107     }
    108 }