Skip to content

Commit 0a116c2

Browse files
committed
Add parsing tests for experimental invoice TLVs
1 parent 1d5f757 commit 0a116c2

File tree

5 files changed

+266
-12
lines changed

5 files changed

+266
-12
lines changed

lightning/src/offers/invoice.rs

+122-7
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,8 @@ macro_rules! invoice_builder_methods { (
363363
InvoiceFields {
364364
payment_paths, created_at, relative_expiry: None, payment_hash, amount_msats,
365365
fallbacks: None, features: Bolt12InvoiceFeatures::empty(), signing_pubkey,
366+
#[cfg(test)]
367+
experimental_baz: None,
366368
}
367369
}
368370

@@ -634,6 +636,8 @@ struct InvoiceFields {
634636
fallbacks: Option<Vec<FallbackAddress>>,
635637
features: Bolt12InvoiceFeatures,
636638
signing_pubkey: PublicKey,
639+
#[cfg(test)]
640+
experimental_baz: Option<u64>,
637641
}
638642

639643
macro_rules! invoice_accessors { ($self: ident, $contents: expr) => {
@@ -1217,7 +1221,10 @@ impl InvoiceFields {
12171221
node_id: Some(&self.signing_pubkey),
12181222
message_paths: None,
12191223
},
1220-
ExperimentalInvoiceTlvStreamRef {},
1224+
ExperimentalInvoiceTlvStreamRef {
1225+
#[cfg(test)]
1226+
experimental_baz: self.experimental_baz,
1227+
},
12211228
)
12221229
}
12231230
}
@@ -1306,12 +1313,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
13061313
});
13071314

13081315
/// Valid type range for experimental invoice TLV records.
1309-
const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
1316+
pub(super) const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
13101317

1318+
#[cfg(not(test))]
13111319
tlv_stream!(
13121320
ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, EXPERIMENTAL_INVOICE_TYPES, {}
13131321
);
13141322

1323+
#[cfg(test)]
1324+
tlv_stream!(
1325+
ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, EXPERIMENTAL_INVOICE_TYPES, {
1326+
(3_999_999_999, experimental_baz: (u64, HighZeroBytesDroppedBigSize)),
1327+
}
1328+
);
1329+
13151330
pub(super) type BlindedPathIter<'a> = core::iter::Map<
13161331
core::slice::Iter<'a, BlindedPaymentPath>,
13171332
for<'r> fn(&'r BlindedPaymentPath) -> &'r BlindedPath,
@@ -1446,7 +1461,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14461461
},
14471462
experimental_offer_tlv_stream,
14481463
experimental_invoice_request_tlv_stream,
1449-
ExperimentalInvoiceTlvStream {},
1464+
ExperimentalInvoiceTlvStream {
1465+
#[cfg(test)]
1466+
experimental_baz,
1467+
},
14501468
) = tlv_stream;
14511469

14521470
if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) }
@@ -1473,6 +1491,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14731491
let fields = InvoiceFields {
14741492
payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
14751493
features, signing_pubkey,
1494+
#[cfg(test)]
1495+
experimental_baz,
14761496
};
14771497

14781498
check_invoice_signing_pubkey(&fields.signing_pubkey, &offer_tlv_stream)?;
@@ -1543,7 +1563,7 @@ pub(super) fn check_invoice_signing_pubkey(
15431563

15441564
#[cfg(test)]
15451565
mod tests {
1546-
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
1566+
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, EXPERIMENTAL_INVOICE_TYPES, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
15471567

15481568
use bitcoin::{CompressedPublicKey, WitnessProgram, WitnessVersion};
15491569
use bitcoin::constants::ChainHash;
@@ -1563,7 +1583,7 @@ mod tests {
15631583
use crate::ln::inbound_payment::ExpandedKey;
15641584
use crate::ln::msgs::DecodeError;
15651585
use crate::offers::invoice_request::{ExperimentalInvoiceRequestTlvStreamRef, InvoiceRequestTlvStreamRef};
1566-
use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
1586+
use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, TlvStream, self};
15671587
use crate::offers::nonce::Nonce;
15681588
use crate::offers::offer::{Amount, ExperimentalOfferTlvStreamRef, OfferTlvStreamRef, Quantity};
15691589
use crate::prelude::*;
@@ -1739,7 +1759,9 @@ mod tests {
17391759
ExperimentalInvoiceRequestTlvStreamRef {
17401760
experimental_bar: None,
17411761
},
1742-
ExperimentalInvoiceTlvStreamRef {},
1762+
ExperimentalInvoiceTlvStreamRef {
1763+
experimental_baz: None,
1764+
},
17431765
),
17441766
);
17451767

@@ -1839,7 +1861,9 @@ mod tests {
18391861
ExperimentalInvoiceRequestTlvStreamRef {
18401862
experimental_bar: None,
18411863
},
1842-
ExperimentalInvoiceTlvStreamRef {},
1864+
ExperimentalInvoiceTlvStreamRef {
1865+
experimental_baz: None,
1866+
},
18431867
),
18441868
);
18451869

@@ -2686,6 +2710,97 @@ mod tests {
26862710
}
26872711
}
26882712

2713+
#[test]
2714+
fn parses_invoice_with_experimental_tlv_records() {
2715+
let secp_ctx = Secp256k1::new();
2716+
let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
2717+
let invoice = OfferBuilder::new(keys.public_key())
2718+
.amount_msats(1000)
2719+
.build().unwrap()
2720+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
2721+
.build().unwrap()
2722+
.sign(payer_sign).unwrap()
2723+
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
2724+
.experimental_baz(42)
2725+
.build().unwrap()
2726+
.sign(|message: &UnsignedBolt12Invoice|
2727+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
2728+
)
2729+
.unwrap();
2730+
2731+
let mut buffer = Vec::new();
2732+
invoice.write(&mut buffer).unwrap();
2733+
2734+
assert!(Bolt12Invoice::try_from(buffer).is_ok());
2735+
2736+
const UNKNOWN_ODD_TYPE: u64 = EXPERIMENTAL_INVOICE_TYPES.start + 1;
2737+
assert!(UNKNOWN_ODD_TYPE % 2 == 1);
2738+
2739+
let mut unsigned_invoice = OfferBuilder::new(keys.public_key())
2740+
.amount_msats(1000)
2741+
.build().unwrap()
2742+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
2743+
.build().unwrap()
2744+
.sign(payer_sign).unwrap()
2745+
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
2746+
.build().unwrap();
2747+
2748+
BigSize(UNKNOWN_ODD_TYPE).write(&mut unsigned_invoice.experimental_bytes).unwrap();
2749+
BigSize(32).write(&mut unsigned_invoice.experimental_bytes).unwrap();
2750+
[42u8; 32].write(&mut unsigned_invoice.experimental_bytes).unwrap();
2751+
2752+
let tlv_stream = TlvStream::new(&unsigned_invoice.bytes)
2753+
.chain(TlvStream::new(&unsigned_invoice.experimental_bytes));
2754+
unsigned_invoice.tagged_hash = TaggedHash::from_tlv_stream(SIGNATURE_TAG, tlv_stream);
2755+
2756+
let invoice = unsigned_invoice
2757+
.sign(|message: &UnsignedBolt12Invoice|
2758+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
2759+
)
2760+
.unwrap();
2761+
2762+
let mut encoded_invoice = Vec::new();
2763+
invoice.write(&mut encoded_invoice).unwrap();
2764+
2765+
if let Err(e) = Bolt12Invoice::try_from(encoded_invoice) {
2766+
panic!("error parsing invoice: {:?}", e);
2767+
}
2768+
2769+
const UNKNOWN_EVEN_TYPE: u64 = EXPERIMENTAL_INVOICE_TYPES.start;
2770+
assert!(UNKNOWN_EVEN_TYPE % 2 == 0);
2771+
2772+
let mut unsigned_invoice = OfferBuilder::new(keys.public_key())
2773+
.amount_msats(1000)
2774+
.build().unwrap()
2775+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
2776+
.build().unwrap()
2777+
.sign(payer_sign).unwrap()
2778+
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
2779+
.build().unwrap();
2780+
2781+
BigSize(UNKNOWN_EVEN_TYPE).write(&mut unsigned_invoice.experimental_bytes).unwrap();
2782+
BigSize(32).write(&mut unsigned_invoice.experimental_bytes).unwrap();
2783+
[42u8; 32].write(&mut unsigned_invoice.experimental_bytes).unwrap();
2784+
2785+
let tlv_stream = TlvStream::new(&unsigned_invoice.bytes)
2786+
.chain(TlvStream::new(&unsigned_invoice.experimental_bytes));
2787+
unsigned_invoice.tagged_hash = TaggedHash::from_tlv_stream(SIGNATURE_TAG, tlv_stream);
2788+
2789+
let invoice = unsigned_invoice
2790+
.sign(|message: &UnsignedBolt12Invoice|
2791+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
2792+
)
2793+
.unwrap();
2794+
2795+
let mut encoded_invoice = Vec::new();
2796+
invoice.write(&mut encoded_invoice).unwrap();
2797+
2798+
match Bolt12Invoice::try_from(encoded_invoice) {
2799+
Ok(_) => panic!("expected error"),
2800+
Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)),
2801+
}
2802+
}
2803+
26892804
#[test]
26902805
fn fails_parsing_invoice_with_out_of_range_tlv_records() {
26912806
let invoice = OfferBuilder::new(recipient_pubkey())

lightning/src/offers/invoice_macros.rs

+5
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ macro_rules! invoice_builder_methods_test { (
9595
$return_value
9696
}
9797

98+
#[cfg_attr(c_bindings, allow(dead_code))]
99+
pub(super) fn experimental_baz($($self_mut)* $self: $self_type, experimental_baz: u64) -> $return_type {
100+
$invoice_fields.experimental_baz = Some(experimental_baz);
101+
$return_value
102+
}
98103
} }
99104

100105
macro_rules! invoice_accessors_common { ($self: ident, $contents: expr, $invoice_type: ty) => {

lightning/src/offers/invoice_request.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1524,6 +1524,7 @@ mod tests {
15241524

15251525
let invoice = invoice_request.respond_with_no_std(payment_paths(), payment_hash(), now())
15261526
.unwrap()
1527+
.experimental_baz(42)
15271528
.build().unwrap()
15281529
.sign(recipient_sign).unwrap();
15291530
match invoice.verify_using_metadata(&expanded_key, &secp_ctx) {
@@ -1616,6 +1617,7 @@ mod tests {
16161617

16171618
let invoice = invoice_request.respond_with_no_std(payment_paths(), payment_hash(), now())
16181619
.unwrap()
1620+
.experimental_baz(42)
16191621
.build().unwrap()
16201622
.sign(recipient_sign).unwrap();
16211623
assert!(invoice.verify_using_metadata(&expanded_key, &secp_ctx).is_err());

lightning/src/offers/refund.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,7 @@ mod tests {
11101110
let invoice = refund
11111111
.respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
11121112
.unwrap()
1113+
.experimental_baz(42)
11131114
.build().unwrap()
11141115
.sign(recipient_sign).unwrap();
11151116
match invoice.verify_using_metadata(&expanded_key, &secp_ctx) {
@@ -1178,6 +1179,7 @@ mod tests {
11781179
let invoice = refund
11791180
.respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
11801181
.unwrap()
1182+
.experimental_baz(42)
11811183
.build().unwrap()
11821184
.sign(recipient_sign).unwrap();
11831185
assert!(invoice.verify_using_metadata(&expanded_key, &secp_ctx).is_err());

0 commit comments

Comments
 (0)