Skip to content

Commit 1fa1e99

Browse files
committed
Invoice serialization in iterative style
1 parent f4c8340 commit 1fa1e99

File tree

6 files changed

+223
-362
lines changed

6 files changed

+223
-362
lines changed

fuzz/src/bolt11_deser.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use crate::utils::test_logger;
1111
use bech32::Fe32;
1212
use bitcoin::secp256k1::{Secp256k1, SecretKey};
1313
use lightning_invoice::{
14-
Bolt11Invoice, FromBase32, RawBolt11Invoice, RawDataPart, RawHrp, RawTaggedField, TaggedField,
15-
ToBase32,
14+
Base32Iterable, Bolt11Invoice, FromBase32, RawBolt11Invoice, RawDataPart, RawHrp,
15+
RawTaggedField, TaggedField,
1616
};
1717
use std::str::FromStr;
1818

@@ -32,13 +32,13 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
3232
Err(_) => return,
3333
};
3434

35+
let invoice_data_base32 = invoice_data.fe_iter().collect::<Vec<_>>();
3536
// Our data encoding is not worse than the input
36-
assert!(invoice_data.to_base32().len() <= bech32.len());
37+
assert!(invoice_data_base32.len() <= bech32.len());
3738

3839
// Our data serialization is loss-less
3940
assert_eq!(
40-
RawDataPart::from_base32(&invoice_data.to_base32())
41-
.expect("faild parsing out own encoding"),
41+
RawDataPart::from_base32(&invoice_data_base32).expect("faild parsing out own encoding"),
4242
invoice_data
4343
);
4444

lightning-invoice/src/de.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,19 @@ use bitcoin::secp256k1::PublicKey;
2323

2424
use super::{Bolt11Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, Bolt11InvoiceSignature, PositiveTimestamp,
2525
Bolt11SemanticError, PrivateRoute, Bolt11ParseError, ParseOrSemanticError, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawBolt11Invoice,
26-
constants, SignedRawBolt11Invoice, RawDataPart, Bolt11InvoiceFeatures, FromBase32};
26+
constants, SignedRawBolt11Invoice, RawDataPart, Bolt11InvoiceFeatures};
2727

2828
use self::hrp_sm::parse_hrp;
2929

30+
/// Trait for paring/converting base32 slice.
31+
pub trait FromBase32: Sized {
32+
/// The associated error which can be returned from parsing (e.g. because of bad padding).
33+
type Err;
34+
35+
/// Convert a base32 slice to `Self`.
36+
fn from_base32(b32: &[Fe32]) -> Result<Self, Self::Err>;
37+
}
38+
3039
// FromBase32 implementations are here, because the trait is in this module.
3140

3241
impl FromBase32 for Vec<u8> {
@@ -40,22 +49,21 @@ impl FromBase32 for Vec<u8> {
4049
impl FromBase32 for PaymentSecret {
4150
type Err = CheckedHrpstringError;
4251

43-
fn from_base32(field_data: &[Fe32]) -> Result<PaymentSecret, CheckedHrpstringError> {
52+
fn from_base32(field_data: &[Fe32]) -> Result<Self, Self::Err> {
4453
if field_data.len() != 52 {
4554
return Err(CheckedHrpstringError::Checksum(ChecksumError::InvalidLength)) // TODO(bech32): not entirely accurate
46-
} else {
47-
let data_bytes = Vec::<u8>::from_base32(field_data)?;
48-
let mut payment_secret = [0; 32];
49-
payment_secret.copy_from_slice(&data_bytes);
50-
Ok(PaymentSecret(payment_secret))
5155
}
56+
let data_bytes = Vec::<u8>::from_base32(field_data)?;
57+
let mut payment_secret = [0; 32];
58+
payment_secret.copy_from_slice(&data_bytes);
59+
Ok(PaymentSecret(payment_secret))
5260
}
5361
}
5462

5563
impl FromBase32 for Bolt11InvoiceFeatures {
5664
type Err = CheckedHrpstringError;
5765

58-
fn from_base32(field_data: &[Fe32]) -> Result<Bolt11InvoiceFeatures, CheckedHrpstringError> {
66+
fn from_base32(field_data: &[Fe32]) -> Result<Self, Self::Err> {
5967
// Explanation for the "7": the normal way to round up when dividing is to add the divisor
6068
// minus one before dividing
6169
let length_bytes = (field_data.len() * 5 + 7) / 8 as usize;

lightning-invoice/src/lib.rs

Lines changed: 19 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -77,51 +77,11 @@ mod prelude {
7777

7878
use crate::prelude::*;
7979

80-
/// Interface to write `Fe32`s into a sink
81-
pub trait WriteBase32 {
82-
/// Write error
83-
type Err: fmt::Debug;
84-
85-
/// Write a `Fe32` slice
86-
fn write(&mut self, data: &Vec<Fe32>) -> Result<(), Self::Err> {
87-
for b in data {
88-
self.write_fe32(*b)?;
89-
}
90-
Ok(())
91-
}
92-
93-
/// Write a single `Fe32`
94-
fn write_fe32(&mut self, data: Fe32) -> Result<(), Self::Err>;
95-
}
96-
97-
/// A trait for converting a value to a type `T` that represents a `Fe32` slice.
98-
pub trait ToBase32 {
99-
/// Convert `Self` to base32 vector
100-
fn to_base32(&self) -> Vec<Fe32> {
101-
let mut vec = Vec::new();
102-
self.write_base32(&mut vec).unwrap();
103-
vec
104-
}
105-
106-
/// Encode as base32 and write it to the supplied writer
107-
/// Implementations shouldn't allocate.
108-
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err>;
109-
}
110-
111-
/// Interface to calculate the length of the base32 representation before actually serializing
112-
pub trait Base32Len: ToBase32 {
113-
/// Calculate the base32 serialized length
114-
fn base32_len(&self) -> usize;
115-
}
116-
117-
/// Trait for paring/converting base32 slice. It is the reciprocal of `ToBase32`.
118-
pub trait FromBase32: Sized {
119-
/// The associated error which can be returned from parsing (e.g. because of bad padding).
120-
type Err;
121-
122-
/// Convert a base32 slice to `Self`.
123-
fn from_base32(b32: &[Fe32]) -> Result<Self, Self::Err>;
124-
}
80+
/// Re-export serialization traits
81+
#[cfg(fuzzing)]
82+
pub use crate::ser::Base32Iterable;
83+
#[cfg(fuzzing)]
84+
pub use crate::de::FromBase32;
12585

12686
/// Errors that indicate what is wrong with the invoice. They have some granularity for debug
12787
/// reasons, but should generally result in an "invalid BOLT11 invoice" message for the user.
@@ -347,6 +307,15 @@ pub struct RawHrp {
347307
pub si_prefix: Option<SiPrefix>,
348308
}
349309

310+
impl RawHrp {
311+
/// Convert to bech32::Hrp
312+
pub fn to_hrp(&self) -> bech32::Hrp {
313+
let hrp_str = self.to_string();
314+
let s = core::str::from_utf8(&hrp_str.as_bytes()).expect("asserted to be ASCII");
315+
bech32::Hrp::parse_unchecked(s)
316+
}
317+
}
318+
350319
/// Data of the [`RawBolt11Invoice`] that is encoded in the data part
351320
#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)]
352321
pub struct RawDataPart {
@@ -1024,6 +993,8 @@ macro_rules! find_all_extract {
1024993
impl RawBolt11Invoice {
1025994
/// Hash the HRP as bytes and signatureless data part.
1026995
fn hash_from_parts(hrp_bytes: &[u8], data_without_signature: &[Fe32]) -> [u8; 32] {
996+
use crate::de::FromBase32;
997+
1027998
let mut preimage = Vec::<u8>::from(hrp_bytes);
1028999

10291000
let mut data_part = Vec::from(data_without_signature);
@@ -1048,9 +1019,11 @@ impl RawBolt11Invoice {
10481019

10491020
/// Calculate the hash of the encoded `RawBolt11Invoice` which should be signed.
10501021
pub fn signable_hash(&self) -> [u8; 32] {
1022+
use crate::ser::Base32Iterable;
1023+
10511024
RawBolt11Invoice::hash_from_parts(
10521025
self.hrp.to_string().as_bytes(),
1053-
&self.data.to_base32()
1026+
&self.data.fe_iter().collect::<Vec<Fe32>>(),
10541027
)
10551028
}
10561029

0 commit comments

Comments
 (0)