Skip to content

Commit 8a38c88

Browse files
Send held_htlc_available with counterparty reply path
As part of supporting sending payments as an often-offline sender, the sender needs to send held_htlc_available onion messages such that the reply path to the message terminates at their always-online channel counterparty that is holding the HTLC. That way when the recipient responds with release_held_htlc, the sender's counterparty will receive that message. After laying groundwork over some past commits, here we as an async sender send held_htlc_available messages using reply paths created by our always-online channel counterparty.
1 parent 85117be commit 8a38c88

File tree

2 files changed

+55
-6
lines changed

2 files changed

+55
-6
lines changed

lightning/src/ln/channel.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ use crate::ln::onion_utils::{
7070
use crate::ln::script::{self, ShutdownScript};
7171
use crate::ln::types::ChannelId;
7272
use crate::ln::LN_MAX_MSG_LEN;
73+
use crate::offers::static_invoice::StaticInvoice;
7374
use crate::routing::gossip::NodeId;
7475
use crate::sign::ecdsa::EcdsaChannelSigner;
7576
use crate::sign::tx_builder::{HTLCAmountDirection, NextCommitmentStats, SpecTxBuilder, TxBuilder};
@@ -8184,10 +8185,25 @@ where
81848185
/// waiting on this revoke_and_ack. The generation of this new commitment_signed may also fail,
81858186
/// generating an appropriate error *after* the channel state has been updated based on the
81868187
/// revoke_and_ack message.
8188+
///
8189+
/// The static invoices will be used by us as an async sender to enqueue [`HeldHtlcAvailable`]
8190+
/// onion messages for the often-offline recipient, and the blinded reply paths the invoices are
8191+
/// paired with were created by our channel counterparty and will be used as reply paths for
8192+
/// corresponding [`ReleaseHeldHtlc`] messages.
8193+
///
8194+
/// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
8195+
/// [`ReleaseHeldHtlc`]: crate::onion_message::async_payments::ReleaseHeldHtlc
81878196
pub fn revoke_and_ack<F: Deref, L: Deref>(
81888197
&mut self, msg: &msgs::RevokeAndACK, fee_estimator: &LowerBoundedFeeEstimator<F>,
81898198
logger: &L, hold_mon_update: bool,
8190-
) -> Result<(Vec<(HTLCSource, PaymentHash)>, Option<ChannelMonitorUpdate>), ChannelError>
8199+
) -> Result<
8200+
(
8201+
Vec<(HTLCSource, PaymentHash)>,
8202+
Vec<(StaticInvoice, BlindedMessagePath)>,
8203+
Option<ChannelMonitorUpdate>,
8204+
),
8205+
ChannelError,
8206+
>
81918207
where
81928208
F::Target: FeeEstimator,
81938209
L::Target: Logger,
@@ -8302,6 +8318,7 @@ where
83028318
let mut finalized_claimed_htlcs = Vec::new();
83038319
let mut update_fail_htlcs = Vec::new();
83048320
let mut update_fail_malformed_htlcs = Vec::new();
8321+
let mut static_invoices = Vec::new();
83058322
let mut require_commitment = false;
83068323
let mut value_to_self_msat_diff: i64 = 0;
83078324

@@ -8417,6 +8434,24 @@ where
84178434
}
84188435
}
84198436
for htlc in pending_outbound_htlcs.iter_mut() {
8437+
for (htlc_id, blinded_path) in &msg.release_htlc_message_paths {
8438+
if htlc.htlc_id != *htlc_id {
8439+
continue;
8440+
}
8441+
let static_invoice = match htlc.source.static_invoice() {
8442+
Some(inv) if htlc.hold_htlc.is_some() => inv,
8443+
_ => {
8444+
// We should only be using our counterparty's release_htlc_message_path if we
8445+
// originally configured the HTLC to be held with them until the recipient comes
8446+
// online. Otherwise, our counterparty could include paths for all of our HTLCs and
8447+
// use the responses sent to their paths to determine which of our HTLCs are async
8448+
// payments.
8449+
log_trace!(logger, "Counterparty included release_htlc_message_path for non-async payment HTLC {}", htlc_id);
8450+
continue;
8451+
},
8452+
};
8453+
static_invoices.push((static_invoice, blinded_path.clone()));
8454+
}
84208455
if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
84218456
log_trace!(
84228457
logger,
@@ -8484,9 +8519,9 @@ where
84848519
self.context
84858520
.blocked_monitor_updates
84868521
.push(PendingChannelMonitorUpdate { update: monitor_update });
8487-
return Ok(($htlcs_to_fail, None));
8522+
return Ok(($htlcs_to_fail, static_invoices, None));
84888523
} else {
8489-
return Ok(($htlcs_to_fail, Some(monitor_update)));
8524+
return Ok(($htlcs_to_fail, static_invoices, Some(monitor_update)));
84908525
}
84918526
};
84928527
}

lightning/src/ln/channelmanager.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,16 @@ impl HTLCSource {
873873
_ => None,
874874
}
875875
}
876+
877+
pub(crate) fn static_invoice(&self) -> Option<StaticInvoice> {
878+
match self {
879+
Self::OutboundRoute {
880+
bolt12_invoice: Some(PaidBolt12Invoice::StaticInvoice(inv)),
881+
..
882+
} => Some(inv.clone()),
883+
_ => None,
884+
}
885+
}
876886
}
877887

878888
/// This enum is used to specify which error data to send to peers when failing back an HTLC
@@ -11011,7 +11021,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1101111021

1101211022
#[rustfmt::skip]
1101311023
fn internal_revoke_and_ack(&self, counterparty_node_id: &PublicKey, msg: &msgs::RevokeAndACK) -> Result<(), MsgHandleErrInternal> {
11014-
let htlcs_to_fail = {
11024+
let (htlcs_to_fail, static_invoices) = {
1101511025
let per_peer_state = self.per_peer_state.read().unwrap();
1101611026
let mut peer_state_lock = per_peer_state.get(counterparty_node_id)
1101711027
.ok_or_else(|| {
@@ -11027,15 +11037,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1102711037
let mon_update_blocked = self.raa_monitor_updates_held(
1102811038
&peer_state.actions_blocking_raa_monitor_updates, msg.channel_id,
1102911039
*counterparty_node_id);
11030-
let (htlcs_to_fail, monitor_update_opt) = try_channel_entry!(self, peer_state,
11040+
let (htlcs_to_fail, static_invoices, monitor_update_opt) = try_channel_entry!(self, peer_state,
1103111041
chan.revoke_and_ack(&msg, &self.fee_estimator, &&logger, mon_update_blocked), chan_entry);
1103211042
if let Some(monitor_update) = monitor_update_opt {
1103311043
let funding_txo = funding_txo_opt
1103411044
.expect("Funding outpoint must have been set for RAA handling to succeed");
1103511045
handle_new_monitor_update!(self, funding_txo, monitor_update,
1103611046
peer_state_lock, peer_state, per_peer_state, chan);
1103711047
}
11038-
htlcs_to_fail
11048+
(htlcs_to_fail, static_invoices)
1103911049
} else {
1104011050
return try_channel_entry!(self, peer_state, Err(ChannelError::close(
1104111051
"Got a revoke_and_ack message for an unfunded channel!".into())), chan_entry);
@@ -11045,6 +11055,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1104511055
}
1104611056
};
1104711057
self.fail_holding_cell_htlcs(htlcs_to_fail, msg.channel_id, counterparty_node_id);
11058+
for (static_invoice, reply_path) in static_invoices {
11059+
let res = self.flow.enqueue_held_htlc_available(&static_invoice, HeldHtlcReplyPath::ToCounterparty { path: reply_path });
11060+
debug_assert!(res.is_ok(), "enqueue_held_htlc_available can only fail for non-async senders");
11061+
}
1104811062
Ok(())
1104911063
}
1105011064

0 commit comments

Comments
 (0)