Skip to content

Commit 004c76a

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 b8fb800 commit 004c76a

File tree

2 files changed

+71
-70
lines changed

2 files changed

+71
-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: 69 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,28 @@ 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
///
18-
/// Private to this crate to avoid polluting the API.
33+
/// Private to this crate (except in fuzzing) to avoid polluting the API.
1934
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>;
35+
/// Serialize this object, returning an iterator over bech32 field elements.
36+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's;
2437
}
2538

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

3447
impl<const N: usize> Base32Iterable for [u8; N] {
35-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
48+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
3649
self[..].fe_iter()
3750
}
3851
}
@@ -45,8 +58,8 @@ impl<const N: usize> Base32Len for [u8; N] {
4558
}
4659

4760
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())
61+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
62+
self.iter().copied().bytes_to_fes()
5063
}
5164
}
5265

@@ -58,8 +71,8 @@ impl Base32Len for [u8] {
5871
}
5972

6073
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())
74+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
75+
self.iter().copied().bytes_to_fes()
6376
}
6477
}
6578

@@ -71,8 +84,8 @@ impl Base32Len for Vec<u8> {
7184
}
7285

7386
impl Base32Iterable for PaymentSecret {
74-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
75-
Box::new(self.0[..].fe_iter())
87+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
88+
self.0[..].fe_iter()
7689
}
7790
}
7891

@@ -88,7 +101,7 @@ impl Base32Iterable for Bolt11InvoiceFeatures {
88101
/// starting from the rightmost bit,
89102
/// and taking the resulting 5-bit values in reverse (left-to-right),
90103
/// with the leading 0's skipped.
91-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
104+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
92105
// Fe32 conversion cannot be used, because this packs from right, right-to-left
93106
let mut input_iter = self.le_flags().iter();
94107
// Carry bits, 0..7 bits
@@ -126,7 +139,7 @@ impl Base32Iterable for Bolt11InvoiceFeatures {
126139
output.push(Fe32::try_from(next_out8 & 31u8).expect("<32"))
127140
}
128141
// Take result in reverse order, and skip leading 0s
129-
Box::new(output.into_iter().rev().skip_while(|e| *e == Fe32::Q))
142+
output.into_iter().rev().skip_while(|e| *e == Fe32::Q)
130143
}
131144
}
132145

@@ -241,36 +254,35 @@ fn encoded_int_be_base32_size(int: u64) -> usize {
241254
}
242255

243256
impl Base32Iterable for RawDataPart {
244-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
257+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
245258
let ts_iter = self.timestamp.fe_iter();
246259
let fields_iter = self.tagged_fields.iter().map(RawTaggedField::fe_iter).flatten();
247-
Box::new(ts_iter.chain(fields_iter))
260+
ts_iter.chain(fields_iter)
248261
}
249262
}
250263

251264
impl Base32Iterable for PositiveTimestamp {
252-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
265+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
253266
let fes = encode_int_be_base32(self.as_unix_timestamp());
254267
debug_assert!(fes.len() <= 7, "Invalid timestamp length");
255268
let to_pad = 7 - fes.len();
256-
Box::new(core::iter::repeat(Fe32::Q).take(to_pad).chain(fes))
269+
core::iter::repeat(Fe32::Q).take(to_pad).chain(fes)
257270
}
258271
}
259272

260273
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.
274+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
275+
define_iterator_enum!(TwoIters, A, B);
264276
match *self {
265-
RawTaggedField::UnknownSemantics(ref content) => Box::new(content.iter().copied()),
266-
RawTaggedField::KnownSemantics(ref tagged_field) => tagged_field.fe_iter(),
277+
RawTaggedField::UnknownSemantics(ref content) => TwoIters::A(content.iter().copied()),
278+
RawTaggedField::KnownSemantics(ref tagged_field) => TwoIters::B(tagged_field.fe_iter()),
267279
}
268280
}
269281
}
270282

271283
impl Base32Iterable for Sha256 {
272-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
273-
Box::new(self.0[..].fe_iter())
284+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
285+
self.0[..].fe_iter()
274286
}
275287
}
276288

@@ -281,8 +293,8 @@ impl Base32Len for Sha256 {
281293
}
282294

283295
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())
296+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
297+
self.0 .0.as_bytes().fe_iter()
286298
}
287299
}
288300

@@ -293,8 +305,8 @@ impl Base32Len for Description {
293305
}
294306

295307
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())
308+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
309+
self.serialize().into_iter().bytes_to_fes()
298310
}
299311
}
300312

@@ -305,8 +317,8 @@ impl Base32Len for PayeePubKey {
305317
}
306318

307319
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()))
320+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
321+
encode_int_be_base32(self.as_seconds())
310322
}
311323
}
312324

@@ -317,8 +329,8 @@ impl Base32Len for ExpiryTime {
317329
}
318330

319331
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))
332+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
333+
encode_int_be_base32(self.0)
322334
}
323335
}
324336

@@ -329,8 +341,8 @@ impl Base32Len for MinFinalCltvExpiryDelta {
329341
}
330342

331343
impl Base32Iterable for Fallback {
332-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
333-
Box::new(match *self {
344+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
345+
match *self {
334346
Fallback::SegWitProgram { version: v, program: ref p } => {
335347
let v = Fe32::try_from(v.to_num()).expect("valid version");
336348
core::iter::once(v).chain(p[..].fe_iter())
@@ -343,7 +355,7 @@ impl Base32Iterable for Fallback {
343355
// 18 'J'
344356
core::iter::once(Fe32::J).chain(hash[..].fe_iter())
345357
},
346-
})
358+
}
347359
}
348360
}
349361

@@ -371,7 +383,7 @@ type RouteHintHopIter = iter::Chain<
371383
>;
372384

373385
impl Base32Iterable for PrivateRoute {
374-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
386+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
375387
fn serialize_to_iter(hop: &RouteHintHop) -> RouteHintHopIter {
376388
let i1 = hop.src_node_id.serialize().into_iter();
377389
let i2 = u64::to_be_bytes(hop.short_channel_id).into_iter();
@@ -381,7 +393,7 @@ impl Base32Iterable for PrivateRoute {
381393
i1.chain(i2).chain(i3).chain(i4).chain(i5)
382394
}
383395

384-
Box::new(self.0 .0.iter().map(serialize_to_iter).flatten().bytes_to_fes())
396+
self.0 .0.iter().map(serialize_to_iter).flatten().bytes_to_fes()
385397
}
386398
}
387399

@@ -391,16 +403,11 @@ impl Base32Len for PrivateRoute {
391403
}
392404
}
393405

394-
// Shorthand type
395-
type TaggedFieldIter<I> = core::iter::Chain<core::array::IntoIter<Fe32, 3>, I>;
396-
397406
impl Base32Iterable for TaggedField {
398-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
407+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
399408
/// Writes a tagged field: tag, length and data. `tag` should be in `0..32` otherwise the
400409
/// function will panic.
401-
fn write_tagged_field<'s, P>(
402-
tag: u8, payload: &'s P,
403-
) -> TaggedFieldIter<Box<dyn Iterator<Item = Fe32> + 's>>
410+
fn write_tagged_field<'s, P>(tag: u8, payload: &'s P) -> impl Iterator<Item = Fe32> + 's
404411
where
405412
P: Base32Iterable + Base32Len + ?Sized,
406413
{
@@ -416,54 +423,49 @@ impl Base32Iterable for TaggedField {
416423
.chain(payload.fe_iter())
417424
}
418425

419-
// we will also need a giant enum for this
420-
Box::new(match *self {
426+
define_iterator_enum!(ManyIters, A, B, C, D, E, F, G, H, I, J, K);
427+
match *self {
421428
TaggedField::PaymentHash(ref hash) => {
422-
write_tagged_field(constants::TAG_PAYMENT_HASH, hash)
429+
ManyIters::A(write_tagged_field(constants::TAG_PAYMENT_HASH, hash))
423430
},
424431
TaggedField::Description(ref description) => {
425-
write_tagged_field(constants::TAG_DESCRIPTION, description)
432+
ManyIters::B(write_tagged_field(constants::TAG_DESCRIPTION, description))
426433
},
427434
TaggedField::PayeePubKey(ref pub_key) => {
428-
write_tagged_field(constants::TAG_PAYEE_PUB_KEY, pub_key)
435+
ManyIters::C(write_tagged_field(constants::TAG_PAYEE_PUB_KEY, pub_key))
429436
},
430437
TaggedField::DescriptionHash(ref hash) => {
431-
write_tagged_field(constants::TAG_DESCRIPTION_HASH, hash)
438+
ManyIters::D(write_tagged_field(constants::TAG_DESCRIPTION_HASH, hash))
432439
},
433440
TaggedField::ExpiryTime(ref duration) => {
434-
write_tagged_field(constants::TAG_EXPIRY_TIME, duration)
441+
ManyIters::E(write_tagged_field(constants::TAG_EXPIRY_TIME, duration))
435442
},
436443
TaggedField::MinFinalCltvExpiryDelta(ref expiry) => {
437-
write_tagged_field(constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA, expiry)
444+
ManyIters::F(write_tagged_field(constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA, expiry))
438445
},
439446
TaggedField::Fallback(ref fallback_address) => {
440-
write_tagged_field(constants::TAG_FALLBACK, fallback_address)
447+
ManyIters::G(write_tagged_field(constants::TAG_FALLBACK, fallback_address))
441448
},
442449
TaggedField::PrivateRoute(ref route_hops) => {
443-
write_tagged_field(constants::TAG_PRIVATE_ROUTE, route_hops)
450+
ManyIters::H(write_tagged_field(constants::TAG_PRIVATE_ROUTE, route_hops))
444451
},
445452
TaggedField::PaymentSecret(ref payment_secret) => {
446-
write_tagged_field(constants::TAG_PAYMENT_SECRET, payment_secret)
453+
ManyIters::I(write_tagged_field(constants::TAG_PAYMENT_SECRET, payment_secret))
447454
},
448455
TaggedField::PaymentMetadata(ref payment_metadata) => {
449-
write_tagged_field(constants::TAG_PAYMENT_METADATA, payment_metadata)
456+
ManyIters::J(write_tagged_field(constants::TAG_PAYMENT_METADATA, payment_metadata))
450457
},
451458
TaggedField::Features(ref features) => {
452-
write_tagged_field(constants::TAG_FEATURES, features)
459+
ManyIters::K(write_tagged_field(constants::TAG_FEATURES, features))
453460
},
454-
})
461+
}
455462
}
456463
}
457464

458465
impl Base32Iterable for Bolt11InvoiceSignature {
459-
fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
466+
fn fe_iter<'s>(&'s self) -> impl Iterator<Item = Fe32> + 's {
460467
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-
)
468+
signature.into_iter().chain(core::iter::once(recovery_id.to_i32() as u8)).bytes_to_fes()
467469
}
468470
}
469471

0 commit comments

Comments
 (0)