Skip to content

Commit 1d5f757

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 98ef5cd commit 1d5f757

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
@@ -506,7 +506,9 @@ impl UnsignedBolt12Invoice {
506506
record.write(&mut bytes).unwrap();
507507
}
508508

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

512514
let mut experimental_bytes = Vec::new();
@@ -515,6 +517,8 @@ impl UnsignedBolt12Invoice {
515517
record.write(&mut experimental_bytes).unwrap();
516518
}
517519

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

@@ -872,14 +876,15 @@ impl Bolt12Invoice {
872876
let (
873877
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
874878
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
879+
experimental_invoice_tlv_stream,
875880
) = self.contents.as_tlv_stream();
876881
let signature_tlv_stream = SignatureTlvStreamRef {
877882
signature: Some(&self.signature),
878883
};
879884
(
880885
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
881886
signature_tlv_stream, experimental_offer_tlv_stream,
882-
experimental_invoice_request_tlv_stream,
887+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
883888
)
884889
}
885890

@@ -1140,9 +1145,12 @@ impl InvoiceContents {
11401145
InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.as_tlv_stream(),
11411146
InvoiceContents::ForRefund { refund, .. } => refund.as_tlv_stream(),
11421147
};
1143-
let invoice = self.fields().as_tlv_stream();
1148+
let (invoice, experimental_invoice) = self.fields().as_tlv_stream();
11441149

1145-
(payer, offer, invoice_request, invoice, experimental_offer, experimental_invoice_request)
1150+
(
1151+
payer, offer, invoice_request, invoice, experimental_offer,
1152+
experimental_invoice_request, experimental_invoice,
1153+
)
11461154
}
11471155
}
11481156

@@ -1190,24 +1198,27 @@ pub(super) fn filter_fallbacks(
11901198
}
11911199

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

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

@@ -1245,11 +1256,13 @@ impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
12451256
let (
12461257
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
12471258
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1259+
experimental_invoice_tlv_stream,
12481260
) = tlv_stream;
12491261
let contents = InvoiceContents::try_from(
12501262
(
12511263
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
12521264
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1265+
experimental_invoice_tlv_stream,
12531266
)
12541267
)?;
12551268

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

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

13141334
type FullInvoiceTlvStream =(
13151335
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream,
1316-
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
1336+
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream,
13171337
);
13181338

13191339
type FullInvoiceTlvStreamRef<'a> = (
@@ -1324,6 +1344,7 @@ type FullInvoiceTlvStreamRef<'a> = (
13241344
SignatureTlvStreamRef<'a>,
13251345
ExperimentalOfferTlvStreamRef,
13261346
ExperimentalInvoiceRequestTlvStreamRef,
1347+
ExperimentalInvoiceTlvStreamRef,
13271348
);
13281349

13291350
impl CursorReadable for FullInvoiceTlvStream {
@@ -1335,19 +1356,20 @@ impl CursorReadable for FullInvoiceTlvStream {
13351356
let signature = CursorReadable::read(r)?;
13361357
let experimental_offer = CursorReadable::read(r)?;
13371358
let experimental_invoice_request = CursorReadable::read(r)?;
1359+
let experimental_invoice = CursorReadable::read(r)?;
13381360

13391361
Ok(
13401362
(
13411363
payer, offer, invoice_request, invoice, signature, experimental_offer,
1342-
experimental_invoice_request,
1364+
experimental_invoice_request, experimental_invoice,
13431365
)
13441366
)
13451367
}
13461368
}
13471369

13481370
type PartialInvoiceTlvStream = (
13491371
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream,
1350-
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
1372+
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream,
13511373
);
13521374

13531375
type PartialInvoiceTlvStreamRef<'a> = (
@@ -1357,6 +1379,7 @@ type PartialInvoiceTlvStreamRef<'a> = (
13571379
InvoiceTlvStreamRef<'a>,
13581380
ExperimentalOfferTlvStreamRef,
13591381
ExperimentalInvoiceRequestTlvStreamRef,
1382+
ExperimentalInvoiceTlvStreamRef,
13601383
);
13611384

13621385
impl CursorReadable for PartialInvoiceTlvStream {
@@ -1367,11 +1390,12 @@ impl CursorReadable for PartialInvoiceTlvStream {
13671390
let invoice = CursorReadable::read(r)?;
13681391
let experimental_offer = CursorReadable::read(r)?;
13691392
let experimental_invoice_request = CursorReadable::read(r)?;
1393+
let experimental_invoice = CursorReadable::read(r)?;
13701394

13711395
Ok(
13721396
(
13731397
payer, offer, invoice_request, invoice, experimental_offer,
1374-
experimental_invoice_request,
1398+
experimental_invoice_request, experimental_invoice,
13751399
)
13761400
)
13771401
}
@@ -1387,11 +1411,13 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
13871411
SignatureTlvStream { signature },
13881412
experimental_offer_tlv_stream,
13891413
experimental_invoice_request_tlv_stream,
1414+
experimental_invoice_tlv_stream,
13901415
) = tlv_stream;
13911416
let contents = InvoiceContents::try_from(
13921417
(
13931418
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
13941419
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1420+
experimental_invoice_tlv_stream,
13951421
)
13961422
)?;
13971423

@@ -1420,6 +1446,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14201446
},
14211447
experimental_offer_tlv_stream,
14221448
experimental_invoice_request_tlv_stream,
1449+
ExperimentalInvoiceTlvStream {},
14231450
) = tlv_stream;
14241451

14251452
if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) }
@@ -1516,7 +1543,7 @@ pub(super) fn check_invoice_signing_pubkey(
15161543

15171544
#[cfg(test)]
15181545
mod tests {
1519-
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
1546+
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
15201547

15211548
use bitcoin::{CompressedPublicKey, WitnessProgram, WitnessVersion};
15221549
use bitcoin::constants::ChainHash;
@@ -1712,6 +1739,7 @@ mod tests {
17121739
ExperimentalInvoiceRequestTlvStreamRef {
17131740
experimental_bar: None,
17141741
},
1742+
ExperimentalInvoiceTlvStreamRef {},
17151743
),
17161744
);
17171745

@@ -1811,6 +1839,7 @@ mod tests {
18111839
ExperimentalInvoiceRequestTlvStreamRef {
18121840
experimental_bar: None,
18131841
},
1842+
ExperimentalInvoiceTlvStreamRef {},
18141843
),
18151844
);
18161845

@@ -2007,7 +2036,7 @@ mod tests {
20072036
.relative_expiry(one_hour.as_secs() as u32)
20082037
.build().unwrap()
20092038
.sign(recipient_sign).unwrap();
2010-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2039+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20112040
#[cfg(feature = "std")]
20122041
assert!(!invoice.is_expired());
20132042
assert_eq!(invoice.relative_expiry(), one_hour);
@@ -2023,7 +2052,7 @@ mod tests {
20232052
.relative_expiry(one_hour.as_secs() as u32 - 1)
20242053
.build().unwrap()
20252054
.sign(recipient_sign).unwrap();
2026-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2055+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20272056
#[cfg(feature = "std")]
20282057
assert!(invoice.is_expired());
20292058
assert_eq!(invoice.relative_expiry(), one_hour - Duration::from_secs(1));
@@ -2042,7 +2071,7 @@ mod tests {
20422071
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
20432072
.build().unwrap()
20442073
.sign(recipient_sign).unwrap();
2045-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2074+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20462075
assert_eq!(invoice.amount_msats(), 1001);
20472076
assert_eq!(tlv_stream.amount, Some(1001));
20482077
}
@@ -2060,7 +2089,7 @@ mod tests {
20602089
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
20612090
.build().unwrap()
20622091
.sign(recipient_sign).unwrap();
2063-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2092+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20642093
assert_eq!(invoice.amount_msats(), 2000);
20652094
assert_eq!(tlv_stream.amount, Some(2000));
20662095

@@ -2098,7 +2127,7 @@ mod tests {
20982127
.fallback_v1_p2tr_tweaked(&tweaked_pubkey)
20992128
.build().unwrap()
21002129
.sign(recipient_sign).unwrap();
2101-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2130+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
21022131
assert_eq!(
21032132
invoice.fallbacks(),
21042133
vec![
@@ -2141,7 +2170,7 @@ mod tests {
21412170
.allow_mpp()
21422171
.build().unwrap()
21432172
.sign(recipient_sign).unwrap();
2144-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2173+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
21452174
assert_eq!(invoice.invoice_features(), &features);
21462175
assert_eq!(tlv_stream.features, Some(&features));
21472176
}

lightning/src/offers/invoice_request.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,7 +1538,7 @@ mod tests {
15381538
let (
15391539
payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
15401540
mut invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream,
1541-
experimental_invoice_request_tlv_stream,
1541+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
15421542
) = invoice.as_tlv_stream();
15431543
invoice_request_tlv_stream.amount = Some(2000);
15441544
invoice_tlv_stream.amount = Some(2000);
@@ -1547,6 +1547,7 @@ mod tests {
15471547
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
15481548
let experimental_tlv_stream = (
15491549
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1550+
experimental_invoice_tlv_stream,
15501551
);
15511552
let mut bytes = Vec::new();
15521553
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1567,7 +1568,7 @@ mod tests {
15671568
let (
15681569
mut payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
15691570
mut signature_tlv_stream, experimental_offer_tlv_stream,
1570-
experimental_invoice_request_tlv_stream,
1571+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
15711572
) = invoice.as_tlv_stream();
15721573
let metadata = payer_tlv_stream.metadata.unwrap().iter().copied().rev().collect();
15731574
payer_tlv_stream.metadata = Some(&metadata);
@@ -1576,6 +1577,7 @@ mod tests {
15761577
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
15771578
let experimental_tlv_stream = (
15781579
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1580+
experimental_invoice_tlv_stream,
15791581
);
15801582
let mut bytes = Vec::new();
15811583
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1625,7 +1627,7 @@ mod tests {
16251627
let (
16261628
payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
16271629
mut invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream,
1628-
experimental_invoice_request_tlv_stream,
1630+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
16291631
) = invoice.as_tlv_stream();
16301632
invoice_request_tlv_stream.amount = Some(2000);
16311633
invoice_tlv_stream.amount = Some(2000);
@@ -1634,6 +1636,7 @@ mod tests {
16341636
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
16351637
let experimental_tlv_stream = (
16361638
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1639+
experimental_invoice_tlv_stream,
16371640
);
16381641
let mut bytes = Vec::new();
16391642
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1656,7 +1659,7 @@ mod tests {
16561659
let (
16571660
payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream, invoice_tlv_stream,
16581661
mut signature_tlv_stream, experimental_offer_tlv_stream,
1659-
experimental_invoice_request_tlv_stream,
1662+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
16601663
) = invoice.as_tlv_stream();
16611664
let payer_id = pubkey(1);
16621665
invoice_request_tlv_stream.payer_id = Some(&payer_id);
@@ -1665,6 +1668,7 @@ mod tests {
16651668
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
16661669
let experimental_tlv_stream = (
16671670
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1671+
experimental_invoice_tlv_stream,
16681672
);
16691673
let mut bytes = Vec::new();
16701674
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();

0 commit comments

Comments
 (0)