From e8ed6127b1d8fa6833b64f9e2ec2ad300a966993 Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:32:13 +0200 Subject: [PATCH] More padding, tests, move to new file --- lightning-invoice/src/bech32_util.rs | 127 +++++++++++++++++++++++++++ lightning-invoice/src/de.rs | 2 +- lightning-invoice/src/lib.rs | 93 ++------------------ 3 files changed, 134 insertions(+), 88 deletions(-) create mode 100644 lightning-invoice/src/bech32_util.rs diff --git a/lightning-invoice/src/bech32_util.rs b/lightning-invoice/src/bech32_util.rs new file mode 100644 index 00000000000..66b25736106 --- /dev/null +++ b/lightning-invoice/src/bech32_util.rs @@ -0,0 +1,127 @@ +use bech32::Fe32; + +/// Adaptor to pad a Fe32 iter +// #[derive(Clone, PartialEq, Eq)] +pub struct FesPadder> { + end_reached: bool, + fe32_count: usize, + pad_count: u8, + iter: I, +} + +/// Compute how many trailing extra 5-bit elements are needed +/// such that no significant bits are dropped if the last byte is dropped. +/// Returns 0 (result falls on byte boundary), 1, or 2. +fn pad_count_from_fe32_count(fe32_count: usize) -> u8 { + let leftover_bits = (fe32_count * 5) % 8; + if leftover_bits == 0 { + 0 + } else { + let needed_bits = 8 - leftover_bits; // 1..7 + let needed_extra_fe32s = (needed_bits + (5 - 1)) / 5; // 1..2 + needed_extra_fe32s as u8 + } +} + +fn padded_count(fe32_count: usize) -> usize { + fe32_count + pad_count_from_fe32_count(fe32_count) as usize +} + +impl FesPadder +where + I: Iterator, +{ + fn new(iter: I) -> Self { + Self { + end_reached: false, + fe32_count: 0, + pad_count: 0, + iter, + } + } +} + +impl Iterator for FesPadder +where + I: Iterator, +{ + type Item = Fe32; + + fn next(&mut self) -> Option { + if let Some(elem) = self.iter.next() { + self.fe32_count += 1; + Some(elem) + } else { + // end reached + if !self.end_reached { + self.end_reached = true; + self.pad_count = pad_count_from_fe32_count(self.fe32_count); + } + if self.pad_count > 0 { + self.pad_count -= 1; + Some(Fe32::Q) + } else { + None + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let (fes_min, fes_max) = self.iter.size_hint(); + // +1 because we set last_fe with call to `next`. + let min = padded_count(fes_min + 1); + let max = fes_max.map(|max| padded_count(max)); + (min, max) + } +} + +/// Trait to pad an Fe32 iterator +pub trait FesPaddable: Sized + Iterator { + /// Pad the iterator + fn pad_fes(self) -> FesPadder { + FesPadder::new(self) + } +} + +impl FesPaddable for I where I: Iterator {} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_pad_count_from_fe32_count() { + assert_eq!(pad_count_from_fe32_count(1), 1); + assert_eq!(pad_count_from_fe32_count(2), 2); + assert_eq!(pad_count_from_fe32_count(3), 1); + assert_eq!(pad_count_from_fe32_count(4), 1); + assert_eq!(pad_count_from_fe32_count(5), 2); + assert_eq!(pad_count_from_fe32_count(6), 1); + assert_eq!(pad_count_from_fe32_count(7), 1); + assert_eq!(pad_count_from_fe32_count(8), 0); + } + + #[test] + fn test_frombech32_with_pad() { + // use crate::ser::Base32Iterable; + // use crate::de::FromBase32; + use bech32::{Fe32, Fe32IterExt}; + + let fes = vec![1, 2, 3, 4, 5].iter().map(|v| Fe32::try_from(*v).unwrap()).collect::>(); + assert_eq!(fes.len(), 5); + + assert_eq!( + fes.iter().copied() + .fes_to_bytes() + .collect::>(), + vec![8, 134, 66] + ); + assert_eq!( + fes.iter().copied() + .pad_fes() + .fes_to_bytes() + .collect::>(), + vec![8, 134, 66, 128] + ); + } +} diff --git a/lightning-invoice/src/de.rs b/lightning-invoice/src/de.rs index f0f358f6f14..d07334a7aa4 100644 --- a/lightning-invoice/src/de.rs +++ b/lightning-invoice/src/de.rs @@ -85,7 +85,7 @@ impl FromBase32 for Bolt11InvoiceFeatures { /// and taking the resulting 8-bit values (right to left), /// with the leading 0's skipped. fn from_base32(field_data: &[Fe32]) -> Result { - use crate::FesPaddable; + use crate::bech32_util::FesPaddable; let mut output = field_data .iter() diff --git a/lightning-invoice/src/lib.rs b/lightning-invoice/src/lib.rs index 632ca16b0f0..45b45e4a2ed 100644 --- a/lightning-invoice/src/lib.rs +++ b/lightning-invoice/src/lib.rs @@ -65,6 +65,7 @@ use lightning_types::string::UntrustedString; mod de; mod ser; mod tb; +mod bech32_util; #[cfg(test)] mod test_ser_de; @@ -1023,93 +1024,6 @@ macro_rules! find_all_extract { }; } -/// Adaptor to pad a Fe32 iter -// #[derive(Clone, PartialEq, Eq)] -pub struct FesPadder> { - end_reached: bool, - fe32_count: usize, - pad_count: u8, - iter: I, -} - -impl FesPadder -where - I: Iterator, -{ - // type Item = u8; - - fn new(iter: I) -> Self { - Self { - end_reached: false, - fe32_count: 0, - pad_count: 0, - iter, - } - } - - /// Compute how many trailing extra 5-bit elements are needed - /// such that no significant bits are dropped if the last byte is dropped. - /// Returns 0 (result falls on byte boundary), 1, or 2. - fn pad_count_from_fe32_count(fe32_count: usize) -> u8 { - let leftover_bits = (fe32_count * 5) % 8; - if leftover_bits == 0 { - 0 - } else { - let needed_bits = 8 - leftover_bits; // 1..7 - let needed_extra_fe32s = (needed_bits + (5 - 1)) / 5; // 1..2 - needed_extra_fe32s as u8 - } - } - - fn padded_count(fe32_count: usize) -> usize { - fe32_count + Self::pad_count_from_fe32_count(fe32_count) as usize - } -} - -impl Iterator for FesPadder -where - I: Iterator, -{ - type Item = Fe32; - - fn next(&mut self) -> Option { - if let Some(elem) = self.iter.next() { - self.fe32_count += 1; - Some(elem) - } else { - // end reached - if !self.end_reached { - self.end_reached = true; - self.pad_count = Self::pad_count_from_fe32_count(self.fe32_count); - } - if self.pad_count > 0 { - self.pad_count -= 1; - Some(Fe32::Q) - } else { - None - } - } - } - - fn size_hint(&self) -> (usize, Option) { - let (fes_min, fes_max) = self.iter.size_hint(); - // +1 because we set last_fe with call to `next`. - let min = Self::padded_count(fes_min + 1); - let max = fes_max.map(|max| Self::padded_count(max)); - (min, max) - } -} - -/// Trait to pad an Fe32 iterator -pub trait FesPaddable: Sized + Iterator { - /// Pad the iterator - fn pad_fes(self) -> FesPadder { - FesPadder::new(self) - } -} - -impl FesPaddable for I where I: Iterator {} - #[allow(missing_docs)] impl RawBolt11Invoice { /// Hash the HRP (as bytes) and signatureless data part (as Fe32 iterator) @@ -1120,6 +1034,7 @@ impl RawBolt11Invoice { /// Hash the HRP as bytes and signatureless data part. fn hash_from_parts(hrp_bytes: &[u8], data_without_signature: &[Fe32]) -> [u8; 32] { + use crate::bech32_util::FesPaddable; use crate::bech32::Fe32IterExt; let data_part = Vec::from(data_without_signature); @@ -1140,13 +1055,16 @@ impl RawBolt11Invoice { } /// Hash ... + #[cfg(test)] fn preimage_from_parts_iter<'s>(hrp_bytes: &[u8], data_without_signature_iter: Box + 's>) -> Vec { let data_part_signature = data_without_signature_iter.collect::>(); Self::preimage_from_parts(hrp_bytes, &data_part_signature[..]) } /// Hash ... + #[cfg(test)] fn preimage_from_parts(hrp_bytes: &[u8], data_without_signature: &[Fe32]) -> Vec { + use crate::bech32_util::FesPaddable; use crate::bech32::Fe32IterExt; let data_part = Vec::from(data_without_signature); @@ -1189,6 +1107,7 @@ impl RawBolt11Invoice { } /// Calculate the hash of the encoded `RawBolt11Invoice` which should be signed. + #[cfg(test)] pub fn hash_preimage(&self) -> Vec { use crate::ser::Base32Iterable;