Skip to content

Commit 0297a1e

Browse files
Support sending async payments as an always-online sender.
Async receive is not yet supported. Here we process inbound release_htlc onion messages, check that they actually correspond to one of our outbound payments, and actually forward the HTLCs. Valid release_htlc receipt indicates that the recipient has now come online to receive.
1 parent 69356e7 commit 0297a1e

File tree

2 files changed

+79
-9
lines changed

2 files changed

+79
-9
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4378,6 +4378,21 @@ where
43784378
res
43794379
}
43804380

4381+
#[cfg(async_payments)]
4382+
fn send_payment_for_static_invoice(
4383+
&self, payment_id: PaymentId, payment_release_secret: [u8; 32]
4384+
) -> Result<(), Bolt12PaymentError> {
4385+
let best_block_height = self.best_block.read().unwrap().height;
4386+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
4387+
self.pending_outbound_payments
4388+
.send_payment_for_static_invoice(
4389+
payment_id, payment_release_secret, &self.router, self.list_usable_channels(),
4390+
|| self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, &self,
4391+
&self.secp_ctx, best_block_height, &self.logger, &self.pending_events,
4392+
|args| self.send_payment_along_path(args)
4393+
)
4394+
}
4395+
43814396
/// Signals that no further attempts for the given payment should occur. Useful if you have a
43824397
/// pending outbound payment with retries remaining, but wish to stop retrying the payment before
43834398
/// retries are exhausted.
@@ -11171,7 +11186,17 @@ where
1117111186
None
1117211187
}
1117311188

11174-
fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {}
11189+
fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {
11190+
#[cfg(async_payments)] {
11191+
let AsyncPaymentsContext::OutboundPayment { payment_id } = _context;
11192+
if let Err(e) = self.send_payment_for_static_invoice(payment_id, _message.payment_release_secret) {
11193+
log_trace!(
11194+
self.logger, "Failed to release held HTLC with payment id {} and release secret {:02x?}: {:?}",
11195+
payment_id, _message.payment_release_secret, e
11196+
);
11197+
}
11198+
}
11199+
}
1117511200

1117611201
fn release_pending_messages(&self) -> Vec<(AsyncPaymentsMessage, MessageSendInstructions)> {
1117711202
core::mem::take(&mut self.pending_async_payments_messages.lock().unwrap())

lightning/src/ln/outbound_payment.rs

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -856,16 +856,17 @@ impl OutboundPayments {
856856
route_params.max_total_routing_fee_msat = Some(max_fee_msat);
857857
}
858858
self.send_payment_for_bolt12_invoice_internal(
859-
payment_id, payment_hash, route_params, retry_strategy, router, first_hops, inflight_htlcs,
860-
entropy_source, node_signer, node_id_lookup, secp_ctx, best_block_height, logger,
861-
pending_events, send_payment_along_path
859+
payment_id, payment_hash, None, route_params, retry_strategy, router, first_hops,
860+
inflight_htlcs, entropy_source, node_signer, node_id_lookup, secp_ctx, best_block_height,
861+
logger, pending_events, send_payment_along_path
862862
)
863863
}
864864

865865
fn send_payment_for_bolt12_invoice_internal<
866866
R: Deref, ES: Deref, NS: Deref, NL: Deref, IH, SP, L: Deref
867867
>(
868-
&self, payment_id: PaymentId, payment_hash: PaymentHash, mut route_params: RouteParameters,
868+
&self, payment_id: PaymentId, payment_hash: PaymentHash,
869+
keysend_preimage: Option<PaymentPreimage>, mut route_params: RouteParameters,
869870
retry_strategy: Retry, router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH,
870871
entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL,
871872
secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32, logger: &L,
@@ -923,12 +924,13 @@ impl OutboundPayments {
923924

924925
let payment_params = Some(route_params.payment_params.clone());
925926
let (retryable_payment, onion_session_privs) = self.create_pending_payment(
926-
payment_hash, recipient_onion.clone(), None, &route, Some(retry_strategy), payment_params,
927-
entropy_source, best_block_height
927+
payment_hash, recipient_onion.clone(), keysend_preimage, &route, Some(retry_strategy),
928+
payment_params, entropy_source, best_block_height
928929
);
929930
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
930931
hash_map::Entry::Occupied(entry) => match entry.get() {
931-
PendingOutboundPayment::InvoiceReceived { .. } => {
932+
PendingOutboundPayment::InvoiceReceived { .. }
933+
| PendingOutboundPayment::StaticInvoiceReceived { .. } => {
932934
*entry.into_mut() = retryable_payment;
933935
},
934936
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
@@ -937,7 +939,7 @@ impl OutboundPayments {
937939
}
938940

939941
let result = self.pay_route_internal(
940-
&route, payment_hash, &recipient_onion, None, payment_id,
942+
&route, payment_hash, &recipient_onion, keysend_preimage, payment_id,
941943
Some(route_params.final_value_msat), onion_session_privs, node_signer,
942944
best_block_height, &send_payment_along_path
943945
);
@@ -1033,6 +1035,49 @@ impl OutboundPayments {
10331035
};
10341036
}
10351037

1038+
#[cfg(async_payments)]
1039+
pub(super) fn send_payment_for_static_invoice<
1040+
R: Deref, ES: Deref, NS: Deref, NL: Deref, IH, SP, L: Deref
1041+
>(
1042+
&self, payment_id: PaymentId, payment_release_secret: [u8; 32], router: &R,
1043+
first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
1044+
node_id_lookup: &NL, secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32, logger: &L,
1045+
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
1046+
send_payment_along_path: SP,
1047+
) -> Result<(), Bolt12PaymentError>
1048+
where
1049+
R::Target: Router,
1050+
ES::Target: EntropySource,
1051+
NS::Target: NodeSigner,
1052+
NL::Target: NodeIdLookUp,
1053+
L::Target: Logger,
1054+
IH: Fn() -> InFlightHtlcs,
1055+
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
1056+
{
1057+
let (payment_hash, keysend_preimage, route_params, retry_strategy) =
1058+
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
1059+
hash_map::Entry::Occupied(entry) => match entry.get() {
1060+
PendingOutboundPayment::StaticInvoiceReceived {
1061+
payment_hash, payment_release_secret: release_secret, route_params, retry_strategy,
1062+
keysend_preimage, ..
1063+
} => {
1064+
if payment_release_secret != *release_secret {
1065+
return Err(Bolt12PaymentError::UnexpectedInvoice)
1066+
}
1067+
(*payment_hash, *keysend_preimage, route_params.clone(), *retry_strategy)
1068+
},
1069+
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
1070+
},
1071+
hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
1072+
};
1073+
1074+
self.send_payment_for_bolt12_invoice_internal(
1075+
payment_id, payment_hash, Some(keysend_preimage), route_params, retry_strategy, router,
1076+
first_hops, inflight_htlcs, entropy_source, node_signer, node_id_lookup, secp_ctx,
1077+
best_block_height, logger, pending_events, send_payment_along_path
1078+
)
1079+
}
1080+
10361081
pub(super) fn check_retry_payments<R: Deref, ES: Deref, NS: Deref, SP, IH, FH, L: Deref>(
10371082
&self, router: &R, first_hops: FH, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
10381083
best_block_height: u32,

0 commit comments

Comments
 (0)