diff --git a/fuzz/src/invoice_request_deser.rs b/fuzz/src/invoice_request_deser.rs index f93199cf94..fb5122ec23 100644 --- a/fuzz/src/invoice_request_deser.rs +++ b/fuzz/src/invoice_request_deser.rs @@ -85,7 +85,7 @@ fn build_response( let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext { offer_id: OfferId([42; 32]), invoice_request: InvoiceRequestFields { - payer_id: invoice_request.payer_id(), + payer_signing_pubkey: invoice_request.payer_signing_pubkey(), quantity: invoice_request.quantity(), payer_note_truncated: invoice_request .payer_note() diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 03164e04fb..b87c2ebf3b 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -65,7 +65,7 @@ use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutb use crate::ln::wire::Encode; use crate::offers::invoice::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice}; use crate::offers::invoice_error::InvoiceError; -use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequest, InvoiceRequestBuilder}; +use crate::offers::invoice_request::{DerivedPayerSigningPubkey, InvoiceRequest, InvoiceRequestBuilder}; use crate::offers::nonce::Nonce; use crate::offers::offer::{Offer, OfferBuilder}; use crate::offers::parse::Bolt12SemanticError; @@ -9115,7 +9115,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => { .and_then(|paths| paths.into_iter().next().ok_or(())) .map_err(|_| Bolt12SemanticError::MissingPaths)?; - let builder = RefundBuilder::deriving_payer_id( + let builder = RefundBuilder::deriving_signing_pubkey( node_id, expanded_key, nonce, secp_ctx, amount_msats, payment_id )? .chain_hash($self.chain_hash) @@ -9197,7 +9197,7 @@ where /// # Limitations /// /// Requires a direct connection to an introduction node in [`Offer::paths`] or to - /// [`Offer::signing_pubkey`], if empty. A similar restriction applies to the responding + /// [`Offer::issuer_signing_pubkey`], if empty. A similar restriction applies to the responding /// [`Bolt12Invoice::payment_paths`]. /// /// # Errors @@ -9226,8 +9226,8 @@ where let secp_ctx = &self.secp_ctx; let nonce = Nonce::from_entropy_source(entropy); - let builder: InvoiceRequestBuilder = offer - .request_invoice_deriving_payer_id(expanded_key, nonce, secp_ctx, payment_id)? + let builder: InvoiceRequestBuilder = offer + .request_invoice_deriving_signing_pubkey(expanded_key, nonce, secp_ctx, payment_id)? .into(); let builder = builder.chain_hash(self.chain_hash)?; @@ -9288,10 +9288,10 @@ where let message = OffersMessage::InvoiceRequest(invoice_request.clone()); pending_offers_messages.push((message, instructions)); }); - } else if let Some(signing_pubkey) = invoice_request.signing_pubkey() { + } else if let Some(node_id) = invoice_request.issuer_signing_pubkey() { for reply_path in reply_paths { let instructions = MessageSendInstructions::WithSpecifiedReplyPath { - destination: Destination::Node(signing_pubkey), + destination: Destination::Node(node_id), reply_path, }; let message = OffersMessage::InvoiceRequest(invoice_request.clone()); @@ -9299,7 +9299,7 @@ where } } else { debug_assert!(false); - return Err(Bolt12SemanticError::MissingSigningPubkey); + return Err(Bolt12SemanticError::MissingIssuerSigningPubkey); } Ok(()) @@ -9315,9 +9315,9 @@ where /// # Limitations /// /// Requires a direct connection to an introduction node in [`Refund::paths`] or to - /// [`Refund::payer_id`], if empty. This request is best effort; an invoice will be sent to each - /// node meeting the aforementioned criteria, but there's no guarantee that they will be - /// received and no retries will be made. + /// [`Refund::payer_signing_pubkey`], if empty. This request is best effort; an invoice will be + /// sent to each node meeting the aforementioned criteria, but there's no guarantee that they + /// will be received and no retries will be made. /// /// # Errors /// @@ -9378,7 +9378,7 @@ where if refund.paths().is_empty() { for reply_path in reply_paths { let instructions = MessageSendInstructions::WithSpecifiedReplyPath { - destination: Destination::Node(refund.payer_id()), + destination: Destination::Node(refund.payer_signing_pubkey()), reply_path, }; let message = OffersMessage::Invoice(invoice.clone()); diff --git a/lightning/src/ln/offers_tests.rs b/lightning/src/ln/offers_tests.rs index 3b9317e467..b8e6fca5bb 100644 --- a/lightning/src/ln/offers_tests.rs +++ b/lightning/src/ln/offers_tests.rs @@ -305,7 +305,7 @@ fn prefers_non_tor_nodes_in_blinded_paths() { .create_offer_builder(None).unwrap() .amount_msats(10_000_000) .build().unwrap(); - assert_ne!(offer.signing_pubkey(), Some(bob_id)); + assert_ne!(offer.issuer_signing_pubkey(), Some(bob_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { let introduction_node_id = resolve_introduction_node(david, &path); @@ -321,7 +321,7 @@ fn prefers_non_tor_nodes_in_blinded_paths() { .create_offer_builder(None).unwrap() .amount_msats(10_000_000) .build().unwrap(); - assert_ne!(offer.signing_pubkey(), Some(bob_id)); + assert_ne!(offer.issuer_signing_pubkey(), Some(bob_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { let introduction_node_id = resolve_introduction_node(david, &path); @@ -372,7 +372,7 @@ fn prefers_more_connected_nodes_in_blinded_paths() { .create_offer_builder(None).unwrap() .amount_msats(10_000_000) .build().unwrap(); - assert_ne!(offer.signing_pubkey(), Some(bob_id)); + assert_ne!(offer.issuer_signing_pubkey(), Some(bob_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { let introduction_node_id = resolve_introduction_node(david, &path); @@ -541,7 +541,7 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() { .unwrap() .amount_msats(10_000_000) .build().unwrap(); - assert_ne!(offer.signing_pubkey(), Some(alice_id)); + assert_ne!(offer.issuer_signing_pubkey(), Some(alice_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(bob_id)); @@ -566,13 +566,13 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() { let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext { offer_id: offer.id(), invoice_request: InvoiceRequestFields { - payer_id: invoice_request.payer_id(), + payer_signing_pubkey: invoice_request.payer_signing_pubkey(), quantity: None, payer_note_truncated: None, }, }); assert_eq!(invoice_request.amount_msats(), None); - assert_ne!(invoice_request.payer_id(), david_id); + assert_ne!(invoice_request.payer_signing_pubkey(), david_id); assert_eq!(reply_path.introduction_node(), &IntroductionNode::NodeId(charlie_id)); let onion_message = alice.onion_messenger.next_onion_message_for_peer(charlie_id).unwrap(); @@ -651,7 +651,7 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() { .build().unwrap(); assert_eq!(refund.amount_msats(), 10_000_000); assert_eq!(refund.absolute_expiry(), Some(absolute_expiry)); - assert_ne!(refund.payer_id(), david_id); + assert_ne!(refund.payer_signing_pubkey(), david_id); assert!(!refund.paths().is_empty()); for path in refund.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(charlie_id)); @@ -709,7 +709,7 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() { .create_offer_builder(None).unwrap() .amount_msats(10_000_000) .build().unwrap(); - assert_ne!(offer.signing_pubkey(), Some(alice_id)); + assert_ne!(offer.issuer_signing_pubkey(), Some(alice_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(alice_id)); @@ -726,13 +726,13 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() { let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext { offer_id: offer.id(), invoice_request: InvoiceRequestFields { - payer_id: invoice_request.payer_id(), + payer_signing_pubkey: invoice_request.payer_signing_pubkey(), quantity: None, payer_note_truncated: None, }, }); assert_eq!(invoice_request.amount_msats(), None); - assert_ne!(invoice_request.payer_id(), bob_id); + assert_ne!(invoice_request.payer_signing_pubkey(), bob_id); assert_eq!(reply_path.introduction_node(), &IntroductionNode::NodeId(bob_id)); let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap(); @@ -779,7 +779,7 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() { .build().unwrap(); assert_eq!(refund.amount_msats(), 10_000_000); assert_eq!(refund.absolute_expiry(), Some(absolute_expiry)); - assert_ne!(refund.payer_id(), bob_id); + assert_ne!(refund.payer_signing_pubkey(), bob_id); assert!(!refund.paths().is_empty()); for path in refund.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(bob_id)); @@ -832,7 +832,7 @@ fn pays_for_offer_without_blinded_paths() { .clear_paths() .amount_msats(10_000_000) .build().unwrap(); - assert_eq!(offer.signing_pubkey(), Some(alice_id)); + assert_eq!(offer.issuer_signing_pubkey(), Some(alice_id)); assert!(offer.paths().is_empty()); let payment_id = PaymentId([1; 32]); @@ -846,7 +846,7 @@ fn pays_for_offer_without_blinded_paths() { let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext { offer_id: offer.id(), invoice_request: InvoiceRequestFields { - payer_id: invoice_request.payer_id(), + payer_signing_pubkey: invoice_request.payer_signing_pubkey(), quantity: None, payer_note_truncated: None, }, @@ -886,7 +886,7 @@ fn pays_for_refund_without_blinded_paths() { .unwrap() .clear_paths() .build().unwrap(); - assert_eq!(refund.payer_id(), bob_id); + assert_eq!(refund.payer_signing_pubkey(), bob_id); assert!(refund.paths().is_empty()); expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id); @@ -955,7 +955,7 @@ fn send_invoice_requests_with_distinct_reply_path() { .unwrap() .amount_msats(10_000_000) .build().unwrap(); - assert_ne!(offer.signing_pubkey(), Some(alice_id)); + assert_ne!(offer.issuer_signing_pubkey(), Some(alice_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(bob_id)); @@ -1040,7 +1040,7 @@ fn send_invoice_for_refund_with_distinct_reply_path() { .create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None) .unwrap() .build().unwrap(); - assert_ne!(refund.payer_id(), alice_id); + assert_ne!(refund.payer_signing_pubkey(), alice_id); for path in refund.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(bob_id)); } @@ -1090,7 +1090,7 @@ fn creates_and_pays_for_offer_with_retry() { .create_offer_builder(None).unwrap() .amount_msats(10_000_000) .build().unwrap(); - assert_ne!(offer.signing_pubkey(), Some(alice_id)); + assert_ne!(offer.issuer_signing_pubkey(), Some(alice_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(alice_id)); @@ -1112,13 +1112,13 @@ fn creates_and_pays_for_offer_with_retry() { let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext { offer_id: offer.id(), invoice_request: InvoiceRequestFields { - payer_id: invoice_request.payer_id(), + payer_signing_pubkey: invoice_request.payer_signing_pubkey(), quantity: None, payer_note_truncated: None, }, }); assert_eq!(invoice_request.amount_msats(), None); - assert_ne!(invoice_request.payer_id(), bob_id); + assert_ne!(invoice_request.payer_signing_pubkey(), bob_id); assert_eq!(reply_path.introduction_node(), &IntroductionNode::NodeId(bob_id)); let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap(); bob.onion_messenger.handle_onion_message(alice_id, &onion_message); @@ -1176,7 +1176,7 @@ fn pays_bolt12_invoice_asynchronously() { let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext { offer_id: offer.id(), invoice_request: InvoiceRequestFields { - payer_id: invoice_request.payer_id(), + payer_signing_pubkey: invoice_request.payer_signing_pubkey(), quantity: None, payer_note_truncated: None, }, @@ -1248,7 +1248,7 @@ fn creates_offer_with_blinded_path_using_unannounced_introduction_node() { .create_offer_builder(None).unwrap() .amount_msats(10_000_000) .build().unwrap(); - assert_ne!(offer.signing_pubkey(), Some(alice_id)); + assert_ne!(offer.issuer_signing_pubkey(), Some(alice_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(bob_id)); @@ -1265,12 +1265,12 @@ fn creates_offer_with_blinded_path_using_unannounced_introduction_node() { let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext { offer_id: offer.id(), invoice_request: InvoiceRequestFields { - payer_id: invoice_request.payer_id(), + payer_signing_pubkey: invoice_request.payer_signing_pubkey(), quantity: None, payer_note_truncated: None, }, }); - assert_ne!(invoice_request.payer_id(), bob_id); + assert_ne!(invoice_request.payer_signing_pubkey(), bob_id); assert_eq!(reply_path.introduction_node(), &IntroductionNode::NodeId(alice_id)); let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap(); @@ -1315,7 +1315,7 @@ fn creates_refund_with_blinded_path_using_unannounced_introduction_node() { .create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None) .unwrap() .build().unwrap(); - assert_ne!(refund.payer_id(), bob_id); + assert_ne!(refund.payer_signing_pubkey(), bob_id); assert!(!refund.paths().is_empty()); for path in refund.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(alice_id)); @@ -1379,7 +1379,7 @@ fn fails_authentication_when_handling_invoice_request() { .amount_msats(10_000_000) .build().unwrap(); assert_eq!(offer.metadata(), None); - assert_ne!(offer.signing_pubkey(), Some(alice_id)); + assert_ne!(offer.issuer_signing_pubkey(), Some(alice_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(bob_id)); @@ -1411,7 +1411,7 @@ fn fails_authentication_when_handling_invoice_request() { let (invoice_request, reply_path) = extract_invoice_request(alice, &onion_message); assert_eq!(invoice_request.amount_msats(), None); - assert_ne!(invoice_request.payer_id(), david_id); + assert_ne!(invoice_request.payer_signing_pubkey(), david_id); assert_eq!(reply_path.introduction_node(), &IntroductionNode::NodeId(charlie_id)); assert_eq!(alice.onion_messenger.next_onion_message_for_peer(charlie_id), None); @@ -1441,7 +1441,7 @@ fn fails_authentication_when_handling_invoice_request() { let (invoice_request, reply_path) = extract_invoice_request(alice, &onion_message); assert_eq!(invoice_request.amount_msats(), None); - assert_ne!(invoice_request.payer_id(), david_id); + assert_ne!(invoice_request.payer_signing_pubkey(), david_id); assert_eq!(reply_path.introduction_node(), &IntroductionNode::NodeId(charlie_id)); assert_eq!(alice.onion_messenger.next_onion_message_for_peer(charlie_id), None); @@ -1490,7 +1490,7 @@ fn fails_authentication_when_handling_invoice_for_offer() { .unwrap() .amount_msats(10_000_000) .build().unwrap(); - assert_ne!(offer.signing_pubkey(), Some(alice_id)); + assert_ne!(offer.issuer_signing_pubkey(), Some(alice_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(bob_id)); @@ -1543,7 +1543,7 @@ fn fails_authentication_when_handling_invoice_for_offer() { let (invoice_request, reply_path) = extract_invoice_request(alice, &onion_message); assert_eq!(invoice_request.amount_msats(), None); - assert_ne!(invoice_request.payer_id(), david_id); + assert_ne!(invoice_request.payer_signing_pubkey(), david_id); assert_eq!(reply_path.introduction_node(), &IntroductionNode::NodeId(charlie_id)); let onion_message = alice.onion_messenger.next_onion_message_for_peer(charlie_id).unwrap(); @@ -1598,7 +1598,7 @@ fn fails_authentication_when_handling_invoice_for_refund() { .create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None) .unwrap() .build().unwrap(); - assert_ne!(refund.payer_id(), david_id); + assert_ne!(refund.payer_signing_pubkey(), david_id); assert!(!refund.paths().is_empty()); for path in refund.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(charlie_id)); @@ -1632,7 +1632,7 @@ fn fails_authentication_when_handling_invoice_for_refund() { .create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None) .unwrap() .build().unwrap(); - assert_ne!(refund.payer_id(), david_id); + assert_ne!(refund.payer_signing_pubkey(), david_id); assert!(!refund.paths().is_empty()); for path in refund.paths() { assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(charlie_id)); diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs index 865939b078..648c0fba65 100644 --- a/lightning/src/offers/invoice.rs +++ b/lightning/src/offers/invoice.rs @@ -714,6 +714,16 @@ macro_rules! invoice_accessors { ($self: ident, $contents: expr) => { $contents.supported_quantity() } + /// The public key used by the recipient to sign invoices. + /// + /// From [`Offer::issuer_signing_pubkey`] and may be `None`; also `None` if the invoice was + /// created in response to a [`Refund`]. + /// + /// [`Offer::issuer_signing_pubkey`]: crate::offers::offer::Offer::issuer_signing_pubkey + pub fn issuer_signing_pubkey(&$self) -> Option { + $contents.issuer_signing_pubkey() + } + /// An unpredictable series of bytes from the payer. /// /// From [`InvoiceRequest::payer_metadata`] or [`Refund::payer_metadata`]. @@ -739,8 +749,8 @@ macro_rules! invoice_accessors { ($self: ident, $contents: expr) => { /// refund in case there are no [`message_paths`]. /// /// [`message_paths`]: Self::message_paths - pub fn payer_id(&$self) -> PublicKey { - $contents.payer_id() + pub fn payer_signing_pubkey(&$self) -> PublicKey { + $contents.payer_signing_pubkey() } /// A payer-provided note reflected back in the invoice. @@ -761,13 +771,36 @@ macro_rules! invoice_accessors { ($self: ident, $contents: expr) => { } } } +macro_rules! invoice_accessors_signing_pubkey { + ($self: ident, $contents: expr, $invoice_type: ty) => +{ + /// A typically transient public key corresponding to the key used to sign the invoice. + /// + /// If the invoices was created in response to an [`Offer`], then this will be: + /// - [`Offer::issuer_signing_pubkey`] if it's `Some`, otherwise + /// - the final blinded node id from a [`BlindedMessagePath`] in [`Offer::paths`] if `None`. + /// + /// If the invoice was created in response to a [`Refund`], then it is a valid pubkey chosen by + /// the recipient. + /// + /// [`Offer`]: crate::offers::offer::Offer + /// [`Offer::issuer_signing_pubkey`]: crate::offers::offer::Offer::issuer_signing_pubkey + /// [`Offer::paths`]: crate::offers::offer::Offer::paths + /// [`Refund`]: crate::offers::refund::Refund + pub fn signing_pubkey(&$self) -> PublicKey { + $contents.signing_pubkey() + } +} } + impl UnsignedBolt12Invoice { - invoice_accessors_common!(self, self.contents, Bolt12Invoice); + invoice_accessors_common!(self, self.contents, UnsignedBolt12Invoice); + invoice_accessors_signing_pubkey!(self, self.contents, UnsignedBolt12Invoice); invoice_accessors!(self, self.contents); } impl Bolt12Invoice { invoice_accessors_common!(self, self.contents, Bolt12Invoice); + invoice_accessors_signing_pubkey!(self, self.contents, Bolt12Invoice); invoice_accessors!(self, self.contents); /// Signature of the invoice verified using [`Bolt12Invoice::signing_pubkey`]. @@ -954,6 +987,15 @@ impl InvoiceContents { } } + fn issuer_signing_pubkey(&self) -> Option { + match self { + InvoiceContents::ForOffer { invoice_request, .. } => { + invoice_request.inner.offer.issuer_signing_pubkey() + }, + InvoiceContents::ForRefund { .. } => None, + } + } + fn payer_metadata(&self) -> &[u8] { match self { InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.metadata(), @@ -975,10 +1017,10 @@ impl InvoiceContents { } } - fn payer_id(&self) -> PublicKey { + fn payer_signing_pubkey(&self) -> PublicKey { match self { - InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.payer_id(), - InvoiceContents::ForRefund { refund, .. } => refund.payer_id(), + InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.payer_signing_pubkey(), + InvoiceContents::ForRefund { refund, .. } => refund.payer_signing_pubkey(), } } @@ -1057,9 +1099,9 @@ impl InvoiceContents { }); let tlv_stream = offer_records.chain(invreq_records); - let payer_id = self.payer_id(); + let signing_pubkey = self.payer_signing_pubkey(); signer::verify_payer_metadata( - metadata.as_ref(), key, iv_bytes, payer_id, tlv_stream, secp_ctx, + metadata.as_ref(), key, iv_bytes, signing_pubkey, tlv_stream, secp_ctx, ) } @@ -1335,7 +1377,7 @@ impl TryFrom for InvoiceContents { check_invoice_signing_pubkey(&fields.signing_pubkey, &offer_tlv_stream)?; - if offer_tlv_stream.node_id.is_none() && offer_tlv_stream.paths.is_none() { + if offer_tlv_stream.issuer_id.is_none() && offer_tlv_stream.paths.is_none() { let refund = RefundContents::try_from( (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) )?; @@ -1373,9 +1415,9 @@ pub(super) fn construct_payment_paths( pub(super) fn check_invoice_signing_pubkey( invoice_signing_pubkey: &PublicKey, offer_tlv_stream: &OfferTlvStream ) -> Result<(), Bolt12SemanticError> { - match (&offer_tlv_stream.node_id, &offer_tlv_stream.paths) { - (Some(expected_signing_pubkey), _) => { - if invoice_signing_pubkey != expected_signing_pubkey { + match (&offer_tlv_stream.issuer_id, &offer_tlv_stream.paths) { + (Some(issuer_signing_pubkey), _) => { + if invoice_signing_pubkey != issuer_signing_pubkey { return Err(Bolt12SemanticError::InvalidSigningPubkey); } }, @@ -1484,7 +1526,7 @@ mod tests { assert_eq!(unsigned_invoice.amount_msats(), 1000); assert_eq!(unsigned_invoice.invoice_request_features(), &InvoiceRequestFeatures::empty()); assert_eq!(unsigned_invoice.quantity(), None); - assert_eq!(unsigned_invoice.payer_id(), payer_pubkey()); + assert_eq!(unsigned_invoice.payer_signing_pubkey(), payer_pubkey()); assert_eq!(unsigned_invoice.payer_note(), None); assert_eq!(unsigned_invoice.payment_paths(), payment_paths.as_slice()); assert_eq!(unsigned_invoice.created_at(), now); @@ -1526,7 +1568,7 @@ mod tests { assert_eq!(invoice.amount_msats(), 1000); assert_eq!(invoice.invoice_request_features(), &InvoiceRequestFeatures::empty()); assert_eq!(invoice.quantity(), None); - assert_eq!(invoice.payer_id(), payer_pubkey()); + assert_eq!(invoice.payer_signing_pubkey(), payer_pubkey()); assert_eq!(invoice.payer_note(), None); assert_eq!(invoice.payment_paths(), payment_paths.as_slice()); assert_eq!(invoice.created_at(), now); @@ -1561,7 +1603,7 @@ mod tests { paths: None, issuer: None, quantity_max: None, - node_id: Some(&recipient_pubkey()), + issuer_id: Some(&recipient_pubkey()), }, InvoiceRequestTlvStreamRef { chain: None, @@ -1624,7 +1666,7 @@ mod tests { assert_eq!(invoice.amount_msats(), 1000); assert_eq!(invoice.invoice_request_features(), &InvoiceRequestFeatures::empty()); assert_eq!(invoice.quantity(), None); - assert_eq!(invoice.payer_id(), payer_pubkey()); + assert_eq!(invoice.payer_signing_pubkey(), payer_pubkey()); assert_eq!(invoice.payer_note(), None); assert_eq!(invoice.payment_paths(), payment_paths.as_slice()); assert_eq!(invoice.created_at(), now); @@ -1654,7 +1696,7 @@ mod tests { paths: None, issuer: None, quantity_max: None, - node_id: None, + issuer_id: None, }, InvoiceRequestTlvStreamRef { chain: None, @@ -1791,7 +1833,7 @@ mod tests { let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx) .amount_msats(1000) - // Omit the path so that node_id is used for the signing pubkey instead of deriving + // Omit the path so that node_id is used for the signing pubkey instead of deriving it .build().unwrap(); let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap() @@ -2359,7 +2401,7 @@ mod tests { }; let invoice = OfferBuilder::new(recipient_pubkey()) - .clear_signing_pubkey() + .clear_issuer_signing_pubkey() .amount_msats(1000) .path(paths[0].clone()) .path(paths[1].clone()) @@ -2381,7 +2423,7 @@ mod tests { } let invoice = OfferBuilder::new(recipient_pubkey()) - .clear_signing_pubkey() + .clear_issuer_signing_pubkey() .amount_msats(1000) .path(paths[0].clone()) .path(paths[1].clone()) diff --git a/lightning/src/offers/invoice_macros.rs b/lightning/src/offers/invoice_macros.rs index 3037ccfa80..c41ff8b243 100644 --- a/lightning/src/offers/invoice_macros.rs +++ b/lightning/src/offers/invoice_macros.rs @@ -139,11 +139,6 @@ macro_rules! invoice_accessors_common { ($self: ident, $contents: expr, $invoice pub fn invoice_features(&$self) -> &Bolt12InvoiceFeatures { $contents.features() } - - /// The public key corresponding to the key used to sign the invoice. - pub fn signing_pubkey(&$self) -> PublicKey { - $contents.signing_pubkey() - } } } pub(super) use invoice_accessors_common; diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs index dc2fd4bf1d..95100a2a2e 100644 --- a/lightning/src/offers/invoice_request.rs +++ b/lightning/src/offers/invoice_request.rs @@ -36,8 +36,8 @@ //! let pubkey = PublicKey::from(keys); //! let mut buffer = Vec::new(); //! -//! # use lightning::offers::invoice_request::{ExplicitPayerId, InvoiceRequestBuilder}; -//! # >::from( +//! # use lightning::offers::invoice_request::{ExplicitPayerSigningPubkey, InvoiceRequestBuilder}; +//! # >::from( //! "lno1qcp4256ypq" //! .parse::()? //! .request_invoice(vec![42; 64], pubkey)? @@ -102,11 +102,11 @@ pub(super) const IV_BYTES: &[u8; IV_LEN] = b"LDK Invreq ~~~~~"; /// This is not exported to bindings users as builder patterns don't map outside of move semantics. /// /// [module-level documentation]: self -pub struct InvoiceRequestBuilder<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> { +pub struct InvoiceRequestBuilder<'a, 'b, P: PayerSigningPubkeyStrategy, T: secp256k1::Signing> { offer: &'a Offer, - invoice_request: InvoiceRequestContentsWithoutPayerId, - payer_id: Option, - payer_id_strategy: core::marker::PhantomData

, + invoice_request: InvoiceRequestContentsWithoutPayerSigningPubkey, + payer_signing_pubkey: Option, + payer_signing_pubkey_strategy: core::marker::PhantomData

, secp_ctx: Option<&'b Secp256k1>, } @@ -116,11 +116,11 @@ pub struct InvoiceRequestBuilder<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signi /// /// [module-level documentation]: self #[cfg(c_bindings)] -pub struct InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> { +pub struct InvoiceRequestWithExplicitPayerSigningPubkeyBuilder<'a, 'b> { offer: &'a Offer, - invoice_request: InvoiceRequestContentsWithoutPayerId, - payer_id: Option, - payer_id_strategy: core::marker::PhantomData, + invoice_request: InvoiceRequestContentsWithoutPayerSigningPubkey, + payer_signing_pubkey: Option, + payer_signing_pubkey_strategy: core::marker::PhantomData, secp_ctx: Option<&'b Secp256k1>, } @@ -130,47 +130,47 @@ pub struct InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> { /// /// [module-level documentation]: self #[cfg(c_bindings)] -pub struct InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b> { +pub struct InvoiceRequestWithDerivedPayerSigningPubkeyBuilder<'a, 'b> { offer: &'a Offer, - invoice_request: InvoiceRequestContentsWithoutPayerId, - payer_id: Option, - payer_id_strategy: core::marker::PhantomData, + invoice_request: InvoiceRequestContentsWithoutPayerSigningPubkey, + payer_signing_pubkey: Option, + payer_signing_pubkey_strategy: core::marker::PhantomData, secp_ctx: Option<&'b Secp256k1>, } -/// Indicates how [`InvoiceRequest::payer_id`] will be set. +/// Indicates how [`InvoiceRequest::payer_signing_pubkey`] will be set. /// /// This is not exported to bindings users as builder patterns don't map outside of move semantics. -pub trait PayerIdStrategy {} +pub trait PayerSigningPubkeyStrategy {} -/// [`InvoiceRequest::payer_id`] will be explicitly set. +/// [`InvoiceRequest::payer_signing_pubkey`] will be explicitly set. /// /// This is not exported to bindings users as builder patterns don't map outside of move semantics. -pub struct ExplicitPayerId {} +pub struct ExplicitPayerSigningPubkey {} -/// [`InvoiceRequest::payer_id`] will be derived. +/// [`InvoiceRequest::payer_signing_pubkey`] will be derived. /// /// This is not exported to bindings users as builder patterns don't map outside of move semantics. -pub struct DerivedPayerId {} +pub struct DerivedPayerSigningPubkey {} -impl PayerIdStrategy for ExplicitPayerId {} -impl PayerIdStrategy for DerivedPayerId {} +impl PayerSigningPubkeyStrategy for ExplicitPayerSigningPubkey {} +impl PayerSigningPubkeyStrategy for DerivedPayerSigningPubkey {} -macro_rules! invoice_request_explicit_payer_id_builder_methods { ($self: ident, $self_type: ty) => { +macro_rules! invoice_request_explicit_payer_signing_pubkey_builder_methods { ($self: ident, $self_type: ty) => { #[cfg_attr(c_bindings, allow(dead_code))] - pub(super) fn new(offer: &'a Offer, metadata: Vec, payer_id: PublicKey) -> Self { + pub(super) fn new(offer: &'a Offer, metadata: Vec, signing_pubkey: PublicKey) -> Self { Self { offer, invoice_request: Self::create_contents(offer, Metadata::Bytes(metadata)), - payer_id: Some(payer_id), - payer_id_strategy: core::marker::PhantomData, + payer_signing_pubkey: Some(signing_pubkey), + payer_signing_pubkey_strategy: core::marker::PhantomData, secp_ctx: None, } } #[cfg_attr(c_bindings, allow(dead_code))] pub(super) fn deriving_metadata( - offer: &'a Offer, payer_id: PublicKey, expanded_key: &ExpandedKey, nonce: Nonce, + offer: &'a Offer, signing_pubkey: PublicKey, expanded_key: &ExpandedKey, nonce: Nonce, payment_id: PaymentId, ) -> Self { let payment_id = Some(payment_id); @@ -179,8 +179,8 @@ macro_rules! invoice_request_explicit_payer_id_builder_methods { ($self: ident, Self { offer, invoice_request: Self::create_contents(offer, metadata), - payer_id: Some(payer_id), - payer_id_strategy: core::marker::PhantomData, + payer_signing_pubkey: Some(signing_pubkey), + payer_signing_pubkey_strategy: core::marker::PhantomData, secp_ctx: None, } } @@ -194,11 +194,11 @@ macro_rules! invoice_request_explicit_payer_id_builder_methods { ($self: ident, } } } -macro_rules! invoice_request_derived_payer_id_builder_methods { ( +macro_rules! invoice_request_derived_payer_signing_pubkey_builder_methods { ( $self: ident, $self_type: ty, $secp_context: ty ) => { #[cfg_attr(c_bindings, allow(dead_code))] - pub(super) fn deriving_payer_id( + pub(super) fn deriving_signing_pubkey( offer: &'a Offer, expanded_key: &ExpandedKey, nonce: Nonce, secp_ctx: &'b Secp256k1<$secp_context>, payment_id: PaymentId ) -> Self { @@ -208,8 +208,8 @@ macro_rules! invoice_request_derived_payer_id_builder_methods { ( Self { offer, invoice_request: Self::create_contents(offer, metadata), - payer_id: None, - payer_id_strategy: core::marker::PhantomData, + payer_signing_pubkey: None, + payer_signing_pubkey_strategy: core::marker::PhantomData, secp_ctx: Some(secp_ctx), } } @@ -236,9 +236,9 @@ macro_rules! invoice_request_builder_methods { ( $self: ident, $self_type: ty, $return_type: ty, $return_value: expr, $secp_context: ty $(, $self_mut: tt)? ) => { #[cfg_attr(c_bindings, allow(dead_code))] - fn create_contents(offer: &Offer, metadata: Metadata) -> InvoiceRequestContentsWithoutPayerId { + fn create_contents(offer: &Offer, metadata: Metadata) -> InvoiceRequestContentsWithoutPayerSigningPubkey { let offer = offer.contents.clone(); - InvoiceRequestContentsWithoutPayerId { + InvoiceRequestContentsWithoutPayerSigningPubkey { payer: PayerContents(metadata), offer, chain: None, amount_msats: None, features: InvoiceRequestFeatures::empty(), quantity: None, payer_note: None, } @@ -343,7 +343,7 @@ macro_rules! invoice_request_builder_methods { ( debug_assert!(tlv_stream.2.payer_id.is_none()); tlv_stream.0.metadata = None; if !metadata.derives_payer_keys() { - tlv_stream.2.payer_id = $self.payer_id.as_ref(); + tlv_stream.2.payer_id = $self.payer_signing_pubkey.as_ref(); } let (derived_metadata, derived_keys) = @@ -351,23 +351,23 @@ macro_rules! invoice_request_builder_methods { ( metadata = derived_metadata; keys = derived_keys; if let Some(keys) = keys { - debug_assert!($self.payer_id.is_none()); - $self.payer_id = Some(keys.public_key()); + debug_assert!($self.payer_signing_pubkey.is_none()); + $self.payer_signing_pubkey = Some(keys.public_key()); } $self.invoice_request.payer.0 = metadata; } debug_assert!($self.invoice_request.payer.0.as_bytes().is_some()); - debug_assert!($self.payer_id.is_some()); - let payer_id = $self.payer_id.unwrap(); + debug_assert!($self.payer_signing_pubkey.is_some()); + let payer_signing_pubkey = $self.payer_signing_pubkey.unwrap(); let invoice_request = InvoiceRequestContents { #[cfg(not(c_bindings))] inner: $self.invoice_request, #[cfg(c_bindings)] inner: $self.invoice_request.clone(), - payer_id, + payer_signing_pubkey, }; let unsigned_invoice_request = UnsignedInvoiceRequest::new($self.offer, invoice_request); @@ -410,15 +410,15 @@ macro_rules! invoice_request_builder_test_methods { ( } } } -impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, ExplicitPayerId, T> { - invoice_request_explicit_payer_id_builder_methods!(self, Self); +impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, ExplicitPayerSigningPubkey, T> { + invoice_request_explicit_payer_signing_pubkey_builder_methods!(self, Self); } -impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T> { - invoice_request_derived_payer_id_builder_methods!(self, Self, T); +impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerSigningPubkey, T> { + invoice_request_derived_payer_signing_pubkey_builder_methods!(self, Self, T); } -impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, P, T> { +impl<'a, 'b, P: PayerSigningPubkeyStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, P, T> { invoice_request_builder_methods!(self, Self, Self, self, T, mut); #[cfg(test)] @@ -426,55 +426,55 @@ impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a } #[cfg(all(c_bindings, not(test)))] -impl<'a, 'b> InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> { - invoice_request_explicit_payer_id_builder_methods!(self, &mut Self); +impl<'a, 'b> InvoiceRequestWithExplicitPayerSigningPubkeyBuilder<'a, 'b> { + invoice_request_explicit_payer_signing_pubkey_builder_methods!(self, &mut Self); invoice_request_builder_methods!(self, &mut Self, (), (), secp256k1::All); } #[cfg(all(c_bindings, test))] -impl<'a, 'b> InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> { - invoice_request_explicit_payer_id_builder_methods!(self, &mut Self); +impl<'a, 'b> InvoiceRequestWithExplicitPayerSigningPubkeyBuilder<'a, 'b> { + invoice_request_explicit_payer_signing_pubkey_builder_methods!(self, &mut Self); invoice_request_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All); invoice_request_builder_test_methods!(self, &mut Self, &mut Self, self); } #[cfg(all(c_bindings, not(test)))] -impl<'a, 'b> InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b> { - invoice_request_derived_payer_id_builder_methods!(self, &mut Self, secp256k1::All); +impl<'a, 'b> InvoiceRequestWithDerivedPayerSigningPubkeyBuilder<'a, 'b> { + invoice_request_derived_payer_signing_pubkey_builder_methods!(self, &mut Self, secp256k1::All); invoice_request_builder_methods!(self, &mut Self, (), (), secp256k1::All); } #[cfg(all(c_bindings, test))] -impl<'a, 'b> InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b> { - invoice_request_derived_payer_id_builder_methods!(self, &mut Self, secp256k1::All); +impl<'a, 'b> InvoiceRequestWithDerivedPayerSigningPubkeyBuilder<'a, 'b> { + invoice_request_derived_payer_signing_pubkey_builder_methods!(self, &mut Self, secp256k1::All); invoice_request_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All); invoice_request_builder_test_methods!(self, &mut Self, &mut Self, self); } #[cfg(c_bindings)] -impl<'a, 'b> From> -for InvoiceRequestBuilder<'a, 'b, ExplicitPayerId, secp256k1::All> { - fn from(builder: InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b>) -> Self { - let InvoiceRequestWithExplicitPayerIdBuilder { - offer, invoice_request, payer_id, payer_id_strategy, secp_ctx, +impl<'a, 'b> From> +for InvoiceRequestBuilder<'a, 'b, ExplicitPayerSigningPubkey, secp256k1::All> { + fn from(builder: InvoiceRequestWithExplicitPayerSigningPubkeyBuilder<'a, 'b>) -> Self { + let InvoiceRequestWithExplicitPayerSigningPubkeyBuilder { + offer, invoice_request, payer_signing_pubkey, payer_signing_pubkey_strategy, secp_ctx, } = builder; Self { - offer, invoice_request, payer_id, payer_id_strategy, secp_ctx, + offer, invoice_request, payer_signing_pubkey, payer_signing_pubkey_strategy, secp_ctx, } } } #[cfg(c_bindings)] -impl<'a, 'b> From> -for InvoiceRequestBuilder<'a, 'b, DerivedPayerId, secp256k1::All> { - fn from(builder: InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b>) -> Self { - let InvoiceRequestWithDerivedPayerIdBuilder { - offer, invoice_request, payer_id, payer_id_strategy, secp_ctx, +impl<'a, 'b> From> +for InvoiceRequestBuilder<'a, 'b, DerivedPayerSigningPubkey, secp256k1::All> { + fn from(builder: InvoiceRequestWithDerivedPayerSigningPubkeyBuilder<'a, 'b>) -> Self { + let InvoiceRequestWithDerivedPayerSigningPubkeyBuilder { + offer, invoice_request, payer_signing_pubkey, payer_signing_pubkey_strategy, secp_ctx, } = builder; Self { - offer, invoice_request, payer_id, payer_id_strategy, secp_ctx, + offer, invoice_request, payer_signing_pubkey, payer_signing_pubkey_strategy, secp_ctx, } } } @@ -548,7 +548,7 @@ macro_rules! unsigned_invoice_request_sign_method { ( pub fn sign( $($self_mut)* $self: $self_type, sign: F ) -> Result { - let pubkey = $self.contents.payer_id; + let pubkey = $self.contents.payer_signing_pubkey; let signature = merkle::sign_message(sign, &$self, pubkey)?; // Append the signature TLV record to the bytes. @@ -630,13 +630,13 @@ pub struct VerifiedInvoiceRequest { #[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq))] pub(super) struct InvoiceRequestContents { - pub(super) inner: InvoiceRequestContentsWithoutPayerId, - payer_id: PublicKey, + pub(super) inner: InvoiceRequestContentsWithoutPayerSigningPubkey, + payer_signing_pubkey: PublicKey, } #[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq))] -pub(super) struct InvoiceRequestContentsWithoutPayerId { +pub(super) struct InvoiceRequestContentsWithoutPayerSigningPubkey { pub(super) payer: PayerContents, pub(super) offer: OfferContents, chain: Option, @@ -648,9 +648,9 @@ pub(super) struct InvoiceRequestContentsWithoutPayerId { macro_rules! invoice_request_accessors { ($self: ident, $contents: expr) => { /// An unpredictable series of bytes, typically containing information about the derivation of - /// [`payer_id`]. + /// [`payer_signing_pubkey`]. /// - /// [`payer_id`]: Self::payer_id + /// [`payer_signing_pubkey`]: Self::payer_signing_pubkey pub fn payer_metadata(&$self) -> &[u8] { $contents.metadata() } @@ -679,8 +679,8 @@ macro_rules! invoice_request_accessors { ($self: ident, $contents: expr) => { } /// A possibly transient pubkey used to sign the invoice request. - pub fn payer_id(&$self) -> PublicKey { - $contents.payer_id() + pub fn payer_signing_pubkey(&$self) -> PublicKey { + $contents.payer_signing_pubkey() } /// A payer-provided note which will be seen by the recipient and reflected back in the invoice @@ -728,7 +728,7 @@ macro_rules! invoice_request_respond_with_explicit_signing_pubkey_methods { ( /// The `payment_paths` parameter is useful for maintaining the payment recipient's privacy. It /// must contain one or more elements ordered from most-preferred to least-preferred, if there's /// a preference. Note, however, that any privacy is lost if a public node id was used for - /// [`Offer::signing_pubkey`]. + /// [`Offer::issuer_signing_pubkey`]. /// /// Errors if the request contains unknown required features. /// @@ -749,9 +749,9 @@ macro_rules! invoice_request_respond_with_explicit_signing_pubkey_methods { ( return Err(Bolt12SemanticError::UnknownRequiredFeatures); } - let signing_pubkey = match $contents.contents.inner.offer.signing_pubkey() { + let signing_pubkey = match $contents.contents.inner.offer.issuer_signing_pubkey() { Some(signing_pubkey) => signing_pubkey, - None => return Err(Bolt12SemanticError::MissingSigningPubkey), + None => return Err(Bolt12SemanticError::MissingIssuerSigningPubkey), }; <$builder>::for_offer(&$contents, payment_paths, created_at, payment_hash, signing_pubkey) @@ -763,7 +763,7 @@ macro_rules! invoice_request_respond_with_explicit_signing_pubkey_methods { ( &$self, payment_paths: Vec, payment_hash: PaymentHash, created_at: core::time::Duration, signing_pubkey: PublicKey ) -> Result<$builder, Bolt12SemanticError> { - debug_assert!($contents.contents.inner.offer.signing_pubkey().is_none()); + debug_assert!($contents.contents.inner.offer.issuer_signing_pubkey().is_none()); if $contents.invoice_request_features().requires_unknown_bits() { return Err(Bolt12SemanticError::UnknownRequiredFeatures); @@ -856,9 +856,9 @@ impl InvoiceRequest { } impl InvoiceRequest { - /// Signature of the invoice request using [`payer_id`]. + /// Signature of the invoice request using [`payer_signing_pubkey`]. /// - /// [`payer_id`]: Self::payer_id + /// [`payer_signing_pubkey`]: Self::payer_signing_pubkey pub fn signature(&self) -> Signature { self.signature } @@ -914,9 +914,9 @@ macro_rules! invoice_request_respond_with_derived_signing_pubkey_methods { ( Some(keys) => keys, }; - match $contents.contents.inner.offer.signing_pubkey() { + match $contents.contents.inner.offer.issuer_signing_pubkey() { Some(signing_pubkey) => debug_assert_eq!(signing_pubkey, keys.public_key()), - None => return Err(Bolt12SemanticError::MissingSigningPubkey), + None => return Err(Bolt12SemanticError::MissingIssuerSigningPubkey), } <$builder>::for_offer_using_keys( @@ -939,14 +939,14 @@ impl VerifiedInvoiceRequest { pub(crate) fn fields(&self) -> InvoiceRequestFields { let InvoiceRequestContents { - payer_id, - inner: InvoiceRequestContentsWithoutPayerId { + payer_signing_pubkey, + inner: InvoiceRequestContentsWithoutPayerSigningPubkey { payer: _, offer: _, chain: _, amount_msats: _, features: _, quantity, payer_note }, } = &self.inner.contents; InvoiceRequestFields { - payer_id: *payer_id, + payer_signing_pubkey: *payer_signing_pubkey, quantity: *quantity, payer_note_truncated: payer_note.clone() .map(|mut s| { s.truncate(PAYER_NOTE_LIMIT); UntrustedString(s) }), @@ -975,8 +975,8 @@ impl InvoiceRequestContents { self.inner.quantity } - pub(super) fn payer_id(&self) -> PublicKey { - self.payer_id + pub(super) fn payer_signing_pubkey(&self) -> PublicKey { + self.payer_signing_pubkey } pub(super) fn payer_note(&self) -> Option { @@ -986,12 +986,12 @@ impl InvoiceRequestContents { pub(super) fn as_tlv_stream(&self) -> PartialInvoiceRequestTlvStreamRef { let (payer, offer, mut invoice_request) = self.inner.as_tlv_stream(); - invoice_request.payer_id = Some(&self.payer_id); + invoice_request.payer_id = Some(&self.payer_signing_pubkey); (payer, offer, invoice_request) } } -impl InvoiceRequestContentsWithoutPayerId { +impl InvoiceRequestContentsWithoutPayerSigningPubkey { pub(super) fn metadata(&self) -> &[u8] { self.payer.0.as_bytes().map(|bytes| bytes.as_slice()).unwrap_or(&[]) } @@ -1054,9 +1054,10 @@ impl Readable for InvoiceRequest { /// Valid type range for invoice_request TLV records. pub(super) const INVOICE_REQUEST_TYPES: core::ops::Range = 80..160; -/// TLV record type for [`InvoiceRequest::payer_id`] and [`Refund::payer_id`]. +/// TLV record type for [`InvoiceRequest::payer_signing_pubkey`] and +/// [`Refund::payer_signing_pubkey`]. /// -/// [`Refund::payer_id`]: crate::offers::refund::Refund::payer_id +/// [`Refund::payer_signing_pubkey`]: crate::offers::refund::Refund::payer_signing_pubkey pub(super) const INVOICE_REQUEST_PAYER_ID_TYPE: u64 = 88; // This TLV stream is used for both InvoiceRequest and Refund, but not all TLV records are valid for @@ -1139,7 +1140,7 @@ impl TryFrom> for InvoiceRequest { Some(signature) => signature, }; let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes); - merkle::verify_signature(&signature, &message, contents.payer_id)?; + merkle::verify_signature(&signature, &message, contents.payer_signing_pubkey)?; Ok(InvoiceRequest { bytes, contents, signature }) } @@ -1176,8 +1177,8 @@ impl TryFrom for InvoiceRequestContents { let features = features.unwrap_or_else(InvoiceRequestFeatures::empty); - let payer_id = match payer_id { - None => return Err(Bolt12SemanticError::MissingPayerId), + let payer_signing_pubkey = match payer_id { + None => return Err(Bolt12SemanticError::MissingPayerSigningPubkey), Some(payer_id) => payer_id, }; @@ -1186,10 +1187,10 @@ impl TryFrom for InvoiceRequestContents { } Ok(InvoiceRequestContents { - inner: InvoiceRequestContentsWithoutPayerId { + inner: InvoiceRequestContentsWithoutPayerSigningPubkey { payer, offer, chain, amount_msats: amount, features, quantity, payer_note, }, - payer_id, + payer_signing_pubkey, }) } } @@ -1200,7 +1201,7 @@ impl TryFrom for InvoiceRequestContents { #[derive(Clone, Debug, Eq, PartialEq)] pub struct InvoiceRequestFields { /// A possibly transient pubkey used to sign the invoice request. - pub payer_id: PublicKey, + pub payer_signing_pubkey: PublicKey, /// The quantity of the offer's item conforming to [`Offer::is_valid_quantity`]. pub quantity: Option, @@ -1216,7 +1217,7 @@ pub const PAYER_NOTE_LIMIT: usize = 512; impl Writeable for InvoiceRequestFields { fn write(&self, writer: &mut W) -> Result<(), io::Error> { write_tlv_fields!(writer, { - (0, self.payer_id, required), + (0, self.payer_signing_pubkey, required), (2, self.quantity.map(|v| HighZeroBytesDroppedBigSize(v)), option), (4, self.payer_note_truncated.as_ref().map(|s| WithoutLength(&s.0)), option), }); @@ -1227,13 +1228,13 @@ impl Writeable for InvoiceRequestFields { impl Readable for InvoiceRequestFields { fn read(reader: &mut R) -> Result { _init_and_read_len_prefixed_tlv_fields!(reader, { - (0, payer_id, required), + (0, payer_signing_pubkey, required), (2, quantity, (option, encoding: (u64, HighZeroBytesDroppedBigSize))), (4, payer_note_truncated, (option, encoding: (String, WithoutLength))), }); Ok(InvoiceRequestFields { - payer_id: payer_id.0.unwrap(), + payer_signing_pubkey: payer_signing_pubkey.0.unwrap(), quantity, payer_note_truncated: payer_note_truncated.map(|s| UntrustedString(s)), }) @@ -1297,12 +1298,12 @@ mod tests { assert_eq!(unsigned_invoice_request.paths(), &[]); assert_eq!(unsigned_invoice_request.issuer(), None); assert_eq!(unsigned_invoice_request.supported_quantity(), Quantity::One); - assert_eq!(unsigned_invoice_request.signing_pubkey(), Some(recipient_pubkey())); + assert_eq!(unsigned_invoice_request.issuer_signing_pubkey(), Some(recipient_pubkey())); assert_eq!(unsigned_invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin)); assert_eq!(unsigned_invoice_request.amount_msats(), None); assert_eq!(unsigned_invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty()); assert_eq!(unsigned_invoice_request.quantity(), None); - assert_eq!(unsigned_invoice_request.payer_id(), payer_pubkey()); + assert_eq!(unsigned_invoice_request.payer_signing_pubkey(), payer_pubkey()); assert_eq!(unsigned_invoice_request.payer_note(), None); match UnsignedInvoiceRequest::try_from(buffer) { @@ -1329,12 +1330,12 @@ mod tests { assert_eq!(invoice_request.paths(), &[]); assert_eq!(invoice_request.issuer(), None); assert_eq!(invoice_request.supported_quantity(), Quantity::One); - assert_eq!(invoice_request.signing_pubkey(), Some(recipient_pubkey())); + assert_eq!(invoice_request.issuer_signing_pubkey(), Some(recipient_pubkey())); assert_eq!(invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin)); assert_eq!(invoice_request.amount_msats(), None); assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty()); assert_eq!(invoice_request.quantity(), None); - assert_eq!(invoice_request.payer_id(), payer_pubkey()); + assert_eq!(invoice_request.payer_signing_pubkey(), payer_pubkey()); assert_eq!(invoice_request.payer_note(), None); let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice_request.bytes); @@ -1355,7 +1356,7 @@ mod tests { paths: None, issuer: None, quantity_max: None, - node_id: Some(&recipient_pubkey()), + issuer_id: Some(&recipient_pubkey()), }, InvoiceRequestTlvStreamRef { chain: None, @@ -1405,7 +1406,7 @@ mod tests { #[test] fn builds_invoice_request_with_derived_metadata() { - let payer_id = payer_pubkey(); + let signing_pubkey = payer_pubkey(); let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32])); let entropy = FixedEntropy {}; let nonce = Nonce::from_entropy_source(&entropy); @@ -1416,11 +1417,11 @@ mod tests { .amount_msats(1000) .build().unwrap(); let invoice_request = offer - .request_invoice_deriving_metadata(payer_id, &expanded_key, nonce, payment_id) + .request_invoice_deriving_metadata(signing_pubkey, &expanded_key, nonce, payment_id) .unwrap() .build().unwrap() .sign(payer_sign).unwrap(); - assert_eq!(invoice_request.payer_id(), payer_pubkey()); + assert_eq!(invoice_request.payer_signing_pubkey(), payer_pubkey()); let invoice = invoice_request.respond_with_no_std(payment_paths(), payment_hash(), now()) .unwrap() @@ -1482,7 +1483,7 @@ mod tests { } #[test] - fn builds_invoice_request_with_derived_payer_id() { + fn builds_invoice_request_with_derived_payer_signing_pubkey() { let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32])); let entropy = FixedEntropy {}; let nonce = Nonce::from_entropy_source(&entropy); @@ -1493,7 +1494,7 @@ mod tests { .amount_msats(1000) .build().unwrap(); let invoice_request = offer - .request_invoice_deriving_payer_id(&expanded_key, nonce, &secp_ctx, payment_id) + .request_invoice_deriving_signing_pubkey(&expanded_key, nonce, &secp_ctx, payment_id) .unwrap() .build_and_sign() .unwrap(); @@ -2215,7 +2216,7 @@ mod tests { } #[test] - fn fails_parsing_invoice_request_without_payer_id() { + fn fails_parsing_invoice_request_without_payer_signing_pubkey() { let offer = OfferBuilder::new(recipient_pubkey()) .amount_msats(1000) .build().unwrap(); @@ -2229,19 +2230,19 @@ mod tests { match InvoiceRequest::try_from(buffer) { Ok(_) => panic!("expected error"), - Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPayerId)), + Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPayerSigningPubkey)), } } #[test] - fn fails_parsing_invoice_request_without_node_id() { + fn fails_parsing_invoice_request_without_issuer_id() { let offer = OfferBuilder::new(recipient_pubkey()) .amount_msats(1000) .build().unwrap(); let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap(); let mut tlv_stream = unsigned_invoice_request.contents.as_tlv_stream(); - tlv_stream.1.node_id = None; + tlv_stream.1.issuer_id = None; let mut buffer = Vec::new(); tlv_stream.write(&mut buffer).unwrap(); @@ -2249,7 +2250,7 @@ mod tests { match InvoiceRequest::try_from(buffer) { Ok(_) => panic!("expected error"), Err(e) => { - assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)); + assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey)); }, } } @@ -2334,7 +2335,7 @@ mod tests { .amount_msats(1000) .supported_quantity(Quantity::Unbounded) .build().unwrap(); - assert_eq!(offer.signing_pubkey(), Some(node_id)); + assert_eq!(offer.issuer_signing_pubkey(), Some(node_id)); let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap() .chain(Network::Testnet).unwrap() @@ -2349,7 +2350,7 @@ mod tests { assert_eq!( fields, InvoiceRequestFields { - payer_id: payer_pubkey(), + payer_signing_pubkey: payer_pubkey(), quantity: Some(1), payer_note_truncated: Some(UntrustedString("0".repeat(PAYER_NOTE_LIMIT))), } diff --git a/lightning/src/offers/nonce.rs b/lightning/src/offers/nonce.rs index 1dd21e6c83..0675414125 100644 --- a/lightning/src/offers/nonce.rs +++ b/lightning/src/offers/nonce.rs @@ -20,11 +20,11 @@ use crate::prelude::*; /// A 128-bit number used only once. /// -/// Needed when constructing [`Offer::metadata`] and deriving [`Offer::signing_pubkey`] from +/// Needed when constructing [`Offer::metadata`] and deriving [`Offer::issuer_signing_pubkey`] from /// [`ExpandedKey`]. Must not be reused for any other derivation without first hashing. /// /// [`Offer::metadata`]: crate::offers::offer::Offer::metadata -/// [`Offer::signing_pubkey`]: crate::offers::offer::Offer::signing_pubkey +/// [`Offer::issuer_signing_pubkey`]: crate::offers::offer::Offer::issuer_signing_pubkey /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Nonce(pub(crate) [u8; Self::LENGTH]); diff --git a/lightning/src/offers/offer.rs b/lightning/src/offers/offer.rs index 27a79166bc..56dbb71ece 100644 --- a/lightning/src/offers/offer.rs +++ b/lightning/src/offers/offer.rs @@ -99,11 +99,11 @@ use crate::util::string::PrintableString; #[cfg(not(c_bindings))] use { - crate::offers::invoice_request::{DerivedPayerId, ExplicitPayerId, InvoiceRequestBuilder}, + crate::offers::invoice_request::{DerivedPayerSigningPubkey, ExplicitPayerSigningPubkey, InvoiceRequestBuilder}, }; #[cfg(c_bindings)] use { - crate::offers::invoice_request::{InvoiceRequestWithDerivedPayerIdBuilder, InvoiceRequestWithExplicitPayerIdBuilder}, + crate::offers::invoice_request::{InvoiceRequestWithDerivedPayerSigningPubkeyBuilder, InvoiceRequestWithExplicitPayerSigningPubkeyBuilder}, }; #[allow(unused_imports)] @@ -207,8 +207,8 @@ impl MetadataStrategy for DerivedMetadata {} macro_rules! offer_explicit_metadata_builder_methods { ( $self: ident, $self_type: ty, $return_type: ty, $return_value: expr ) => { - /// Creates a new builder for an offer using the [`Offer::signing_pubkey`] for signing invoices. - /// The associated secret key must be remembered while the offer is valid. + /// Creates a new builder for an offer using the `signing_pubkey` for signing invoices. The + /// associated secret key must be remembered while the offer is valid. /// /// Use a different pubkey per offer to avoid correlating offers. /// @@ -224,7 +224,7 @@ macro_rules! offer_explicit_metadata_builder_methods { ( offer: OfferContents { chains: None, metadata: None, amount: None, description: None, features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None, - supported_quantity: Quantity::One, signing_pubkey: Some(signing_pubkey), + supported_quantity: Quantity::One, issuer_signing_pubkey: Some(signing_pubkey), }, metadata_strategy: core::marker::PhantomData, secp_ctx: None, @@ -244,7 +244,7 @@ macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => { /// Similar to [`OfferBuilder::new`] except, if [`OfferBuilder::path`] is called, the signing /// pubkey is derived from the given [`ExpandedKey`] and [`Nonce`]. This provides recipient /// privacy by using a different signing pubkey for each offer. Otherwise, the provided - /// `node_id` is used for the signing pubkey. + /// `node_id` is used for [`Offer::issuer_signing_pubkey`]. /// /// Also, sets the metadata when [`OfferBuilder::build`] is called such that it can be used by /// [`InvoiceRequest::verify_using_metadata`] to determine if the request was produced for the @@ -265,7 +265,7 @@ macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => { offer: OfferContents { chains: None, metadata: Some(metadata), amount: None, description: None, features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None, - supported_quantity: Quantity::One, signing_pubkey: Some(node_id), + supported_quantity: Quantity::One, issuer_signing_pubkey: Some(node_id), }, metadata_strategy: core::marker::PhantomData, secp_ctx: Some(secp_ctx), @@ -342,7 +342,7 @@ macro_rules! offer_builder_methods { ( } /// Adds a blinded path to [`Offer::paths`]. Must include at least one path if only connected by - /// private channels or if [`Offer::signing_pubkey`] is not a public node id. + /// private channels or if [`Offer::issuer_signing_pubkey`] is not a public node id. /// /// Successive calls to this method will add another blinded path. Caller is responsible for not /// adding duplicate paths. @@ -403,7 +403,7 @@ macro_rules! offer_builder_methods { ( debug_assert_eq!(tlv_stream.metadata, None); tlv_stream.metadata = None; if metadata.derives_recipient_keys() { - tlv_stream.node_id = None; + tlv_stream.issuer_id = None; } // Either replace the signing pubkey with the derived pubkey or include the metadata @@ -412,7 +412,7 @@ macro_rules! offer_builder_methods { ( let (derived_metadata, keys) = metadata.derive_from(iv_bytes, tlv_stream, $self.secp_ctx); match keys { - Some(keys) => $self.offer.signing_pubkey = Some(keys.public_key()), + Some(keys) => $self.offer.issuer_signing_pubkey = Some(keys.public_key()), None => $self.offer.metadata = Some(derived_metadata), } } else { @@ -459,8 +459,8 @@ macro_rules! offer_builder_test_methods { ( } #[cfg_attr(c_bindings, allow(dead_code))] - pub(crate) fn clear_signing_pubkey($($self_mut)* $self: $self_type) -> $return_type { - $self.offer.signing_pubkey = None; + pub(crate) fn clear_issuer_signing_pubkey($($self_mut)* $self: $self_type) -> $return_type { + $self.offer.issuer_signing_pubkey = None; $return_value } @@ -570,7 +570,7 @@ pub(super) struct OfferContents { issuer: Option, paths: Option>, supported_quantity: Quantity, - signing_pubkey: Option, + issuer_signing_pubkey: Option, } macro_rules! offer_accessors { ($self: ident, $contents: expr) => { @@ -631,9 +631,19 @@ macro_rules! offer_accessors { ($self: ident, $contents: expr) => { $contents.supported_quantity() } - /// The public key used by the recipient to sign invoices. - pub fn signing_pubkey(&$self) -> Option { - $contents.signing_pubkey() + /// The public key corresponding to the key used by the recipient to sign invoices. + /// - If [`Offer::paths`] is empty, MUST be `Some` and contain the recipient's node id for + /// sending an [`InvoiceRequest`]. + /// - If [`Offer::paths`] is not empty, MAY be `Some` and contain a transient id. + /// - If `None`, the signing pubkey will be the final blinded node id from the + /// [`BlindedMessagePath`] in [`Offer::paths`] used to send the [`InvoiceRequest`]. + /// + /// See also [`Bolt12Invoice::signing_pubkey`]. + /// + /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest + /// [`Bolt12Invoice::signing_pubkey`]: crate::offers::invoice::Bolt12Invoice::signing_pubkey + pub fn issuer_signing_pubkey(&$self) -> Option { + $contents.issuer_signing_pubkey() } } } @@ -685,10 +695,10 @@ impl Offer { } } -macro_rules! request_invoice_derived_payer_id { ($self: ident, $builder: ty) => { +macro_rules! request_invoice_derived_signing_pubkey { ($self: ident, $builder: ty) => { /// Similar to [`Offer::request_invoice`] except it: - /// - derives the [`InvoiceRequest::payer_id`] such that a different key can be used for each - /// request, + /// - derives the [`InvoiceRequest::payer_signing_pubkey`] such that a different key can be used + /// for each request, /// - sets [`InvoiceRequest::payer_metadata`] when [`InvoiceRequestBuilder::build`] is called /// such that it can be used by [`Bolt12Invoice::verify_using_metadata`] to determine if the /// invoice was requested using a base [`ExpandedKey`] from which the payer id was derived, @@ -698,11 +708,11 @@ macro_rules! request_invoice_derived_payer_id { ($self: ident, $builder: ty) => /// /// Useful to protect the sender's privacy. /// - /// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id + /// [`InvoiceRequest::payer_signing_pubkey`]: crate::offers::invoice_request::InvoiceRequest::payer_signing_pubkey /// [`InvoiceRequest::payer_metadata`]: crate::offers::invoice_request::InvoiceRequest::payer_metadata /// [`Bolt12Invoice::verify_using_metadata`]: crate::offers::invoice::Bolt12Invoice::verify_using_metadata /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey - pub fn request_invoice_deriving_payer_id< + pub fn request_invoice_deriving_signing_pubkey< 'a, 'b, #[cfg(not(c_bindings))] T: secp256k1::Signing @@ -718,34 +728,35 @@ macro_rules! request_invoice_derived_payer_id { ($self: ident, $builder: ty) => return Err(Bolt12SemanticError::UnknownRequiredFeatures); } - Ok(<$builder>::deriving_payer_id($self, expanded_key, nonce, secp_ctx, payment_id)) + Ok(<$builder>::deriving_signing_pubkey($self, expanded_key, nonce, secp_ctx, payment_id)) } } } -macro_rules! request_invoice_explicit_payer_id { ($self: ident, $builder: ty) => { - /// Similar to [`Offer::request_invoice_deriving_payer_id`] except uses `payer_id` for the - /// [`InvoiceRequest::payer_id`] instead of deriving a different key for each request. +macro_rules! request_invoice_explicit_signing_pubkey { ($self: ident, $builder: ty) => { + /// Similar to [`Offer::request_invoice_deriving_signing_pubkey`] except uses `signing_pubkey` + /// for the [`InvoiceRequest::payer_signing_pubkey`] instead of deriving a different key for + /// each request. /// - /// Useful for recurring payments using the same `payer_id` with different invoices. + /// Useful for recurring payments using the same `signing_pubkey` with different invoices. /// - /// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id + /// [`InvoiceRequest::payer_signing_pubkey`]: crate::offers::invoice_request::InvoiceRequest::payer_signing_pubkey pub fn request_invoice_deriving_metadata( - &$self, payer_id: PublicKey, expanded_key: &ExpandedKey, nonce: Nonce, + &$self, signing_pubkey: PublicKey, expanded_key: &ExpandedKey, nonce: Nonce, payment_id: PaymentId ) -> Result<$builder, Bolt12SemanticError> { if $self.offer_features().requires_unknown_bits() { return Err(Bolt12SemanticError::UnknownRequiredFeatures); } - Ok(<$builder>::deriving_metadata($self, payer_id, expanded_key, nonce, payment_id)) + Ok(<$builder>::deriving_metadata($self, signing_pubkey, expanded_key, nonce, payment_id)) } - /// Creates an [`InvoiceRequestBuilder`] for the offer with the given `metadata` and `payer_id`, - /// which will be reflected in the `Bolt12Invoice` response. + /// Creates an [`InvoiceRequestBuilder`] for the offer with the given `metadata` and + /// `signing_pubkey`, which will be reflected in the `Bolt12Invoice` response. /// - /// The `metadata` is useful for including information about the derivation of `payer_id` such - /// that invoice response handling can be stateless. Also serves as payer-provided entropy while - /// hashing in the signature calculation. + /// The `metadata` is useful for including information about the derivation of `signing_pubkey` + /// such that invoice response handling can be stateless. Also serves as payer-provided entropy + /// while hashing in the signature calculation. /// /// This should not leak any information such as by using a simple BIP-32 derivation path. /// Otherwise, payments may be correlated. @@ -754,26 +765,26 @@ macro_rules! request_invoice_explicit_payer_id { ($self: ident, $builder: ty) => /// /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest pub fn request_invoice( - &$self, metadata: Vec, payer_id: PublicKey + &$self, metadata: Vec, signing_pubkey: PublicKey ) -> Result<$builder, Bolt12SemanticError> { if $self.offer_features().requires_unknown_bits() { return Err(Bolt12SemanticError::UnknownRequiredFeatures); } - Ok(<$builder>::new($self, metadata, payer_id)) + Ok(<$builder>::new($self, metadata, signing_pubkey)) } } } #[cfg(not(c_bindings))] impl Offer { - request_invoice_derived_payer_id!(self, InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T>); - request_invoice_explicit_payer_id!(self, InvoiceRequestBuilder); + request_invoice_derived_signing_pubkey!(self, InvoiceRequestBuilder<'a, 'b, DerivedPayerSigningPubkey, T>); + request_invoice_explicit_signing_pubkey!(self, InvoiceRequestBuilder); } #[cfg(c_bindings)] impl Offer { - request_invoice_derived_payer_id!(self, InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b>); - request_invoice_explicit_payer_id!(self, InvoiceRequestWithExplicitPayerIdBuilder); + request_invoice_derived_signing_pubkey!(self, InvoiceRequestWithDerivedPayerSigningPubkeyBuilder<'a, 'b>); + request_invoice_explicit_signing_pubkey!(self, InvoiceRequestWithExplicitPayerSigningPubkeyBuilder); } #[cfg(test)] @@ -916,8 +927,8 @@ impl OfferContents { } } - pub(super) fn signing_pubkey(&self) -> Option { - self.signing_pubkey + pub(super) fn issuer_signing_pubkey(&self) -> Option { + self.issuer_signing_pubkey } pub(super) fn verify_using_metadata( @@ -943,11 +954,11 @@ impl OfferContents { let tlv_stream = TlvStream::new(bytes).range(OFFER_TYPES).filter(|record| { match record.r#type { OFFER_METADATA_TYPE => false, - OFFER_NODE_ID_TYPE => !metadata.derives_recipient_keys(), + OFFER_ISSUER_ID_TYPE => !metadata.derives_recipient_keys(), _ => true, } }); - let signing_pubkey = match self.signing_pubkey() { + let signing_pubkey = match self.issuer_signing_pubkey() { Some(signing_pubkey) => signing_pubkey, None => return Err(()), }; @@ -987,7 +998,7 @@ impl OfferContents { paths: self.paths.as_ref(), issuer: self.issuer.as_ref(), quantity_max: self.supported_quantity.to_tlv_record(), - node_id: self.signing_pubkey.as_ref(), + issuer_id: self.issuer_signing_pubkey.as_ref(), } } } @@ -1063,8 +1074,8 @@ pub(super) const OFFER_TYPES: core::ops::Range = 1..80; /// TLV record type for [`Offer::metadata`]. const OFFER_METADATA_TYPE: u64 = 4; -/// TLV record type for [`Offer::signing_pubkey`]. -const OFFER_NODE_ID_TYPE: u64 = 22; +/// TLV record type for [`Offer::issuer_signing_pubkey`]. +const OFFER_ISSUER_ID_TYPE: u64 = 22; tlv_stream!(OfferTlvStream, OfferTlvStreamRef, OFFER_TYPES, { (2, chains: (Vec, WithoutLength)), @@ -1077,7 +1088,7 @@ tlv_stream!(OfferTlvStream, OfferTlvStreamRef, OFFER_TYPES, { (16, paths: (Vec, WithoutLength)), (18, issuer: (String, WithoutLength)), (20, quantity_max: (u64, HighZeroBytesDroppedBigSize)), - (OFFER_NODE_ID_TYPE, node_id: PublicKey), + (OFFER_ISSUER_ID_TYPE, issuer_id: PublicKey), }); impl Bech32Encode for Offer { @@ -1111,7 +1122,7 @@ impl TryFrom for OfferContents { fn try_from(tlv_stream: OfferTlvStream) -> Result { let OfferTlvStream { chains, metadata, currency, amount, description, features, absolute_expiry, paths, - issuer, quantity_max, node_id, + issuer, quantity_max, issuer_id, } = tlv_stream; let metadata = metadata.map(|metadata| Metadata::Bytes(metadata)); @@ -1141,15 +1152,15 @@ impl TryFrom for OfferContents { Some(n) => Quantity::Bounded(NonZeroU64::new(n).unwrap()), }; - let (signing_pubkey, paths) = match (node_id, paths) { - (None, None) => return Err(Bolt12SemanticError::MissingSigningPubkey), + let (issuer_signing_pubkey, paths) = match (issuer_id, paths) { + (None, None) => return Err(Bolt12SemanticError::MissingIssuerSigningPubkey), (_, Some(paths)) if paths.is_empty() => return Err(Bolt12SemanticError::MissingPaths), - (node_id, paths) => (node_id, paths), + (issuer_id, paths) => (issuer_id, paths), }; Ok(OfferContents { chains, metadata, amount, description, features, absolute_expiry, issuer, paths, - supported_quantity, signing_pubkey, + supported_quantity, issuer_signing_pubkey, }) } } @@ -1210,7 +1221,7 @@ mod tests { assert_eq!(offer.issuer(), None); assert_eq!(offer.supported_quantity(), Quantity::One); assert!(!offer.expects_quantity()); - assert_eq!(offer.signing_pubkey(), Some(pubkey(42))); + assert_eq!(offer.issuer_signing_pubkey(), Some(pubkey(42))); assert_eq!( offer.as_tlv_stream(), @@ -1225,7 +1236,7 @@ mod tests { paths: None, issuer: None, quantity_max: None, - node_id: Some(&pubkey(42)), + issuer_id: Some(&pubkey(42)), }, ); @@ -1307,7 +1318,7 @@ mod tests { .amount_msats(1000) .build().unwrap(); assert!(offer.metadata().is_some()); - assert_eq!(offer.signing_pubkey(), Some(node_id)); + assert_eq!(offer.issuer_signing_pubkey(), Some(node_id)); let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap() @@ -1376,7 +1387,7 @@ mod tests { .path(blinded_path) .build().unwrap(); assert!(offer.metadata().is_none()); - assert_ne!(offer.signing_pubkey(), Some(node_id)); + assert_ne!(offer.issuer_signing_pubkey(), Some(node_id)); let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap() @@ -1409,8 +1420,8 @@ mod tests { // Fails verification with altered signing pubkey let mut tlv_stream = offer.as_tlv_stream(); - let signing_pubkey = pubkey(1); - tlv_stream.node_id = Some(&signing_pubkey); + let issuer_id = pubkey(1); + tlv_stream.issuer_id = Some(&issuer_id); let mut encoded_offer = Vec::new(); tlv_stream.write(&mut encoded_offer).unwrap(); @@ -1567,10 +1578,10 @@ mod tests { .unwrap(); let tlv_stream = offer.as_tlv_stream(); assert_eq!(offer.paths(), paths.as_slice()); - assert_eq!(offer.signing_pubkey(), Some(pubkey(42))); + assert_eq!(offer.issuer_signing_pubkey(), Some(pubkey(42))); assert_ne!(pubkey(42), pubkey(44)); assert_eq!(tlv_stream.paths, Some(&paths)); - assert_eq!(tlv_stream.node_id, Some(&pubkey(42))); + assert_eq!(tlv_stream.issuer_id, Some(&pubkey(42))); } #[test] @@ -1773,7 +1784,7 @@ mod tests { BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] }, ] )) - .clear_signing_pubkey() + .clear_issuer_signing_pubkey() .build() .unwrap(); if let Err(e) = offer.to_string().parse::() { @@ -1828,14 +1839,14 @@ mod tests { } #[test] - fn parses_offer_with_node_id() { + fn parses_offer_with_issuer_id() { let offer = OfferBuilder::new(pubkey(42)).build().unwrap(); if let Err(e) = offer.to_string().parse::() { panic!("error parsing offer: {:?}", e); } let mut tlv_stream = offer.as_tlv_stream(); - tlv_stream.node_id = None; + tlv_stream.issuer_id = None; let mut encoded_offer = Vec::new(); tlv_stream.write(&mut encoded_offer).unwrap(); @@ -1843,7 +1854,7 @@ mod tests { match Offer::try_from(encoded_offer) { Ok(_) => panic!("expected error"), Err(e) => { - assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)); + assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey)); }, } } @@ -1874,6 +1885,8 @@ mod bolt12_tests { fn parses_bech32_encoded_offers() { let offers = [ // Minimal bolt12 offer + "lno1zcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese", + // with description (but no amount) "lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg", // for testnet @@ -1918,8 +1931,11 @@ mod bolt12_tests { // ... and with sciddir introduction node "lno1pgx9getnwss8vetrw3hhyucs3yqqqqqqqqqqqqp2qgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqyqqqqqqqqqqqqqqqqqqqqqqqqqqqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqgzyg3zyg3zyg3z93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj", - // ... and with second blinded path via Carol (0x434343...), blinding 020202... - "lno1pgx9getnwss8vetrw3hhyucsl5q5yqeyv5l2cs6y3qqzesrth7mlzrlp3xg7xhulusczm04x6g6nms9trspqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqsqqqqqqqqqqqqqqqqqqqqqqqqqqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqpqg3zyg3zyg3zygz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqzqqqqqqqqqqqqqqqqqqqqqqqqqqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqqyzyg3zyg3zyg3zzcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese", + // with no issuer_id and blinded path via Bob (0x424242...), blinding 020202... + "lno1pgx9getnwss8vetrw3hhyucs5ypjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zygs", + + //... and with second blinded path via 1x2x3 (direction 1), blinding 020202... + "lno1pgx9getnwss8vetrw3hhyucsl5qj5qeyv5l2cs6y3qqzesrth7mlzrlp3xg7xhulusczm04x6g6nms9trspqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqsqqqqqqqqqqqqqqqqqqqqqqqqqqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqpqg3zyg3zyg3zygpqqqqzqqqqgqqxqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqqsg3zyg3zyg3zygtzzqhwcuj966ma9n9nqwqtl032xeyv6755yeflt235pmww58egx6rxry", // unknown odd field "lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxfppf5x2mrvdamk7unvvs", @@ -1948,7 +1964,7 @@ mod bolt12_tests { // Malformed: empty assert_eq!( "lno1".parse::(), - Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)), + Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey)), ); // Malformed: truncated at type @@ -2053,7 +2069,7 @@ mod bolt12_tests { Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), ); - // Malformed: invalid offer_node_id + // Malformed: invalid offer_issuer_id assert_eq!( "lno1pgz5znzfgdz3vggzqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvps".parse::(), Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), @@ -2078,10 +2094,16 @@ mod bolt12_tests { Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription)), ); - // Missing offer_node_id" + // Missing offer_issuer_id assert_eq!( "lno1pgx9getnwss8vetrw3hhyuc".parse::(), - Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)), + Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey)), + ); + + // Second offer_path is empty + assert_eq!( + "lno1pgx9getnwss8vetrw3hhyucsespjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zygszqqqqyqqqqsqqvpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsq".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), ); } } diff --git a/lightning/src/offers/parse.rs b/lightning/src/offers/parse.rs index 8b9f64d8b3..fc5d839517 100644 --- a/lightning/src/offers/parse.rs +++ b/lightning/src/offers/parse.rs @@ -157,12 +157,10 @@ pub enum Bolt12SemanticError { UnexpectedFeatures, /// A required description was not provided. MissingDescription, - /// A signing pubkey was not provided. - MissingSigningPubkey, - /// A signing pubkey was provided but a different one was expected. - InvalidSigningPubkey, - /// A signing pubkey was provided but was not expected. - UnexpectedSigningPubkey, + /// An issuer's signing pubkey was not provided. + MissingIssuerSigningPubkey, + /// An issuer's signing pubkey was provided but was not expected. + UnexpectedIssuerSigningPubkey, /// A quantity was expected but was missing. MissingQuantity, /// An unsupported quantity was provided. @@ -175,8 +173,8 @@ pub enum Bolt12SemanticError { UnexpectedMetadata, /// Payer metadata was expected but was missing. MissingPayerMetadata, - /// A payer id was expected but was missing. - MissingPayerId, + /// A payer signing pubkey was expected but was missing. + MissingPayerSigningPubkey, /// The payment id for a refund or request is already in use. DuplicatePaymentId, /// Blinded paths were expected but were missing. @@ -191,6 +189,10 @@ pub enum Bolt12SemanticError { MissingPaymentHash, /// An invoice payment hash was provided but was not expected. UnexpectedPaymentHash, + /// A signing pubkey was not provided. + MissingSigningPubkey, + /// A signing pubkey was provided but a different one was expected. + InvalidSigningPubkey, /// A signature was expected but was missing. MissingSignature, } diff --git a/lightning/src/offers/payer.rs b/lightning/src/offers/payer.rs index 22180ce5f6..0ec5721dc3 100644 --- a/lightning/src/offers/payer.rs +++ b/lightning/src/offers/payer.rs @@ -16,9 +16,10 @@ use crate::util::ser::WithoutLength; use crate::prelude::*; /// An unpredictable sequence of bytes typically containing information needed to derive -/// [`InvoiceRequest::payer_id`]. +/// [`InvoiceRequest::payer_signing_pubkey`] and [`Refund::payer_signing_pubkey`]. /// -/// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id +/// [`InvoiceRequest::payer_signing_pubkey`]: crate::offers::invoice_request::InvoiceRequest::payer_signing_pubkey +/// [`Refund::payer_signing_pubkey`]: crate::offers::refund::Refund::payer_signing_pubkey #[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq))] pub(super) struct PayerContents(pub Metadata); diff --git a/lightning/src/offers/refund.rs b/lightning/src/offers/refund.rs index c07055c5c1..aa09253b2d 100644 --- a/lightning/src/offers/refund.rs +++ b/lightning/src/offers/refund.rs @@ -150,8 +150,8 @@ pub struct RefundMaybeWithDerivedMetadataBuilder<'a> { } macro_rules! refund_explicit_metadata_builder_methods { () => { - /// Creates a new builder for a refund using the [`Refund::payer_id`] for the public node id to - /// send to if no [`Refund::paths`] are set. Otherwise, it may be a transient pubkey. + /// Creates a new builder for a refund using the `signing_pubkey` for the public node id to send + /// to if no [`Refund::paths`] are set. Otherwise, `signing_pubkey` may be a transient pubkey. /// /// Additionally, sets the required (empty) [`Refund::description`], [`Refund::payer_metadata`], /// and [`Refund::amount_msats`]. @@ -164,7 +164,7 @@ macro_rules! refund_explicit_metadata_builder_methods { () => { /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager /// [`ChannelManager::create_refund_builder`]: crate::ln::channelmanager::ChannelManager::create_refund_builder pub fn new( - metadata: Vec, payer_id: PublicKey, amount_msats: u64 + metadata: Vec, signing_pubkey: PublicKey, amount_msats: u64 ) -> Result { if amount_msats > MAX_VALUE_MSAT { return Err(Bolt12SemanticError::InvalidAmount); @@ -175,7 +175,7 @@ macro_rules! refund_explicit_metadata_builder_methods { () => { refund: RefundContents { payer: PayerContents(metadata), description: String::new(), absolute_expiry: None, issuer: None, chain: None, amount_msats, features: InvoiceRequestFeatures::empty(), - quantity: None, payer_id, payer_note: None, paths: None, + quantity: None, payer_signing_pubkey: signing_pubkey, payer_note: None, paths: None, }, secp_ctx: None, }) @@ -202,7 +202,7 @@ macro_rules! refund_builder_methods { ( /// [`Bolt12Invoice::verify_using_metadata`]: crate::offers::invoice::Bolt12Invoice::verify_using_metadata /// [`Bolt12Invoice::verify_using_payer_data`]: crate::offers::invoice::Bolt12Invoice::verify_using_payer_data /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey - pub fn deriving_payer_id( + pub fn deriving_signing_pubkey( node_id: PublicKey, expanded_key: &ExpandedKey, nonce: Nonce, secp_ctx: &'a Secp256k1<$secp_context>, amount_msats: u64, payment_id: PaymentId ) -> Result { @@ -217,7 +217,7 @@ macro_rules! refund_builder_methods { ( refund: RefundContents { payer: PayerContents(metadata), description: String::new(), absolute_expiry: None, issuer: None, chain: None, amount_msats, features: InvoiceRequestFeatures::empty(), - quantity: None, payer_id: node_id, payer_note: None, paths: None, + quantity: None, payer_signing_pubkey: node_id, payer_note: None, paths: None, }, secp_ctx: Some(secp_ctx), }) @@ -249,7 +249,7 @@ macro_rules! refund_builder_methods { ( } /// Adds a blinded path to [`Refund::paths`]. Must include at least one path if only connected - /// by private channels or if [`Refund::payer_id`] is not a public node id. + /// by private channels or if [`Refund::payer_signing_pubkey`] is not a public node id. /// /// Successive calls to this method will add another blinded path. Caller is responsible for not /// adding duplicate paths. @@ -324,7 +324,7 @@ macro_rules! refund_builder_methods { ( metadata.derive_from(iv_bytes, tlv_stream, $self.secp_ctx); metadata = derived_metadata; if let Some(keys) = keys { - $self.refund.payer_id = keys.public_key(); + $self.refund.payer_signing_pubkey = keys.public_key(); } $self.refund.payer.0 = metadata; @@ -434,7 +434,7 @@ pub(super) struct RefundContents { amount_msats: u64, features: InvoiceRequestFeatures, quantity: Option, - payer_id: PublicKey, + payer_signing_pubkey: PublicKey, payer_note: Option, paths: Option>, } @@ -477,9 +477,9 @@ impl Refund { } /// An unpredictable series of bytes, typically containing information about the derivation of - /// [`payer_id`]. + /// [`payer_signing_pubkey`]. /// - /// [`payer_id`]: Self::payer_id + /// [`payer_signing_pubkey`]: Self::payer_signing_pubkey pub fn payer_metadata(&self) -> &[u8] { self.contents.metadata() } @@ -510,8 +510,8 @@ impl Refund { /// transient pubkey. /// /// [`paths`]: Self::paths - pub fn payer_id(&self) -> PublicKey { - self.contents.payer_id() + pub fn payer_signing_pubkey(&self) -> PublicKey { + self.contents.payer_signing_pubkey() } /// Payer provided note to include in the invoice. @@ -727,8 +727,8 @@ impl RefundContents { /// transient pubkey. /// /// [`paths`]: Self::paths - pub fn payer_id(&self) -> PublicKey { - self.payer_id + pub fn payer_signing_pubkey(&self) -> PublicKey { + self.payer_signing_pubkey } /// Payer provided note to include in the invoice. @@ -752,7 +752,7 @@ impl RefundContents { paths: None, issuer: self.issuer.as_ref(), quantity_max: None, - node_id: None, + issuer_id: None, }; let features = { @@ -765,7 +765,7 @@ impl RefundContents { amount: Some(self.amount_msats), features, quantity: self.quantity, - payer_id: Some(&self.payer_id), + payer_id: Some(&self.payer_signing_pubkey), payer_note: self.payer_note.as_ref(), paths: self.paths.as_ref(), }; @@ -844,7 +844,7 @@ impl TryFrom for RefundContents { OfferTlvStream { chains, metadata, currency, amount: offer_amount, description, features: offer_features, absolute_expiry, paths: offer_paths, issuer, quantity_max, - node_id, + issuer_id, }, InvoiceRequestTlvStream { chain, amount, features, quantity, payer_id, payer_note, paths @@ -887,8 +887,8 @@ impl TryFrom for RefundContents { return Err(Bolt12SemanticError::UnexpectedQuantity); } - if node_id.is_some() { - return Err(Bolt12SemanticError::UnexpectedSigningPubkey); + if issuer_id.is_some() { + return Err(Bolt12SemanticError::UnexpectedIssuerSigningPubkey); } let amount_msats = match amount { @@ -901,14 +901,14 @@ impl TryFrom for RefundContents { let features = features.unwrap_or_else(InvoiceRequestFeatures::empty); - let payer_id = match payer_id { - None => return Err(Bolt12SemanticError::MissingPayerId), + let payer_signing_pubkey = match payer_id { + None => return Err(Bolt12SemanticError::MissingPayerSigningPubkey), Some(payer_id) => payer_id, }; Ok(RefundContents { payer, description, absolute_expiry, issuer, chain, amount_msats, features, quantity, - payer_id, payer_note, paths, + payer_signing_pubkey, payer_note, paths, }) } } @@ -985,7 +985,7 @@ mod tests { assert_eq!(refund.chain(), ChainHash::using_genesis_block(Network::Bitcoin)); assert_eq!(refund.amount_msats(), 1000); assert_eq!(refund.features(), &InvoiceRequestFeatures::empty()); - assert_eq!(refund.payer_id(), payer_pubkey()); + assert_eq!(refund.payer_signing_pubkey(), payer_pubkey()); assert_eq!(refund.payer_note(), None); assert_eq!( @@ -1003,7 +1003,7 @@ mod tests { paths: None, issuer: None, quantity_max: None, - node_id: None, + issuer_id: None, }, InvoiceRequestTlvStreamRef { chain: None, @@ -1040,10 +1040,10 @@ mod tests { let payment_id = PaymentId([1; 32]); let refund = RefundBuilder - ::deriving_payer_id(node_id, &expanded_key, nonce, &secp_ctx, 1000, payment_id) + ::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx, 1000, payment_id) .unwrap() .build().unwrap(); - assert_eq!(refund.payer_id(), node_id); + assert_eq!(refund.payer_signing_pubkey(), node_id); // Fails verification with altered fields let invoice = refund @@ -1089,7 +1089,7 @@ mod tests { } #[test] - fn builds_refund_with_derived_payer_id() { + fn builds_refund_with_derived_signing_pubkey() { let node_id = payer_pubkey(); let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32])); let entropy = FixedEntropy {}; @@ -1106,11 +1106,11 @@ mod tests { ); let refund = RefundBuilder - ::deriving_payer_id(node_id, &expanded_key, nonce, &secp_ctx, 1000, payment_id) + ::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx, 1000, payment_id) .unwrap() .path(blinded_path) .build().unwrap(); - assert_ne!(refund.payer_id(), node_id); + assert_ne!(refund.payer_signing_pubkey(), node_id); let invoice = refund .respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now()) @@ -1211,7 +1211,7 @@ mod tests { .build() .unwrap(); let (_, _, invoice_request_tlv_stream) = refund.as_tlv_stream(); - assert_eq!(refund.payer_id(), pubkey(42)); + assert_eq!(refund.payer_signing_pubkey(), pubkey(42)); assert_eq!(refund.paths(), paths.as_slice()); assert_ne!(pubkey(42), pubkey(44)); assert_eq!(invoice_request_tlv_stream.payer_id, Some(&pubkey(42))); @@ -1395,7 +1395,7 @@ mod tests { match Refund::try_from(tlv_stream.to_bytes()) { Ok(_) => panic!("expected error"), Err(e) => { - assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPayerId)); + assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPayerSigningPubkey)); }, } } @@ -1509,14 +1509,14 @@ mod tests { }, } - let node_id = payer_pubkey(); + let issuer_id = payer_pubkey(); let mut tlv_stream = refund.as_tlv_stream(); - tlv_stream.1.node_id = Some(&node_id); + tlv_stream.1.issuer_id = Some(&issuer_id); match Refund::try_from(tlv_stream.to_bytes()) { Ok(_) => panic!("expected error"), Err(e) => { - assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::UnexpectedSigningPubkey)); + assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::UnexpectedIssuerSigningPubkey)); }, } } diff --git a/lightning/src/offers/static_invoice.rs b/lightning/src/offers/static_invoice.rs index 4910c57c5a..bf88bd9446 100644 --- a/lightning/src/offers/static_invoice.rs +++ b/lightning/src/offers/static_invoice.rs @@ -110,8 +110,8 @@ impl<'a> StaticInvoiceBuilder<'a> { return Err(Bolt12SemanticError::MissingPaths); } - let offer_signing_pubkey = - offer.signing_pubkey().ok_or(Bolt12SemanticError::MissingSigningPubkey)?; + let issuer_signing_pubkey = + offer.issuer_signing_pubkey().ok_or(Bolt12SemanticError::MissingIssuerSigningPubkey)?; let keys = offer .verify(nonce, &expanded_key, &secp_ctx) @@ -120,7 +120,7 @@ impl<'a> StaticInvoiceBuilder<'a> { .ok_or(Bolt12SemanticError::MissingSigningPubkey)?; let signing_pubkey = keys.public_key(); - if signing_pubkey != offer_signing_pubkey { + if signing_pubkey != issuer_signing_pubkey { return Err(Bolt12SemanticError::InvalidSigningPubkey); } @@ -241,6 +241,30 @@ macro_rules! invoice_accessors { ($self: ident, $contents: expr) => { pub fn supported_quantity(&$self) -> Quantity { $contents.supported_quantity() } + + /// The public key used by the recipient to sign invoices, from + /// [`Offer::issuer_signing_pubkey`]. + /// + /// [`Offer::issuer_signing_pubkey`]: crate::offers::offer::Offer::issuer_signing_pubkey + pub fn issuer_signing_pubkey(&$self) -> Option { + $contents.issuer_signing_pubkey() + } +} } + +macro_rules! invoice_accessors_signing_pubkey { + ($self: ident, $contents: expr, $invoice_type: ty) => +{ + /// The public key corresponding to the key used to sign the invoice. + /// + /// This will be: + /// - [`Offer::issuer_signing_pubkey`] if it's `Some`, otherwise + /// - the final blinded node id from a [`BlindedMessagePath`] in [`Offer::paths`] if `None`. + /// + /// [`Offer::issuer_signing_pubkey`]: crate::offers::offer::Offer::issuer_signing_pubkey + /// [`Offer::paths`]: crate::offers::offer::Offer::paths + pub fn signing_pubkey(&$self) -> PublicKey { + $contents.signing_pubkey() + } } } impl UnsignedStaticInvoice { @@ -271,7 +295,8 @@ impl UnsignedStaticInvoice { Ok(StaticInvoice { bytes: self.bytes, contents: self.contents, signature }) } - invoice_accessors_common!(self, self.contents, StaticInvoice); + invoice_accessors_common!(self, self.contents, UnsignedStaticInvoice); + invoice_accessors_signing_pubkey!(self, self.contents, UnsignedStaticInvoice); invoice_accessors!(self, self.contents); } @@ -307,6 +332,7 @@ where impl StaticInvoice { invoice_accessors_common!(self, self.contents, StaticInvoice); + invoice_accessors_signing_pubkey!(self, self.contents, StaticInvoice); invoice_accessors!(self, self.contents); /// Signature of the invoice verified using [`StaticInvoice::signing_pubkey`]. @@ -418,6 +444,10 @@ impl InvoiceContents { self.offer.supported_quantity() } + fn issuer_signing_pubkey(&self) -> Option { + self.offer.issuer_signing_pubkey() + } + fn payment_paths(&self) -> &[BlindedPaymentPath] { &self.payment_paths[..] } @@ -707,11 +737,9 @@ mod tests { assert!(invoice.fallbacks().is_empty()); assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty()); - let offer_signing_pubkey = offer.signing_pubkey().unwrap(); + let signing_pubkey = offer.issuer_signing_pubkey().unwrap(); let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice.bytes); - assert!( - merkle::verify_signature(&invoice.signature, &message, offer_signing_pubkey).is_ok() - ); + assert!(merkle::verify_signature(&invoice.signature, &message, signing_pubkey).is_ok()); let paths = vec![blinded_path()]; assert_eq!( @@ -728,7 +756,7 @@ mod tests { paths: Some(&paths), issuer: None, quantity_max: None, - node_id: Some(&offer_signing_pubkey), + issuer_id: Some(&signing_pubkey), }, InvoiceTlvStreamRef { paths: Some(Iterable( @@ -741,7 +769,7 @@ mod tests { amount: None, fallbacks: None, features: None, - node_id: Some(&offer_signing_pubkey), + node_id: Some(&signing_pubkey), message_paths: Some(&paths), }, SignatureTlvStreamRef { signature: Some(&invoice.signature()) }, @@ -880,7 +908,7 @@ mod tests { } #[test] - fn fails_build_offer_signing_pubkey() { + fn fails_building_with_missing_issuer_signing_pubkey() { let node_id = recipient_pubkey(); let now = now(); let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32])); @@ -894,16 +922,15 @@ mod tests { .build() .unwrap(); - // Error if offer signing pubkey is missing. - let mut offer_missing_signing_pubkey = valid_offer.clone(); - let mut offer_tlv_stream = offer_missing_signing_pubkey.as_tlv_stream(); - offer_tlv_stream.node_id.take(); + let mut offer_missing_issuer_id = valid_offer.clone(); + let mut offer_tlv_stream = offer_missing_issuer_id.as_tlv_stream(); + offer_tlv_stream.issuer_id.take(); let mut buffer = Vec::new(); offer_tlv_stream.write(&mut buffer).unwrap(); - offer_missing_signing_pubkey = Offer::try_from(buffer).unwrap(); + offer_missing_issuer_id = Offer::try_from(buffer).unwrap(); if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys( - &offer_missing_signing_pubkey, + &offer_missing_issuer_id, payment_paths(), vec![blinded_path()], now, @@ -911,12 +938,20 @@ mod tests { nonce, &secp_ctx, ) { - assert_eq!(e, Bolt12SemanticError::MissingSigningPubkey); + assert_eq!(e, Bolt12SemanticError::MissingIssuerSigningPubkey); } else { panic!("expected error") } + } + + #[test] + fn fails_building_with_invalid_metadata() { + let now = now(); + let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32])); + let entropy = FixedEntropy {}; + let nonce = Nonce::from_entropy_source(&entropy); + let secp_ctx = Secp256k1::new(); - // Error if the offer's metadata cannot be verified. let offer = OfferBuilder::new(recipient_pubkey()) .path(blinded_path()) .metadata(vec![42; 32]) diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index 4b8a9e0255..8ad34f2d65 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -878,7 +878,7 @@ pub trait NodeSigner { /// Implementors may check that the `invoice_request` is expected rather than blindly signing /// the tagged hash. An `Ok` result should sign `invoice_request.tagged_hash().as_digest()` with /// the node's signing key or an ephemeral key to preserve privacy, whichever is associated with - /// [`UnsignedInvoiceRequest::payer_id`]. + /// [`UnsignedInvoiceRequest::payer_signing_pubkey`]. /// /// [`TaggedHash`]: crate::offers::merkle::TaggedHash fn sign_bolt12_invoice_request(