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 §ion.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 §ion.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 }