iuna

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

ledger_reveal.rs (10308B)


      1 use std::collections::{BTreeMap, BTreeSet};
      2 
      3 use anyhow::{Context, Result, bail};
      4 
      5 use super::ledger_ops::verify_address_signature;
      6 use super::reveal::{reveal_bundle_slot_mask, reveal_committee_mask};
      7 use super::{
      8     Amount, Ledger, MAX_REVEAL_BUNDLE_BYTES, MaskedBlindedReveal, REVEAL_COMMITTEE_SIZE,
      9     RevealBundle, RevealBundlePayload, RevealBundleSection, RevealBundleSignature, Wallet,
     10 };
     11 
     12 impl Ledger {
     13     pub fn build_reveal_bundle(&self, wallet: &Wallet) -> Result<Option<RevealBundle>> {
     14         let height = self.tip().height + 1;
     15         let prev_hash = self.tip().hash.clone();
     16         let Some(member) = self
     17             .reveal_committee_for_next_block()
     18             .into_iter()
     19             .find(|member| member.owner == wallet.address())
     20         else {
     21             return Ok(None);
     22         };
     23         let mut reveals = self.valid_pending_blinded_reveals();
     24         reveals.sort_by(|left, right| {
     25             self.reveal_fee_order_key(right)
     26                 .cmp(&self.reveal_fee_order_key(left))
     27                 .then_with(|| left.commitment.cmp(&right.commitment))
     28         });
     29 
     30         let mut selected = Vec::new();
     31         for reveal in reveals {
     32             let mut candidate = selected.clone();
     33             candidate.push(reveal);
     34             let bundle = wallet.reveal_bundle(RevealBundlePayload {
     35                 height,
     36                 prev_hash: prev_hash.clone(),
     37                 slot: member.slot,
     38                 member: wallet.address().to_string(),
     39                 reveals: candidate.clone(),
     40             });
     41             if bundle.serialized_size_bytes()? <= MAX_REVEAL_BUNDLE_BYTES {
     42                 selected = candidate;
     43             }
     44         }
     45         if selected.is_empty() {
     46             return Ok(None);
     47         }
     48         Ok(Some(wallet.reveal_bundle(RevealBundlePayload {
     49             height,
     50             prev_hash,
     51             slot: member.slot,
     52             member: wallet.address().to_string(),
     53             reveals: selected,
     54         })))
     55     }
     56 
     57     pub fn validate_next_block_reveal_bundles(
     58         &self,
     59         bundles: Vec<RevealBundle>,
     60     ) -> Result<Vec<RevealBundle>> {
     61         let expected_height = self.tip().height + 1;
     62         let expected_prev_hash = self.tip().hash.clone();
     63         self.validate_reveal_bundles_for_block(expected_height, &expected_prev_hash, bundles)
     64     }
     65 
     66     pub(super) fn reveal_bundle_section_from_bundles(
     67         &self,
     68         bundles: Vec<RevealBundle>,
     69     ) -> RevealBundleSection {
     70         let signatures = bundles
     71             .iter()
     72             .map(|bundle| RevealBundleSignature {
     73                 slot: bundle.slot,
     74                 member: bundle.member.clone(),
     75                 signature: bundle.signature.clone(),
     76             })
     77             .collect::<Vec<_>>();
     78         let mut by_commitment: BTreeMap<String, MaskedBlindedReveal> = BTreeMap::new();
     79         for bundle in bundles {
     80             let slot_mask = reveal_bundle_slot_mask(bundle.slot).unwrap_or(0);
     81             for reveal in bundle.reveals {
     82                 by_commitment
     83                     .entry(reveal.commitment.clone())
     84                     .and_modify(|masked| masked.bundle_mask |= slot_mask)
     85                     .or_insert(MaskedBlindedReveal {
     86                         reveal,
     87                         bundle_mask: slot_mask,
     88                     });
     89             }
     90         }
     91         let mut reveals = by_commitment.into_values().collect::<Vec<_>>();
     92         reveals.sort_by(|left, right| {
     93             self.reveal_fee_order_key(&right.reveal)
     94                 .cmp(&self.reveal_fee_order_key(&left.reveal))
     95                 .then_with(|| left.reveal.commitment.cmp(&right.reveal.commitment))
     96         });
     97         RevealBundleSection {
     98             signatures,
     99             reveals,
    100         }
    101     }
    102 
    103     pub(super) fn validate_reveal_bundle_section_for_block(
    104         &self,
    105         expected_height: u64,
    106         expected_prev_hash: &str,
    107         section: &RevealBundleSection,
    108     ) -> Result<()> {
    109         if section.signatures.len() > REVEAL_COMMITTEE_SIZE {
    110             bail!("block has too many reveal bundle signatures");
    111         }
    112         if section
    113             .signatures
    114             .windows(2)
    115             .any(|pair| pair[0].slot >= pair[1].slot)
    116         {
    117             bail!("reveal bundle signatures are not in slot order");
    118         }
    119         let committee = self
    120             .reveal_committee_for_height(expected_height)
    121             .into_iter()
    122             .map(|member| (member.slot, member))
    123             .collect::<BTreeMap<_, _>>();
    124         let mut seen_slots = BTreeSet::new();
    125         let mut seen_members = BTreeSet::new();
    126         let mut included_mask = 0_u8;
    127         for signature in &section.signatures {
    128             if usize::from(signature.slot) >= REVEAL_COMMITTEE_SIZE {
    129                 bail!("reveal bundle slot is invalid");
    130             }
    131             if !seen_slots.insert(signature.slot) {
    132                 bail!("duplicate reveal bundle slot");
    133             }
    134             if !seen_members.insert(signature.member.clone()) {
    135                 bail!("duplicate reveal bundle member");
    136             }
    137             let member = committee
    138                 .get(&signature.slot)
    139                 .context("reveal bundle slot is not assigned")?;
    140             if signature.member != member.owner {
    141                 bail!("reveal bundle member is not assigned to slot");
    142             }
    143             included_mask |= reveal_bundle_slot_mask(signature.slot)?;
    144         }
    145 
    146         let mut seen_reveals = BTreeSet::new();
    147         let mut previous_key: Option<((u128, Amount), String)> = None;
    148         for masked in &section.reveals {
    149             if masked.bundle_mask == 0 {
    150                 bail!("masked blinded reveal is not assigned to a reveal bundle");
    151             }
    152             if masked.bundle_mask & !reveal_committee_mask() != 0 {
    153                 bail!("masked blinded reveal references an invalid reveal bundle slot");
    154             }
    155             if masked.bundle_mask & !included_mask != 0 {
    156                 bail!("masked blinded reveal references a missing reveal bundle signature");
    157             }
    158             if !seen_reveals.insert(masked.reveal.commitment.clone()) {
    159                 bail!("duplicate blinded reveal in reveal bundle section");
    160             }
    161             self.pending_reveal_transaction(&masked.reveal)?;
    162             let key = (
    163                 self.reveal_fee_order_key(&masked.reveal),
    164                 masked.reveal.commitment.clone(),
    165             );
    166             if let Some((previous_fee_key, previous_commitment)) = &previous_key {
    167                 if key.0 > *previous_fee_key
    168                     || key.0 == *previous_fee_key && key.1 < *previous_commitment
    169                 {
    170                     bail!("reveal bundle section is not fee ordered");
    171                 }
    172             }
    173             previous_key = Some(key);
    174         }
    175 
    176         for bundle in section.expand(expected_height, expected_prev_hash) {
    177             if bundle.serialized_size_bytes()? > MAX_REVEAL_BUNDLE_BYTES {
    178                 bail!("reveal bundle exceeds max size");
    179             }
    180             verify_address_signature(
    181                 &bundle.member,
    182                 &bundle.canonical_payload(),
    183                 &bundle.signature,
    184                 "reveal bundle",
    185             )?;
    186         }
    187         Ok(())
    188     }
    189 
    190     fn validate_reveal_bundles_for_block(
    191         &self,
    192         expected_height: u64,
    193         expected_prev_hash: &str,
    194         mut bundles: Vec<RevealBundle>,
    195     ) -> Result<Vec<RevealBundle>> {
    196         if bundles.len() > REVEAL_COMMITTEE_SIZE {
    197             bail!("block has too many reveal bundles");
    198         }
    199         if bundles.windows(2).any(|pair| pair[0].slot >= pair[1].slot) {
    200             bail!("reveal bundles are not in slot order");
    201         }
    202         bundles.sort_by_key(|bundle| bundle.slot);
    203         let committee = self
    204             .reveal_committee_for_height(expected_height)
    205             .into_iter()
    206             .map(|member| (member.slot, member))
    207             .collect::<BTreeMap<_, _>>();
    208         let mut seen_slots = BTreeSet::new();
    209         let mut seen_members = BTreeSet::new();
    210         for bundle in &bundles {
    211             if bundle.height != expected_height {
    212                 bail!("reveal bundle height is invalid");
    213             }
    214             if bundle.prev_hash != expected_prev_hash {
    215                 bail!("reveal bundle parent hash is invalid");
    216             }
    217             if usize::from(bundle.slot) >= REVEAL_COMMITTEE_SIZE {
    218                 bail!("reveal bundle slot is invalid");
    219             }
    220             if !seen_slots.insert(bundle.slot) {
    221                 bail!("duplicate reveal bundle slot");
    222             }
    223             if !seen_members.insert(bundle.member.clone()) {
    224                 bail!("duplicate reveal bundle member");
    225             }
    226             let member = committee
    227                 .get(&bundle.slot)
    228                 .context("reveal bundle slot is not assigned")?;
    229             if bundle.member != member.owner {
    230                 bail!("reveal bundle member is not assigned to slot");
    231             }
    232             if bundle.serialized_size_bytes()? > MAX_REVEAL_BUNDLE_BYTES {
    233                 bail!("reveal bundle exceeds max size");
    234             }
    235             verify_address_signature(
    236                 &bundle.member,
    237                 &bundle.canonical_payload(),
    238                 &bundle.signature,
    239                 "reveal bundle",
    240             )?;
    241             let mut seen_bundle_reveals = BTreeSet::new();
    242             let mut previous_key: Option<((u128, Amount), String)> = None;
    243             for reveal in &bundle.reveals {
    244                 if !seen_bundle_reveals.insert(reveal.commitment.clone()) {
    245                     bail!("duplicate blinded reveal in reveal bundle");
    246                 }
    247                 self.pending_reveal_transaction(reveal)?;
    248                 let key = (self.reveal_fee_order_key(reveal), reveal.commitment.clone());
    249                 if let Some((previous_fee_key, previous_commitment)) = &previous_key {
    250                     if key.0 > *previous_fee_key
    251                         || key.0 == *previous_fee_key && key.1 < *previous_commitment
    252                     {
    253                         bail!("reveal bundle is not fee ordered");
    254                     }
    255                 }
    256                 previous_key = Some(key);
    257             }
    258         }
    259         Ok(bundles)
    260     }
    261 }