Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 03e4a96

Browse files
committedAug 14, 2024
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 c986aa0 commit 03e4a96

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
@@ -496,7 +496,9 @@ impl UnsignedBolt12Invoice {
496496
record.write(&mut bytes).unwrap();
497497
}
498498

499-
let (_, _, _, invoice_tlv_stream, _, _) = contents.as_tlv_stream();
499+
let (_, _, _, invoice_tlv_stream, _, _, experimental_invoice_tlv_stream) =
500+
contents.as_tlv_stream();
501+
500502
invoice_tlv_stream.write(&mut bytes).unwrap();
501503

502504
let mut experimental_bytes = Vec::new();
@@ -505,6 +507,8 @@ impl UnsignedBolt12Invoice {
505507
record.write(&mut experimental_bytes).unwrap();
506508
}
507509

510+
experimental_invoice_tlv_stream.write(&mut experimental_bytes).unwrap();
511+
508512
let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes));
509513
let tagged_hash = TaggedHash::from_tlv_stream(SIGNATURE_TAG, tlv_stream);
510514

@@ -862,14 +866,15 @@ impl Bolt12Invoice {
862866
let (
863867
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
864868
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
869+
experimental_invoice_tlv_stream,
865870
) = self.contents.as_tlv_stream();
866871
let signature_tlv_stream = SignatureTlvStreamRef {
867872
signature: Some(&self.signature),
868873
};
869874
(
870875
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
871876
signature_tlv_stream, experimental_offer_tlv_stream,
872-
experimental_invoice_request_tlv_stream,
877+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
873878
)
874879
}
875880

@@ -1130,9 +1135,12 @@ impl InvoiceContents {
11301135
InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.as_tlv_stream(),
11311136
InvoiceContents::ForRefund { refund, .. } => refund.as_tlv_stream(),
11321137
};
1133-
let invoice = self.fields().as_tlv_stream();
1138+
let (invoice, experimental_invoice) = self.fields().as_tlv_stream();
11341139

1135-
(payer, offer, invoice_request, invoice, experimental_offer, experimental_invoice_request)
1140+
(
1141+
payer, offer, invoice_request, invoice, experimental_offer,
1142+
experimental_invoice_request, experimental_invoice,
1143+
)
11361144
}
11371145
}
11381146

@@ -1181,24 +1189,27 @@ pub(super) fn filter_fallbacks(
11811189
}
11821190

11831191
impl InvoiceFields {
1184-
fn as_tlv_stream(&self) -> InvoiceTlvStreamRef {
1192+
fn as_tlv_stream(&self) -> (InvoiceTlvStreamRef, ExperimentalInvoiceTlvStreamRef) {
11851193
let features = {
11861194
if self.features == Bolt12InvoiceFeatures::empty() { None }
11871195
else { Some(&self.features) }
11881196
};
11891197

1190-
InvoiceTlvStreamRef {
1191-
paths: Some(Iterable(self.payment_paths.iter().map(|(_, path)| path))),
1192-
blindedpay: Some(Iterable(self.payment_paths.iter().map(|(payinfo, _)| payinfo))),
1193-
created_at: Some(self.created_at.as_secs()),
1194-
relative_expiry: self.relative_expiry.map(|duration| duration.as_secs() as u32),
1195-
payment_hash: Some(&self.payment_hash),
1196-
amount: Some(self.amount_msats),
1197-
fallbacks: self.fallbacks.as_ref(),
1198-
features,
1199-
node_id: Some(&self.signing_pubkey),
1200-
message_paths: None,
1201-
}
1198+
(
1199+
InvoiceTlvStreamRef {
1200+
paths: Some(Iterable(self.payment_paths.iter().map(|(_, path)| path))),
1201+
blindedpay: Some(Iterable(self.payment_paths.iter().map(|(payinfo, _)| 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+
},
1211+
ExperimentalInvoiceTlvStreamRef {},
1212+
)
12021213
}
12031214
}
12041215

@@ -1236,11 +1247,13 @@ impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
12361247
let (
12371248
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
12381249
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1250+
experimental_invoice_tlv_stream,
12391251
) = tlv_stream;
12401252
let contents = InvoiceContents::try_from(
12411253
(
12421254
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
12431255
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1256+
experimental_invoice_tlv_stream,
12441257
)
12451258
)?;
12461259

@@ -1283,6 +1296,13 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
12831296
(236, message_paths: (Vec<BlindedPath>, WithoutLength)),
12841297
});
12851298

1299+
/// Valid type range for experimental invoice TLV records.
1300+
const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
1301+
1302+
tlv_stream!(
1303+
ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, EXPERIMENTAL_INVOICE_TYPES, {}
1304+
);
1305+
12861306
pub(super) type BlindedPathIter<'a> = core::iter::Map<
12871307
core::slice::Iter<'a, (BlindedPayInfo, BlindedPath)>,
12881308
for<'r> fn(&'r (BlindedPayInfo, BlindedPath)) -> &'r BlindedPath,
@@ -1342,7 +1362,7 @@ impl_writeable!(FallbackAddress, { version, program });
13421362

13431363
type FullInvoiceTlvStream =(
13441364
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream,
1345-
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
1365+
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream,
13461366
);
13471367

13481368
type FullInvoiceTlvStreamRef<'a> = (
@@ -1353,6 +1373,7 @@ type FullInvoiceTlvStreamRef<'a> = (
13531373
SignatureTlvStreamRef<'a>,
13541374
ExperimentalOfferTlvStreamRef,
13551375
ExperimentalInvoiceRequestTlvStreamRef,
1376+
ExperimentalInvoiceTlvStreamRef,
13561377
);
13571378

13581379
impl SeekReadable for FullInvoiceTlvStream {
@@ -1364,19 +1385,20 @@ impl SeekReadable for FullInvoiceTlvStream {
13641385
let signature = SeekReadable::read(r)?;
13651386
let experimental_offer = SeekReadable::read(r)?;
13661387
let experimental_invoice_request = SeekReadable::read(r)?;
1388+
let experimental_invoice = SeekReadable::read(r)?;
13671389

13681390
Ok(
13691391
(
13701392
payer, offer, invoice_request, invoice, signature, experimental_offer,
1371-
experimental_invoice_request,
1393+
experimental_invoice_request, experimental_invoice,
13721394
)
13731395
)
13741396
}
13751397
}
13761398

13771399
type PartialInvoiceTlvStream = (
13781400
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream,
1379-
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
1401+
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream,
13801402
);
13811403

13821404
type PartialInvoiceTlvStreamRef<'a> = (
@@ -1386,6 +1408,7 @@ type PartialInvoiceTlvStreamRef<'a> = (
13861408
InvoiceTlvStreamRef<'a>,
13871409
ExperimentalOfferTlvStreamRef,
13881410
ExperimentalInvoiceRequestTlvStreamRef,
1411+
ExperimentalInvoiceTlvStreamRef,
13891412
);
13901413

13911414
impl SeekReadable for PartialInvoiceTlvStream {
@@ -1396,11 +1419,12 @@ impl SeekReadable for PartialInvoiceTlvStream {
13961419
let invoice = SeekReadable::read(r)?;
13971420
let experimental_offer = SeekReadable::read(r)?;
13981421
let experimental_invoice_request = SeekReadable::read(r)?;
1422+
let experimental_invoice= SeekReadable::read(r)?;
13991423

14001424
Ok(
14011425
(
14021426
payer, offer, invoice_request, invoice, experimental_offer,
1403-
experimental_invoice_request,
1427+
experimental_invoice_request, experimental_invoice,
14041428
)
14051429
)
14061430
}
@@ -1416,11 +1440,13 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
14161440
SignatureTlvStream { signature },
14171441
experimental_offer_tlv_stream,
14181442
experimental_invoice_request_tlv_stream,
1443+
experimental_invoice_tlv_stream,
14191444
) = tlv_stream;
14201445
let contents = InvoiceContents::try_from(
14211446
(
14221447
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
14231448
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1449+
experimental_invoice_tlv_stream,
14241450
)
14251451
)?;
14261452

@@ -1449,6 +1475,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14491475
},
14501476
experimental_offer_tlv_stream,
14511477
experimental_invoice_request_tlv_stream,
1478+
ExperimentalInvoiceTlvStream {},
14521479
) = tlv_stream;
14531480

14541481
if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) }
@@ -1540,7 +1567,7 @@ pub(super) fn check_invoice_signing_pubkey(
15401567

15411568
#[cfg(test)]
15421569
mod tests {
1543-
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
1570+
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
15441571

15451572
use bitcoin::{WitnessProgram, WitnessVersion};
15461573
use bitcoin::blockdata::constants::ChainHash;
@@ -1735,6 +1762,7 @@ mod tests {
17351762
ExperimentalInvoiceRequestTlvStreamRef {
17361763
experimental_bar: None,
17371764
},
1765+
ExperimentalInvoiceTlvStreamRef {},
17381766
),
17391767
);
17401768

@@ -1834,6 +1862,7 @@ mod tests {
18341862
ExperimentalInvoiceRequestTlvStreamRef {
18351863
experimental_bar: None,
18361864
},
1865+
ExperimentalInvoiceTlvStreamRef {},
18371866
),
18381867
);
18391868

@@ -2032,7 +2061,7 @@ mod tests {
20322061
.relative_expiry(one_hour.as_secs() as u32)
20332062
.build().unwrap()
20342063
.sign(recipient_sign).unwrap();
2035-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2064+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20362065
#[cfg(feature = "std")]
20372066
assert!(!invoice.is_expired());
20382067
assert_eq!(invoice.relative_expiry(), one_hour);
@@ -2048,7 +2077,7 @@ mod tests {
20482077
.relative_expiry(one_hour.as_secs() as u32 - 1)
20492078
.build().unwrap()
20502079
.sign(recipient_sign).unwrap();
2051-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2080+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20522081
#[cfg(feature = "std")]
20532082
assert!(invoice.is_expired());
20542083
assert_eq!(invoice.relative_expiry(), one_hour - Duration::from_secs(1));
@@ -2067,7 +2096,7 @@ mod tests {
20672096
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
20682097
.build().unwrap()
20692098
.sign(recipient_sign).unwrap();
2070-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2099+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20712100
assert_eq!(invoice.amount_msats(), 1001);
20722101
assert_eq!(tlv_stream.amount, Some(1001));
20732102
}
@@ -2085,7 +2114,7 @@ mod tests {
20852114
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
20862115
.build().unwrap()
20872116
.sign(recipient_sign).unwrap();
2088-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2117+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
20892118
assert_eq!(invoice.amount_msats(), 2000);
20902119
assert_eq!(tlv_stream.amount, Some(2000));
20912120

@@ -2123,7 +2152,7 @@ mod tests {
21232152
.fallback_v1_p2tr_tweaked(&tweaked_pubkey)
21242153
.build().unwrap()
21252154
.sign(recipient_sign).unwrap();
2126-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2155+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
21272156
assert_eq!(
21282157
invoice.fallbacks(),
21292158
vec![
@@ -2166,7 +2195,7 @@ mod tests {
21662195
.allow_mpp()
21672196
.build().unwrap()
21682197
.sign(recipient_sign).unwrap();
2169-
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
2198+
let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream();
21702199
assert_eq!(invoice.invoice_features(), &features);
21712200
assert_eq!(tlv_stream.features, Some(&features));
21722201
}

‎lightning/src/offers/invoice_request.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,7 +1527,7 @@ mod tests {
15271527
let (
15281528
payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
15291529
mut invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream,
1530-
experimental_invoice_request_tlv_stream,
1530+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
15311531
) = invoice.as_tlv_stream();
15321532
invoice_request_tlv_stream.amount = Some(2000);
15331533
invoice_tlv_stream.amount = Some(2000);
@@ -1536,6 +1536,7 @@ mod tests {
15361536
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
15371537
let experimental_tlv_stream = (
15381538
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1539+
experimental_invoice_tlv_stream,
15391540
);
15401541
let mut bytes = Vec::new();
15411542
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1556,7 +1557,7 @@ mod tests {
15561557
let (
15571558
mut payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
15581559
mut signature_tlv_stream, experimental_offer_tlv_stream,
1559-
experimental_invoice_request_tlv_stream,
1560+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
15601561
) = invoice.as_tlv_stream();
15611562
let metadata = payer_tlv_stream.metadata.unwrap().iter().copied().rev().collect();
15621563
payer_tlv_stream.metadata = Some(&metadata);
@@ -1565,6 +1566,7 @@ mod tests {
15651566
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
15661567
let experimental_tlv_stream = (
15671568
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1569+
experimental_invoice_tlv_stream,
15681570
);
15691571
let mut bytes = Vec::new();
15701572
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1614,7 +1616,7 @@ mod tests {
16141616
let (
16151617
payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
16161618
mut invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream,
1617-
experimental_invoice_request_tlv_stream,
1619+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
16181620
) = invoice.as_tlv_stream();
16191621
invoice_request_tlv_stream.amount = Some(2000);
16201622
invoice_tlv_stream.amount = Some(2000);
@@ -1623,6 +1625,7 @@ mod tests {
16231625
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
16241626
let experimental_tlv_stream = (
16251627
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1628+
experimental_invoice_tlv_stream,
16261629
);
16271630
let mut bytes = Vec::new();
16281631
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1645,7 +1648,7 @@ mod tests {
16451648
let (
16461649
payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream, invoice_tlv_stream,
16471650
mut signature_tlv_stream, experimental_offer_tlv_stream,
1648-
experimental_invoice_request_tlv_stream,
1651+
experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
16491652
) = invoice.as_tlv_stream();
16501653
let payer_id = pubkey(1);
16511654
invoice_request_tlv_stream.payer_id = Some(&payer_id);
@@ -1654,6 +1657,7 @@ mod tests {
16541657
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
16551658
let experimental_tlv_stream = (
16561659
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1660+
experimental_invoice_tlv_stream,
16571661
);
16581662
let mut bytes = Vec::new();
16591663
(&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Please sign in to comment.