Skip to content

Commit 45838b2

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 4085f05 commit 45838b2

File tree

2 files changed

+51
-6
lines changed

2 files changed

+51
-6
lines changed

lightning/src/ln/channel.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ use crate::ln::script::{self, ShutdownScript};
8080
use crate::ln::types::ChannelId;
8181
#[cfg(splicing)]
8282
use crate::ln::LN_MAX_MSG_LEN;
83+
use crate::offers::static_invoice::StaticInvoice;
8384
use crate::routing::gossip::NodeId;
8485
use crate::sign::ecdsa::EcdsaChannelSigner;
8586
use crate::sign::tx_builder::{HTLCAmountDirection, NextCommitmentStats, SpecTxBuilder, TxBuilder};
@@ -7992,10 +7993,25 @@ where
79927993
/// waiting on this revoke_and_ack. The generation of this new commitment_signed may also fail,
79937994
/// generating an appropriate error *after* the channel state has been updated based on the
79947995
/// revoke_and_ack message.
7996+
///
7997+
/// The static invoices will be used by us as an async sender to enqueue [`HeldHtlcAvailable`]
7998+
/// onion messages for the often-offline recipient, and the blinded reply paths the invoices are
7999+
/// paired with were created by our channel counterparty and will be used as reply paths for
8000+
/// corresponding [`ReleaseHeldHtlc`] messages.
8001+
///
8002+
/// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
8003+
/// [`ReleaseHeldHtlc`]: crate::onion_message::async_payments::ReleaseHeldHtlc
79958004
pub fn revoke_and_ack<F: Deref, L: Deref>(
79968005
&mut self, msg: &msgs::RevokeAndACK, fee_estimator: &LowerBoundedFeeEstimator<F>,
79978006
logger: &L, hold_mon_update: bool,
7998-
) -> Result<(Vec<(HTLCSource, PaymentHash)>, Option<ChannelMonitorUpdate>), ChannelError>
8007+
) -> Result<
8008+
(
8009+
Vec<(HTLCSource, PaymentHash)>,
8010+
Vec<(StaticInvoice, BlindedMessagePath)>,
8011+
Option<ChannelMonitorUpdate>,
8012+
),
8013+
ChannelError,
8014+
>
79998015
where
80008016
F::Target: FeeEstimator,
80018017
L::Target: Logger,
@@ -8110,6 +8126,7 @@ where
81108126
let mut finalized_claimed_htlcs = Vec::new();
81118127
let mut update_fail_htlcs = Vec::new();
81128128
let mut update_fail_malformed_htlcs = Vec::new();
8129+
let mut static_invoices = Vec::new();
81138130
let mut require_commitment = false;
81148131
let mut value_to_self_msat_diff: i64 = 0;
81158132

@@ -8225,6 +8242,20 @@ where
82258242
}
82268243
}
82278244
for htlc in pending_outbound_htlcs.iter_mut() {
8245+
for (htlc_id, blinded_path) in &msg.release_htlc_message_paths {
8246+
if htlc.htlc_id != *htlc_id {
8247+
continue;
8248+
}
8249+
let static_invoice = match htlc.source.static_invoice() {
8250+
Some(inv) => inv,
8251+
None => {
8252+
// This is reachable but it means the counterparty is buggy and included a release
8253+
// path for an HTLC that we didn't originally flag as a hold_htlc.
8254+
continue;
8255+
},
8256+
};
8257+
static_invoices.push((static_invoice, blinded_path.clone()));
8258+
}
82288259
if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
82298260
log_trace!(
82308261
logger,
@@ -8292,9 +8323,9 @@ where
82928323
self.context
82938324
.blocked_monitor_updates
82948325
.push(PendingChannelMonitorUpdate { update: monitor_update });
8295-
return Ok(($htlcs_to_fail, None));
8326+
return Ok(($htlcs_to_fail, static_invoices, None));
82968327
} else {
8297-
return Ok(($htlcs_to_fail, Some(monitor_update)));
8328+
return Ok(($htlcs_to_fail, static_invoices, Some(monitor_update)));
82988329
}
82998330
};
83008331
}

lightning/src/ln/channelmanager.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,16 @@ impl HTLCSource {
860860
_ => false,
861861
}
862862
}
863+
864+
pub(crate) fn static_invoice(&self) -> Option<StaticInvoice> {
865+
match self {
866+
Self::OutboundRoute {
867+
bolt12_invoice: Some(PaidBolt12Invoice::StaticInvoice(inv)),
868+
..
869+
} => Some(inv.clone()),
870+
_ => None,
871+
}
872+
}
863873
}
864874

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

1103911049
#[rustfmt::skip]
1104011050
fn internal_revoke_and_ack(&self, counterparty_node_id: &PublicKey, msg: &msgs::RevokeAndACK) -> Result<(), MsgHandleErrInternal> {
11041-
let htlcs_to_fail = {
11051+
let (htlcs_to_fail, static_invoices) = {
1104211052
let per_peer_state = self.per_peer_state.read().unwrap();
1104311053
let mut peer_state_lock = per_peer_state.get(counterparty_node_id)
1104411054
.ok_or_else(|| {
@@ -11054,15 +11064,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1105411064
let mon_update_blocked = self.raa_monitor_updates_held(
1105511065
&peer_state.actions_blocking_raa_monitor_updates, msg.channel_id,
1105611066
*counterparty_node_id);
11057-
let (htlcs_to_fail, monitor_update_opt) = try_channel_entry!(self, peer_state,
11067+
let (htlcs_to_fail, static_invoices, monitor_update_opt) = try_channel_entry!(self, peer_state,
1105811068
chan.revoke_and_ack(&msg, &self.fee_estimator, &&logger, mon_update_blocked), chan_entry);
1105911069
if let Some(monitor_update) = monitor_update_opt {
1106011070
let funding_txo = funding_txo_opt
1106111071
.expect("Funding outpoint must have been set for RAA handling to succeed");
1106211072
handle_new_monitor_update!(self, funding_txo, monitor_update,
1106311073
peer_state_lock, peer_state, per_peer_state, chan);
1106411074
}
11065-
htlcs_to_fail
11075+
(htlcs_to_fail, static_invoices)
1106611076
} else {
1106711077
return try_channel_entry!(self, peer_state, Err(ChannelError::close(
1106811078
"Got a revoke_and_ack message for an unfunded channel!".into())), chan_entry);
@@ -11072,6 +11082,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1107211082
}
1107311083
};
1107411084
self.fail_holding_cell_htlcs(htlcs_to_fail, msg.channel_id, counterparty_node_id);
11085+
for (static_invoice, reply_path) in static_invoices {
11086+
let res = self.flow.enqueue_held_htlc_available(&static_invoice, HeldHtlcReplyPath::ToCounterparty { path: reply_path });
11087+
debug_assert!(res.is_ok(), "enqueue_held_htlc_available can only fail for non-async senders");
11088+
}
1107511089
Ok(())
1107611090
}
1107711091

0 commit comments

Comments
 (0)