@@ -158,6 +158,102 @@ 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
+ /// The remote node announced the HTLC with update_add_htlc but the HTLC is not added to any
177
+ /// commitment transactions yet.
178
+ ///
179
+ /// We intend to forward the HTLC as it is correctly formed and is forwardable to the next hop.
180
+ RemoteAnnouncedForward,
181
+ /// The remote node announced the HTLC with update_add_htlc but the HTLC is not added to any
182
+ /// commitment transactions yet.
183
+ ///
184
+ /// We intend to fail the HTLC as it is malformed or we are unable to forward to the next hop,
185
+ /// for example if the peer is disconnected or not enough capacity is available.
186
+ RemoteAnnouncedFail,
187
+ /// We have added this HTLC in our commitment transaction by receiving commitment_signed and
188
+ /// returning revoke_and_ack. We are awaiting the appropriate revoke_and_ack's from the remote
189
+ /// before this HTLC is included on the remote commitment transaction.
190
+ ///
191
+ /// We intend to forward the HTLC as it is correctly formed and is forwardable to the next hop.
192
+ AwaitingRemoteRevokeToAddForward,
193
+ /// We have added this HTLC in our commitment transaction by receiving commitment_signed and
194
+ /// returning revoke_and_ack. We are awaiting the appropriate revoke_and_ack's from the remote
195
+ /// before this HTLC is included on the remote commitment transaction.
196
+ ///
197
+ /// We intend to fail the HTLC as it is malformed or we are unable to forward to the next hop,
198
+ /// for example if the peer is disconnected or not enough capacity is available.
199
+ AwaitingRemoteRevokeToAddFail,
200
+ /// This HTLC has been included in the commitment_signed and revoke_and_ack messages on both sides
201
+ /// and is included in both commitment transactions.
202
+ ///
203
+ /// This HTLC is now safe to either forward or be claimed as a payment by us. The HTLC will
204
+ /// remain in this state until the forwarded upstream HTLC has been resolved and we resolve this
205
+ /// HTLC correspondingly, or until we claim it as a payment. If it is part of a multipart
206
+ /// payment, it will only be claimed together with other required parts.
207
+ Committed,
208
+ /// We have received the preimage for this HTLC and it is being removed by fulfilling it with
209
+ /// update_fulfill_htlc. This HTLC is still on both commitment transactions, but we are awaiting
210
+ /// the appropriate revoke_and_ack's from the remote before this HTLC is removed from the remote
211
+ /// commitment transaction after update_fulfill_htlc.
212
+ AwaitingRemoteRevokeToRemoveFulfill,
213
+ /// The HTLC is being removed by failing it with update_fail_htlc or update_fail_malformed_htlc.
214
+ /// This HTLC is still on both commitment transactions, but we are awaiting the appropriate
215
+ /// revoke_and_ack's from the remote before this HTLC is removed from the remote commitment
216
+ /// transaction.
217
+ AwaitingRemoteRevokeToRemoveFail,
218
+ }
219
+
220
+ impl From<&InboundHTLCState> for InboundHTLCStateDetails {
221
+ fn from(state: &InboundHTLCState) -> InboundHTLCStateDetails {
222
+ match state {
223
+ InboundHTLCState::RemoteAnnounced(PendingHTLCStatus::Forward(_)) =>
224
+ InboundHTLCStateDetails::RemoteAnnouncedForward,
225
+ InboundHTLCState::RemoteAnnounced(PendingHTLCStatus::Fail(_)) =>
226
+ InboundHTLCStateDetails::RemoteAnnouncedFail,
227
+ InboundHTLCState::AwaitingRemoteRevokeToAnnounce(PendingHTLCStatus::Forward(_)) =>
228
+ InboundHTLCStateDetails::AwaitingRemoteRevokeToAddForward,
229
+ InboundHTLCState::AwaitingRemoteRevokeToAnnounce(PendingHTLCStatus::Fail(_)) =>
230
+ InboundHTLCStateDetails::AwaitingRemoteRevokeToAddFail,
231
+ InboundHTLCState::AwaitingAnnouncedRemoteRevoke(PendingHTLCStatus::Forward(_)) =>
232
+ InboundHTLCStateDetails::AwaitingRemoteRevokeToAddForward,
233
+ InboundHTLCState::AwaitingAnnouncedRemoteRevoke(PendingHTLCStatus::Fail(_)) =>
234
+ InboundHTLCStateDetails::AwaitingRemoteRevokeToAddFail,
235
+ InboundHTLCState::Committed =>
236
+ InboundHTLCStateDetails::Committed,
237
+ InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(_)) =>
238
+ InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFail,
239
+ InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailMalformed(_)) =>
240
+ InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFail,
241
+ InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_)) =>
242
+ InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFulfill,
243
+ }
244
+ }
245
+ }
246
+
247
+ impl_writeable_tlv_based_enum!(InboundHTLCStateDetails,
248
+ (0, RemoteAnnouncedForward) => {},
249
+ (2, RemoteAnnouncedFail) => {},
250
+ (4, AwaitingRemoteRevokeToAddForward) => {},
251
+ (6, AwaitingRemoteRevokeToAddFail) => {},
252
+ (8, Committed) => {},
253
+ (10, AwaitingRemoteRevokeToRemoveFulfill) => {},
254
+ (12, AwaitingRemoteRevokeToRemoveFail) => {};
255
+ );
256
+
161
257
struct InboundHTLCOutput {
162
258
htlc_id: u64,
163
259
amount_msat: u64,
@@ -166,6 +262,44 @@ struct InboundHTLCOutput {
166
262
state: InboundHTLCState,
167
263
}
168
264
265
+ /// Exposes details around pending inbound HTLCs.
266
+ #[derive(Clone, Debug, PartialEq)]
267
+ pub struct InboundHTLCDetails {
268
+ /// The HTLC ID.
269
+ /// The IDs are incremented by 1 starting from 0 for each offered HTLC.
270
+ /// They are unique per channel and inbound/outbound direction, unless an HTLC was only announced
271
+ /// and not part of any commitment transaction.
272
+ pub htlc_id: u64,
273
+ /// The amount in msat.
274
+ pub amount_msat: u64,
275
+ /// The block height at which this HTLC expires.
276
+ pub cltv_expiry: u32,
277
+ /// The payment hash.
278
+ pub payment_hash: PaymentHash,
279
+ /// The state of the HTLC in the state machine.
280
+ /// Determines on which commitment transactions the HTLC is included and what message the HTLC is
281
+ /// waiting for to advance to the next state.
282
+ /// See [InboundHTLCStateDetails] for information on the specific states.
283
+ pub state: InboundHTLCStateDetails,
284
+ /// Whether the HTLC has an output below the local dust limit. If so, the output will be trimmed
285
+ /// from the local commitment transaction and added to the commitment transaction fee.
286
+ /// This takes into account the second-stage HTLC transactions as well.
287
+ ///
288
+ /// When the local commitment transaction is broadcasted as part of a unilateral closure,
289
+ /// the value of this HTLC will therefore not be claimable but instead burned as a transaction
290
+ /// fee.
291
+ pub is_dust: bool,
292
+ }
293
+
294
+ impl_writeable_tlv_based!(InboundHTLCDetails, {
295
+ (0, htlc_id, required),
296
+ (2, amount_msat, required),
297
+ (4, cltv_expiry, required),
298
+ (6, payment_hash, required),
299
+ (8, state, required),
300
+ (10, is_dust, required),
301
+ });
302
+
169
303
#[cfg_attr(test, derive(Clone, Debug, PartialEq))]
170
304
enum OutboundHTLCState {
171
305
/// Added by us and included in a commitment_signed (if we were AwaitingRemoteRevoke when we
@@ -199,6 +333,72 @@ enum OutboundHTLCState {
199
333
AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome),
200
334
}
201
335
336
+ /// Exposes the state of pending outbound HTLCs.
337
+ ///
338
+ /// At a high level, an HTLC being forwarded from one Lightning node to another Lightning node goes
339
+ /// through the following states in the state machine:
340
+ /// - Announced for addition by the originating node through the update_add_htlc message.
341
+ /// - Added to the commitment transaction of the receiving node and originating node in turn
342
+ /// through the exchange of commitment_signed and revoke_and_ack messages.
343
+ /// - Announced for resolution (fulfillment or failure) by the receiving node through either one of
344
+ /// the update_fulfill_htlc, update_fail_htlc, and update_fail_malformed_htlc messages.
345
+ /// - Removed from the commitment transaction of the originating node and receiving node in turn
346
+ /// through the exchange of commitment_signed and revoke_and_ack messages.
347
+ ///
348
+ /// This can be used to inspect what next message an HTLC is waiting for to advance its state.
349
+ #[derive(Clone, Debug, PartialEq)]
350
+ pub enum OutboundHTLCStateDetails {
351
+ /// We are awaiting the appropriate revoke_and_ack's from the remote before the HTLC is be added
352
+ /// on the remote's commitment transaction after update_add_htlc.
353
+ AwaitingRemoteRevokeToAdd,
354
+ /// The HTLC is included on the remote's commitment transaction through a commitment_signed and
355
+ /// revoke_and_ack exchange.
356
+ ///
357
+ /// The HTLC will remain in this state until the remote node resolves the HTLC, or until we
358
+ /// unilaterally close the channel due to a timeout with an uncooperative remote node.
359
+ Committed,
360
+ /// The HTLC has been fulfilled succesfully by the remote with a preimage in update_fulfill_htlc,
361
+ /// and we removed the HTLC from our commitment transaction through a commitment_signed and
362
+ /// revoke_and_ack exchange. We are awaiting the appropriate revoke_and_ack's from the remote for
363
+ /// the removal from its commitment transaction.
364
+ AwaitingRemoteRevokeToRemoveSuccess,
365
+ /// The HTLC has been failed by the remote with update_fail_htlc or update_fail_malformed_htlc,
366
+ /// and we removed the HTLC from our commitment transaction through a commitment_signed and
367
+ /// revoke_and_ack exchange. We are awaiting the appropriate revoke_and_ack's from the remote for
368
+ /// the removal from its commitment transaction.
369
+ AwaitingRemoteRevokeToRemoveFailure,
370
+ }
371
+
372
+ impl From<&OutboundHTLCState> for OutboundHTLCStateDetails {
373
+ fn from(state: &OutboundHTLCState) -> OutboundHTLCStateDetails {
374
+ match state {
375
+ OutboundHTLCState::LocalAnnounced(_) =>
376
+ OutboundHTLCStateDetails::AwaitingRemoteRevokeToAdd,
377
+ OutboundHTLCState::Committed =>
378
+ OutboundHTLCStateDetails::Committed,
379
+ // RemoteRemoved states are ignored as the state is transient and the remote has not committed to
380
+ // the state yet.
381
+ OutboundHTLCState::RemoteRemoved(_) =>
382
+ OutboundHTLCStateDetails::Committed,
383
+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_)) =>
384
+ OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveSuccess,
385
+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Failure(_)) =>
386
+ OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFailure,
387
+ OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) =>
388
+ OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveSuccess,
389
+ OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Failure(_)) =>
390
+ OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFailure,
391
+ }
392
+ }
393
+ }
394
+
395
+ impl_writeable_tlv_based_enum!(OutboundHTLCStateDetails,
396
+ (0, AwaitingRemoteRevokeToAdd) => {},
397
+ (2, Committed) => {},
398
+ (4, AwaitingRemoteRevokeToRemoveSuccess) => {},
399
+ (6, AwaitingRemoteRevokeToRemoveFailure) => {};
400
+ );
401
+
202
402
#[derive(Clone)]
203
403
#[cfg_attr(test, derive(Debug, PartialEq))]
204
404
enum OutboundHTLCOutcome {
@@ -237,6 +437,49 @@ struct OutboundHTLCOutput {
237
437
skimmed_fee_msat: Option<u64>,
238
438
}
239
439
440
+ /// Exposes details around pending outbound HTLCs.
441
+ #[derive(Clone, Debug, PartialEq)]
442
+ pub struct OutboundHTLCDetails {
443
+ /// The HTLC ID.
444
+ /// The IDs are incremented by 1 starting from 0 for each offered HTLC.
445
+ /// They are unique per channel and inbound/outbound direction, unless an HTLC was only announced
446
+ /// and not part of any commitment transaction.
447
+ ///
448
+ /// Not present when we are awaiting a remote revocation and the HTLC is not added yet.
449
+ pub htlc_id: Option<u64>,
450
+ /// The amount in msat.
451
+ pub amount_msat: u64,
452
+ /// The block height at which this HTLC expires.
453
+ pub cltv_expiry: u32,
454
+ /// The payment hash.
455
+ pub payment_hash: PaymentHash,
456
+ /// The state of the HTLC in the state machine.
457
+ /// Determines on which commitment transactions the HTLC is included and what message the HTLC is
458
+ /// waiting for to advance to the next state.
459
+ /// See [OutboundHTLCStateDetails] for information on the specific states.
460
+ pub state: OutboundHTLCStateDetails,
461
+ /// The extra fee being skimmed off the top of this HTLC.
462
+ pub skimmed_fee_msat: Option<u64>,
463
+ /// Whether the HTLC has an output below the local dust limit. If so, the output will be trimmed
464
+ /// from the local commitment transaction and added to the commitment transaction fee.
465
+ /// This takes into account the second-stage HTLC transactions as well.
466
+ ///
467
+ /// When the local commitment transaction is broadcasted as part of a unilateral closure,
468
+ /// the value of this HTLC will therefore not be claimable but instead burned as a transaction
469
+ /// fee.
470
+ pub is_dust: bool,
471
+ }
472
+
473
+ impl_writeable_tlv_based!(OutboundHTLCDetails, {
474
+ (0, htlc_id, required),
475
+ (2, amount_msat, required),
476
+ (4, cltv_expiry, required),
477
+ (6, payment_hash, required),
478
+ (8, state, required),
479
+ (10, skimmed_fee_msat, required),
480
+ (12, is_dust, required),
481
+ });
482
+
240
483
/// See AwaitingRemoteRevoke ChannelState for more info
241
484
#[cfg_attr(test, derive(Clone, Debug, PartialEq))]
242
485
enum HTLCUpdateAwaitingACK {
@@ -1966,6 +2209,96 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
1966
2209
stats
1967
2210
}
1968
2211
2212
+ /// Returns information on all pending inbound HTLCs.
2213
+ pub fn get_pending_inbound_htlc_details(&self) -> Vec<InboundHTLCDetails> {
2214
+ let mut holding_cell_states = HashMap::new();
2215
+ for holding_cell_update in self.holding_cell_htlc_updates.iter() {
2216
+ match holding_cell_update {
2217
+ HTLCUpdateAwaitingACK::ClaimHTLC { htlc_id, .. } => {
2218
+ holding_cell_states.insert(
2219
+ htlc_id,
2220
+ InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFulfill,
2221
+ );
2222
+ },
2223
+ HTLCUpdateAwaitingACK::FailHTLC { htlc_id, .. } => {
2224
+ holding_cell_states.insert(
2225
+ htlc_id,
2226
+ InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFail,
2227
+ );
2228
+ },
2229
+ HTLCUpdateAwaitingACK::FailMalformedHTLC { htlc_id, .. } => {
2230
+ holding_cell_states.insert(
2231
+ htlc_id,
2232
+ InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFail,
2233
+ );
2234
+ },
2235
+ _ => {},
2236
+ }
2237
+ }
2238
+ let mut inbound_details = Vec::new();
2239
+ let htlc_success_dust_limit = if self.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
2240
+ 0
2241
+ } else {
2242
+ let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
2243
+ dust_buffer_feerate * htlc_success_tx_weight(self.get_channel_type()) / 1000
2244
+ };
2245
+ let holder_dust_limit_success_sat = htlc_success_dust_limit + self.holder_dust_limit_satoshis;
2246
+ for htlc in self.pending_inbound_htlcs.iter() {
2247
+ inbound_details.push(InboundHTLCDetails{
2248
+ htlc_id: htlc.htlc_id,
2249
+ amount_msat: htlc.amount_msat,
2250
+ cltv_expiry: htlc.cltv_expiry,
2251
+ payment_hash: htlc.payment_hash,
2252
+ state: holding_cell_states.remove(&htlc.htlc_id).unwrap_or((&htlc.state).into()),
2253
+ is_dust: htlc.amount_msat / 1000 < holder_dust_limit_success_sat,
2254
+ });
2255
+ }
2256
+ inbound_details
2257
+ }
2258
+
2259
+ /// Returns information on all pending outbound HTLCs.
2260
+ pub fn get_pending_outbound_htlc_details(&self) -> Vec<OutboundHTLCDetails> {
2261
+ let mut outbound_details = Vec::new();
2262
+ let htlc_timeout_dust_limit = if self.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
2263
+ 0
2264
+ } else {
2265
+ let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
2266
+ dust_buffer_feerate * htlc_success_tx_weight(self.get_channel_type()) / 1000
2267
+ };
2268
+ let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.holder_dust_limit_satoshis;
2269
+ for htlc in self.pending_outbound_htlcs.iter() {
2270
+ outbound_details.push(OutboundHTLCDetails{
2271
+ htlc_id: Some(htlc.htlc_id),
2272
+ amount_msat: htlc.amount_msat,
2273
+ cltv_expiry: htlc.cltv_expiry,
2274
+ payment_hash: htlc.payment_hash,
2275
+ skimmed_fee_msat: htlc.skimmed_fee_msat,
2276
+ state: (&htlc.state).into(),
2277
+ is_dust: htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat,
2278
+ });
2279
+ }
2280
+ for holding_cell_update in self.holding_cell_htlc_updates.iter() {
2281
+ if let HTLCUpdateAwaitingACK::AddHTLC {
2282
+ amount_msat,
2283
+ cltv_expiry,
2284
+ payment_hash,
2285
+ skimmed_fee_msat,
2286
+ ..
2287
+ } = *holding_cell_update {
2288
+ outbound_details.push(OutboundHTLCDetails{
2289
+ htlc_id: None,
2290
+ amount_msat: amount_msat,
2291
+ cltv_expiry: cltv_expiry,
2292
+ payment_hash: payment_hash,
2293
+ skimmed_fee_msat: skimmed_fee_msat,
2294
+ state: OutboundHTLCStateDetails::AwaitingRemoteRevokeToAdd,
2295
+ is_dust: amount_msat / 1000 < holder_dust_limit_timeout_sat,
2296
+ });
2297
+ }
2298
+ }
2299
+ outbound_details
2300
+ }
2301
+
1969
2302
/// Get the available balances, see [`AvailableBalances`]'s fields for more info.
1970
2303
/// Doesn't bother handling the
1971
2304
/// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
0 commit comments