Skip to content

Commit fb9e979

Browse files
committed
Drop Boxing of iterators during BOLT 11 invoice serialization
Now that we have an MSRV that supports returning `impl Trait` in trait methods, we can use it to avoid the `Box<dyn ...>` we had spewed all over our BOLT 11 invoice serialization.
1 parent 495f713 commit fb9e979

File tree

2 files changed

+70
-70
lines changed

2 files changed

+70
-70
lines changed

lightning-invoice/src/lib.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ use bitcoin::secp256k1::ecdsa::RecoverableSignature;
4040
use bitcoin::secp256k1::PublicKey;
4141
use bitcoin::secp256k1::{Message, Secp256k1};
4242

43-
use alloc::boxed::Box;
4443
use alloc::string;
4544
use core::cmp::Ordering;
4645
use core::fmt::{self, Display, Formatter};
@@ -1078,8 +1077,8 @@ macro_rules! find_all_extract {
10781077
#[allow(missing_docs)]
10791078
impl RawBolt11Invoice {
10801079
/// Hash the HRP (as bytes) and signatureless data part (as Fe32 iterator)
1081-
fn hash_from_parts<'s>(
1082-
hrp_bytes: &[u8], data_without_signature: Box<dyn Iterator<Item = Fe32> + 's>,
1080+
fn hash_from_parts<'s, I: Iterator<Item = Fe32> + 's>(
1081+
hrp_bytes: &[u8], data_without_signature: I,
10831082
) -> [u8; 32] {
10841083
use crate::bech32::Fe32IterExt;
10851084
use bitcoin::hashes::HashEngine;

lightning-invoice/src/ser.rs

Lines changed: 68 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use alloc::boxed::Box;
21
use core::fmt;
32
use core::fmt::{Display, Formatter};
43
use core::{array, iter};
@@ -13,14 +12,27 @@ use super::{
1312
SignedRawBolt11Invoice, TaggedField,
1413
};
1514

15+
macro_rules! define_iterator_enum {
16+
($name: ident, $($n: ident),*) => {
17+
enum $name<$($n: Iterator<Item = Fe32>,)*> {
18+
$($n($n),)*
19+
}
20+
impl<$($n: Iterator<Item = Fe32>,)*> Iterator for $name<$($n,)*> {
21+
type Item = Fe32;
22+
fn next(&mut self) -> Option<Fe32> {
23+
match self {
24+
$(Self::$n(iter) => iter.next(),)*
25+
}
26+
}
27+
}
28+
}
29+
}
30+
1631
/// Objects that can be encoded to base32 (bech32).
1732
///
1833
/// Private to this crate to avoid polluting the API.
19-
pub trait Base32Iterable {
20-
/// apoelstra: In future we want to replace this Box<dyn Iterator> with an explicit
21-
/// associated type, to avoid the allocation. But we cannot do this until
22-
/// Rust 1.65 and GATs since the iterator may contain a reference to self.
23-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's>;
34+
pub(crate) trait Base32Iterable {
35+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's;
2436
}
2537

2638
/// Interface to calculate the length of the base32 representation before actually serializing
@@ -32,7 +44,7 @@ pub(crate) trait Base32Len: Base32Iterable {
3244
// Base32Iterable & Base32Len implementations are here, because the traits are in this module.
3345

3446
impl<const N: usize> Base32Iterable for [u8; N] {
35-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
47+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
3648
self[..].fe_iter()
3749
}
3850
}
@@ -45,8 +57,8 @@ impl<const N: usize> Base32Len for [u8; N] {
4557
}
4658

4759
impl Base32Iterable for [u8] {
48-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
49-
Box::new(self.iter().copied().bytes_to_fes())
60+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
61+
self.iter().copied().bytes_to_fes()
5062
}
5163
}
5264

@@ -58,8 +70,8 @@ impl Base32Len for [u8] {
5870
}
5971

6072
impl Base32Iterable for Vec<u8> {
61-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
62-
Box::new(self.iter().copied().bytes_to_fes())
73+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
74+
self.iter().copied().bytes_to_fes()
6375
}
6476
}
6577

@@ -71,8 +83,8 @@ impl Base32Len for Vec<u8> {
7183
}
7284

7385
impl Base32Iterable for PaymentSecret {
74-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
75-
Box::new(self.0[..].fe_iter())
86+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
87+
self.0[..].fe_iter()
7688
}
7789
}
7890

@@ -88,7 +100,7 @@ impl Base32Iterable for Bolt11InvoiceFeatures {
88100
/// starting from the rightmost bit,
89101
/// and taking the resulting 5-bit values in reverse (left-to-right),
90102
/// with the leading 0's skipped.
91-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
103+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
92104
// Fe32 conversion cannot be used, because this packs from right, right-to-left
93105
let mut input_iter = self.le_flags().iter();
94106
// Carry bits, 0..7 bits
@@ -126,7 +138,7 @@ impl Base32Iterable for Bolt11InvoiceFeatures {
126138
output.push(Fe32::try_from(next_out8 & 31u8).expect("<32"))
127139
}
128140
// Take result in reverse order, and skip leading 0s
129-
Box::new(output.into_iter().rev().skip_while(|e| *e == Fe32::Q))
141+
output.into_iter().rev().skip_while(|e| *e == Fe32::Q)
130142
}
131143
}
132144

@@ -241,36 +253,35 @@ fn encoded_int_be_base32_size(int: u64) -> usize {
241253
}
242254

243255
impl Base32Iterable for RawDataPart {
244-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
256+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
245257
let ts_iter = self.timestamp.fe_iter();
246258
let fields_iter = self.tagged_fields.iter().map(RawTaggedField::fe_iter).flatten();
247-
Box::new(ts_iter.chain(fields_iter))
259+
ts_iter.chain(fields_iter)
248260
}
249261
}
250262

251263
impl Base32Iterable for PositiveTimestamp {
252-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
264+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
253265
let fes = encode_int_be_base32(self.as_unix_timestamp());
254266
debug_assert!(fes.len() <= 7, "Invalid timestamp length");
255267
let to_pad = 7 - fes.len();
256-
Box::new(core::iter::repeat(Fe32::Q).take(to_pad).chain(fes))
268+
core::iter::repeat(Fe32::Q).take(to_pad).chain(fes)
257269
}
258270
}
259271

260272
impl Base32Iterable for RawTaggedField {
261-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
262-
// Annoyingly, when we move to explicit types, we will need an
263-
// explicit enum holding the two iterator variants.
273+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
274+
define_iterator_enum!(TwoIters, A, B);
264275
match *self {
265-
RawTaggedField::UnknownSemantics(ref content) => Box::new(content.iter().copied()),
266-
RawTaggedField::KnownSemantics(ref tagged_field) => tagged_field.fe_iter(),
276+
RawTaggedField::UnknownSemantics(ref content) => TwoIters::A(content.iter().copied()),
277+
RawTaggedField::KnownSemantics(ref tagged_field) => TwoIters::B(tagged_field.fe_iter()),
267278
}
268279
}
269280
}
270281

271282
impl Base32Iterable for Sha256 {
272-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
273-
Box::new(self.0[..].fe_iter())
283+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
284+
self.0[..].fe_iter()
274285
}
275286
}
276287

@@ -281,8 +292,8 @@ impl Base32Len for Sha256 {
281292
}
282293

283294
impl Base32Iterable for Description {
284-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
285-
Box::new(self.0 .0.as_bytes().fe_iter())
295+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
296+
self.0 .0.as_bytes().fe_iter()
286297
}
287298
}
288299

@@ -293,8 +304,8 @@ impl Base32Len for Description {
293304
}
294305

295306
impl Base32Iterable for PayeePubKey {
296-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
297-
Box::new(self.serialize().into_iter().bytes_to_fes())
307+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
308+
self.serialize().into_iter().bytes_to_fes()
298309
}
299310
}
300311

@@ -305,8 +316,8 @@ impl Base32Len for PayeePubKey {
305316
}
306317

307318
impl Base32Iterable for ExpiryTime {
308-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
309-
Box::new(encode_int_be_base32(self.as_seconds()))
319+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
320+
encode_int_be_base32(self.as_seconds())
310321
}
311322
}
312323

@@ -317,8 +328,8 @@ impl Base32Len for ExpiryTime {
317328
}
318329

319330
impl Base32Iterable for MinFinalCltvExpiryDelta {
320-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
321-
Box::new(encode_int_be_base32(self.0))
331+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
332+
encode_int_be_base32(self.0)
322333
}
323334
}
324335

@@ -329,8 +340,8 @@ impl Base32Len for MinFinalCltvExpiryDelta {
329340
}
330341

331342
impl Base32Iterable for Fallback {
332-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
333-
Box::new(match *self {
343+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
344+
match *self {
334345
Fallback::SegWitProgram { version: v, program: ref p } => {
335346
let v = Fe32::try_from(v.to_num()).expect("valid version");
336347
core::iter::once(v).chain(p[..].fe_iter())
@@ -343,7 +354,7 @@ impl Base32Iterable for Fallback {
343354
// 18 'J'
344355
core::iter::once(Fe32::J).chain(hash[..].fe_iter())
345356
},
346-
})
357+
}
347358
}
348359
}
349360

@@ -371,7 +382,7 @@ type RouteHintHopIter = iter::Chain<
371382
>;
372383

373384
impl Base32Iterable for PrivateRoute {
374-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
385+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
375386
fn serialize_to_iter(hop: &RouteHintHop) -> RouteHintHopIter {
376387
let i1 = hop.src_node_id.serialize().into_iter();
377388
let i2 = u64::to_be_bytes(hop.short_channel_id).into_iter();
@@ -381,7 +392,7 @@ impl Base32Iterable for PrivateRoute {
381392
i1.chain(i2).chain(i3).chain(i4).chain(i5)
382393
}
383394

384-
Box::new(self.0 .0.iter().map(serialize_to_iter).flatten().bytes_to_fes())
395+
self.0 .0.iter().map(serialize_to_iter).flatten().bytes_to_fes()
385396
}
386397
}
387398

@@ -391,16 +402,11 @@ impl Base32Len for PrivateRoute {
391402
}
392403
}
393404

394-
// Shorthand type
395-
type TaggedFieldIter<I> = core::iter::Chain<core::array::IntoIter<Fe32, 3>, I>;
396-
397405
impl Base32Iterable for TaggedField {
398-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
406+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
399407
/// Writes a tagged field: tag, length and data. `tag` should be in `0..32` otherwise the
400408
/// function will panic.
401-
fn write_tagged_field<'s, P>(
402-
tag: u8, payload: &'s P,
403-
) -> TaggedFieldIter<Box<dyn Iterator<Item = Fe32> + 's>>
409+
fn write_tagged_field<'s, P>(tag: u8, payload: &'s P) -> impl Iterator<Item = Fe32> + 's
404410
where
405411
P: Base32Iterable + Base32Len + ?Sized,
406412
{
@@ -416,54 +422,49 @@ impl Base32Iterable for TaggedField {
416422
.chain(payload.fe_iter())
417423
}
418424

419-
// we will also need a giant enum for this
420-
Box::new(match *self {
425+
define_iterator_enum!(ManyIters, A, B, C, D, E, F, G, H, I, J, K);
426+
match *self {
421427
TaggedField::PaymentHash(ref hash) => {
422-
write_tagged_field(constants::TAG_PAYMENT_HASH, hash)
428+
ManyIters::A(write_tagged_field(constants::TAG_PAYMENT_HASH, hash))
423429
},
424430
TaggedField::Description(ref description) => {
425-
write_tagged_field(constants::TAG_DESCRIPTION, description)
431+
ManyIters::B(write_tagged_field(constants::TAG_DESCRIPTION, description))
426432
},
427433
TaggedField::PayeePubKey(ref pub_key) => {
428-
write_tagged_field(constants::TAG_PAYEE_PUB_KEY, pub_key)
434+
ManyIters::C(write_tagged_field(constants::TAG_PAYEE_PUB_KEY, pub_key))
429435
},
430436
TaggedField::DescriptionHash(ref hash) => {
431-
write_tagged_field(constants::TAG_DESCRIPTION_HASH, hash)
437+
ManyIters::D(write_tagged_field(constants::TAG_DESCRIPTION_HASH, hash))
432438
},
433439
TaggedField::ExpiryTime(ref duration) => {
434-
write_tagged_field(constants::TAG_EXPIRY_TIME, duration)
440+
ManyIters::E(write_tagged_field(constants::TAG_EXPIRY_TIME, duration))
435441
},
436442
TaggedField::MinFinalCltvExpiryDelta(ref expiry) => {
437-
write_tagged_field(constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA, expiry)
443+
ManyIters::F(write_tagged_field(constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA, expiry))
438444
},
439445
TaggedField::Fallback(ref fallback_address) => {
440-
write_tagged_field(constants::TAG_FALLBACK, fallback_address)
446+
ManyIters::G(write_tagged_field(constants::TAG_FALLBACK, fallback_address))
441447
},
442448
TaggedField::PrivateRoute(ref route_hops) => {
443-
write_tagged_field(constants::TAG_PRIVATE_ROUTE, route_hops)
449+
ManyIters::H(write_tagged_field(constants::TAG_PRIVATE_ROUTE, route_hops))
444450
},
445451
TaggedField::PaymentSecret(ref payment_secret) => {
446-
write_tagged_field(constants::TAG_PAYMENT_SECRET, payment_secret)
452+
ManyIters::I(write_tagged_field(constants::TAG_PAYMENT_SECRET, payment_secret))
447453
},
448454
TaggedField::PaymentMetadata(ref payment_metadata) => {
449-
write_tagged_field(constants::TAG_PAYMENT_METADATA, payment_metadata)
455+
ManyIters::J(write_tagged_field(constants::TAG_PAYMENT_METADATA, payment_metadata))
450456
},
451457
TaggedField::Features(ref features) => {
452-
write_tagged_field(constants::TAG_FEATURES, features)
458+
ManyIters::K(write_tagged_field(constants::TAG_FEATURES, features))
453459
},
454-
})
460+
}
455461
}
456462
}
457463

458464
impl Base32Iterable for Bolt11InvoiceSignature {
459-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
465+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
460466
let (recovery_id, signature) = self.0.serialize_compact();
461-
Box::new(
462-
signature
463-
.into_iter()
464-
.chain(core::iter::once(recovery_id.to_i32() as u8))
465-
.bytes_to_fes(),
466-
)
467+
signature.into_iter().chain(core::iter::once(recovery_id.to_i32() as u8)).bytes_to_fes()
467468
}
468469
}
469470

0 commit comments

Comments
 (0)