@@ -158,6 +158,72 @@ enum InboundHTLCState {
158158 LocalRemoved(InboundHTLCRemovalReason),
159159}
160160
161+ /// Exposes the state of pending inbound HTLCs.
162+ ///
163+ /// At a high level, an HTLC being forwarded from one Lightning node to another Lightning node goes
164+ /// through the following states in the state machine:
165+ /// - Announced for addition by the originating node through the update_add_htlc message.
166+ /// - Added to the commitment transaction of the receiving node and originating node in turn
167+ /// through the exchange of commitment_signed and revoke_and_ack messages.
168+ /// - Announced for resolution (fulfillment or failure) by the receiving node through either one of
169+ /// the update_fulfill_htlc, update_fail_htlc, and update_fail_malformed_htlc messages.
170+ /// - Removed from the commitment transaction of the originating node and receiving node in turn
171+ /// through the exchange of commitment_signed and revoke_and_ack messages.
172+ ///
173+ /// This can be used to inspect what next message an HTLC is waiting for to advance its state.
174+ #[derive(Clone, Debug, PartialEq)]
175+ pub enum InboundHTLCStateDetails {
176+ /// We have added this HTLC in our commitment transaction by receiving commitment_signed and
177+ /// returning revoke_and_ack. We are awaiting the appropriate revoke_and_ack's from the remote
178+ /// before this HTLC is included on the remote commitment transaction.
179+ AwaitingRemoteRevokeToAdd,
180+ /// This HTLC has been included in the commitment_signed and revoke_and_ack messages on both sides
181+ /// and is included in both commitment transactions.
182+ ///
183+ /// This HTLC is now safe to either forward or be claimed as a payment by us. The HTLC will
184+ /// remain in this state until the forwarded upstream HTLC has been resolved and we resolve this
185+ /// HTLC correspondingly, or until we claim it as a payment. If it is part of a multipart
186+ /// payment, it will only be claimed together with other required parts.
187+ Committed,
188+ /// We have received the preimage for this HTLC and it is being removed by fulfilling it with
189+ /// update_fulfill_htlc. This HTLC is still on both commitment transactions, but we are awaiting
190+ /// the appropriate revoke_and_ack's from the remote before this HTLC is removed from the remote
191+ /// commitment transaction after update_fulfill_htlc.
192+ AwaitingRemoteRevokeToRemoveFulfill,
193+ /// The HTLC is being removed by failing it with update_fail_htlc or update_fail_malformed_htlc.
194+ /// This HTLC is still on both commitment transactions, but we are awaiting the appropriate
195+ /// revoke_and_ack's from the remote before this HTLC is removed from the remote commitment
196+ /// transaction.
197+ AwaitingRemoteRevokeToRemoveFail,
198+ }
199+
200+ impl From<&InboundHTLCState> for Option<InboundHTLCStateDetails> {
201+ fn from(state: &InboundHTLCState) -> Option<InboundHTLCStateDetails> {
202+ match state {
203+ InboundHTLCState::RemoteAnnounced(_) => None,
204+ InboundHTLCState::AwaitingRemoteRevokeToAnnounce(_) =>
205+ Some(InboundHTLCStateDetails::AwaitingRemoteRevokeToAdd),
206+ InboundHTLCState::AwaitingAnnouncedRemoteRevoke(_) =>
207+ Some(InboundHTLCStateDetails::AwaitingRemoteRevokeToAdd),
208+ InboundHTLCState::Committed =>
209+ Some(InboundHTLCStateDetails::Committed),
210+ InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(_)) =>
211+ Some(InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFail),
212+ InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailMalformed(_)) =>
213+ Some(InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFail),
214+ InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_)) =>
215+ Some(InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFulfill),
216+ }
217+ }
218+ }
219+
220+ impl_writeable_tlv_based_enum_upgradable!(InboundHTLCStateDetails,
221+ (0, AwaitingRemoteRevokeToAdd) => {},
222+ (2, Committed) => {},
223+ (4, AwaitingRemoteRevokeToRemoveFulfill) => {},
224+ (6, AwaitingRemoteRevokeToRemoveFail) => {};
225+ );
226+
161227struct InboundHTLCOutput {
162228 htlc_id: u64,
163229 amount_msat: u64,
@@ -166,6 +232,48 @@ struct InboundHTLCOutput {
166232 state: InboundHTLCState,
167233}
168234
235+ /// Exposes details around pending inbound HTLCs.
236+ #[derive(Clone, Debug, PartialEq)]
237+ pub struct InboundHTLCDetails {
238+ /// The HTLC ID.
239+ /// The IDs are incremented by 1 starting from 0 for each offered HTLC.
240+ /// They are unique per channel and inbound/outbound direction, unless an HTLC was only announced
241+ /// and not part of any commitment transaction.
242+ pub htlc_id: u64,
243+ /// The amount in msat.
244+ pub amount_msat: u64,
245+ /// The block height at which this HTLC expires.
246+ pub cltv_expiry: u32,
247+ /// The payment hash.
248+ pub payment_hash: PaymentHash,
249+ /// The state of the HTLC in the state machine.
250+ /// Determines on which commitment transactions the HTLC is included and what message the HTLC is
251+ /// waiting for to advance to the next state.
252+ /// See [`InboundHTLCStateDetails`] for information on the specific states.
253+ pub state: Option<InboundHTLCStateDetails>,
254+ /// Whether the HTLC has an output below the local dust limit. If so, the output will be trimmed
255+ /// from the local commitment transaction and added to the commitment transaction fee.
256+ /// For non-anchor channels, this takes into account the cost of the second-stage HTLC
257+ /// transactions as well.
258+ ///
259+ /// When the local commitment transaction is broadcasted as part of a unilateral closure,
260+ /// the value of this HTLC will therefore not be claimable but instead burned as a transaction
261+ /// fee.
262+ ///
263+ /// Note that dust limits are specific to each party. An HTLC can be dust for the local
264+ /// commitment transaction but not for the counterparty's commitment transaction and vice versa.
265+ pub is_dust: bool,
266+ }
267+
268+ impl_writeable_tlv_based!(InboundHTLCDetails, {
269+ (0, htlc_id, required),
270+ (2, amount_msat, required),
271+ (4, cltv_expiry, required),
272+ (6, payment_hash, required),
273+ (7, state, upgradable_option),
274+ (8, is_dust, required),
275+ });
276+
169277#[cfg_attr(test, derive(Clone, Debug, PartialEq))]
170278enum OutboundHTLCState {
171279 /// Added by us and included in a commitment_signed (if we were AwaitingRemoteRevoke when we
@@ -199,6 +307,72 @@ enum OutboundHTLCState {
199307 AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome),
200308}
201309
310+ /// Exposes the state of pending outbound HTLCs.
311+ ///
312+ /// At a high level, an HTLC being forwarded from one Lightning node to another Lightning node goes
313+ /// through the following states in the state machine:
314+ /// - Announced for addition by the originating node through the update_add_htlc message.
315+ /// - Added to the commitment transaction of the receiving node and originating node in turn
316+ /// through the exchange of commitment_signed and revoke_and_ack messages.
317+ /// - Announced for resolution (fulfillment or failure) by the receiving node through either one of
318+ /// the update_fulfill_htlc, update_fail_htlc, and update_fail_malformed_htlc messages.
319+ /// - Removed from the commitment transaction of the originating node and receiving node in turn
320+ /// through the exchange of commitment_signed and revoke_and_ack messages.
321+ ///
322+ /// This can be used to inspect what next message an HTLC is waiting for to advance its state.
323+ #[derive(Clone, Debug, PartialEq)]
324+ pub enum OutboundHTLCStateDetails {
325+ /// We are awaiting the appropriate revoke_and_ack's from the remote before the HTLC is added
326+ /// on the remote's commitment transaction after update_add_htlc.
327+ AwaitingRemoteRevokeToAdd,
328+ /// The HTLC has been added to the remote's commitment transaction by sending commitment_signed
329+ /// and receiving revoke_and_ack in return.
330+ ///
331+ /// The HTLC will remain in this state until the remote node resolves the HTLC, or until we
332+ /// unilaterally close the channel due to a timeout with an uncooperative remote node.
333+ Committed,
334+ /// The HTLC has been fulfilled successfully by the remote with a preimage in update_fulfill_htlc,
335+ /// and we removed the HTLC from our commitment transaction by receiving commitment_signed and
336+ /// returning revoke_and_ack. We are awaiting the appropriate revoke_and_ack's from the remote
337+ /// for the removal from its commitment transaction.
338+ AwaitingRemoteRevokeToRemoveSuccess,
339+ /// The HTLC has been failed by the remote with update_fail_htlc or update_fail_malformed_htlc,
340+ /// and we removed the HTLC from our commitment transaction by receiving commitment_signed and
341+ /// returning revoke_and_ack. We are awaiting the appropriate revoke_and_ack's from the remote
342+ /// for the removal from its commitment transaction.
343+ AwaitingRemoteRevokeToRemoveFailure,
344+ }
345+
346+ impl From<&OutboundHTLCState> for OutboundHTLCStateDetails {
347+ fn from(state: &OutboundHTLCState) -> OutboundHTLCStateDetails {
348+ match state {
349+ OutboundHTLCState::LocalAnnounced(_) =>
350+ OutboundHTLCStateDetails::AwaitingRemoteRevokeToAdd,
351+ OutboundHTLCState::Committed =>
352+ OutboundHTLCStateDetails::Committed,
353+ // RemoteRemoved states are ignored as the state is transient and the remote has not committed to
354+ // the state yet.
355+ OutboundHTLCState::RemoteRemoved(_) =>
356+ OutboundHTLCStateDetails::Committed,
357+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_)) =>
358+ OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveSuccess,
359+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Failure(_)) =>
360+ OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFailure,
361+ OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) =>
362+ OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveSuccess,
363+ OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Failure(_)) =>
364+ OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFailure,
365+ }
366+ }
367+ }
368+
369+ impl_writeable_tlv_based_enum_upgradable!(OutboundHTLCStateDetails,
370+ (0, AwaitingRemoteRevokeToAdd) => {},
371+ (2, Committed) => {},
372+ (4, AwaitingRemoteRevokeToRemoveSuccess) => {},
373+ (6, AwaitingRemoteRevokeToRemoveFailure) => {};
374+ );
375+
202376#[derive(Clone)]
203377#[cfg_attr(test, derive(Debug, PartialEq))]
204378enum OutboundHTLCOutcome {
@@ -237,6 +411,53 @@ struct OutboundHTLCOutput {
237411 skimmed_fee_msat: Option<u64>,
238412}
239413
414+ /// Exposes details around pending outbound HTLCs.
415+ #[derive(Clone, Debug, PartialEq)]
416+ pub struct OutboundHTLCDetails {
417+ /// The HTLC ID.
418+ /// The IDs are incremented by 1 starting from 0 for each offered HTLC.
419+ /// They are unique per channel and inbound/outbound direction, unless an HTLC was only announced
420+ /// and not part of any commitment transaction.
421+ ///
422+ /// Not present when we are awaiting a remote revocation and the HTLC is not added yet.
423+ pub htlc_id: Option<u64>,
424+ /// The amount in msat.
425+ pub amount_msat: u64,
426+ /// The block height at which this HTLC expires.
427+ pub cltv_expiry: u32,
428+ /// The payment hash.
429+ pub payment_hash: PaymentHash,
430+ /// The state of the HTLC in the state machine.
431+ /// Determines on which commitment transactions the HTLC is included and what message the HTLC is
432+ /// waiting for to advance to the next state.
433+ /// See [`OutboundHTLCStateDetails`] for information on the specific states.
434+ pub state: Option<OutboundHTLCStateDetails>,
435+ /// The extra fee being skimmed off the top of this HTLC.
436+ pub skimmed_fee_msat: Option<u64>,
437+ /// Whether the HTLC has an output below the local dust limit. If so, the output will be trimmed
438+ /// from the local commitment transaction and added to the commitment transaction fee.
439+ /// For non-anchor channels, this takes into account the cost of the second-stage HTLC
440+ /// transactions as well.
441+ ///
442+ /// When the local commitment transaction is broadcasted as part of a unilateral closure,
443+ /// the value of this HTLC will therefore not be claimable but instead burned as a transaction
444+ /// fee.
445+ ///
446+ /// Note that dust limits are specific to each party. An HTLC can be dust for the local
447+ /// commitment transaction but not for the counterparty's commitment transaction and vice versa.
448+ pub is_dust: bool,
449+ }
450+
451+ impl_writeable_tlv_based!(OutboundHTLCDetails, {
452+ (0, htlc_id, required),
453+ (2, amount_msat, required),
454+ (4, cltv_expiry, required),
455+ (6, payment_hash, required),
456+ (7, state, upgradable_option),
457+ (8, skimmed_fee_msat, required),
458+ (10, is_dust, required),
459+ });
460+
240461/// See AwaitingRemoteRevoke ChannelState for more info
241462#[cfg_attr(test, derive(Clone, Debug, PartialEq))]
242463enum HTLCUpdateAwaitingACK {
@@ -1994,6 +2215,99 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
19942215 stats
19952216 }
19962217
2218+ /// Returns information on all pending inbound HTLCs.
2219+ pub fn get_pending_inbound_htlc_details(&self) -> Vec<InboundHTLCDetails> {
2220+ let mut holding_cell_states = new_hash_map();
2221+ for holding_cell_update in self.holding_cell_htlc_updates.iter() {
2222+ match holding_cell_update {
2223+ HTLCUpdateAwaitingACK::ClaimHTLC { htlc_id, .. } => {
2224+ holding_cell_states.insert(
2225+ htlc_id,
2226+ InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFulfill,
2227+ );
2228+ },
2229+ HTLCUpdateAwaitingACK::FailHTLC { htlc_id, .. } => {
2230+ holding_cell_states.insert(
2231+ htlc_id,
2232+ InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFail,
2233+ );
2234+ },
2235+ HTLCUpdateAwaitingACK::FailMalformedHTLC { htlc_id, .. } => {
2236+ holding_cell_states.insert(
2237+ htlc_id,
2238+ InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFail,
2239+ );
2240+ },
2241+ // Outbound HTLC.
2242+ HTLCUpdateAwaitingACK::AddHTLC { .. } => {},
2243+ }
2244+ }
2245+ let mut inbound_details = Vec::new();
2246+ let htlc_success_dust_limit = if self.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
2247+ 0
2248+ } else {
2249+ let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
2250+ dust_buffer_feerate * htlc_success_tx_weight(self.get_channel_type()) / 1000
2251+ };
2252+ let holder_dust_limit_success_sat = htlc_success_dust_limit + self.holder_dust_limit_satoshis;
2253+ for htlc in self.pending_inbound_htlcs.iter() {
2254+ if let Some(state_details) = (&htlc.state).into() {
2255+ inbound_details.push(InboundHTLCDetails{
2256+ htlc_id: htlc.htlc_id,
2257+ amount_msat: htlc.amount_msat,
2258+ cltv_expiry: htlc.cltv_expiry,
2259+ payment_hash: htlc.payment_hash,
2260+ state: Some(holding_cell_states.remove(&htlc.htlc_id).unwrap_or(state_details)),
2261+ is_dust: htlc.amount_msat / 1000 < holder_dust_limit_success_sat,
2262+ });
2263+ }
2264+ }
2265+ inbound_details
2266+ }
2267+
2268+ /// Returns information on all pending outbound HTLCs.
2269+ pub fn get_pending_outbound_htlc_details(&self) -> Vec<OutboundHTLCDetails> {
2270+ let mut outbound_details = Vec::new();
2271+ let htlc_timeout_dust_limit = if self.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
2272+ 0
2273+ } else {
2274+ let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
2275+ dust_buffer_feerate * htlc_success_tx_weight(self.get_channel_type()) / 1000
2276+ };
2277+ let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.holder_dust_limit_satoshis;
2278+ for htlc in self.pending_outbound_htlcs.iter() {
2279+ outbound_details.push(OutboundHTLCDetails{
2280+ htlc_id: Some(htlc.htlc_id),
2281+ amount_msat: htlc.amount_msat,
2282+ cltv_expiry: htlc.cltv_expiry,
2283+ payment_hash: htlc.payment_hash,
2284+ skimmed_fee_msat: htlc.skimmed_fee_msat,
2285+ state: Some((&htlc.state).into()),
2286+ is_dust: htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat,
2287+ });
2288+ }
2289+ for holding_cell_update in self.holding_cell_htlc_updates.iter() {
2290+ if let HTLCUpdateAwaitingACK::AddHTLC {
2291+ amount_msat,
2292+ cltv_expiry,
2293+ payment_hash,
2294+ skimmed_fee_msat,
2295+ ..
2296+ } = *holding_cell_update {
2297+ outbound_details.push(OutboundHTLCDetails{
2298+ htlc_id: None,
2299+ amount_msat: amount_msat,
2300+ cltv_expiry: cltv_expiry,
2301+ payment_hash: payment_hash,
2302+ skimmed_fee_msat: skimmed_fee_msat,
2303+ state: Some(OutboundHTLCStateDetails::AwaitingRemoteRevokeToAdd),
2304+ is_dust: amount_msat / 1000 < holder_dust_limit_timeout_sat,
2305+ });
2306+ }
2307+ }
2308+ outbound_details
2309+ }
2310+
19972311 /// Get the available balances, see [`AvailableBalances`]'s fields for more info.
19982312 /// Doesn't bother handling the
19992313 /// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
0 commit comments