Skip to content

Commit 55c6c56

Browse files
committed
Add parsing tests for experimental invoice TLVs
1 parent 89eafe3 commit 55c6c56

File tree

5 files changed

+282
-12
lines changed

5 files changed

+282
-12
lines changed

lightning/src/offers/invoice.rs

+129-7
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, InvoiceRequ
117117
use crate::ln::inbound_payment::{ExpandedKey, IV_LEN};
118118
use crate::ln::msgs::DecodeError;
119119
use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common};
120+
#[cfg(test)]
121+
use crate::offers::invoice_macros::invoice_builder_test_methods;
120122
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};
121123
use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, self};
122124
use crate::offers::nonce::Nonce;
@@ -359,6 +361,8 @@ macro_rules! invoice_builder_methods { (
359361
InvoiceFields {
360362
payment_paths, created_at, relative_expiry: None, payment_hash, amount_msats,
361363
fallbacks: None, features: Bolt12InvoiceFeatures::empty(), signing_pubkey,
364+
#[cfg(test)]
365+
experimental_baz: None,
362366
}
363367
}
364368

@@ -385,6 +389,9 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
385389
impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> {
386390
invoice_builder_methods!(self, Self, Self, self, S, mut);
387391
invoice_builder_methods_common!(self, Self, self.invoice.fields_mut(), Self, self, S, Bolt12Invoice, mut);
392+
393+
#[cfg(test)]
394+
invoice_builder_test_methods!(self, Self, self.invoice.fields_mut(), Self, self, mut);
388395
}
389396

390397
#[cfg(all(c_bindings, not(test)))]
@@ -399,6 +406,7 @@ impl<'a> InvoiceWithExplicitSigningPubkeyBuilder<'a> {
399406
invoice_explicit_signing_pubkey_builder_methods!(self, &mut Self);
400407
invoice_builder_methods!(self, &mut Self, &mut Self, self, ExplicitSigningPubkey);
401408
invoice_builder_methods_common!(self, &mut Self, self.invoice.fields_mut(), &mut Self, self, ExplicitSigningPubkey, Bolt12Invoice);
409+
invoice_builder_test_methods!(self, &mut Self, self.invoice.fields_mut(), &mut Self, self);
402410
}
403411

404412
#[cfg(all(c_bindings, not(test)))]
@@ -413,6 +421,7 @@ impl<'a> InvoiceWithDerivedSigningPubkeyBuilder<'a> {
413421
invoice_derived_signing_pubkey_builder_methods!(self, &mut Self);
414422
invoice_builder_methods!(self, &mut Self, &mut Self, self, DerivedSigningPubkey);
415423
invoice_builder_methods_common!(self, &mut Self, self.invoice.fields_mut(), &mut Self, self, DerivedSigningPubkey, Bolt12Invoice);
424+
invoice_builder_test_methods!(self, &mut Self, self.invoice.fields_mut(), &mut Self, self);
416425
}
417426

418427
#[cfg(c_bindings)]
@@ -624,6 +633,8 @@ struct InvoiceFields {
624633
fallbacks: Option<Vec<FallbackAddress>>,
625634
features: Bolt12InvoiceFeatures,
626635
signing_pubkey: PublicKey,
636+
#[cfg(test)]
637+
experimental_baz: Option<u64>,
627638
}
628639

629640
macro_rules! invoice_accessors { ($self: ident, $contents: expr) => {
@@ -1208,7 +1219,10 @@ impl InvoiceFields {
12081219
node_id: Some(&self.signing_pubkey),
12091220
message_paths: None,
12101221
},
1211-
ExperimentalInvoiceTlvStreamRef {},
1222+
ExperimentalInvoiceTlvStreamRef {
1223+
#[cfg(test)]
1224+
experimental_baz: self.experimental_baz,
1225+
},
12121226
)
12131227
}
12141228
}
@@ -1297,12 +1311,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
12971311
});
12981312

12991313
/// Valid type range for experimental invoice TLV records.
1300-
const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
1314+
pub(super) const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
13011315

1316+
#[cfg(not(test))]
13021317
tlv_stream!(
13031318
ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, EXPERIMENTAL_INVOICE_TYPES, {}
13041319
);
13051320

1321+
#[cfg(test)]
1322+
tlv_stream!(
1323+
ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, EXPERIMENTAL_INVOICE_TYPES, {
1324+
(3_999_999_999, experimental_baz: (u64, HighZeroBytesDroppedBigSize)),
1325+
}
1326+
);
1327+
13061328
pub(super) type BlindedPathIter<'a> = core::iter::Map<
13071329
core::slice::Iter<'a, (BlindedPayInfo, BlindedPath)>,
13081330
for<'r> fn(&'r (BlindedPayInfo, BlindedPath)) -> &'r BlindedPath,
@@ -1475,7 +1497,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14751497
},
14761498
experimental_offer_tlv_stream,
14771499
experimental_invoice_request_tlv_stream,
1478-
ExperimentalInvoiceTlvStream {},
1500+
ExperimentalInvoiceTlvStream {
1501+
#[cfg(test)]
1502+
experimental_baz,
1503+
},
14791504
) = tlv_stream;
14801505

14811506
if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) }
@@ -1502,6 +1527,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
15021527
let fields = InvoiceFields {
15031528
payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
15041529
features, signing_pubkey,
1530+
#[cfg(test)]
1531+
experimental_baz,
15051532
};
15061533

15071534
check_invoice_signing_pubkey(&fields.signing_pubkey, &offer_tlv_stream)?;
@@ -1567,7 +1594,7 @@ pub(super) fn check_invoice_signing_pubkey(
15671594

15681595
#[cfg(test)]
15691596
mod tests {
1570-
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
1597+
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, EXPERIMENTAL_INVOICE_TYPES, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
15711598

15721599
use bitcoin::{WitnessProgram, WitnessVersion};
15731600
use bitcoin::blockdata::constants::ChainHash;
@@ -1586,7 +1613,7 @@ mod tests {
15861613
use crate::ln::inbound_payment::ExpandedKey;
15871614
use crate::ln::msgs::DecodeError;
15881615
use crate::offers::invoice_request::{ExperimentalInvoiceRequestTlvStreamRef, InvoiceRequestTlvStreamRef};
1589-
use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
1616+
use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, TlvStream, self};
15901617
use crate::offers::nonce::Nonce;
15911618
use crate::offers::offer::{Amount, ExperimentalOfferTlvStreamRef, OfferTlvStreamRef, Quantity};
15921619
use crate::prelude::*;
@@ -1762,7 +1789,9 @@ mod tests {
17621789
ExperimentalInvoiceRequestTlvStreamRef {
17631790
experimental_bar: None,
17641791
},
1765-
ExperimentalInvoiceTlvStreamRef {},
1792+
ExperimentalInvoiceTlvStreamRef {
1793+
experimental_baz: None,
1794+
},
17661795
),
17671796
);
17681797

@@ -1862,7 +1891,9 @@ mod tests {
18621891
ExperimentalInvoiceRequestTlvStreamRef {
18631892
experimental_bar: None,
18641893
},
1865-
ExperimentalInvoiceTlvStreamRef {},
1894+
ExperimentalInvoiceTlvStreamRef {
1895+
experimental_baz: None,
1896+
},
18661897
),
18671898
);
18681899

@@ -2713,6 +2744,97 @@ mod tests {
27132744
}
27142745
}
27152746

2747+
#[test]
2748+
fn parses_invoice_with_experimental_tlv_records() {
2749+
let secp_ctx = Secp256k1::new();
2750+
let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
2751+
let invoice = OfferBuilder::new(keys.public_key())
2752+
.amount_msats(1000)
2753+
.build().unwrap()
2754+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
2755+
.build().unwrap()
2756+
.sign(payer_sign).unwrap()
2757+
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
2758+
.experimental_baz(42)
2759+
.build().unwrap()
2760+
.sign(|message: &UnsignedBolt12Invoice|
2761+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
2762+
)
2763+
.unwrap();
2764+
2765+
let mut buffer = Vec::new();
2766+
invoice.write(&mut buffer).unwrap();
2767+
2768+
assert!(Bolt12Invoice::try_from(buffer).is_ok());
2769+
2770+
const UNKNOWN_ODD_TYPE: u64 = EXPERIMENTAL_INVOICE_TYPES.start + 1;
2771+
assert!(UNKNOWN_ODD_TYPE % 2 == 1);
2772+
2773+
let mut unsigned_invoice = OfferBuilder::new(keys.public_key())
2774+
.amount_msats(1000)
2775+
.build().unwrap()
2776+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
2777+
.build().unwrap()
2778+
.sign(payer_sign).unwrap()
2779+
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
2780+
.build().unwrap();
2781+
2782+
BigSize(UNKNOWN_ODD_TYPE).write(&mut unsigned_invoice.experimental_bytes).unwrap();
2783+
BigSize(32).write(&mut unsigned_invoice.experimental_bytes).unwrap();
2784+
[42u8; 32].write(&mut unsigned_invoice.experimental_bytes).unwrap();
2785+
2786+
let tlv_stream = TlvStream::new(&unsigned_invoice.bytes)
2787+
.chain(TlvStream::new(&unsigned_invoice.experimental_bytes));
2788+
unsigned_invoice.tagged_hash = TaggedHash::from_tlv_stream(SIGNATURE_TAG, tlv_stream);
2789+
2790+
let invoice = unsigned_invoice
2791+
.sign(|message: &UnsignedBolt12Invoice|
2792+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
2793+
)
2794+
.unwrap();
2795+
2796+
let mut encoded_invoice = Vec::new();
2797+
invoice.write(&mut encoded_invoice).unwrap();
2798+
2799+
if let Err(e) = Bolt12Invoice::try_from(encoded_invoice) {
2800+
panic!("error parsing invoice: {:?}", e);
2801+
}
2802+
2803+
const UNKNOWN_EVEN_TYPE: u64 = EXPERIMENTAL_INVOICE_TYPES.start;
2804+
assert!(UNKNOWN_EVEN_TYPE % 2 == 0);
2805+
2806+
let mut unsigned_invoice = OfferBuilder::new(keys.public_key())
2807+
.amount_msats(1000)
2808+
.build().unwrap()
2809+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
2810+
.build().unwrap()
2811+
.sign(payer_sign).unwrap()
2812+
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
2813+
.build().unwrap();
2814+
2815+
BigSize(UNKNOWN_EVEN_TYPE).write(&mut unsigned_invoice.experimental_bytes).unwrap();
2816+
BigSize(32).write(&mut unsigned_invoice.experimental_bytes).unwrap();
2817+
[42u8; 32].write(&mut unsigned_invoice.experimental_bytes).unwrap();
2818+
2819+
let tlv_stream = TlvStream::new(&unsigned_invoice.bytes)
2820+
.chain(TlvStream::new(&unsigned_invoice.experimental_bytes));
2821+
unsigned_invoice.tagged_hash = TaggedHash::from_tlv_stream(SIGNATURE_TAG, tlv_stream);
2822+
2823+
let invoice = unsigned_invoice
2824+
.sign(|message: &UnsignedBolt12Invoice|
2825+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
2826+
)
2827+
.unwrap();
2828+
2829+
let mut encoded_invoice = Vec::new();
2830+
invoice.write(&mut encoded_invoice).unwrap();
2831+
2832+
match Bolt12Invoice::try_from(encoded_invoice) {
2833+
Ok(_) => panic!("expected error"),
2834+
Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)),
2835+
}
2836+
}
2837+
27162838
#[test]
27172839
fn fails_parsing_invoice_with_out_of_range_tlv_records() {
27182840
let invoice = OfferBuilder::new(recipient_pubkey())

lightning/src/offers/invoice_macros.rs

+14
Original file line numberDiff line numberDiff line change
@@ -126,5 +126,19 @@ macro_rules! invoice_accessors_common { ($self: ident, $contents: expr, $invoice
126126
}
127127
} }
128128

129+
#[cfg(test)]
130+
macro_rules! invoice_builder_test_methods { (
131+
$self: ident, $self_type: ty, $invoice_fields: expr, $return_type: ty, $return_value: expr
132+
$(, $self_mut: tt)?
133+
) => {
134+
#[cfg_attr(c_bindings, allow(dead_code))]
135+
pub(super) fn experimental_baz($($self_mut)* $self: $self_type, experimental_baz: u64) -> $return_type {
136+
$invoice_fields.experimental_baz = Some(experimental_baz);
137+
$return_value
138+
}
139+
} }
140+
129141
pub(super) use invoice_accessors_common;
130142
pub(super) use invoice_builder_methods_common;
143+
#[cfg(test)]
144+
pub(super) use invoice_builder_test_methods;

lightning/src/offers/invoice_request.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,7 @@ mod tests {
15131513

15141514
let invoice = invoice_request.respond_with_no_std(payment_paths(), payment_hash(), now())
15151515
.unwrap()
1516+
.experimental_baz(42)
15161517
.build().unwrap()
15171518
.sign(recipient_sign).unwrap();
15181519
match invoice.verify_using_metadata(&expanded_key, &secp_ctx) {
@@ -1605,6 +1606,7 @@ mod tests {
16051606

16061607
let invoice = invoice_request.respond_with_no_std(payment_paths(), payment_hash(), now())
16071608
.unwrap()
1609+
.experimental_baz(42)
16081610
.build().unwrap()
16091611
.sign(recipient_sign).unwrap();
16101612
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
@@ -1109,6 +1109,7 @@ mod tests {
11091109
let invoice = refund
11101110
.respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
11111111
.unwrap()
1112+
.experimental_baz(42)
11121113
.build().unwrap()
11131114
.sign(recipient_sign).unwrap();
11141115
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)