Skip to content

Commit

Permalink
More padding, tests, move to new file
Browse files Browse the repository at this point in the history
  • Loading branch information
optout21 committed Sep 9, 2024
1 parent 0fbc37c commit e8ed612
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 88 deletions.
127 changes: 127 additions & 0 deletions lightning-invoice/src/bech32_util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use bech32::Fe32;

/// Adaptor to pad a Fe32 iter
// #[derive(Clone, PartialEq, Eq)]
pub struct FesPadder<I: Iterator<Item = Fe32>> {
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<I> FesPadder<I>
where
I: Iterator<Item = Fe32>,
{
fn new(iter: I) -> Self {
Self {
end_reached: false,
fe32_count: 0,
pad_count: 0,
iter,
}
}
}

impl<I> Iterator for FesPadder<I>
where
I: Iterator<Item = Fe32>,
{
type Item = Fe32;

fn next(&mut self) -> Option<Self::Item> {
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<usize>) {
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<Item = Fe32> {
/// Pad the iterator
fn pad_fes(self) -> FesPadder<Self> {
FesPadder::new(self)
}
}

impl<I> FesPaddable for I where I: Iterator<Item = Fe32> {}

#[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::<Vec<Fe32>>();
assert_eq!(fes.len(), 5);

assert_eq!(
fes.iter().copied()
.fes_to_bytes()
.collect::<Vec<u8>>(),
vec![8, 134, 66]
);
assert_eq!(
fes.iter().copied()
.pad_fes()
.fes_to_bytes()
.collect::<Vec<u8>>(),
vec![8, 134, 66, 128]
);
}
}
2 changes: 1 addition & 1 deletion lightning-invoice/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, Self::Err> {
use crate::FesPaddable;
use crate::bech32_util::FesPaddable;

let mut output = field_data
.iter()
Expand Down
93 changes: 6 additions & 87 deletions lightning-invoice/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ use lightning_types::string::UntrustedString;
mod de;
mod ser;
mod tb;
mod bech32_util;

#[cfg(test)]
mod test_ser_de;
Expand Down Expand Up @@ -1023,93 +1024,6 @@ macro_rules! find_all_extract {
};
}

/// Adaptor to pad a Fe32 iter
// #[derive(Clone, PartialEq, Eq)]
pub struct FesPadder<I: Iterator<Item = Fe32>> {
end_reached: bool,
fe32_count: usize,
pad_count: u8,
iter: I,
}

impl<I> FesPadder<I>
where
I: Iterator<Item = Fe32>,
{
// 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<I> Iterator for FesPadder<I>
where
I: Iterator<Item = Fe32>,
{
type Item = Fe32;

fn next(&mut self) -> Option<Self::Item> {
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<usize>) {
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<Item = Fe32> {
/// Pad the iterator
fn pad_fes(self) -> FesPadder<Self> {
FesPadder::new(self)
}
}

impl<I> FesPaddable for I where I: Iterator<Item = Fe32> {}

#[allow(missing_docs)]
impl RawBolt11Invoice {
/// Hash the HRP (as bytes) and signatureless data part (as Fe32 iterator)
Expand All @@ -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);
Expand All @@ -1140,13 +1055,16 @@ impl RawBolt11Invoice {
}

/// Hash ...
#[cfg(test)]
fn preimage_from_parts_iter<'s>(hrp_bytes: &[u8], data_without_signature_iter: Box<dyn Iterator<Item = Fe32> + 's>) -> Vec<u8> {
let data_part_signature = data_without_signature_iter.collect::<Vec<Fe32>>();
Self::preimage_from_parts(hrp_bytes, &data_part_signature[..])
}

/// Hash ...
#[cfg(test)]
fn preimage_from_parts(hrp_bytes: &[u8], data_without_signature: &[Fe32]) -> Vec<u8> {
use crate::bech32_util::FesPaddable;
use crate::bech32::Fe32IterExt;

let data_part = Vec::from(data_without_signature);
Expand Down Expand Up @@ -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<u8> {
use crate::ser::Base32Iterable;

Expand Down

0 comments on commit e8ed612

Please sign in to comment.