iuna

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

ledger_prepare.rs (6183B)


      1 use anyhow::{Result, bail};
      2 
      3 use super::{
      4     FinalizerMode, Ledger, PreparedBlock, RECOVERY_BLOCK_DELAY_MS, RevealBundle, Wallet,
      5     ensure_block_has_burn, ensure_block_has_burn_from, recovery_vdf_seed_for_child, run_vdf,
      6     ticket_block_min_timestamp, vdf_seed_for_child,
      7 };
      8 
      9 impl Ledger {
     10     pub fn mine_next_block(&self, wallet: &Wallet, timestamp_ms: u64) -> Result<super::Block> {
     11         let prepared = self.prepare_next_block(wallet.address(), timestamp_ms)?;
     12         let vdf_output = run_vdf(prepared.vdf_seed(), prepared.vdf_rounds());
     13         Ok(prepared.finish(wallet, vdf_output))
     14     }
     15 
     16     pub fn mine_recovery_block(&self, wallet: &Wallet, timestamp_ms: u64) -> Result<super::Block> {
     17         let prepared = self.prepare_recovery_block(wallet.address(), timestamp_ms)?;
     18         let vdf_output = run_vdf(prepared.vdf_seed(), prepared.vdf_rounds());
     19         Ok(prepared.finish(wallet, vdf_output))
     20     }
     21 
     22     pub fn prepare_next_block(&self, miner: &str, timestamp_ms: u64) -> Result<PreparedBlock> {
     23         self.prepare_next_block_with_reveal_bundles(miner, timestamp_ms, Vec::new())
     24     }
     25 
     26     pub fn prepare_next_block_with_reveal_bundles(
     27         &self,
     28         miner: &str,
     29         timestamp_ms: u64,
     30         reveal_bundles: Vec<RevealBundle>,
     31     ) -> Result<PreparedBlock> {
     32         self.prepare_next_block_with_required_burn_and_reveal_bundles(
     33             miner,
     34             timestamp_ms,
     35             reveal_bundles,
     36             None,
     37         )
     38     }
     39 
     40     pub(crate) fn prepare_next_block_with_required_burn_and_reveal_bundles(
     41         &self,
     42         miner: &str,
     43         timestamp_ms: u64,
     44         reveal_bundles: Vec<RevealBundle>,
     45         required_burn_signature: Option<&str>,
     46     ) -> Result<PreparedBlock> {
     47         let height = self.tip().height + 1;
     48         let Some((finalizer_rank, leader_ticket)) = self.finalizer_ticket_for_miner(height, miner)
     49         else {
     50             bail!("cannot mine block without a mature burn ticket");
     51         };
     52         if self.expected_leader_for_next_block().is_none() {
     53             bail!("no selected leader for block {height}");
     54         }
     55 
     56         let reveal_bundles = self.validate_next_block_reveal_bundles(reveal_bundles)?;
     57         let reveal_bundle_section = self.reveal_bundle_section_from_bundles(reveal_bundles);
     58         let selection = self.select_block_transactions(required_burn_signature)?;
     59         ensure_block_has_burn(&selection.transactions)?;
     60 
     61         let tip = self.tip();
     62         let prev_hash = tip.hash.clone();
     63         let timestamp_ms = timestamp_ms.max(ticket_block_min_timestamp(tip, finalizer_rank)?);
     64         let bundle_hashes = reveal_bundle_section.reveal_bundle_hashes(height, &prev_hash);
     65         let vdf_seed = vdf_seed_for_child(&prev_hash, height, &bundle_hashes);
     66         Ok(PreparedBlock {
     67             height,
     68             prev_hash,
     69             timestamp_ms,
     70             miner: miner.to_string(),
     71             finalizer_mode: FinalizerMode::Ticket,
     72             reward: self
     73                 .expected_reward_for_next_block(&selection.transactions, &reveal_bundle_section)?,
     74             vdf_rounds: self.vdf_rounds_for_finalizer_rank(finalizer_rank)?,
     75             vdf_seed,
     76             finalizer_rank,
     77             leader_ticket: Some(leader_ticket),
     78             blinded_transactions: selection.blinded_transactions,
     79             reveal_bundle_section,
     80             transactions: selection.transactions,
     81         })
     82     }
     83 
     84     pub fn recovery_block_available_at(&self, timestamp_ms: u64) -> bool {
     85         timestamp_ms >= self.recovery_block_min_timestamp()
     86     }
     87 
     88     pub fn recovery_block_min_timestamp(&self) -> u64 {
     89         self.tip()
     90             .timestamp_ms
     91             .saturating_add(RECOVERY_BLOCK_DELAY_MS)
     92     }
     93 
     94     pub fn prepare_recovery_block(&self, miner: &str, timestamp_ms: u64) -> Result<PreparedBlock> {
     95         self.prepare_recovery_block_with_reveal_bundles(miner, timestamp_ms, Vec::new())
     96     }
     97 
     98     pub fn prepare_recovery_block_with_reveal_bundles(
     99         &self,
    100         miner: &str,
    101         timestamp_ms: u64,
    102         reveal_bundles: Vec<RevealBundle>,
    103     ) -> Result<PreparedBlock> {
    104         self.prepare_recovery_block_with_required_burn_and_reveal_bundles(
    105             miner,
    106             timestamp_ms,
    107             reveal_bundles,
    108             None,
    109         )
    110     }
    111 
    112     pub(crate) fn prepare_recovery_block_with_required_burn_and_reveal_bundles(
    113         &self,
    114         miner: &str,
    115         timestamp_ms: u64,
    116         reveal_bundles: Vec<RevealBundle>,
    117         required_burn_signature: Option<&str>,
    118     ) -> Result<PreparedBlock> {
    119         let height = self.tip().height + 1;
    120         let min_timestamp = self.recovery_block_min_timestamp();
    121         if timestamp_ms < min_timestamp {
    122             bail!("recovery block is not available before timestamp {min_timestamp}");
    123         }
    124 
    125         let reveal_bundles = self.validate_next_block_reveal_bundles(reveal_bundles)?;
    126         let reveal_bundle_section = self.reveal_bundle_section_from_bundles(reveal_bundles);
    127         let selection = self.select_recovery_block_transactions(miner, required_burn_signature)?;
    128         ensure_block_has_burn(&selection.transactions)?;
    129         ensure_block_has_burn_from(&selection.transactions, miner)?;
    130 
    131         let tip = self.tip();
    132         let prev_hash = tip.hash.clone();
    133         let timestamp_ms = timestamp_ms.max(tip.timestamp_ms + 1);
    134         let bundle_hashes = reveal_bundle_section.reveal_bundle_hashes(height, &prev_hash);
    135         let vdf_seed =
    136             recovery_vdf_seed_for_child(&prev_hash, height, timestamp_ms, &bundle_hashes);
    137         Ok(PreparedBlock {
    138             height,
    139             prev_hash,
    140             timestamp_ms,
    141             miner: miner.to_string(),
    142             finalizer_mode: FinalizerMode::Recovery,
    143             finalizer_rank: 0,
    144             reward: self
    145                 .expected_reward_for_next_block(&selection.transactions, &reveal_bundle_section)?,
    146             vdf_rounds: self.recovery_vdf_rounds()?,
    147             vdf_seed,
    148             leader_ticket: None,
    149             blinded_transactions: selection.blinded_transactions,
    150             reveal_bundle_section,
    151             transactions: selection.transactions,
    152         })
    153     }
    154 }