Skip to content

Commit 6a708e2

Browse files
committed
Test verification with experimental offer TLVs
Offer metadata is generated from the offer TLVs and should included those in the experimental range. When verifying invoice request and invoice messages, these TLVs must be included. Similarly, OfferId construction should included these TLVs as well. Modify the BOLT12 verification tests to cover these TLVs.
1 parent e01e731 commit 6a708e2

File tree

5 files changed

+121
-10
lines changed

5 files changed

+121
-10
lines changed

lightning/src/offers/invoice.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,7 +1716,9 @@ mod tests {
17161716
message_paths: None,
17171717
},
17181718
SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
1719-
ExperimentalOfferTlvStreamRef {},
1719+
ExperimentalOfferTlvStreamRef {
1720+
experimental_foo: None,
1721+
},
17201722
),
17211723
);
17221724

@@ -1810,7 +1812,9 @@ mod tests {
18101812
message_paths: None,
18111813
},
18121814
SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
1813-
ExperimentalOfferTlvStreamRef {},
1815+
ExperimentalOfferTlvStreamRef {
1816+
experimental_foo: None,
1817+
},
18141818
),
18151819
);
18161820

@@ -1904,6 +1908,7 @@ mod tests {
19041908
let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
19051909
.amount_msats(1000)
19061910
.path(blinded_path)
1911+
.experimental_foo(42)
19071912
.build().unwrap();
19081913
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
19091914
.build().unwrap()
@@ -1925,6 +1930,7 @@ mod tests {
19251930
let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
19261931
.amount_msats(1000)
19271932
// Omit the path so that node_id is used for the signing pubkey instead of deriving it
1933+
.experimental_foo(42)
19281934
.build().unwrap();
19291935
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
19301936
.build().unwrap()
@@ -1946,6 +1952,7 @@ mod tests {
19461952
let secp_ctx = Secp256k1::new();
19471953

19481954
let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
1955+
.experimental_foo(42)
19491956
.build().unwrap();
19501957

19511958
if let Err(e) = refund

lightning/src/offers/invoice_request.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,9 @@ mod tests {
14341434
paths: None,
14351435
},
14361436
SignatureTlvStreamRef { signature: Some(&invoice_request.signature()) },
1437-
ExperimentalOfferTlvStreamRef {},
1437+
ExperimentalOfferTlvStreamRef {
1438+
experimental_foo: None,
1439+
},
14381440
),
14391441
);
14401442

@@ -1482,6 +1484,7 @@ mod tests {
14821484

14831485
let offer = OfferBuilder::new(recipient_pubkey())
14841486
.amount_msats(1000)
1487+
.experimental_foo(42)
14851488
.build().unwrap();
14861489
let invoice_request = offer
14871490
.request_invoice_deriving_metadata(signing_pubkey, &expanded_key, nonce, payment_id)
@@ -1565,6 +1568,7 @@ mod tests {
15651568

15661569
let offer = OfferBuilder::new(recipient_pubkey())
15671570
.amount_msats(1000)
1571+
.experimental_foo(42)
15681572
.build().unwrap();
15691573
let invoice_request = offer
15701574
.request_invoice_deriving_signing_pubkey(&expanded_key, nonce, &secp_ctx, payment_id)

lightning/src/offers/offer.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ macro_rules! offer_explicit_metadata_builder_methods { (
225225
chains: None, metadata: None, amount: None, description: None,
226226
features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
227227
supported_quantity: Quantity::One, issuer_signing_pubkey: Some(signing_pubkey),
228+
#[cfg(test)]
229+
experimental_foo: None,
228230
},
229231
metadata_strategy: core::marker::PhantomData,
230232
secp_ctx: None,
@@ -266,6 +268,8 @@ macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => {
266268
chains: None, metadata: Some(metadata), amount: None, description: None,
267269
features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
268270
supported_quantity: Quantity::One, issuer_signing_pubkey: Some(node_id),
271+
#[cfg(test)]
272+
experimental_foo: None,
269273
},
270274
metadata_strategy: core::marker::PhantomData,
271275
secp_ctx: Some(secp_ctx),
@@ -464,6 +468,12 @@ macro_rules! offer_builder_test_methods { (
464468
$return_value
465469
}
466470

471+
#[cfg_attr(c_bindings, allow(dead_code))]
472+
pub(super) fn experimental_foo($($self_mut)* $self: $self_type, experimental_foo: u64) -> $return_type {
473+
$self.offer.experimental_foo = Some(experimental_foo);
474+
$return_value
475+
}
476+
467477
#[cfg_attr(c_bindings, allow(dead_code))]
468478
pub(super) fn build_unchecked($self: $self_type) -> Offer {
469479
$self.build_without_checks()
@@ -571,6 +581,8 @@ pub(super) struct OfferContents {
571581
paths: Option<Vec<BlindedMessagePath>>,
572582
supported_quantity: Quantity,
573583
issuer_signing_pubkey: Option<PublicKey>,
584+
#[cfg(test)]
585+
experimental_foo: Option<u64>,
574586
}
575587

576588
macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
@@ -1010,7 +1022,10 @@ impl OfferContents {
10101022
issuer_id: self.issuer_signing_pubkey.as_ref(),
10111023
};
10121024

1013-
let experimental_offer = ExperimentalOfferTlvStreamRef {};
1025+
let experimental_offer = ExperimentalOfferTlvStreamRef {
1026+
#[cfg(test)]
1027+
experimental_foo: self.experimental_foo,
1028+
};
10141029

10151030
(offer, experimental_offer)
10161031
}
@@ -1107,9 +1122,15 @@ tlv_stream!(OfferTlvStream, OfferTlvStreamRef<'a>, OFFER_TYPES, {
11071122
/// Valid type range for experimental offer TLV records.
11081123
pub(super) const EXPERIMENTAL_OFFER_TYPES: core::ops::Range<u64> = 1_000_000_000..2_000_000_000;
11091124

1125+
#[cfg(not(test))]
11101126
tlv_stream!(ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, EXPERIMENTAL_OFFER_TYPES, {
11111127
});
11121128

1129+
#[cfg(test)]
1130+
tlv_stream!(ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, EXPERIMENTAL_OFFER_TYPES, {
1131+
(1_999_999_999, experimental_foo: (u64, HighZeroBytesDroppedBigSize)),
1132+
});
1133+
11131134
type FullOfferTlvStream = (OfferTlvStream, ExperimentalOfferTlvStream);
11141135

11151136
type FullOfferTlvStreamRef<'a> = (OfferTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef);
@@ -1157,7 +1178,10 @@ impl TryFrom<FullOfferTlvStream> for OfferContents {
11571178
chains, metadata, currency, amount, description, features, absolute_expiry, paths,
11581179
issuer, quantity_max, issuer_id,
11591180
},
1160-
ExperimentalOfferTlvStream {},
1181+
ExperimentalOfferTlvStream {
1182+
#[cfg(test)]
1183+
experimental_foo,
1184+
},
11611185
) = tlv_stream;
11621186

11631187
let metadata = metadata.map(|metadata| Metadata::Bytes(metadata));
@@ -1196,6 +1220,8 @@ impl TryFrom<FullOfferTlvStream> for OfferContents {
11961220
Ok(OfferContents {
11971221
chains, metadata, amount, description, features, absolute_expiry, issuer, paths,
11981222
supported_quantity, issuer_signing_pubkey,
1223+
#[cfg(test)]
1224+
experimental_foo,
11991225
})
12001226
}
12011227
}
@@ -1274,7 +1300,9 @@ mod tests {
12741300
quantity_max: None,
12751301
issuer_id: Some(&pubkey(42)),
12761302
},
1277-
ExperimentalOfferTlvStreamRef {},
1303+
ExperimentalOfferTlvStreamRef {
1304+
experimental_foo: None,
1305+
},
12781306
),
12791307
);
12801308

@@ -1354,6 +1382,7 @@ mod tests {
13541382
use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
13551383
let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
13561384
.amount_msats(1000)
1385+
.experimental_foo(42)
13571386
.build().unwrap();
13581387
assert!(offer.metadata().is_some());
13591388
assert_eq!(offer.issuer_signing_pubkey(), Some(node_id));
@@ -1423,6 +1452,7 @@ mod tests {
14231452
let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
14241453
.amount_msats(1000)
14251454
.path(blinded_path)
1455+
.experimental_foo(42)
14261456
.build().unwrap();
14271457
assert!(offer.metadata().is_none());
14281458
assert_ne!(offer.issuer_signing_pubkey(), Some(node_id));

lightning/src/offers/refund.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ macro_rules! refund_explicit_metadata_builder_methods { () => {
176176
payer: PayerContents(metadata), description: String::new(), absolute_expiry: None,
177177
issuer: None, chain: None, amount_msats, features: InvoiceRequestFeatures::empty(),
178178
quantity: None, payer_signing_pubkey: signing_pubkey, payer_note: None, paths: None,
179+
#[cfg(test)]
180+
experimental_foo: None,
179181
},
180182
secp_ctx: None,
181183
})
@@ -218,6 +220,8 @@ macro_rules! refund_builder_methods { (
218220
payer: PayerContents(metadata), description: String::new(), absolute_expiry: None,
219221
issuer: None, chain: None, amount_msats, features: InvoiceRequestFeatures::empty(),
220222
quantity: None, payer_signing_pubkey: node_id, payer_note: None, paths: None,
223+
#[cfg(test)]
224+
experimental_foo: None,
221225
},
222226
secp_ctx: Some(secp_ctx),
223227
})
@@ -358,6 +362,12 @@ macro_rules! refund_builder_test_methods { (
358362
$self.refund.features = features;
359363
$return_value
360364
}
365+
366+
#[cfg_attr(c_bindings, allow(dead_code))]
367+
pub(super) fn experimental_foo($($self_mut)* $self: $self_type, experimental_foo: u64) -> $return_type {
368+
$self.refund.experimental_foo = Some(experimental_foo);
369+
$return_value
370+
}
361371
} }
362372

363373
impl<'a> RefundBuilder<'a, secp256k1::SignOnly> {
@@ -437,6 +447,8 @@ pub(super) struct RefundContents {
437447
payer_signing_pubkey: PublicKey,
438448
payer_note: Option<String>,
439449
paths: Option<Vec<BlindedMessagePath>>,
450+
#[cfg(test)]
451+
experimental_foo: Option<u64>,
440452
}
441453

442454
impl Refund {
@@ -770,7 +782,10 @@ impl RefundContents {
770782
paths: self.paths.as_ref(),
771783
};
772784

773-
let experimental_offer = ExperimentalOfferTlvStreamRef {};
785+
let experimental_offer = ExperimentalOfferTlvStreamRef {
786+
#[cfg(test)]
787+
experimental_foo: self.experimental_foo,
788+
};
774789

775790
(payer, offer, invoice_request, experimental_offer)
776791
}
@@ -855,7 +870,10 @@ impl TryFrom<RefundTlvStream> for RefundContents {
855870
InvoiceRequestTlvStream {
856871
chain, amount, features, quantity, payer_id, payer_note, paths
857872
},
858-
_experimental_offer_tlv_stream,
873+
ExperimentalOfferTlvStream {
874+
#[cfg(test)]
875+
experimental_foo,
876+
},
859877
) = tlv_stream;
860878

861879
let payer = match payer_metadata {
@@ -916,6 +934,8 @@ impl TryFrom<RefundTlvStream> for RefundContents {
916934
Ok(RefundContents {
917935
payer, description, absolute_expiry, issuer, chain, amount_msats, features, quantity,
918936
payer_signing_pubkey, payer_note, paths,
937+
#[cfg(test)]
938+
experimental_foo,
919939
})
920940
}
921941
}
@@ -1021,7 +1041,9 @@ mod tests {
10211041
payer_note: None,
10221042
paths: None,
10231043
},
1024-
ExperimentalOfferTlvStreamRef {},
1044+
ExperimentalOfferTlvStreamRef {
1045+
experimental_foo: None,
1046+
},
10251047
),
10261048
);
10271049

@@ -1050,6 +1072,7 @@ mod tests {
10501072
let refund = RefundBuilder
10511073
::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx, 1000, payment_id)
10521074
.unwrap()
1075+
.experimental_foo(42)
10531076
.build().unwrap();
10541077
assert_eq!(refund.payer_signing_pubkey(), node_id);
10551078

@@ -1117,6 +1140,7 @@ mod tests {
11171140
::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx, 1000, payment_id)
11181141
.unwrap()
11191142
.path(blinded_path)
1143+
.experimental_foo(42)
11201144
.build().unwrap();
11211145
assert_ne!(refund.payer_signing_pubkey(), node_id);
11221146

lightning/src/offers/static_invoice.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,7 @@ mod tests {
848848
message_paths: Some(&paths),
849849
},
850850
SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
851-
ExperimentalOfferTlvStreamRef {},
851+
ExperimentalOfferTlvStreamRef { experimental_foo: None },
852852
)
853853
);
854854

@@ -916,6 +916,52 @@ mod tests {
916916
}
917917
}
918918

919+
#[test]
920+
fn builds_invoice_from_offer_using_derived_key() {
921+
let node_id = recipient_pubkey();
922+
let now = now();
923+
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
924+
let entropy = FixedEntropy {};
925+
let nonce = Nonce::from_entropy_source(&entropy);
926+
let secp_ctx = Secp256k1::new();
927+
928+
let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
929+
.path(blinded_path())
930+
.experimental_foo(42)
931+
.build()
932+
.unwrap();
933+
934+
if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
935+
&offer,
936+
payment_paths(),
937+
vec![blinded_path()],
938+
now,
939+
&expanded_key,
940+
nonce,
941+
&secp_ctx,
942+
)
943+
.unwrap()
944+
.build_and_sign(&secp_ctx)
945+
{
946+
panic!("error building invoice: {:?}", e);
947+
}
948+
949+
let expanded_key = ExpandedKey::new(&KeyMaterial([41; 32]));
950+
if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
951+
&offer,
952+
payment_paths(),
953+
vec![blinded_path()],
954+
now,
955+
&expanded_key,
956+
nonce,
957+
&secp_ctx,
958+
) {
959+
assert_eq!(e, Bolt12SemanticError::InvalidMetadata);
960+
} else {
961+
panic!("expected error")
962+
}
963+
}
964+
919965
#[test]
920966
fn fails_build_with_missing_paths() {
921967
let node_id = recipient_pubkey();

0 commit comments

Comments
 (0)