iuna

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

pow.rs (5463B)


      1 use anyhow::{Context, Result};
      2 
      3 use super::super::helpers::auto_pow_salt;
      4 use super::super::{
      5     AUTO_POW_NONCE_ATTEMPTS_PER_WORKER_TICK, AutoPowMineCursor, AutoPowMineJob,
      6     MINE_ACTIONS_PER_ANCHOR_LIMIT, NodeCore, Transaction,
      7 };
      8 use crate::domain::MineSearchOutcome;
      9 
     10 impl NodeCore {
     11     pub fn prepare_automatic_pow_mining(&mut self) -> Result<Option<Transaction>> {
     12         let Some(job) = self.prepare_automatic_pow_mining_job()? else {
     13             return Ok(None);
     14         };
     15         let (job, outcome) = job.search()?;
     16         self.finish_automatic_pow_mining_job(job, outcome)
     17     }
     18 
     19     pub fn prepare_automatic_pow_mining_job(&mut self) -> Result<Option<AutoPowMineJob>> {
     20         if self.wallet.is_locked() {
     21             if self.pow_mining_enabled {
     22                 self.last_auto_pow_mine_status = Some("wallet is locked".to_string());
     23             }
     24             return Ok(None);
     25         }
     26         if self.pow_mining_enabled && !self.has_real_chain() {
     27             self.last_auto_pow_mine_status =
     28                 Some("waiting for a real chain before PoW mining can start".to_string());
     29             self.auto_pow_mine_cursor = None;
     30             return Ok(None);
     31         }
     32 
     33         self.prepare_automatic_pow_mine()
     34     }
     35 
     36     pub fn finish_automatic_pow_mining_job(
     37         &mut self,
     38         job: AutoPowMineJob,
     39         outcome: MineSearchOutcome,
     40     ) -> Result<Option<Transaction>> {
     41         if !self.pow_mining_enabled || !self.has_real_chain() {
     42             return Ok(None);
     43         }
     44         let current_anchor = self
     45             .ledger
     46             .chain()
     47             .last()
     48             .map(|block| block.hash.clone())
     49             .context("ledger has no anchor block")?;
     50         if current_anchor != job.anchor() {
     51             self.auto_pow_mine_cursor = None;
     52             self.last_auto_pow_mine_status =
     53                 Some("discarded stale PoW search result after chain tip changed".to_string());
     54             return Ok(None);
     55         }
     56         let mut searched = outcome.attempts;
     57         if let Some(cursor) = &mut self.auto_pow_mine_cursor {
     58             if cursor.anchor == current_anchor {
     59                 cursor.next_nonce = outcome.next_nonce;
     60                 cursor.searched = cursor.searched.saturating_add(outcome.attempts);
     61                 searched = cursor.searched;
     62             }
     63         }
     64         let Some(tx) = outcome.transaction else {
     65             self.last_auto_pow_mine_status = Some(format!(
     66                 "searched {searched} PoW nonces for the current tip; no proof yet"
     67             ));
     68             return Ok(None);
     69         };
     70         if self.ledger.pending_mine_count_for_anchor(&current_anchor)
     71             >= MINE_ACTIONS_PER_ANCHOR_LIMIT
     72         {
     73             self.last_auto_pow_mine_anchor = Some(current_anchor);
     74             self.last_auto_pow_mine_status =
     75                 Some("waiting for next chain tip after queued mine actions".to_string());
     76             self.auto_pow_mine_cursor = None;
     77             return Ok(None);
     78         }
     79         self.submit_public_mine_action(tx.clone())?;
     80         self.last_auto_pow_mine_anchor = Some(current_anchor);
     81         self.last_auto_pow_mine_status = Some(format!(
     82             "queued mine action after {searched} PoW nonce attempts for the current tip"
     83         ));
     84         Ok(Some(tx))
     85     }
     86 
     87     pub fn record_automatic_pow_mining_error(&mut self, message: String) {
     88         self.last_auto_pow_mine_status = Some(message);
     89     }
     90 
     91     pub(super) fn prepare_automatic_pow_mine(&mut self) -> Result<Option<AutoPowMineJob>> {
     92         if !self.pow_mining_enabled {
     93             self.last_auto_pow_mine_status = None;
     94             self.auto_pow_mine_cursor = None;
     95             return Ok(None);
     96         }
     97         let anchor = self
     98             .ledger
     99             .chain()
    100             .last()
    101             .map(|block| block.hash.clone())
    102             .context("ledger has no anchor block")?;
    103         if self.ledger.pending_mine_count_for_anchor(&anchor) >= MINE_ACTIONS_PER_ANCHOR_LIMIT {
    104             self.last_auto_pow_mine_anchor = Some(anchor);
    105             self.last_auto_pow_mine_status =
    106                 Some("waiting for next chain tip after queued mine actions".to_string());
    107             self.auto_pow_mine_cursor = None;
    108             return Ok(None);
    109         }
    110         let wallet_address = self.wallet.address().to_string();
    111         let needs_cursor = self
    112             .auto_pow_mine_cursor
    113             .as_ref()
    114             .is_none_or(|cursor| cursor.anchor != anchor);
    115         if needs_cursor {
    116             self.auto_pow_mine_cursor = Some(AutoPowMineCursor {
    117                 salt: auto_pow_salt(&wallet_address, &anchor),
    118                 anchor: anchor.clone(),
    119                 next_nonce: 0,
    120                 searched: 0,
    121             });
    122         }
    123         let cursor = self
    124             .auto_pow_mine_cursor
    125             .as_ref()
    126             .context("automatic PoW cursor was not initialized")?
    127             .clone();
    128         self.last_auto_pow_mine_status = Some(format!(
    129             "searching PoW nonces for the current tip; searched {} so far",
    130             cursor.searched
    131         ));
    132         Ok(Some(AutoPowMineJob {
    133             ledger: self.wallet_build_ledger()?,
    134             recipient: wallet_address,
    135             anchor,
    136             salt: cursor.salt,
    137             start_nonce: cursor.next_nonce,
    138             max_attempts: AUTO_POW_NONCE_ATTEMPTS_PER_WORKER_TICK
    139                 .saturating_mul(u64::from(self.pow_mining_workers)),
    140         }))
    141     }
    142 }