@@ -158,6 +158,72 @@ enum InboundHTLCState {
158
158
LocalRemoved(InboundHTLCRemovalReason),
159
159
}
160
160
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
+
161
227
struct InboundHTLCOutput {
162
228
htlc_id: u64,
163
229
amount_msat: u64,
@@ -166,6 +232,48 @@ struct InboundHTLCOutput {
166
232
state: InboundHTLCState,
167
233
}
168
234
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
+
169
277
#[cfg_attr(test, derive(Clone, Debug, PartialEq))]
170
278
enum OutboundHTLCState {
171
279
/// Added by us and included in a commitment_signed (if we were AwaitingRemoteRevoke when we
@@ -199,6 +307,72 @@ enum OutboundHTLCState {
199
307
AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome),
200
308
}
201
309
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
+
202
376
#[derive(Clone)]
203
377
#[cfg_attr(test, derive(Debug, PartialEq))]
204
378
enum OutboundHTLCOutcome {
@@ -237,6 +411,53 @@ struct OutboundHTLCOutput {
237
411
skimmed_fee_msat: Option<u64>,
238
412
}
239
413
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
+
240
461
/// See AwaitingRemoteRevoke ChannelState for more info
241
462
#[cfg_attr(test, derive(Clone, Debug, PartialEq))]
242
463
enum HTLCUpdateAwaitingACK {
@@ -1994,6 +2215,99 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
1994
2215
stats
1995
2216
}
1996
2217
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
+
1997
2311
/// Get the available balances, see [`AvailableBalances`]'s fields for more info.
1998
2312
/// Doesn't bother handling the
1999
2313
/// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
0 commit comments