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(¤t_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 }