Skip to content

Commit 2ccc166

Browse files
committed
WIP: Serialize offer without cloning
1 parent 5cb3c44 commit 2ccc166

File tree

3 files changed

+121
-77
lines changed

3 files changed

+121
-77
lines changed

lightning/src/offers/mod.rs

Lines changed: 33 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -123,43 +123,43 @@ impl Offer {
123123
self.signature.as_ref()
124124
}
125125

126-
fn to_tlv_stream(&self) -> OfferTlvStream {
127-
let (currency, amount) = match self.amount.clone() {
126+
fn as_tlv_stream(&self) -> reference::OfferTlvStream {
127+
let (currency, amount) = match &self.amount {
128128
None => (None, None),
129129
Some(Amount::Bitcoin { amount_msats }) => (
130-
None, Some(HighZeroBytesDroppedVarInt(amount_msats))
130+
None, Some(HighZeroBytesDroppedVarInt(*amount_msats))
131131
),
132132
Some(Amount::Currency { iso4217_code, amount }) => (
133-
Some(WithoutLength(iso4217_code)), Some(HighZeroBytesDroppedVarInt(amount))
133+
Some(WithoutLength(iso4217_code)), Some(HighZeroBytesDroppedVarInt(*amount))
134134
),
135135
};
136136

137-
let (paths, node_id) = match self.destination.clone() {
137+
let (paths, node_id) = match &self.destination {
138138
Destination::NodeId(node_id) => (None, Some(node_id)),
139139
Destination::Paths(paths) => (Some(WithoutLength(paths)), None),
140140
};
141141

142-
let (send_invoice, refund_for) = match self.send_invoice.clone() {
142+
let (send_invoice, refund_for) = match &self.send_invoice {
143143
None => (None, None),
144-
Some(SendInvoice { refund_for }) => (Some(()), refund_for),
144+
Some(SendInvoice { refund_for }) => (Some(&()), refund_for.as_ref()),
145145
};
146146

147-
OfferTlvStream {
148-
chains: self.chains.clone().map(|chains| WithoutLength(chains)),
147+
reference::OfferTlvStream {
148+
chains: self.chains.as_ref().map(|chains| WithoutLength(chains)),
149149
currency,
150150
amount,
151-
description: Some(WithoutLength(self.description.clone())),
152-
features: self.features.clone(),
151+
description: Some(WithoutLength(&self.description)),
152+
features: self.features.as_ref(),
153153
absolute_expiry: self.absolute_expiry.map(|d| HighZeroBytesDroppedVarInt(d.as_secs())),
154154
paths,
155-
issuer: self.issuer.clone().map(|issuer| WithoutLength(issuer)),
155+
issuer: self.issuer.as_ref().map(|issuer| WithoutLength(issuer)),
156156
quantity_min: self.quantity_min.map(|quantity| HighZeroBytesDroppedVarInt(quantity)),
157157
quantity_max: self.quantity_max.map(|quantity| HighZeroBytesDroppedVarInt(quantity)),
158-
recurrence: self.recurrence.clone(),
158+
recurrence: self.recurrence.as_ref(),
159159
node_id,
160160
send_invoice,
161161
refund_for,
162-
signature: self.signature,
162+
signature: self.signature.as_ref(),
163163
}
164164
}
165165
}
@@ -208,43 +208,22 @@ pub struct SendInvoice {
208208
refund_for: Option<PaymentHash>,
209209
}
210210

211-
/// An `offer` TLV stream without any semantic checks, apart from any checks performed when parsing
212-
/// the underlying types.
213-
#[derive(Debug)]
214-
struct OfferTlvStream {
215-
chains: Option<WithoutLength<Vec<BlockHash>>>,
216-
currency: Option<WithoutLength<String>>,
217-
amount: Option<HighZeroBytesDroppedVarInt<u64>>,
218-
description: Option<WithoutLength<String>>,
219-
features: Option<OfferFeatures>,
220-
absolute_expiry: Option<HighZeroBytesDroppedVarInt<u64>>,
221-
paths: Option<WithoutLength<Vec<BlindedPath>>>,
222-
issuer: Option<WithoutLength<String>>,
223-
quantity_min: Option<HighZeroBytesDroppedVarInt<u64>>,
224-
quantity_max: Option<HighZeroBytesDroppedVarInt<u64>>,
225-
recurrence: Option<Recurrence>,
226-
node_id: Option<XOnlyPublicKey>,
227-
send_invoice: Option<()>,
228-
refund_for: Option<PaymentHash>,
229-
signature: Option<Signature>,
230-
}
231-
232-
impl_writeable_tlv_stream!(OfferTlvStream, {
233-
(2, chains, option),
234-
(6, currency, option),
235-
(8, amount, option),
236-
(10, description, option),
237-
(12, features, option),
238-
(14, absolute_expiry, option),
239-
(16, paths, option),
240-
(20, issuer, option),
241-
(22, quantity_min, option),
242-
(24, quantity_max, option),
243-
(26, recurrence, option),
244-
(30, node_id, option),
245-
(34, refund_for, option),
246-
(54, send_invoice, option),
247-
(240, signature, option),
211+
tlv_stream!(struct OfferTlvStream {
212+
(2, chains: Vec<BlockHash>),
213+
(6, currency: String),
214+
(8, amount: u64),
215+
(10, description: String),
216+
(12, features: OfferFeatures),
217+
(14, absolute_expiry: u64),
218+
(16, paths: Vec<BlindedPath>),
219+
(20, issuer: String),
220+
(22, quantity_min: u64),
221+
(24, quantity_max: u64),
222+
(26, recurrence: Recurrence),
223+
(30, node_id: XOnlyPublicKey),
224+
(34, refund_for: PaymentHash),
225+
(54, send_invoice: Empty),
226+
(240, signature: Signature),
248227
});
249228

250229
#[derive(Clone, Debug)]
@@ -273,6 +252,8 @@ struct Recurrence {
273252

274253
impl_writeable!(Recurrence, { time_unit, period });
275254

255+
type Empty = ();
256+
276257
/// An offer parsed from a bech32-encoded string as a TLV stream and the corresponding bytes. The
277258
/// latter is used for signature verification.
278259
struct ParsedOffer(OfferTlvStream, Vec<u8>);
@@ -477,16 +458,10 @@ impl FromStr for ParsedOffer {
477458
}
478459

479460
impl core::fmt::Display for Offer {
480-
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
481-
self.to_tlv_stream().fmt(f)
482-
}
483-
}
484-
485-
impl core::fmt::Display for OfferTlvStream {
486461
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
487462
use util::ser::Writeable;
488463
let mut buffer = Vec::new();
489-
self.write(&mut buffer).unwrap();
464+
self.as_tlv_stream().write(&mut buffer).unwrap();
490465

491466
use bitcoin::bech32::ToBase32;
492467
let data = buffer.to_base32();

lightning/src/util/ser.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,12 @@ impl Writeable for WithoutLength<String> {
557557
w.write_all(self.0.as_bytes())
558558
}
559559
}
560+
impl Writeable for WithoutLength<&String> {
561+
#[inline]
562+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
563+
w.write_all(self.0.as_bytes())
564+
}
565+
}
560566
impl Readable for WithoutLength<String> {
561567
#[inline]
562568
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
@@ -571,6 +577,12 @@ impl<T: Writeable> Writeable for WithoutLength<Vec<T>> {
571577
VecWriteWrapper(&self.0).write(w)
572578
}
573579
}
580+
impl<T: Writeable> Writeable for WithoutLength<&Vec<T>> {
581+
#[inline]
582+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
583+
VecWriteWrapper(self.0).write(w)
584+
}
585+
}
574586
impl<T: Readable> Readable for WithoutLength<Vec<T>> {
575587
#[inline]
576588
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {

lightning/src/util/ser_macros.rs

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -427,37 +427,94 @@ macro_rules! impl_writeable_tlv_based {
427427
}
428428
}
429429

430-
/// Implements [`Readable`]/[`Writeable`] for TLV streams (i.e., a sequence of TLV records).
431-
///
432-
///
433-
/// This is like [`impl_writeable_tlv_based`] only without a length prefix for the entire TLV
434-
/// stream.
435-
macro_rules! impl_writeable_tlv_stream {
436-
($st: ident, {$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}) => {
437-
impl ::util::ser::Writeable for $st {
438-
fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), $crate::io::Error> {
439-
encode_tlv_stream!(writer, { $(($type, self.$field, $fieldty)),* });
440-
Ok(())
430+
/// Defines a struct for a TLV stream and a similar struct using references for non-primitive types,
431+
/// implementing [`Readable`] for the former and [`Writeable`] for the latter. Useful as an
432+
/// intermediary format when reading or writing a type encoded as a TLV stream.
433+
macro_rules! tlv_stream {
434+
(struct $name:ident { $(($type:expr, $field:ident : $fieldty:ident$(<$gen:ident>)?)),* $(,)*}) => {
435+
#[derive(Debug)]
436+
struct $name {
437+
$(
438+
$field: Option<tlv_record_type!($fieldty$(<$gen>)?)>,
439+
)*
440+
}
441+
442+
mod reference {
443+
use super::*;
444+
445+
pub(super) struct $name<'a> {
446+
$(
447+
pub(super) $field: Option<tlv_record_ref_type!($fieldty$(<$gen>)?)>,
448+
)*
449+
}
450+
451+
impl<'a> ::util::ser::Writeable for $name<'a> {
452+
fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), $crate::io::Error> {
453+
encode_tlv_stream!(writer, { $(($type, self.$field, option)),* });
454+
Ok(())
455+
}
441456
}
442457
}
443458

444-
impl ::util::ser::Readable for $st {
459+
impl ::util::ser::Readable for $name {
445460
fn read<R: $crate::io::Read>(reader: &mut R) -> Result<Self, ::ln::msgs::DecodeError> {
446461
$(
447-
init_tlv_field_var!($field, $fieldty);
462+
init_tlv_field_var!($field, option);
448463
)*
449-
decode_tlv_stream!(reader, {$(($type, $field, $fieldty)),*});
464+
decode_tlv_stream!(reader, {$(($type, $field, option)),*});
450465

451-
Ok(Self {
452-
$(
453-
$field: init_tlv_based_struct_field!($field, $fieldty)
454-
),*
455-
})
466+
Ok(Self {
467+
$(
468+
$field: init_tlv_based_struct_field!($field, option)
469+
),*
470+
})
456471
}
457472
}
458473
}
459474
}
460475

476+
macro_rules! tlv_record_type {
477+
(u16) => {
478+
HighZeroBytesDroppedVarInt<u16>
479+
};
480+
(u32) => {
481+
HighZeroBytesDroppedVarInt<u32>
482+
};
483+
(u64) => {
484+
HighZeroBytesDroppedVarInt<u64>
485+
};
486+
(String) => {
487+
WithoutLength<String>
488+
};
489+
(Vec<$type:ty>) => {
490+
WithoutLength<Vec<$type>>
491+
};
492+
($type:ident$(<$gen:ident>)?) => {
493+
$type$(<$gen>)?
494+
};
495+
}
496+
497+
macro_rules! tlv_record_ref_type {
498+
(u16) => {
499+
HighZeroBytesDroppedVarInt<u16>
500+
};
501+
(u32) => {
502+
HighZeroBytesDroppedVarInt<u32>
503+
};
504+
(u64) => {
505+
HighZeroBytesDroppedVarInt<u64>
506+
};
507+
(String) => {
508+
WithoutLength<&'a String>
509+
};
510+
(Vec<$type:ty>) => {
511+
WithoutLength<&'a Vec<$type>>
512+
};
513+
($type:ident$(<$gen:ident>)?) => {
514+
&'a $type$(<$gen>)?
515+
};
516+
}
517+
461518
macro_rules! _impl_writeable_tlv_based_enum_common {
462519
($st: ident, $(($variant_id: expr, $variant_name: ident) =>
463520
{$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}

0 commit comments

Comments
 (0)