mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-23 14:50:45 +01:00
Add support for storing a source HRN in BOLT 12 invoice_request
s
When we resolve a Human Readable Name to a BOLT 12 `offer`, we may end up resolving to a wildcard DNS name covering all possible `user` parts. In that case, if we just blindly pay the `offer`, the recipient would have no way to tell which `user` we paid. Instead, BOLT 12 defines a field to include the HRN resolved in the `invoice_request`, which we implement here. We also take this opportunity to remove constant parameters from the `outbound_payment.rs` interface to `channelmanager.rs`
This commit is contained in:
parent
46df35b0ff
commit
e447b49136
4 changed files with 43 additions and 4 deletions
|
@ -1766,6 +1766,7 @@ mod tests {
|
|||
payer_id: Some(&payer_pubkey()),
|
||||
payer_note: None,
|
||||
paths: None,
|
||||
offer_from_hrn: None,
|
||||
},
|
||||
InvoiceTlvStreamRef {
|
||||
paths: Some(Iterable(payment_paths.iter().map(|path| path.inner_blinded_path()))),
|
||||
|
@ -1868,6 +1869,7 @@ mod tests {
|
|||
payer_id: Some(&payer_pubkey()),
|
||||
payer_note: None,
|
||||
paths: None,
|
||||
offer_from_hrn: None,
|
||||
},
|
||||
InvoiceTlvStreamRef {
|
||||
paths: Some(Iterable(payment_paths.iter().map(|path| path.inner_blinded_path()))),
|
||||
|
|
|
@ -75,6 +75,7 @@ use crate::offers::offer::{EXPERIMENTAL_OFFER_TYPES, ExperimentalOfferTlvStream,
|
|||
use crate::offers::parse::{Bolt12ParseError, ParsedMessage, Bolt12SemanticError};
|
||||
use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
|
||||
use crate::offers::signer::{Metadata, MetadataMaterial};
|
||||
use crate::onion_message::dns_resolution::HumanReadableName;
|
||||
use crate::util::ser::{CursorReadable, HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
|
||||
use crate::util::string::{PrintableString, UntrustedString};
|
||||
|
||||
|
@ -241,6 +242,7 @@ macro_rules! invoice_request_builder_methods { (
|
|||
InvoiceRequestContentsWithoutPayerSigningPubkey {
|
||||
payer: PayerContents(metadata), offer, chain: None, amount_msats: None,
|
||||
features: InvoiceRequestFeatures::empty(), quantity: None, payer_note: None,
|
||||
offer_from_hrn: None,
|
||||
#[cfg(test)]
|
||||
experimental_bar: None,
|
||||
}
|
||||
|
@ -301,6 +303,14 @@ macro_rules! invoice_request_builder_methods { (
|
|||
$return_value
|
||||
}
|
||||
|
||||
/// Sets the [`InvoiceRequest::offer_from_hrn`].
|
||||
///
|
||||
/// Successive calls to this method will override the previous setting.
|
||||
pub fn sourced_from_human_readable_name($($self_mut)* $self: $self_type, hrn: HumanReadableName) -> $return_type {
|
||||
$self.invoice_request.offer_from_hrn = Some(hrn);
|
||||
$return_value
|
||||
}
|
||||
|
||||
fn build_with_checks($($self_mut)* $self: $self_type) -> Result<
|
||||
(UnsignedInvoiceRequest, Option<Keypair>, Option<&'b Secp256k1<$secp_context>>),
|
||||
Bolt12SemanticError
|
||||
|
@ -699,6 +709,7 @@ pub(super) struct InvoiceRequestContentsWithoutPayerSigningPubkey {
|
|||
features: InvoiceRequestFeatures,
|
||||
quantity: Option<u64>,
|
||||
payer_note: Option<String>,
|
||||
offer_from_hrn: Option<HumanReadableName>,
|
||||
#[cfg(test)]
|
||||
experimental_bar: Option<u64>,
|
||||
}
|
||||
|
@ -745,6 +756,12 @@ macro_rules! invoice_request_accessors { ($self: ident, $contents: expr) => {
|
|||
pub fn payer_note(&$self) -> Option<PrintableString> {
|
||||
$contents.payer_note()
|
||||
}
|
||||
|
||||
/// If the [`Offer`] was sourced from a BIP 353 Human Readable Name, this should be set by the
|
||||
/// builder to indicate the original [`HumanReadableName`] which was resolved.
|
||||
pub fn offer_from_hrn(&$self) -> &Option<HumanReadableName> {
|
||||
$contents.offer_from_hrn()
|
||||
}
|
||||
} }
|
||||
|
||||
impl UnsignedInvoiceRequest {
|
||||
|
@ -1004,9 +1021,7 @@ impl VerifiedInvoiceRequest {
|
|||
let InvoiceRequestContents {
|
||||
payer_signing_pubkey,
|
||||
inner: InvoiceRequestContentsWithoutPayerSigningPubkey {
|
||||
payer: _, offer: _, chain: _, amount_msats: _, features: _, quantity, payer_note,
|
||||
#[cfg(test)]
|
||||
experimental_bar: _,
|
||||
quantity, payer_note, ..
|
||||
},
|
||||
} = &self.inner.contents;
|
||||
|
||||
|
@ -1049,6 +1064,10 @@ impl InvoiceRequestContents {
|
|||
.map(|payer_note| PrintableString(payer_note.as_str()))
|
||||
}
|
||||
|
||||
pub(super) fn offer_from_hrn(&self) -> &Option<HumanReadableName> {
|
||||
&self.inner.offer_from_hrn
|
||||
}
|
||||
|
||||
pub(super) fn as_tlv_stream(&self) -> PartialInvoiceRequestTlvStreamRef {
|
||||
let (payer, offer, mut invoice_request, experimental_offer, experimental_invoice_request) =
|
||||
self.inner.as_tlv_stream();
|
||||
|
@ -1085,6 +1104,7 @@ impl InvoiceRequestContentsWithoutPayerSigningPubkey {
|
|||
quantity: self.quantity,
|
||||
payer_id: None,
|
||||
payer_note: self.payer_note.as_ref(),
|
||||
offer_from_hrn: self.offer_from_hrn.as_ref(),
|
||||
paths: None,
|
||||
};
|
||||
|
||||
|
@ -1142,6 +1162,7 @@ tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef<'a>, INVOICE_REQ
|
|||
(89, payer_note: (String, WithoutLength)),
|
||||
// Only used for Refund since the onion message of an InvoiceRequest has a reply path.
|
||||
(90, paths: (Vec<BlindedMessagePath>, WithoutLength)),
|
||||
(91, offer_from_hrn: HumanReadableName),
|
||||
});
|
||||
|
||||
/// Valid type range for experimental invoice_request TLV records.
|
||||
|
@ -1266,6 +1287,7 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
|
|||
offer_tlv_stream,
|
||||
InvoiceRequestTlvStream {
|
||||
chain, amount, features, quantity, payer_id, payer_note, paths,
|
||||
offer_from_hrn,
|
||||
},
|
||||
experimental_offer_tlv_stream,
|
||||
ExperimentalInvoiceRequestTlvStream {
|
||||
|
@ -1305,6 +1327,7 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
|
|||
Ok(InvoiceRequestContents {
|
||||
inner: InvoiceRequestContentsWithoutPayerSigningPubkey {
|
||||
payer, offer, chain, amount_msats: amount, features, quantity, payer_note,
|
||||
offer_from_hrn,
|
||||
#[cfg(test)]
|
||||
experimental_bar,
|
||||
},
|
||||
|
@ -1484,6 +1507,7 @@ mod tests {
|
|||
payer_id: Some(&payer_pubkey()),
|
||||
payer_note: None,
|
||||
paths: None,
|
||||
offer_from_hrn: None,
|
||||
},
|
||||
SignatureTlvStreamRef { signature: Some(&invoice_request.signature()) },
|
||||
ExperimentalOfferTlvStreamRef {
|
||||
|
|
|
@ -198,6 +198,11 @@ pub enum Bolt12SemanticError {
|
|||
InvalidSigningPubkey,
|
||||
/// A signature was expected but was missing.
|
||||
MissingSignature,
|
||||
/// A Human Readable Name was provided but was not expected (i.e. was included in a
|
||||
/// [`Refund`]).
|
||||
///
|
||||
/// [`Refund`]: super::refund::Refund
|
||||
UnexpectedHumanReadableName,
|
||||
}
|
||||
|
||||
impl From<CheckedHrpstringError> for Bolt12ParseError {
|
||||
|
|
|
@ -792,6 +792,7 @@ impl RefundContents {
|
|||
payer_id: Some(&self.payer_signing_pubkey),
|
||||
payer_note: self.payer_note.as_ref(),
|
||||
paths: self.paths.as_ref(),
|
||||
offer_from_hrn: None,
|
||||
};
|
||||
|
||||
let experimental_offer = ExperimentalOfferTlvStreamRef {
|
||||
|
@ -888,7 +889,8 @@ impl TryFrom<RefundTlvStream> for RefundContents {
|
|||
issuer_id,
|
||||
},
|
||||
InvoiceRequestTlvStream {
|
||||
chain, amount, features, quantity, payer_id, payer_note, paths
|
||||
chain, amount, features, quantity, payer_id, payer_note, paths,
|
||||
offer_from_hrn,
|
||||
},
|
||||
ExperimentalOfferTlvStream {
|
||||
#[cfg(test)]
|
||||
|
@ -940,6 +942,11 @@ impl TryFrom<RefundTlvStream> for RefundContents {
|
|||
return Err(Bolt12SemanticError::UnexpectedIssuerSigningPubkey);
|
||||
}
|
||||
|
||||
if offer_from_hrn.is_some() {
|
||||
// Only offers can be resolved using Human Readable Names
|
||||
return Err(Bolt12SemanticError::UnexpectedHumanReadableName);
|
||||
}
|
||||
|
||||
let amount_msats = match amount {
|
||||
None => return Err(Bolt12SemanticError::MissingAmount),
|
||||
Some(amount_msats) if amount_msats > MAX_VALUE_MSAT => {
|
||||
|
@ -1066,6 +1073,7 @@ mod tests {
|
|||
payer_id: Some(&payer_pubkey()),
|
||||
payer_note: None,
|
||||
paths: None,
|
||||
offer_from_hrn: None,
|
||||
},
|
||||
ExperimentalOfferTlvStreamRef {
|
||||
experimental_foo: None,
|
||||
|
|
Loading…
Add table
Reference in a new issue