Skip to content

Commit f32566a

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 9524cc6 commit f32566a

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
@@ -1706,7 +1706,9 @@ mod tests {
17061706
message_paths: None,
17071707
},
17081708
SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
1709-
ExperimentalOfferTlvStreamRef {},
1709+
ExperimentalOfferTlvStreamRef {
1710+
experimental_foo: None,
1711+
},
17101712
),
17111713
);
17121714

@@ -1800,7 +1802,9 @@ mod tests {
18001802
message_paths: None,
18011803
},
18021804
SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
1803-
ExperimentalOfferTlvStreamRef {},
1805+
ExperimentalOfferTlvStreamRef {
1806+
experimental_foo: None,
1807+
},
18041808
),
18051809
);
18061810

@@ -1894,6 +1898,7 @@ mod tests {
18941898
let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
18951899
.amount_msats(1000)
18961900
.path(blinded_path)
1901+
.experimental_foo(42)
18971902
.build().unwrap();
18981903
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
18991904
.build().unwrap()
@@ -1915,6 +1920,7 @@ mod tests {
19151920
let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
19161921
.amount_msats(1000)
19171922
// Omit the path so that node_id is used for the signing pubkey instead of deriving it
1923+
.experimental_foo(42)
19181924
.build().unwrap();
19191925
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
19201926
.build().unwrap()
@@ -1936,6 +1942,7 @@ mod tests {
19361942
let secp_ctx = Secp256k1::new();
19371943

19381944
let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
1945+
.experimental_foo(42)
19391946
.build().unwrap();
19401947

19411948
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
@@ -1426,7 +1426,9 @@ mod tests {
14261426
paths: None,
14271427
},
14281428
SignatureTlvStreamRef { signature: Some(&invoice_request.signature()) },
1429-
ExperimentalOfferTlvStreamRef {},
1429+
ExperimentalOfferTlvStreamRef {
1430+
experimental_foo: None,
1431+
},
14301432
),
14311433
);
14321434

@@ -1474,6 +1476,7 @@ mod tests {
14741476

14751477
let offer = OfferBuilder::new(recipient_pubkey())
14761478
.amount_msats(1000)
1479+
.experimental_foo(42)
14771480
.build().unwrap();
14781481
let invoice_request = offer
14791482
.request_invoice_deriving_metadata(signing_pubkey, &expanded_key, nonce, payment_id)
@@ -1557,6 +1560,7 @@ mod tests {
15571560

15581561
let offer = OfferBuilder::new(recipient_pubkey())
15591562
.amount_msats(1000)
1563+
.experimental_foo(42)
15601564
.build().unwrap();
15611565
let invoice_request = offer
15621566
.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
@@ -840,7 +840,7 @@ mod tests {
840840
message_paths: Some(&paths),
841841
},
842842
SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
843-
ExperimentalOfferTlvStreamRef {},
843+
ExperimentalOfferTlvStreamRef { experimental_foo: None },
844844
)
845845
);
846846

@@ -908,6 +908,52 @@ mod tests {
908908
}
909909
}
910910

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

0 commit comments

Comments
 (0)