Rename Offer::signing_pubkey to Offer::issuer_signing_pubkey

The spec was recently changed to use offer_issuer_id instead of
offer_node_id. LDK always used signing_pubkey to avoid confusion with a
node_id. Rename it to issuer_signing_pubkey now as InvoiceRequest and
Bolt12Invoice will have similarly named methods in upcoming commits.
This commit is contained in:
Jeffrey Czyz 2024-08-01 10:56:51 -05:00
parent 6662c5c7b9
commit df58f26ada
No known key found for this signature in database
GPG key ID: 912EF12EA67705F5
9 changed files with 120 additions and 113 deletions

View file

@ -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
@ -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(())

View file

@ -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));
@ -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));
@ -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]);
@ -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));
@ -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));
@ -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));
@ -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));
@ -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));

View file

@ -1335,7 +1335,7 @@ impl TryFrom<PartialInvoiceTlvStream> 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 +1373,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);
}
},
@ -1561,7 +1561,7 @@ mod tests {
paths: None,
issuer: None,
quantity_max: None,
node_id: Some(&recipient_pubkey()),
issuer_id: Some(&recipient_pubkey()),
},
InvoiceRequestTlvStreamRef {
chain: None,
@ -1654,7 +1654,7 @@ mod tests {
paths: None,
issuer: None,
quantity_max: None,
node_id: None,
issuer_id: None,
},
InvoiceRequestTlvStreamRef {
chain: None,
@ -1791,7 +1791,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 +2359,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 +2381,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())

View file

@ -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<BlindedPaymentPath>, 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);
@ -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(
@ -1297,7 +1297,7 @@ 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());
@ -1329,7 +1329,7 @@ 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());
@ -1355,7 +1355,7 @@ mod tests {
paths: None,
issuer: None,
quantity_max: None,
node_id: Some(&recipient_pubkey()),
issuer_id: Some(&recipient_pubkey()),
},
InvoiceRequestTlvStreamRef {
chain: None,
@ -2234,14 +2234,14 @@ mod tests {
}
#[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 +2249,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 +2334,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()

View file

@ -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]);

View file

@ -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<String>,
paths: Option<Vec<BlindedMessagePath>>,
supported_quantity: Quantity,
signing_pubkey: Option<PublicKey>,
issuer_signing_pubkey: Option<PublicKey>,
}
macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
@ -632,8 +632,8 @@ macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
}
/// The public key used by the recipient to sign invoices.
pub fn signing_pubkey(&$self) -> Option<bitcoin::secp256k1::PublicKey> {
$contents.signing_pubkey()
pub fn issuer_signing_pubkey(&$self) -> Option<bitcoin::secp256k1::PublicKey> {
$contents.issuer_signing_pubkey()
}
} }
@ -916,8 +916,8 @@ impl OfferContents {
}
}
pub(super) fn signing_pubkey(&self) -> Option<PublicKey> {
self.signing_pubkey
pub(super) fn issuer_signing_pubkey(&self) -> Option<PublicKey> {
self.issuer_signing_pubkey
}
pub(super) fn verify_using_metadata<T: secp256k1::Signing>(
@ -943,11 +943,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 +987,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 +1063,8 @@ pub(super) const OFFER_TYPES: core::ops::Range<u64> = 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<ChainHash>, WithoutLength)),
@ -1077,7 +1077,7 @@ tlv_stream!(OfferTlvStream, OfferTlvStreamRef, OFFER_TYPES, {
(16, paths: (Vec<BlindedMessagePath>, 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 +1111,7 @@ impl TryFrom<OfferTlvStream> for OfferContents {
fn try_from(tlv_stream: OfferTlvStream) -> Result<Self, Self::Error> {
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 +1141,15 @@ impl TryFrom<OfferTlvStream> 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 +1210,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 +1225,7 @@ mod tests {
paths: None,
issuer: None,
quantity_max: None,
node_id: Some(&pubkey(42)),
issuer_id: Some(&pubkey(42)),
},
);
@ -1307,7 +1307,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 +1376,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 +1409,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 +1567,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 +1773,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::<Offer>() {
@ -1828,14 +1828,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::<Offer>() {
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 +1843,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));
},
}
}
@ -1948,7 +1948,7 @@ mod bolt12_tests {
// Malformed: empty
assert_eq!(
"lno1".parse::<Offer>(),
Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)),
Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey)),
);
// Malformed: truncated at type
@ -2053,7 +2053,7 @@ mod bolt12_tests {
Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
);
// Malformed: invalid offer_node_id
// Malformed: invalid offer_issuer_id
assert_eq!(
"lno1pgz5znzfgdz3vggzqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvps".parse::<Offer>(),
Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
@ -2078,10 +2078,10 @@ mod bolt12_tests {
Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription)),
);
// Missing offer_node_id"
// Missing offer_issuer_id
assert_eq!(
"lno1pgx9getnwss8vetrw3hhyuc".parse::<Offer>(),
Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)),
Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey)),
);
}
}

View file

@ -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.
@ -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,
}

View file

@ -752,7 +752,7 @@ impl RefundContents {
paths: None,
issuer: self.issuer.as_ref(),
quantity_max: None,
node_id: None,
issuer_id: None,
};
let features = {
@ -844,7 +844,7 @@ impl TryFrom<RefundTlvStream> 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<RefundTlvStream> 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 {
@ -1003,7 +1003,7 @@ mod tests {
paths: None,
issuer: None,
quantity_max: None,
node_id: None,
issuer_id: None,
},
InvoiceRequestTlvStreamRef {
chain: None,
@ -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));
},
}
}

View file

@ -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);
}
@ -707,11 +707,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 +726,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 +739,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 +878,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 +892,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 +908,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])