Skip to content

Commit 46b794e

Browse files
committed
Utility for creating and sending Bolt12Invoices
Add a utility to ChannelManager for creating a Bolt12Invoice for a Refund such that the ChannelManager can recognize the PaymentHash and reconstruct the PaymentPreimage from the PaymentSecret, the latter of which is contained in a BlindedPath within the invoice.
1 parent ffe9ae2 commit 46b794e

File tree

2 files changed

+89
-3
lines changed

2 files changed

+89
-3
lines changed

lightning/src/blinded_path/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl BlindedPath {
8484
}
8585

8686
/// Create a one-hop blinded path for a payment.
87-
pub fn one_hop_for_payment<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>(
87+
pub fn one_hop_for_payment<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
8888
payee_node_id: PublicKey, payee_tlvs: payment::ReceiveTlvs, entropy_source: &ES,
8989
secp_ctx: &Secp256k1<T>
9090
) -> Result<(BlindedPayInfo, Self), ()> {
@@ -105,7 +105,7 @@ impl BlindedPath {
105105
///
106106
/// [`ForwardTlvs`]: crate::blinded_path::payment::ForwardTlvs
107107
// TODO: make all payloads the same size with padding + add dummy hops
108-
pub(crate) fn new_for_payment<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>(
108+
pub(crate) fn new_for_payment<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
109109
intermediate_nodes: &[payment::ForwardNode], payee_node_id: PublicKey,
110110
payee_tlvs: payment::ReceiveTlvs, htlc_maximum_msat: u64, entropy_source: &ES,
111111
secp_ctx: &Secp256k1<T>

lightning/src/ln/channelmanager.rs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use bitcoin::secp256k1::Secp256k1;
3131
use bitcoin::{LockTime, secp256k1, Sequence};
3232

3333
use crate::blinded_path::BlindedPath;
34+
use crate::blinded_path::payment::{PaymentConstraints, ReceiveTlvs};
3435
use crate::chain;
3536
use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock};
3637
use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator};
@@ -56,9 +57,10 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
5657
use crate::ln::outbound_payment;
5758
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
5859
use crate::ln::wire::Encode;
60+
use crate::offers::invoice::{BlindedPayInfo, DEFAULT_RELATIVE_EXPIRY};
5961
use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
6062
use crate::offers::parse::Bolt12SemanticError;
61-
use crate::offers::refund::RefundBuilder;
63+
use crate::offers::refund::{Refund, RefundBuilder};
6264
use crate::onion_message::{Destination, OffersMessage, PendingOnionMessage};
6365
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner};
6466
use 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

Comments
 (0)