Skip to content

Commit

Permalink
Support sending async payments as an always-online sender.
Browse files Browse the repository at this point in the history
Async receive is not yet supported.
  • Loading branch information
valentinewallace committed Jul 8, 2024
1 parent e3a33ef commit 99c472b
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 2 deletions.
27 changes: 26 additions & 1 deletion lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4076,6 +4076,21 @@ where
Ok(())
}

#[cfg(async_payments)]
fn send_payment_for_static_invoice(
&self, payment_id: PaymentId, payment_release_secret: [u8; 32]
) -> Result<(), Bolt12PaymentError> {
let best_block_height = self.best_block.read().unwrap().height;
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
self.pending_outbound_payments
.send_payment_for_static_invoice(
payment_id, payment_release_secret, &self.router, self.list_usable_channels(),
|| self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer,
best_block_height, &self.logger, &self.pending_events,
|args| self.send_payment_along_path(args)
)
}

/// Signals that no further attempts for the given payment should occur. Useful if you have a
/// pending outbound payment with retries remaining, but wish to stop retrying the payment before
/// retries are exhausted.
Expand Down Expand Up @@ -10467,7 +10482,17 @@ where
ResponseInstruction::NoResponse
}

fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _payment_id: Option<PaymentId>) {}
fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _payment_id: Option<PaymentId>) {
#[cfg(async_payments)] {
let payment_id = if let Some(id) = _payment_id { id } else { return };
if let Err(e) = self.send_payment_for_static_invoice(payment_id, _message.payment_release_secret) {
log_trace!(
self.logger, "Failed to release held HTLC with payment id {} and release secret {:02x?}: {:?}",
payment_id, _message.payment_release_secret, e
);
}
}
}

fn release_pending_messages(&self) -> Vec<PendingOnionMessage<AsyncPaymentsMessage>> {
core::mem::take(&mut self.pending_async_payments_messages.lock().unwrap())
Expand Down
57 changes: 56 additions & 1 deletion lightning/src/ln/outbound_payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,47 @@ impl OutboundPayments {
};
}

#[cfg(async_payments)]
pub(super) fn send_payment_for_static_invoice<R: Deref, ES: Deref, NS: Deref, IH, SP, L: Deref>(
&self, payment_id: PaymentId, payment_release_secret: [u8; 32], router: &R,
first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
best_block_height: u32, logger: &L,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
send_payment_along_path: SP,
) -> Result<(), Bolt12PaymentError>
where
R::Target: Router,
ES::Target: EntropySource,
NS::Target: NodeSigner,
L::Target: Logger,
IH: Fn() -> InFlightHtlcs,
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
let (payment_hash, route_params) =
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::StaticInvoiceReceived {
payment_hash, payment_release_secret: release_secret, route_params, ..
} => {
if payment_release_secret != *release_secret {
return Err(Bolt12PaymentError::UnexpectedInvoice)
}
(*payment_hash, route_params.clone())
},
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
},
hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
};

self.find_route_and_send_payment(
payment_hash, payment_id, route_params, router, first_hops, &inflight_htlcs,
entropy_source, node_signer, best_block_height, logger, pending_events,
&send_payment_along_path
);

Ok(())
}

pub(super) fn check_retry_payments<R: Deref, ES: Deref, NS: Deref, SP, IH, FH, L: Deref>(
&self, router: &R, first_hops: FH, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
best_block_height: u32,
Expand Down Expand Up @@ -1177,7 +1218,21 @@ impl OutboundPayments {
*payment.into_mut() = retryable_payment;
(total_amount, recipient_onion, None, onion_session_privs)
},
PendingOutboundPayment::StaticInvoiceReceived { .. } => todo!(),
PendingOutboundPayment::StaticInvoiceReceived {
payment_hash, keysend_preimage, retry_strategy, ..
} => {
let keysend_preimage = Some(*keysend_preimage);
let total_amount = route_params.final_value_msat;
let recipient_onion = RecipientOnionFields::spontaneous_empty();
let retry_strategy = Some(*retry_strategy);
let payment_params = Some(route_params.payment_params.clone());
let (retryable_payment, onion_session_privs) = self.create_pending_payment(
*payment_hash, recipient_onion.clone(), keysend_preimage, &route,
retry_strategy, payment_params, entropy_source, best_block_height
);
*payment.into_mut() = retryable_payment;
(total_amount, recipient_onion, keysend_preimage, onion_session_privs)
},
PendingOutboundPayment::Fulfilled { .. } => {
log_error!(logger, "Payment already completed");
return
Expand Down

0 comments on commit 99c472b

Please sign in to comment.