Skip to content

Commit 575433d

Browse files
committed
Parse experimental invoice TLV records
The BOLT12 spec defines an experimental TLV range that is allowed in offer and invoice_request messages. The remaining TLV-space is for experimental use in invoice messages. Allow this range when parsing an invoice and include it when signing one.
1 parent ad638a5 commit 575433d

File tree

4 files changed

+129
-44
lines changed

4 files changed

+129
-44
lines changed

lightning/src/offers/invoice.rs

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,9 @@ impl UnsignedBolt12Invoice {
505505
record.write(&mut bytes).unwrap();
506506
}
507507

508-
let (_, _, _, invoice_tlv_stream, _, _) = contents.as_tlv_stream();
508+
let (_, _, _, invoice_tlv_stream, _, _, experimental_invoice_tlv_stream) =
509+
contents.as_tlv_stream();
510+
509511
invoice_tlv_stream.write(&mut bytes).unwrap();
510512

511513
let mut experimental_bytes = Vec::new();
@@ -514,6 +516,8 @@ impl UnsignedBolt12Invoice {
514516
record.write(&mut experimental_bytes).unwrap();
515517
}
516518

519+
experimental_invoice_tlv_stream.write(&mut experimental_bytes).unwrap();
520+
517521
let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes));
518522
let tagged_hash = TaggedHash::from_tlv_stream(SIGNATURE_TAG, tlv_stream);
519523

@@ -871,14 +875,15 @@ impl Bolt12Invoice {
871875
let (
872876
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
873877
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
878+
experimental_invoice_tlv_stream,
874879
) = self.contents.as_tlv_stream();
875880
let signature_tlv_stream = SignatureTlvStreamRef {
876881
signature: Some(&self.signature),
877882
};
878883
(
879884
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
880885
signature_tlv_stream, experimental_offer_tlv_stream,
881-
experimental_invoice_request_tlv_stream,
886+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
882887
)
883888
}
884889

@@ -1139,9 +1144,12 @@ impl InvoiceContents {
11391144
InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.as_tlv_stream(),
11401145
InvoiceContents::ForRefund { refund, .. } => refund.as_tlv_stream(),
11411146
};
1142-
let invoice = self.fields().as_tlv_stream();
1147+
let (invoice, experimental_invoice) = self.fields().as_tlv_stream();
11431148

1144-
(payer, offer, invoice_request, invoice, experimental_offer, experimental_invoice_request)
1149+
(
1150+
payer, offer, invoice_request, invoice, experimental_offer,
1151+
experimental_invoice_request, experimental_invoice,
1152+
)
11451153
}
11461154
}
11471155

@@ -1189,24 +1197,27 @@ pub(super) fn filter_fallbacks(
11891197
}
11901198

11911199
impl InvoiceFields {
1192-
fn as_tlv_stream(&self) -> InvoiceTlvStreamRef {
1200+
fn as_tlv_stream(&self) -> (InvoiceTlvStreamRef, ExperimentalInvoiceTlvStreamRef) {
11931201
let features = {
11941202
if self.features == Bolt12InvoiceFeatures::empty() { None }
11951203
else { Some(&self.features) }
11961204
};
11971205

1198-
InvoiceTlvStreamRef {
1199-
paths: Some(Iterable(self.payment_paths.iter().map(|path| path.inner_blinded_path()))),
1200-
blindedpay: Some(Iterable(self.payment_paths.iter().map(|path| &path.payinfo))),
1201-
created_at: Some(self.created_at.as_secs()),
1202-
relative_expiry: self.relative_expiry.map(|duration| duration.as_secs() as u32),
1203-
payment_hash: Some(&self.payment_hash),
1204-
amount: Some(self.amount_msats),
1205-
fallbacks: self.fallbacks.as_ref(),
1206-
features,
1207-
node_id: Some(&self.signing_pubkey),
1208-
message_paths: None,
1209-
}
1206+
(
1207+
InvoiceTlvStreamRef {
1208+
paths: Some(Iterable(self.payment_paths.iter().map(|path| path.inner_blinded_path()))),
1209+
blindedpay: Some(Iterable(self.payment_paths.iter().map(|path| &path.payinfo))),
1210+
created_at: Some(self.created_at.as_secs()),
1211+
relative_expiry: self.relative_expiry.map(|duration| duration.as_secs() as u32),
1212+
payment_hash: Some(&self.payment_hash),
1213+
amount: Some(self.amount_msats),
1214+
fallbacks: self.fallbacks.as_ref(),
1215+
features,
1216+
node_id: Some(&self.signing_pubkey),
1217+
message_paths: None,
1218+
},
1219+
ExperimentalInvoiceTlvStreamRef {},
1220+
)
12101221
}
12111222
}
12121223

@@ -1244,11 +1255,13 @@ impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
12441255
let (
12451256
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
12461257
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1258+
experimental_invoice_tlv_stream,
12471259
) = tlv_stream;
12481260
let contents = InvoiceContents::try_from(
12491261
(
12501262
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
12511263
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1264+
experimental_invoice_tlv_stream,
12521265
)
12531266
)?;
12541267

@@ -1291,6 +1304,13 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
12911304
(236, message_paths: (Vec<BlindedMessagePath>, WithoutLength)),
12921305
});
12931306

1307+
/// Valid type range for experimental invoice TLV records.
1308+
const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
1309+
1310+
tlv_stream!(
1311+
ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, EXPERIMENTAL_INVOICE_TYPES, {}
1312+
);
1313+
12941314
pub(super) type BlindedPathIter<'a> = core::iter::Map<
12951315
core::slice::Iter<'a, BlindedPaymentPath>,
12961316
for<'r> fn(&'r BlindedPaymentPath) -> &'r BlindedPath,
@@ -1312,7 +1332,7 @@ impl_writeable!(FallbackAddress, { version, program });
13121332

13131333
type FullInvoiceTlvStream =(
13141334
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream,
1315-
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
1335+
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream,
13161336
);
13171337

13181338
type FullInvoiceTlvStreamRef<'a> = (
@@ -1323,6 +1343,7 @@ type FullInvoiceTlvStreamRef<'a> = (
13231343
SignatureTlvStreamRef<'a>,
13241344
ExperimentalOfferTlvStreamRef,
13251345
ExperimentalInvoiceRequestTlvStreamRef,
1346+
ExperimentalInvoiceTlvStreamRef,
13261347
);
13271348

13281349
impl CursorReadable for FullInvoiceTlvStream {
@@ -1334,19 +1355,20 @@ impl CursorReadable for FullInvoiceTlvStream {
13341355
let signature = CursorReadable::read(r)?;
13351356
let experimental_offer = CursorReadable::read(r)?;
13361357
let experimental_invoice_request = CursorReadable::read(r)?;
1358+
let experimental_invoice = CursorReadable::read(r)?;
13371359

13381360
Ok(
13391361
(
13401362
payer, offer, invoice_request, invoice, signature, experimental_offer,
1341-
experimental_invoice_request,
1363+
experimental_invoice_request, experimental_invoice,
13421364
)
13431365
)
13441366
}
13451367
}
13461368

13471369
type PartialInvoiceTlvStream = (
13481370
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream,
1349-
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
1371+
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream,
13501372
);
13511373

13521374
type PartialInvoiceTlvStreamRef<'a> = (
@@ -1356,6 +1378,7 @@ type PartialInvoiceTlvStreamRef<'a> = (
13561378
InvoiceTlvStreamRef<'a>,
13571379
ExperimentalOfferTlvStreamRef,
13581380
ExperimentalInvoiceRequestTlvStreamRef,
1381+
ExperimentalInvoiceTlvStreamRef,
13591382
);
13601383

13611384
impl CursorReadable for PartialInvoiceTlvStream {
@@ -1366,11 +1389,12 @@ impl CursorReadable for PartialInvoiceTlvStream {
13661389
let invoice = CursorReadable::read(r)?;
13671390
let experimental_offer = CursorReadable::read(r)?;
13681391
let experimental_invoice_request = CursorReadable::read(r)?;
1392+
let experimental_invoice = CursorReadable::read(r)?;
13691393

13701394
Ok(
13711395
(
13721396
payer, offer, invoice_request, invoice, experimental_offer,
1373-
experimental_invoice_request,
1397+
experimental_invoice_request, experimental_invoice,
13741398
)
13751399
)
13761400
}
@@ -1386,11 +1410,13 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
13861410
SignatureTlvStream { signature },
13871411
experimental_offer_tlv_stream,
13881412
experimental_invoice_request_tlv_stream,
1413+
experimental_invoice_tlv_stream,
13891414
) = tlv_stream;
13901415
let contents = InvoiceContents::try_from(
13911416
(
13921417
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
13931418
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1419+
experimental_invoice_tlv_stream,
13941420
)
13951421
)?;
13961422

@@ -1419,6 +1445,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14191445
},
14201446
experimental_offer_tlv_stream,
14211447
experimental_invoice_request_tlv_stream,
1448+
ExperimentalInvoiceTlvStream {},
14221449
) = tlv_stream;
14231450

14241451
if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) }
@@ -1515,7 +1542,7 @@ pub(super) fn check_invoice_signing_pubkey(
15151542

15161543
#[cfg(test)]
15171544
mod tests {
1518-
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
1545+
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
15191546

15201547
use bitcoin::{CompressedPublicKey, WitnessProgram, WitnessVersion};
15211548
use bitcoin::constants::ChainHash;
@@ -1711,6 +1738,7 @@ mod tests {
17111738
ExperimentalInvoiceRequestTlvStreamRef {
17121739
experimental_bar: None,
17131740
},
1741+
ExperimentalInvoiceTlvStreamRef {},
17141742
),
17151743
);
17161744

@@ -1810,6 +1838,7 @@ mod tests {
18101838
ExperimentalInvoiceRequestTlvStreamRef {
18111839
experimental_bar: None,
18121840
},
1841+
ExperimentalInvoiceTlvStreamRef {},
18131842
),
18141843
);
18151844

@@ -2006,7 +2035,7 @@ mod tests {
20062035
.relative_expiry(one_hour.as_secs() as u32)
20072036
.build().unwrap()
20082037
.sign(recipient_sign).unwrap();
2009-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2038+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20102039
#[cfg(feature = "std")]
20112040
assert!(!invoice.is_expired());
20122041
assert_eq!(invoice.relative_expiry(), one_hour);
@@ -2022,7 +2051,7 @@ mod tests {
20222051
.relative_expiry(one_hour.as_secs() as u32 - 1)
20232052
.build().unwrap()
20242053
.sign(recipient_sign).unwrap();
2025-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2054+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20262055
#[cfg(feature = "std")]
20272056
assert!(invoice.is_expired());
20282057
assert_eq!(invoice.relative_expiry(), one_hour - Duration::from_secs(1));
@@ -2041,7 +2070,7 @@ mod tests {
20412070
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
20422071
.build().unwrap()
20432072
.sign(recipient_sign).unwrap();
2044-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2073+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20452074
assert_eq!(invoice.amount_msats(), 1001);
20462075
assert_eq!(tlv_stream.amount, Some(1001));
20472076
}
@@ -2059,7 +2088,7 @@ mod tests {
20592088
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
20602089
.build().unwrap()
20612090
.sign(recipient_sign).unwrap();
2062-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2091+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20632092
assert_eq!(invoice.amount_msats(), 2000);
20642093
assert_eq!(tlv_stream.amount, Some(2000));
20652094

@@ -2097,7 +2126,7 @@ mod tests {
20972126
.fallback_v1_p2tr_tweaked(&tweaked_pubkey)
20982127
.build().unwrap()
20992128
.sign(recipient_sign).unwrap();
2100-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2129+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
21012130
assert_eq!(
21022131
invoice.fallbacks(),
21032132
vec![
@@ -2140,7 +2169,7 @@ mod tests {
21402169
.allow_mpp()
21412170
.build().unwrap()
21422171
.sign(recipient_sign).unwrap();
2143-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2172+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
21442173
assert_eq!(invoice.invoice_features(), &features);
21452174
assert_eq!(tlv_stream.features, Some(&features));
21462175
}

lightning/src/offers/invoice_request.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,7 +1539,7 @@ mod tests {
15391539
let (
15401540
payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
15411541
mut invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream,
1542-
experimental_invoice_request_tlv_stream,
1542+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
15431543
) = invoice.as_tlv_stream();
15441544
invoice_request_tlv_stream.amount = Some(2000);
15451545
invoice_tlv_stream.amount = Some(2000);
@@ -1548,6 +1548,7 @@ mod tests {
15481548
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
15491549
let experimental_tlv_stream = (
15501550
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1551+
experimental_invoice_tlv_stream,
15511552
);
15521553
let mut bytes = Vec::new();
15531554
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1568,7 +1569,7 @@ mod tests {
15681569
let (
15691570
mut payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
15701571
mut signature_tlv_stream, experimental_offer_tlv_stream,
1571-
experimental_invoice_request_tlv_stream,
1572+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
15721573
) = invoice.as_tlv_stream();
15731574
let metadata = payer_tlv_stream.metadata.unwrap().iter().copied().rev().collect();
15741575
payer_tlv_stream.metadata = Some(&metadata);
@@ -1577,6 +1578,7 @@ mod tests {
15771578
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
15781579
let experimental_tlv_stream = (
15791580
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1581+
experimental_invoice_tlv_stream,
15801582
);
15811583
let mut bytes = Vec::new();
15821584
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1626,7 +1628,7 @@ mod tests {
16261628
let (
16271629
payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
16281630
mut invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream,
1629-
experimental_invoice_request_tlv_stream,
1631+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
16301632
) = invoice.as_tlv_stream();
16311633
invoice_request_tlv_stream.amount = Some(2000);
16321634
invoice_tlv_stream.amount = Some(2000);
@@ -1635,6 +1637,7 @@ mod tests {
16351637
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
16361638
let experimental_tlv_stream = (
16371639
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1640+
experimental_invoice_tlv_stream,
16381641
);
16391642
let mut bytes = Vec::new();
16401643
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1657,7 +1660,7 @@ mod tests {
16571660
let (
16581661
payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream, invoice_tlv_stream,
16591662
mut signature_tlv_stream, experimental_offer_tlv_stream,
1660-
experimental_invoice_request_tlv_stream,
1663+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
16611664
) = invoice.as_tlv_stream();
16621665
let payer_id = pubkey(1);
16631666
invoice_request_tlv_stream.payer_id = Some(&payer_id);
@@ -1666,6 +1669,7 @@ mod tests {
16661669
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
16671670
let experimental_tlv_stream = (
16681671
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1672+
experimental_invoice_tlv_stream,
16691673
);
16701674
let mut bytes = Vec::new();
16711675
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();

0 commit comments

Comments
 (0)