Skip to content

Commit be40cc7

Browse files
committed
Add parsing tests for experimental invoice TLVs
1 parent e102bdd commit be40cc7

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

@@ -633,6 +635,8 @@ struct InvoiceFields {
633635
fallbacks: Option<Vec<FallbackAddress>>,
634636
features: Bolt12InvoiceFeatures,
635637
signing_pubkey: PublicKey,
638+
#[cfg(test)]
639+
experimental_baz: Option<u64>,
636640
}
637641

638642
macro_rules! invoice_accessors { ($self: ident, $contents: expr) => {
@@ -1216,7 +1220,10 @@ impl InvoiceFields {
12161220
node_id: Some(&self.signing_pubkey),
12171221
message_paths: None,
12181222
},
1219-
ExperimentalInvoiceTlvStreamRef {},
1223+
ExperimentalInvoiceTlvStreamRef {
1224+
#[cfg(test)]
1225+
experimental_baz: self.experimental_baz,
1226+
},
12201227
)
12211228
}
12221229
}
@@ -1305,12 +1312,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
13051312
});
13061313

13071314
/// Valid type range for experimental invoice TLV records.
1308-
const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
1315+
pub(super) const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
13091316

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

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

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

14771497
check_invoice_signing_pubkey(&fields.signing_pubkey, &offer_tlv_stream)?;
@@ -1542,7 +1562,7 @@ pub(super) fn check_invoice_signing_pubkey(
15421562

15431563
#[cfg(test)]
15441564
mod tests {
1545-
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
1565+
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, EXPERIMENTAL_INVOICE_TYPES, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
15461566

15471567
use bitcoin::{CompressedPublicKey, WitnessProgram, WitnessVersion};
15481568
use bitcoin::constants::ChainHash;
@@ -1562,7 +1582,7 @@ mod tests {
15621582
use crate::ln::inbound_payment::ExpandedKey;
15631583
use crate::ln::msgs::DecodeError;
15641584
use crate::offers::invoice_request::{ExperimentalInvoiceRequestTlvStreamRef, InvoiceRequestTlvStreamRef};
1565-
use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
1585+
use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, TlvStream, self};
15661586
use crate::offers::nonce::Nonce;
15671587
use crate::offers::offer::{Amount, ExperimentalOfferTlvStreamRef, OfferTlvStreamRef, Quantity};
15681588
use crate::prelude::*;
@@ -1738,7 +1758,9 @@ mod tests {
17381758
ExperimentalInvoiceRequestTlvStreamRef {
17391759
experimental_bar: None,
17401760
},
1741-
ExperimentalInvoiceTlvStreamRef {},
1761+
ExperimentalInvoiceTlvStreamRef {
1762+
experimental_baz: None,
1763+
},
17421764
),
17431765
);
17441766

@@ -1838,7 +1860,9 @@ mod tests {
18381860
ExperimentalInvoiceRequestTlvStreamRef {
18391861
experimental_bar: None,
18401862
},
1841-
ExperimentalInvoiceTlvStreamRef {},
1863+
ExperimentalInvoiceTlvStreamRef {
1864+
experimental_baz: None,
1865+
},
18421866
),
18431867
);
18441868

@@ -2685,6 +2709,97 @@ mod tests {
26852709
}
26862710
}
26872711

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