@@ -31,6 +31,7 @@ use bitcoin::secp256k1::Secp256k1;
3131use bitcoin::{LockTime, secp256k1, Sequence};
3232
3333use crate::blinded_path::BlindedPath;
34+ use crate::blinded_path::payment::{PaymentConstraints, ReceiveTlvs};
3435use crate::chain;
3536use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock};
3637use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator};
@@ -56,9 +57,10 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
5657use crate::ln::outbound_payment;
5758use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
5859use crate::ln::wire::Encode;
60+ use crate::offers::invoice::{BlindedPayInfo, DEFAULT_RELATIVE_EXPIRY};
5961use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
6062use crate::offers::parse::Bolt12SemanticError;
61- use crate::offers::refund::RefundBuilder;
63+ use crate::offers::refund::{Refund, RefundBuilder} ;
6264use crate::onion_message::{Destination, OffersMessage, PendingOnionMessage};
6365use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner};
6466use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
@@ -7445,6 +7447,67 @@ where
74457447 Ok(())
74467448 }
74477449
7450+ /// Creates a [`Bolt12Invoice`] for a [`Refund`] and enqueues it to be sent via an onion
7451+ /// message.
7452+ ///
7453+ /// The resulting invoice uses a [`PaymentHash`] recognized by the [`ChannelManager`] and a
7454+ /// [`BlindedPath`] containing the [`PaymentSecret`] needed to reconstruct the corresponding
7455+ /// [`PaymentPreimage`].
7456+ ///
7457+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
7458+ pub fn request_refund_payment(&self, refund: &Refund) -> Result<(), Bolt12SemanticError> {
7459+ let expanded_key = &self.inbound_payment_key;
7460+ let entropy = &*self.entropy_source;
7461+ let secp_ctx = &self.secp_ctx;
7462+
7463+ let amount_msats = refund.amount_msats();
7464+ let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
7465+
7466+ match self.create_inbound_payment(Some(amount_msats), relative_expiry, None) {
7467+ Ok((payment_hash, payment_secret)) => {
7468+ let payment_paths = vec![
7469+ self.create_one_hop_blinded_payment_path(payment_secret),
7470+ ];
7471+ #[cfg(not(feature = "no-std"))]
7472+ let builder = refund.respond_using_derived_keys(
7473+ payment_paths, payment_hash, expanded_key, entropy
7474+ )?;
7475+ #[cfg(feature = "no-std")]
7476+ let created_at = Duration::from_secs(
7477+ self.highest_seen_timestamp.load(Ordering::Acquire) as u64
7478+ );
7479+ #[cfg(feature = "no-std")]
7480+ let builder = refund.respond_using_derived_keys_no_std(
7481+ payment_paths, payment_hash, created_at, expanded_key, entropy
7482+ )?;
7483+ let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
7484+ let reply_path = self.create_one_hop_blinded_path();
7485+
7486+ let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
7487+ if refund.paths().is_empty() {
7488+ let message = PendingOnionMessage {
7489+ contents: OffersMessage::Invoice(invoice),
7490+ destination: Destination::Node(refund.payer_id()),
7491+ reply_path: Some(reply_path),
7492+ };
7493+ pending_offers_messages.push(message);
7494+ } else {
7495+ for path in refund.paths() {
7496+ let message = PendingOnionMessage {
7497+ contents: OffersMessage::Invoice(invoice.clone()),
7498+ destination: Destination::BlindedPath(path.clone()),
7499+ reply_path: Some(reply_path.clone()),
7500+ };
7501+ pending_offers_messages.push(message);
7502+ }
7503+ }
7504+
7505+ Ok(())
7506+ },
7507+ Err(()) => Err(Bolt12SemanticError::InvalidAmount),
7508+ }
7509+ }
7510+
74487511 /// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
74497512 /// to pay us.
74507513 ///
@@ -7553,6 +7616,29 @@ where
75537616 BlindedPath::one_hop_for_message(self.get_our_node_id(), entropy_source, secp_ctx).unwrap()
75547617 }
75557618
7619+ /// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction
7620+ /// node.
7621+ fn create_one_hop_blinded_payment_path(
7622+ &self, payment_secret: PaymentSecret
7623+ ) -> (BlindedPayInfo, BlindedPath) {
7624+ let entropy_source = self.entropy_source.deref();
7625+ let secp_ctx = &self.secp_ctx;
7626+
7627+ let payee_node_id = self.get_our_node_id();
7628+ let max_cltv_expiry = self.best_block.read().unwrap().height() + LATENCY_GRACE_PERIOD_BLOCKS;
7629+ let payee_tlvs = ReceiveTlvs {
7630+ payment_secret,
7631+ payment_constraints: PaymentConstraints {
7632+ max_cltv_expiry,
7633+ htlc_minimum_msat: 1,
7634+ },
7635+ };
7636+ // TODO: Err for overflow?
7637+ BlindedPath::one_hop_for_payment(
7638+ payee_node_id, payee_tlvs, entropy_source, secp_ctx
7639+ ).unwrap()
7640+ }
7641+
75567642 /// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids
75577643 /// are used when constructing the phantom invoice's route hints.
75587644 ///
0 commit comments