Skip to content

Commit 0e00c71

Browse files
committed
Parse experimental offer TLV records
The BOLT12 spec defines an experimental TLV range that are allowed in offer messages. Allow this range when parsing an offer and include those bytes in any invoice requests. Also include those bytes when computing an OfferId and verifying that an InvoiceRequest is for a valid Offer.
1 parent 82d8c2c commit 0e00c71

File tree

6 files changed

+309
-174
lines changed

6 files changed

+309
-174
lines changed

lightning/src/offers/invoice.rs

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ use crate::offers::invoice_macros::invoice_builder_methods_test;
124124
use crate::offers::invoice_request::{EXPERIMENTAL_INVOICE_REQUEST_TYPES, 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;
127-
use crate::offers::offer::{Amount, EXPERIMENTAL_OFFER_TYPES, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
127+
use crate::offers::offer::{Amount, EXPERIMENTAL_OFFER_TYPES, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
128128
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
129129
use crate::offers::payer::{PAYER_METADATA_TYPE, PayerTlvStream, PayerTlvStreamRef};
130130
use crate::offers::refund::{IV_BYTES_WITH_METADATA as REFUND_IV_BYTES_WITH_METADATA, IV_BYTES_WITHOUT_METADATA as REFUND_IV_BYTES_WITHOUT_METADATA, Refund, RefundContents};
@@ -506,8 +506,7 @@ impl UnsignedBolt12Invoice {
506506
record.write(&mut bytes).unwrap();
507507
}
508508

509-
let (_, _, _, invoice_tlv_stream) = contents.as_tlv_stream();
510-
509+
let (_, _, _, invoice_tlv_stream, _) = contents.as_tlv_stream();
511510
invoice_tlv_stream.write(&mut bytes).unwrap();
512511

513512
let mut experimental_bytes = Vec::new();
@@ -870,13 +869,17 @@ impl Bolt12Invoice {
870869
}
871870

872871
pub(crate) fn as_tlv_stream(&self) -> FullInvoiceTlvStreamRef {
873-
let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) =
874-
self.contents.as_tlv_stream();
872+
let (
873+
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
874+
experimental_offer_tlv_stream,
875+
) = self.contents.as_tlv_stream();
875876
let signature_tlv_stream = SignatureTlvStreamRef {
876877
signature: Some(&self.signature),
877878
};
878-
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
879-
signature_tlv_stream)
879+
(
880+
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
881+
signature_tlv_stream, experimental_offer_tlv_stream,
882+
)
880883
}
881884

882885
pub(crate) fn is_for_refund_without_paths(&self) -> bool {
@@ -1107,7 +1110,7 @@ impl InvoiceContents {
11071110

11081111
fn verify<T: secp256k1::Signing>(
11091112
&self, bytes: &[u8], metadata: &Metadata, key: &ExpandedKey, iv_bytes: &[u8; IV_LEN],
1110-
secp_ctx: &Secp256k1<T>
1113+
secp_ctx: &Secp256k1<T>,
11111114
) -> Result<PaymentId, ()> {
11121115
const EXPERIMENTAL_TYPES: core::ops::Range<u64> =
11131116
EXPERIMENTAL_OFFER_TYPES.start..EXPERIMENTAL_INVOICE_REQUEST_TYPES.end;
@@ -1130,13 +1133,13 @@ impl InvoiceContents {
11301133
}
11311134

11321135
fn as_tlv_stream(&self) -> PartialInvoiceTlvStreamRef {
1133-
let (payer, offer, invoice_request) = match self {
1136+
let (payer, offer, invoice_request, experimental_offer) = match self {
11341137
InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.as_tlv_stream(),
11351138
InvoiceContents::ForRefund { refund, .. } => refund.as_tlv_stream(),
11361139
};
11371140
let invoice = self.fields().as_tlv_stream();
11381141

1139-
(payer, offer, invoice_request, invoice)
1142+
(payer, offer, invoice_request, invoice, experimental_offer)
11401143
}
11411144
}
11421145

@@ -1295,15 +1298,18 @@ pub(super) struct FallbackAddress {
12951298

12961299
impl_writeable!(FallbackAddress, { version, program });
12971300

1298-
type FullInvoiceTlvStream =
1299-
(PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream);
1301+
type FullInvoiceTlvStream =(
1302+
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream,
1303+
ExperimentalOfferTlvStream,
1304+
);
13001305

13011306
type FullInvoiceTlvStreamRef<'a> = (
13021307
PayerTlvStreamRef<'a>,
13031308
OfferTlvStreamRef<'a>,
13041309
InvoiceRequestTlvStreamRef<'a>,
13051310
InvoiceTlvStreamRef<'a>,
13061311
SignatureTlvStreamRef<'a>,
1312+
ExperimentalOfferTlvStreamRef,
13071313
);
13081314

13091315
impl CursorReadable for FullInvoiceTlvStream {
@@ -1313,19 +1319,23 @@ impl CursorReadable for FullInvoiceTlvStream {
13131319
let invoice_request = CursorReadable::read(r)?;
13141320
let invoice = CursorReadable::read(r)?;
13151321
let signature = CursorReadable::read(r)?;
1322+
let experimental_offer = CursorReadable::read(r)?;
13161323

1317-
Ok((payer, offer, invoice_request, invoice, signature))
1324+
Ok((payer, offer, invoice_request, invoice, signature, experimental_offer))
13181325
}
13191326
}
13201327

1321-
type PartialInvoiceTlvStream =
1322-
(PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream);
1328+
type PartialInvoiceTlvStream = (
1329+
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream,
1330+
ExperimentalOfferTlvStream,
1331+
);
13231332

13241333
type PartialInvoiceTlvStreamRef<'a> = (
13251334
PayerTlvStreamRef<'a>,
13261335
OfferTlvStreamRef<'a>,
13271336
InvoiceRequestTlvStreamRef<'a>,
13281337
InvoiceTlvStreamRef<'a>,
1338+
ExperimentalOfferTlvStreamRef,
13291339
);
13301340

13311341
impl CursorReadable for PartialInvoiceTlvStream {
@@ -1334,8 +1344,9 @@ impl CursorReadable for PartialInvoiceTlvStream {
13341344
let offer = CursorReadable::read(r)?;
13351345
let invoice_request = CursorReadable::read(r)?;
13361346
let invoice = CursorReadable::read(r)?;
1347+
let experimental_offer = CursorReadable::read(r)?;
13371348

1338-
Ok((payer, offer, invoice_request, invoice))
1349+
Ok((payer, offer, invoice_request, invoice, experimental_offer))
13391350
}
13401351
}
13411352

@@ -1347,9 +1358,13 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
13471358
let (
13481359
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
13491360
SignatureTlvStream { signature },
1361+
experimental_offer_tlv_stream,
13501362
) = tlv_stream;
13511363
let contents = InvoiceContents::try_from(
1352-
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream)
1364+
(
1365+
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
1366+
experimental_offer_tlv_stream,
1367+
)
13531368
)?;
13541369

13551370
let signature = signature.ok_or(
@@ -1375,6 +1390,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
13751390
paths, blindedpay, created_at, relative_expiry, payment_hash, amount, fallbacks,
13761391
features, node_id, message_paths,
13771392
},
1393+
experimental_offer_tlv_stream,
13781394
) = tlv_stream;
13791395

13801396
if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) }
@@ -1407,12 +1423,18 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14071423

14081424
if offer_tlv_stream.issuer_id.is_none() && offer_tlv_stream.paths.is_none() {
14091425
let refund = RefundContents::try_from(
1410-
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
1426+
(
1427+
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
1428+
experimental_offer_tlv_stream,
1429+
)
14111430
)?;
14121431
Ok(InvoiceContents::ForRefund { refund, fields })
14131432
} else {
14141433
let invoice_request = InvoiceRequestContents::try_from(
1415-
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
1434+
(
1435+
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
1436+
experimental_offer_tlv_stream,
1437+
)
14161438
)?;
14171439
Ok(InvoiceContents::ForOffer { invoice_request, fields })
14181440
}
@@ -1487,7 +1509,7 @@ mod tests {
14871509
use crate::offers::invoice_request::InvoiceRequestTlvStreamRef;
14881510
use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
14891511
use crate::offers::nonce::Nonce;
1490-
use crate::offers::offer::{Amount, OfferTlvStreamRef, Quantity};
1512+
use crate::offers::offer::{Amount, ExperimentalOfferTlvStreamRef, OfferTlvStreamRef, Quantity};
14911513
use crate::prelude::*;
14921514
#[cfg(not(c_bindings))]
14931515
use {
@@ -1655,6 +1677,7 @@ mod tests {
16551677
message_paths: None,
16561678
},
16571679
SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
1680+
ExperimentalOfferTlvStreamRef {},
16581681
),
16591682
);
16601683

@@ -1748,6 +1771,7 @@ mod tests {
17481771
message_paths: None,
17491772
},
17501773
SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
1774+
ExperimentalOfferTlvStreamRef {},
17511775
),
17521776
);
17531777

@@ -1941,7 +1965,7 @@ mod tests {
19411965
.relative_expiry(one_hour.as_secs() as u32)
19421966
.build().unwrap()
19431967
.sign(recipient_sign).unwrap();
1944-
let (_, _, _, tlv_stream, _) = invoice.as_tlv_stream();
1968+
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
19451969
#[cfg(feature = "std")]
19461970
assert!(!invoice.is_expired());
19471971
assert_eq!(invoice.relative_expiry(), one_hour);
@@ -1957,7 +1981,7 @@ mod tests {
19571981
.relative_expiry(one_hour.as_secs() as u32 - 1)
19581982
.build().unwrap()
19591983
.sign(recipient_sign).unwrap();
1960-
let (_, _, _, tlv_stream, _) = invoice.as_tlv_stream();
1984+
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
19611985
#[cfg(feature = "std")]
19621986
assert!(invoice.is_expired());
19631987
assert_eq!(invoice.relative_expiry(), one_hour - Duration::from_secs(1));
@@ -1976,7 +2000,7 @@ mod tests {
19762000
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
19772001
.build().unwrap()
19782002
.sign(recipient_sign).unwrap();
1979-
let (_, _, _, tlv_stream, _) = invoice.as_tlv_stream();
2003+
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
19802004
assert_eq!(invoice.amount_msats(), 1001);
19812005
assert_eq!(tlv_stream.amount, Some(1001));
19822006
}
@@ -1994,7 +2018,7 @@ mod tests {
19942018
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
19952019
.build().unwrap()
19962020
.sign(recipient_sign).unwrap();
1997-
let (_, _, _, tlv_stream, _) = invoice.as_tlv_stream();
2021+
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
19982022
assert_eq!(invoice.amount_msats(), 2000);
19992023
assert_eq!(tlv_stream.amount, Some(2000));
20002024

@@ -2032,7 +2056,7 @@ mod tests {
20322056
.fallback_v1_p2tr_tweaked(&tweaked_pubkey)
20332057
.build().unwrap()
20342058
.sign(recipient_sign).unwrap();
2035-
let (_, _, _, tlv_stream, _) = invoice.as_tlv_stream();
2059+
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
20362060
assert_eq!(
20372061
invoice.fallbacks(),
20382062
vec![
@@ -2075,7 +2099,7 @@ mod tests {
20752099
.allow_mpp()
20762100
.build().unwrap()
20772101
.sign(recipient_sign).unwrap();
2078-
let (_, _, _, tlv_stream, _) = invoice.as_tlv_stream();
2102+
let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream();
20792103
assert_eq!(invoice.invoice_features(), &features);
20802104
assert_eq!(tlv_stream.features, Some(&features));
20812105
}

0 commit comments

Comments
 (0)