Skip to content

Commit d9a9e9d

Browse files
committed
Parse experimental invreq TLV records
The BOLT12 spec defines an experimental TLV range that are allowed in invoice_request messages. Allow this range when parsing an invoice request and include those bytes in any invoice. Also include those bytes when verifying that a Bolt12Invoice is for a valid InvoiceRequest.
1 parent 8b84c3c commit d9a9e9d

File tree

4 files changed

+173
-82
lines changed

4 files changed

+173
-82
lines changed

lightning/src/offers/invoice.rs

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ use crate::ln::msgs::DecodeError;
121121
use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common};
122122
#[cfg(test)]
123123
use crate::offers::invoice_macros::invoice_builder_methods_test;
124-
use crate::offers::invoice_request::{INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
124+
use crate::offers::invoice_request::{EXPERIMENTAL_INVOICE_REQUEST_TYPES, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceRequestTlvStreamRef, INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
125125
use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, self};
126126
use crate::offers::nonce::Nonce;
127127
use crate::offers::offer::{Amount, EXPERIMENTAL_OFFER_TYPES, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
@@ -493,7 +493,8 @@ where
493493
impl UnsignedBolt12Invoice {
494494
fn new(invreq_bytes: &[u8], contents: InvoiceContents) -> Self {
495495
const NON_EXPERIMENTAL_TYPES: core::ops::Range<u64> = 0..INVOICE_REQUEST_TYPES.end;
496-
const EXPERIMENTAL_TYPES: core::ops::Range<u64> = EXPERIMENTAL_OFFER_TYPES;
496+
const EXPERIMENTAL_TYPES: core::ops::Range<u64> =
497+
EXPERIMENTAL_OFFER_TYPES.start..EXPERIMENTAL_INVOICE_REQUEST_TYPES.end;
497498

498499
let mut bytes = Vec::new();
499500

@@ -504,7 +505,7 @@ impl UnsignedBolt12Invoice {
504505
record.write(&mut bytes).unwrap();
505506
}
506507

507-
let (_, _, _, invoice_tlv_stream, _) = contents.as_tlv_stream();
508+
let (_, _, _, invoice_tlv_stream, _, _) = contents.as_tlv_stream();
508509
invoice_tlv_stream.write(&mut bytes).unwrap();
509510

510511
let mut experimental_bytes = Vec::new();
@@ -869,14 +870,15 @@ impl Bolt12Invoice {
869870
pub(crate) fn as_tlv_stream(&self) -> FullInvoiceTlvStreamRef {
870871
let (
871872
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
872-
experimental_offer_tlv_stream,
873+
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
873874
) = self.contents.as_tlv_stream();
874875
let signature_tlv_stream = SignatureTlvStreamRef {
875876
signature: Some(&self.signature),
876877
};
877878
(
878879
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
879880
signature_tlv_stream, experimental_offer_tlv_stream,
881+
experimental_invoice_request_tlv_stream,
880882
)
881883
}
882884

@@ -1110,7 +1112,8 @@ impl InvoiceContents {
11101112
&self, bytes: &[u8], metadata: &Metadata, key: &ExpandedKey, iv_bytes: &[u8; IV_LEN],
11111113
secp_ctx: &Secp256k1<T>,
11121114
) -> Result<PaymentId, ()> {
1113-
const EXPERIMENTAL_TYPES: core::ops::Range<u64> = EXPERIMENTAL_OFFER_TYPES;
1115+
const EXPERIMENTAL_TYPES: core::ops::Range<u64> =
1116+
EXPERIMENTAL_OFFER_TYPES.start..EXPERIMENTAL_INVOICE_REQUEST_TYPES.end;
11141117

11151118
let offer_records = TlvStream::new(bytes).range(OFFER_TYPES);
11161119
let invreq_records = TlvStream::new(bytes).range(INVOICE_REQUEST_TYPES).filter(|record| {
@@ -1130,13 +1133,15 @@ impl InvoiceContents {
11301133
}
11311134

11321135
fn as_tlv_stream(&self) -> PartialInvoiceTlvStreamRef {
1133-
let (payer, offer, invoice_request, experimental_offer) = match self {
1136+
let (
1137+
payer, offer, invoice_request, experimental_offer, experimental_invoice_request,
1138+
) = match self {
11341139
InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.as_tlv_stream(),
11351140
InvoiceContents::ForRefund { refund, .. } => refund.as_tlv_stream(),
11361141
};
11371142
let invoice = self.fields().as_tlv_stream();
11381143

1139-
(payer, offer, invoice_request, invoice, experimental_offer)
1144+
(payer, offer, invoice_request, invoice, experimental_offer, experimental_invoice_request)
11401145
}
11411146
}
11421147

@@ -1238,12 +1243,12 @@ impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
12381243
let ParsedMessage { mut bytes, tlv_stream } = invoice;
12391244
let (
12401245
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
1241-
experimental_offer_tlv_stream,
1246+
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
12421247
) = tlv_stream;
12431248
let contents = InvoiceContents::try_from(
12441249
(
12451250
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
1246-
experimental_offer_tlv_stream,
1251+
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
12471252
)
12481253
)?;
12491254

@@ -1307,7 +1312,7 @@ impl_writeable!(FallbackAddress, { version, program });
13071312

13081313
type FullInvoiceTlvStream =(
13091314
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream,
1310-
ExperimentalOfferTlvStream,
1315+
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
13111316
);
13121317

13131318
type FullInvoiceTlvStreamRef<'a> = (
@@ -1317,6 +1322,7 @@ type FullInvoiceTlvStreamRef<'a> = (
13171322
InvoiceTlvStreamRef<'a>,
13181323
SignatureTlvStreamRef<'a>,
13191324
ExperimentalOfferTlvStreamRef,
1325+
ExperimentalInvoiceRequestTlvStreamRef,
13201326
);
13211327

13221328
impl CursorReadable for FullInvoiceTlvStream {
@@ -1327,14 +1333,20 @@ impl CursorReadable for FullInvoiceTlvStream {
13271333
let invoice = CursorReadable::read(r)?;
13281334
let signature = CursorReadable::read(r)?;
13291335
let experimental_offer = CursorReadable::read(r)?;
1336+
let experimental_invoice_request = CursorReadable::read(r)?;
13301337

1331-
Ok((payer, offer, invoice_request, invoice, signature, experimental_offer))
1338+
Ok(
1339+
(
1340+
payer, offer, invoice_request, invoice, signature, experimental_offer,
1341+
experimental_invoice_request,
1342+
)
1343+
)
13321344
}
13331345
}
13341346

13351347
type PartialInvoiceTlvStream = (
13361348
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream,
1337-
ExperimentalOfferTlvStream,
1349+
ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
13381350
);
13391351

13401352
type PartialInvoiceTlvStreamRef<'a> = (
@@ -1343,6 +1355,7 @@ type PartialInvoiceTlvStreamRef<'a> = (
13431355
InvoiceRequestTlvStreamRef<'a>,
13441356
InvoiceTlvStreamRef<'a>,
13451357
ExperimentalOfferTlvStreamRef,
1358+
ExperimentalInvoiceRequestTlvStreamRef,
13461359
);
13471360

13481361
impl CursorReadable for PartialInvoiceTlvStream {
@@ -1352,8 +1365,14 @@ impl CursorReadable for PartialInvoiceTlvStream {
13521365
let invoice_request = CursorReadable::read(r)?;
13531366
let invoice = CursorReadable::read(r)?;
13541367
let experimental_offer = CursorReadable::read(r)?;
1368+
let experimental_invoice_request = CursorReadable::read(r)?;
13551369

1356-
Ok((payer, offer, invoice_request, invoice, experimental_offer))
1370+
Ok(
1371+
(
1372+
payer, offer, invoice_request, invoice, experimental_offer,
1373+
experimental_invoice_request,
1374+
)
1375+
)
13571376
}
13581377
}
13591378

@@ -1366,11 +1385,12 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
13661385
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
13671386
SignatureTlvStream { signature },
13681387
experimental_offer_tlv_stream,
1388+
experimental_invoice_request_tlv_stream,
13691389
) = tlv_stream;
13701390
let contents = InvoiceContents::try_from(
13711391
(
13721392
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
1373-
experimental_offer_tlv_stream,
1393+
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
13741394
)
13751395
)?;
13761396

@@ -1398,6 +1418,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
13981418
features, node_id, message_paths,
13991419
},
14001420
experimental_offer_tlv_stream,
1421+
experimental_invoice_request_tlv_stream,
14011422
) = tlv_stream;
14021423

14031424
if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) }
@@ -1432,15 +1453,15 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14321453
let refund = RefundContents::try_from(
14331454
(
14341455
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
1435-
experimental_offer_tlv_stream,
1456+
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
14361457
)
14371458
)?;
14381459
Ok(InvoiceContents::ForRefund { refund, fields })
14391460
} else {
14401461
let invoice_request = InvoiceRequestContents::try_from(
14411462
(
14421463
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
1443-
experimental_offer_tlv_stream,
1464+
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
14441465
)
14451466
)?;
14461467
Ok(InvoiceContents::ForOffer { invoice_request, fields })
@@ -1513,7 +1534,7 @@ mod tests {
15131534
use crate::ln::features::{Bolt12InvoiceFeatures, InvoiceRequestFeatures, OfferFeatures};
15141535
use crate::ln::inbound_payment::ExpandedKey;
15151536
use crate::ln::msgs::DecodeError;
1516-
use crate::offers::invoice_request::InvoiceRequestTlvStreamRef;
1537+
use crate::offers::invoice_request::{ExperimentalInvoiceRequestTlvStreamRef, InvoiceRequestTlvStreamRef};
15171538
use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
15181539
use crate::offers::nonce::Nonce;
15191540
use crate::offers::offer::{Amount, ExperimentalOfferTlvStreamRef, OfferTlvStreamRef, Quantity};
@@ -1687,6 +1708,7 @@ mod tests {
16871708
ExperimentalOfferTlvStreamRef {
16881709
experimental_foo: None,
16891710
},
1711+
ExperimentalInvoiceRequestTlvStreamRef {},
16901712
),
16911713
);
16921714

@@ -1783,6 +1805,7 @@ mod tests {
17831805
ExperimentalOfferTlvStreamRef {
17841806
experimental_foo: None,
17851807
},
1808+
ExperimentalInvoiceRequestTlvStreamRef {},
17861809
),
17871810
);
17881811

@@ -1979,7 +2002,7 @@ mod tests {
19792002
.relative_expiry(one_hour.as_secs() as u32)
19802003
.build().unwrap()
19812004
.sign(recipient_sign).unwrap();
1982-
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
2005+
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
19832006
#[cfg(feature = "std")]
19842007
assert!(!invoice.is_expired());
19852008
assert_eq!(invoice.relative_expiry(), one_hour);
@@ -1995,7 +2018,7 @@ mod tests {
19952018
.relative_expiry(one_hour.as_secs() as u32 - 1)
19962019
.build().unwrap()
19972020
.sign(recipient_sign).unwrap();
1998-
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
2021+
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
19992022
#[cfg(feature = "std")]
20002023
assert!(invoice.is_expired());
20012024
assert_eq!(invoice.relative_expiry(), one_hour - Duration::from_secs(1));
@@ -2014,7 +2037,7 @@ mod tests {
20142037
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
20152038
.build().unwrap()
20162039
.sign(recipient_sign).unwrap();
2017-
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
2040+
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
20182041
assert_eq!(invoice.amount_msats(), 1001);
20192042
assert_eq!(tlv_stream.amount, Some(1001));
20202043
}
@@ -2032,7 +2055,7 @@ mod tests {
20322055
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
20332056
.build().unwrap()
20342057
.sign(recipient_sign).unwrap();
2035-
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
2058+
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
20362059
assert_eq!(invoice.amount_msats(), 2000);
20372060
assert_eq!(tlv_stream.amount, Some(2000));
20382061

@@ -2070,7 +2093,7 @@ mod tests {
20702093
.fallback_v1_p2tr_tweaked(&tweaked_pubkey)
20712094
.build().unwrap()
20722095
.sign(recipient_sign).unwrap();
2073-
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
2096+
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
20742097
assert_eq!(
20752098
invoice.fallbacks(),
20762099
vec![
@@ -2113,7 +2136,7 @@ mod tests {
21132136
.allow_mpp()
21142137
.build().unwrap()
21152138
.sign(recipient_sign).unwrap();
2116-
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
2139+
let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream();
21172140
assert_eq!(invoice.invoice_features(), &features);
21182141
assert_eq!(tlv_stream.features, Some(&features));
21192142
}

0 commit comments

Comments
 (0)