Skip to content

Commit 4564de1

Browse files
committed
Add parsing tests for experimental invoice TLVs
1 parent cef5314 commit 4564de1

File tree

5 files changed

+266
-12
lines changed

5 files changed

+266
-12
lines changed

lightning/src/offers/invoice.rs

Lines changed: 122 additions & 7 deletions
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
}
@@ -1294,12 +1301,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
12941301
});
12951302

12961303
/// Valid type range for experimental invoice TLV records.
1297-
const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
1304+
pub(super) const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
12981305

1306+
#[cfg(not(test))]
12991307
tlv_stream!(
13001308
ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, EXPERIMENTAL_INVOICE_TYPES, {}
13011309
);
13021310

1311+
#[cfg(test)]
1312+
tlv_stream!(
1313+
ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, EXPERIMENTAL_INVOICE_TYPES, {
1314+
(3_999_999_999, experimental_baz: (u64, HighZeroBytesDroppedBigSize)),
1315+
}
1316+
);
1317+
13031318
pub(super) type BlindedPathIter<'a> = core::iter::Map<
13041319
core::slice::Iter<'a, BlindedPaymentPath>,
13051320
for<'r> fn(&'r BlindedPaymentPath) -> &'r BlindedPath,
@@ -1434,7 +1449,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14341449
},
14351450
experimental_offer_tlv_stream,
14361451
experimental_invoice_request_tlv_stream,
1437-
ExperimentalInvoiceTlvStream {},
1452+
ExperimentalInvoiceTlvStream {
1453+
#[cfg(test)]
1454+
experimental_baz,
1455+
},
14381456
) = tlv_stream;
14391457

14401458
if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) }
@@ -1461,6 +1479,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14611479
let fields = InvoiceFields {
14621480
payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
14631481
features, signing_pubkey,
1482+
#[cfg(test)]
1483+
experimental_baz,
14641484
};
14651485

14661486
check_invoice_signing_pubkey(&fields.signing_pubkey, &offer_tlv_stream)?;
@@ -1531,7 +1551,7 @@ pub(super) fn check_invoice_signing_pubkey(
15311551

15321552
#[cfg(test)]
15331553
mod tests {
1534-
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
1554+
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, EXPERIMENTAL_INVOICE_TYPES, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
15351555

15361556
use bitcoin::{CompressedPublicKey, WitnessProgram, WitnessVersion};
15371557
use bitcoin::constants::ChainHash;
@@ -1551,7 +1571,7 @@ mod tests {
15511571
use crate::ln::inbound_payment::ExpandedKey;
15521572
use crate::ln::msgs::DecodeError;
15531573
use crate::offers::invoice_request::{ExperimentalInvoiceRequestTlvStreamRef, InvoiceRequestTlvStreamRef};
1554-
use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
1574+
use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, TlvStream, self};
15551575
use crate::offers::nonce::Nonce;
15561576
use crate::offers::offer::{Amount, ExperimentalOfferTlvStreamRef, OfferTlvStreamRef, Quantity};
15571577
use crate::prelude::*;
@@ -1727,7 +1747,9 @@ mod tests {
17271747
ExperimentalInvoiceRequestTlvStreamRef {
17281748
experimental_bar: None,
17291749
},
1730-
ExperimentalInvoiceTlvStreamRef {},
1750+
ExperimentalInvoiceTlvStreamRef {
1751+
experimental_baz: None,
1752+
},
17311753
),
17321754
);
17331755

@@ -1827,7 +1849,9 @@ mod tests {
18271849
ExperimentalInvoiceRequestTlvStreamRef {
18281850
experimental_bar: None,
18291851
},
1830-
ExperimentalInvoiceTlvStreamRef {},
1852+
ExperimentalInvoiceTlvStreamRef {
1853+
experimental_baz: None,
1854+
},
18311855
),
18321856
);
18331857

@@ -2674,6 +2698,97 @@ mod tests {
26742698
}
26752699
}
26762700

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

lightning/src/offers/invoice_macros.rs

Lines changed: 5 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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)