Skip to content

Commit d6bc0cf

Browse files
committed
Optional OfferContents::signing_pubkey
If an Offer contains a path, the blinded_node_id of the path's final hop can be used as the signing pubkey. Make Offer::signing_pubkey and OfferContents::signing_pubkey return an Option to support this. Upcoming commits will implement this behavior.
1 parent 2c0fcf2 commit d6bc0cf

File tree

5 files changed

+56
-34
lines changed

5 files changed

+56
-34
lines changed

lightning/src/ln/channelmanager.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -8765,14 +8765,7 @@ where
87658765
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
87668766

87678767
let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
8768-
if offer.paths().is_empty() {
8769-
let message = new_pending_onion_message(
8770-
OffersMessage::InvoiceRequest(invoice_request),
8771-
Destination::Node(offer.signing_pubkey()),
8772-
Some(reply_path),
8773-
);
8774-
pending_offers_messages.push(message);
8775-
} else {
8768+
if !offer.paths().is_empty() {
87768769
// Send as many invoice requests as there are paths in the offer (with an upper bound).
87778770
// Using only one path could result in a failure if the path no longer exists. But only
87788771
// one invoice for a given payment id will be paid, even if more than one is received.
@@ -8785,6 +8778,16 @@ where
87858778
);
87868779
pending_offers_messages.push(message);
87878780
}
8781+
} else if let Some(signing_pubkey) = offer.signing_pubkey() {
8782+
let message = new_pending_onion_message(
8783+
OffersMessage::InvoiceRequest(invoice_request),
8784+
Destination::Node(signing_pubkey),
8785+
Some(reply_path),
8786+
);
8787+
pending_offers_messages.push(message);
8788+
} else {
8789+
debug_assert!(false);
8790+
return Err(Bolt12SemanticError::MissingSigningPubkey);
87888791
}
87898792

87908793
Ok(())

lightning/src/ln/offers_tests.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ fn prefers_non_tor_nodes_in_blinded_paths() {
271271
.create_offer_builder("coffee".to_string()).unwrap()
272272
.amount_msats(10_000_000)
273273
.build().unwrap();
274-
assert_ne!(offer.signing_pubkey(), bob_id);
274+
assert_ne!(offer.signing_pubkey(), Some(bob_id));
275275
assert!(!offer.paths().is_empty());
276276
for path in offer.paths() {
277277
assert_ne!(path.introduction_node, IntroductionNode::NodeId(bob_id));
@@ -286,7 +286,7 @@ fn prefers_non_tor_nodes_in_blinded_paths() {
286286
.create_offer_builder("coffee".to_string()).unwrap()
287287
.amount_msats(10_000_000)
288288
.build().unwrap();
289-
assert_ne!(offer.signing_pubkey(), bob_id);
289+
assert_ne!(offer.signing_pubkey(), Some(bob_id));
290290
assert!(!offer.paths().is_empty());
291291
for path in offer.paths() {
292292
assert_eq!(path.introduction_node, IntroductionNode::NodeId(bob_id));
@@ -336,7 +336,7 @@ fn prefers_more_connected_nodes_in_blinded_paths() {
336336
.create_offer_builder("coffee".to_string()).unwrap()
337337
.amount_msats(10_000_000)
338338
.build().unwrap();
339-
assert_ne!(offer.signing_pubkey(), bob_id);
339+
assert_ne!(offer.signing_pubkey(), Some(bob_id));
340340
assert!(!offer.paths().is_empty());
341341
for path in offer.paths() {
342342
assert_eq!(path.introduction_node, IntroductionNode::NodeId(nodes[4].node.get_our_node_id()));
@@ -386,7 +386,7 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
386386
.unwrap()
387387
.amount_msats(10_000_000)
388388
.build().unwrap();
389-
assert_ne!(offer.signing_pubkey(), alice_id);
389+
assert_ne!(offer.signing_pubkey(), Some(alice_id));
390390
assert!(!offer.paths().is_empty());
391391
for path in offer.paths() {
392392
assert_eq!(path.introduction_node, IntroductionNode::NodeId(bob_id));
@@ -547,7 +547,7 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
547547
.create_offer_builder("coffee".to_string()).unwrap()
548548
.amount_msats(10_000_000)
549549
.build().unwrap();
550-
assert_ne!(offer.signing_pubkey(), alice_id);
550+
assert_ne!(offer.signing_pubkey(), Some(alice_id));
551551
assert!(!offer.paths().is_empty());
552552
for path in offer.paths() {
553553
assert_eq!(path.introduction_node, IntroductionNode::NodeId(alice_id));
@@ -672,7 +672,7 @@ fn pays_for_offer_without_blinded_paths() {
672672
.clear_paths()
673673
.amount_msats(10_000_000)
674674
.build().unwrap();
675-
assert_eq!(offer.signing_pubkey(), alice_id);
675+
assert_eq!(offer.signing_pubkey(), Some(alice_id));
676676
assert!(offer.paths().is_empty());
677677

678678
let payment_id = PaymentId([1; 32]);

lightning/src/offers/invoice.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,9 @@ macro_rules! invoice_explicit_signing_pubkey_builder_methods { ($self: ident, $s
210210
#[cfg_attr(c_bindings, allow(dead_code))]
211211
pub(super) fn for_offer(
212212
invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
213-
created_at: Duration, payment_hash: PaymentHash
213+
created_at: Duration, payment_hash: PaymentHash, signing_pubkey: PublicKey
214214
) -> Result<Self, Bolt12SemanticError> {
215215
let amount_msats = Self::amount_msats(invoice_request)?;
216-
let signing_pubkey = invoice_request.contents.inner.offer.signing_pubkey();
217216
let contents = InvoiceContents::ForOffer {
218217
invoice_request: invoice_request.contents.clone(),
219218
fields: Self::fields(
@@ -272,7 +271,7 @@ macro_rules! invoice_derived_signing_pubkey_builder_methods { ($self: ident, $se
272271
created_at: Duration, payment_hash: PaymentHash, keys: KeyPair
273272
) -> Result<Self, Bolt12SemanticError> {
274273
let amount_msats = Self::amount_msats(invoice_request)?;
275-
let signing_pubkey = invoice_request.contents.inner.offer.signing_pubkey();
274+
let signing_pubkey = keys.public_key();
276275
let contents = InvoiceContents::ForOffer {
277276
invoice_request: invoice_request.contents.clone(),
278277
fields: Self::fields(

lightning/src/offers/invoice_request.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,15 @@ macro_rules! invoice_request_respond_with_explicit_signing_pubkey_methods { (
747747
return Err(Bolt12SemanticError::UnknownRequiredFeatures);
748748
}
749749

750-
<$builder>::for_offer(&$contents, payment_paths, created_at, payment_hash)
750+
let signing_pubkey = match $contents.contents.inner.offer.signing_pubkey() {
751+
Some(signing_pubkey) => signing_pubkey,
752+
None => {
753+
debug_assert!(false);
754+
return Err(Bolt12SemanticError::MissingSigningPubkey);
755+
},
756+
};
757+
758+
<$builder>::for_offer(&$contents, payment_paths, created_at, payment_hash, signing_pubkey)
751759
}
752760
} }
753761

@@ -855,6 +863,14 @@ macro_rules! invoice_request_respond_with_derived_signing_pubkey_methods { (
855863
Some(keys) => keys,
856864
};
857865

866+
match $contents.contents.inner.offer.signing_pubkey() {
867+
Some(signing_pubkey) => debug_assert_eq!(signing_pubkey, keys.public_key()),
868+
None => {
869+
debug_assert!(false);
870+
return Err(Bolt12SemanticError::MissingSigningPubkey);
871+
},
872+
}
873+
858874
<$builder>::for_offer_using_keys(
859875
&$self.inner, payment_paths, created_at, payment_hash, keys
860876
)
@@ -1233,7 +1249,7 @@ mod tests {
12331249
assert_eq!(unsigned_invoice_request.paths(), &[]);
12341250
assert_eq!(unsigned_invoice_request.issuer(), None);
12351251
assert_eq!(unsigned_invoice_request.supported_quantity(), Quantity::One);
1236-
assert_eq!(unsigned_invoice_request.signing_pubkey(), recipient_pubkey());
1252+
assert_eq!(unsigned_invoice_request.signing_pubkey(), Some(recipient_pubkey()));
12371253
assert_eq!(unsigned_invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
12381254
assert_eq!(unsigned_invoice_request.amount_msats(), None);
12391255
assert_eq!(unsigned_invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty());
@@ -1265,7 +1281,7 @@ mod tests {
12651281
assert_eq!(invoice_request.paths(), &[]);
12661282
assert_eq!(invoice_request.issuer(), None);
12671283
assert_eq!(invoice_request.supported_quantity(), Quantity::One);
1268-
assert_eq!(invoice_request.signing_pubkey(), recipient_pubkey());
1284+
assert_eq!(invoice_request.signing_pubkey(), Some(recipient_pubkey()));
12691285
assert_eq!(invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
12701286
assert_eq!(invoice_request.amount_msats(), None);
12711287
assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty());
@@ -2260,7 +2276,7 @@ mod tests {
22602276
.amount_msats(1000)
22612277
.supported_quantity(Quantity::Unbounded)
22622278
.build().unwrap();
2263-
assert_eq!(offer.signing_pubkey(), node_id);
2279+
assert_eq!(offer.signing_pubkey(), Some(node_id));
22642280

22652281
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
22662282
.chain(Network::Testnet).unwrap()

lightning/src/offers/offer.rs

+17-13
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ macro_rules! offer_explicit_metadata_builder_methods { (
226226
offer: OfferContents {
227227
chains: None, metadata: None, amount: None, description,
228228
features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
229-
supported_quantity: Quantity::One, signing_pubkey,
229+
supported_quantity: Quantity::One, signing_pubkey: Some(signing_pubkey),
230230
},
231231
metadata_strategy: core::marker::PhantomData,
232232
secp_ctx: None,
@@ -265,7 +265,7 @@ macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => {
265265
offer: OfferContents {
266266
chains: None, metadata: Some(metadata), amount: None, description,
267267
features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
268-
supported_quantity: Quantity::One, signing_pubkey: node_id,
268+
supported_quantity: Quantity::One, signing_pubkey: Some(node_id),
269269
},
270270
metadata_strategy: core::marker::PhantomData,
271271
secp_ctx: Some(secp_ctx),
@@ -391,7 +391,7 @@ macro_rules! offer_builder_methods { (
391391
let (derived_metadata, keys) = metadata.derive_from(tlv_stream, $self.secp_ctx);
392392
metadata = derived_metadata;
393393
if let Some(keys) = keys {
394-
$self.offer.signing_pubkey = keys.public_key();
394+
$self.offer.signing_pubkey = Some(keys.public_key());
395395
}
396396
}
397397

@@ -542,7 +542,7 @@ pub(super) struct OfferContents {
542542
issuer: Option<String>,
543543
paths: Option<Vec<BlindedPath>>,
544544
supported_quantity: Quantity,
545-
signing_pubkey: PublicKey,
545+
signing_pubkey: Option<PublicKey>,
546546
}
547547

548548
macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
@@ -604,7 +604,7 @@ macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
604604
}
605605

606606
/// The public key used by the recipient to sign invoices.
607-
pub fn signing_pubkey(&$self) -> bitcoin::secp256k1::PublicKey {
607+
pub fn signing_pubkey(&$self) -> Option<bitcoin::secp256k1::PublicKey> {
608608
$contents.signing_pubkey()
609609
}
610610
} }
@@ -886,7 +886,7 @@ impl OfferContents {
886886
}
887887
}
888888

889-
pub(super) fn signing_pubkey(&self) -> PublicKey {
889+
pub(super) fn signing_pubkey(&self) -> Option<PublicKey> {
890890
self.signing_pubkey
891891
}
892892

@@ -905,8 +905,12 @@ impl OfferContents {
905905
_ => true,
906906
}
907907
});
908+
let signing_pubkey = match self.signing_pubkey() {
909+
Some(signing_pubkey) => signing_pubkey,
910+
None => return Err(()),
911+
};
908912
let keys = signer::verify_recipient_metadata(
909-
metadata, key, IV_BYTES, self.signing_pubkey(), tlv_stream, secp_ctx
913+
metadata, key, IV_BYTES, signing_pubkey, tlv_stream, secp_ctx
910914
)?;
911915

912916
let offer_id = OfferId::from_valid_invreq_tlv_stream(bytes);
@@ -941,7 +945,7 @@ impl OfferContents {
941945
paths: self.paths.as_ref(),
942946
issuer: self.issuer.as_ref(),
943947
quantity_max: self.supported_quantity.to_tlv_record(),
944-
node_id: Some(&self.signing_pubkey),
948+
node_id: self.signing_pubkey.as_ref(),
945949
}
946950
}
947951
}
@@ -1091,7 +1095,7 @@ impl TryFrom<OfferTlvStream> for OfferContents {
10911095

10921096
let signing_pubkey = match node_id {
10931097
None => return Err(Bolt12SemanticError::MissingSigningPubkey),
1094-
Some(node_id) => node_id,
1098+
Some(node_id) => Some(node_id),
10951099
};
10961100

10971101
Ok(OfferContents {
@@ -1154,7 +1158,7 @@ mod tests {
11541158
assert_eq!(offer.paths(), &[]);
11551159
assert_eq!(offer.issuer(), None);
11561160
assert_eq!(offer.supported_quantity(), Quantity::One);
1157-
assert_eq!(offer.signing_pubkey(), pubkey(42));
1161+
assert_eq!(offer.signing_pubkey(), Some(pubkey(42)));
11581162

11591163
assert_eq!(
11601164
offer.as_tlv_stream(),
@@ -1251,7 +1255,7 @@ mod tests {
12511255
::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
12521256
.amount_msats(1000)
12531257
.build().unwrap();
1254-
assert_eq!(offer.signing_pubkey(), node_id);
1258+
assert_eq!(offer.signing_pubkey(), Some(node_id));
12551259

12561260
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
12571261
.build().unwrap()
@@ -1313,7 +1317,7 @@ mod tests {
13131317
.amount_msats(1000)
13141318
.path(blinded_path)
13151319
.build().unwrap();
1316-
assert_ne!(offer.signing_pubkey(), node_id);
1320+
assert_ne!(offer.signing_pubkey(), Some(node_id));
13171321

13181322
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
13191323
.build().unwrap()
@@ -1471,7 +1475,7 @@ mod tests {
14711475
.unwrap();
14721476
let tlv_stream = offer.as_tlv_stream();
14731477
assert_eq!(offer.paths(), paths.as_slice());
1474-
assert_eq!(offer.signing_pubkey(), pubkey(42));
1478+
assert_eq!(offer.signing_pubkey(), Some(pubkey(42)));
14751479
assert_ne!(pubkey(42), pubkey(44));
14761480
assert_eq!(tlv_stream.paths, Some(&paths));
14771481
assert_eq!(tlv_stream.node_id, Some(&pubkey(42)));

0 commit comments

Comments
 (0)