Skip to content

Commit 1a3a29f

Browse files
committed
Include pending HTLCs in ChannelDetails
1 parent 9e4a35a commit 1a3a29f

File tree

4 files changed

+324
-0
lines changed

4 files changed

+324
-0
lines changed

fuzz/src/router.rs

+2
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
241241
config: None,
242242
feerate_sat_per_1000_weight: None,
243243
channel_shutdown_state: Some(channelmanager::ChannelShutdownState::NotShuttingDown),
244+
pending_inbound_htlcs: Vec::new(),
245+
pending_outbound_htlcs: Vec::new(),
244246
});
245247
}
246248
Some(&$first_hops_vec[..])

lightning/src/ln/channel.rs

+301
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,85 @@ enum InboundHTLCState {
152152
LocalRemoved(InboundHTLCRemovalReason),
153153
}
154154

155+
/// Exposes the state of pending inbound HTLCs.
156+
///
157+
/// Fail and fulfill suffixes indicate the resolution of the HTLC.
158+
#[derive(Clone, Debug, PartialEq)]
159+
pub enum InboundHTLCStateDetails {
160+
/// RemoteAnnounced states indicate that the remote sent update_add_htlc for this HTLC while the
161+
/// HTLC is not on any commitment transactions yet. It will be included in the next local
162+
/// commitment transaction when we receive commitment_signed and return revoke_and_ack.
163+
RemoteAnnouncedForward,
164+
/// See above.
165+
RemoteAnnouncedFail,
166+
/// AwaitingRemoteRevokeToAnnounce states indicate that we have received commitment_signed with
167+
/// this HTLC, returned revoke_and_ack, and this HTLC is included on the local commitment
168+
/// transaction but not the remote commitment transaction. The remote hasn't yet revoked their
169+
/// previous state and we have not yet included this HTLC in commitment_signed because we are
170+
/// waiting on the remote's revocation.
171+
AwaitingRemoteRevokeToAnnounceForward,
172+
/// See above.
173+
AwaitingRemoteRevokeToAnnounceFail,
174+
/// AwaitingAnnouncedRemoteRevoke states indicate that we have received commitment_signed with
175+
/// this HTLC, returned revoke_and_ack, and this HTLC is included on the local commitment
176+
/// transaction. We have also included this HTLC in our latest commitment_signed and are now just
177+
/// waiting on the remote's revoke_and_ack before this HTLC will be included on the remote
178+
/// commitment transaction as well and can then get forwarded and/or removed.
179+
AwaitingAnnouncedRemoteRevokeForward,
180+
/// See above.
181+
AwaitingAnnouncedRemoteRevokeFail,
182+
/// Committed indicates that this HTLC has been included in the commitment_signed and
183+
/// revoke_and_ack flow on both sides and is included in both commitment transactions.
184+
Committed,
185+
/// AwaitingRemoteRevokeToRemove states indicate that this HTLC will be removed by us sending
186+
/// update_*_htlc and commitment_signed, but the remote has not sent revoke_and_ack for the
187+
/// previous commitment_signed yet. The HTLC is still on both commitment transactions.
188+
AwaitingRemoteRevokeToRemoveFulfill,
189+
/// See above.
190+
AwaitingRemoteRevokeToRemoveFail,
191+
/// LocalRemoved states indicate that this HTLC has been removed by us and a new
192+
/// commitment_signed was sent, but the HTLC is still on both commitment transactions. When the
193+
/// remote sends the next revoke_and_ack, it will be removed from the remote's commitment
194+
/// transaction.
195+
LocalRemovedFailRelay,
196+
/// See above.
197+
LocalRemovedFailMalformed,
198+
/// See above.
199+
LocalRemovedFulfill,
200+
}
201+
202+
impl From<&InboundHTLCState> for InboundHTLCStateDetails {
203+
fn from(state: &InboundHTLCState) -> InboundHTLCStateDetails {
204+
match state {
205+
InboundHTLCState::RemoteAnnounced(PendingHTLCStatus::Forward(_)) => InboundHTLCStateDetails::RemoteAnnouncedForward,
206+
InboundHTLCState::RemoteAnnounced(PendingHTLCStatus::Fail(_)) => InboundHTLCStateDetails::RemoteAnnouncedFail,
207+
InboundHTLCState::AwaitingRemoteRevokeToAnnounce(PendingHTLCStatus::Forward(_)) => InboundHTLCStateDetails::AwaitingRemoteRevokeToAnnounceForward,
208+
InboundHTLCState::AwaitingRemoteRevokeToAnnounce(PendingHTLCStatus::Fail(_)) => InboundHTLCStateDetails::AwaitingRemoteRevokeToAnnounceFail,
209+
InboundHTLCState::AwaitingAnnouncedRemoteRevoke(PendingHTLCStatus::Forward(_)) => InboundHTLCStateDetails::AwaitingAnnouncedRemoteRevokeForward,
210+
InboundHTLCState::AwaitingAnnouncedRemoteRevoke(PendingHTLCStatus::Fail(_)) => InboundHTLCStateDetails::AwaitingAnnouncedRemoteRevokeFail,
211+
InboundHTLCState::Committed => InboundHTLCStateDetails::Committed,
212+
InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(_)) => InboundHTLCStateDetails::LocalRemovedFailRelay,
213+
InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailMalformed(_)) => InboundHTLCStateDetails::LocalRemovedFailMalformed,
214+
InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_)) => InboundHTLCStateDetails::LocalRemovedFulfill,
215+
}
216+
}
217+
}
218+
219+
impl_writeable_tlv_based_enum!(InboundHTLCStateDetails,
220+
(0, RemoteAnnouncedForward) => {},
221+
(2, RemoteAnnouncedFail) => {},
222+
(4, AwaitingRemoteRevokeToAnnounceForward) => {},
223+
(6, AwaitingRemoteRevokeToAnnounceFail) => {},
224+
(8, AwaitingAnnouncedRemoteRevokeForward) => {},
225+
(10, AwaitingAnnouncedRemoteRevokeFail) => {},
226+
(12, Committed) => {},
227+
(14, AwaitingRemoteRevokeToRemoveFulfill) => {},
228+
(16, AwaitingRemoteRevokeToRemoveFail) => {},
229+
(18, LocalRemovedFailRelay) => {},
230+
(20, LocalRemovedFailMalformed) => {},
231+
(22, LocalRemovedFulfill) => {};
232+
);
233+
155234
struct InboundHTLCOutput {
156235
htlc_id: u64,
157236
amount_msat: u64,
@@ -160,6 +239,35 @@ struct InboundHTLCOutput {
160239
state: InboundHTLCState,
161240
}
162241

242+
/// Exposes details around pending inbound HTLCs.
243+
#[derive(Clone, Debug, PartialEq)]
244+
pub struct InboundHTLCDetails {
245+
/// The corresponding HTLC ID.
246+
pub htlc_id: u64,
247+
/// The amount in msat.
248+
pub amount_msat: u64,
249+
/// The CLTV expiry.
250+
pub cltv_expiry: u32,
251+
/// The payment hash.
252+
pub payment_hash: PaymentHash,
253+
/// The state of the HTLC in the update_*_htlc, commitment_signed, revoke_and_ack flow.
254+
/// Informs on which commitment transactions the HTLC is included.
255+
pub state: InboundHTLCStateDetails,
256+
/// Whether the HTLC has an output below the local dust limit. If so, the output will be trimmed
257+
/// from the local commitment transaction and added to the commitment transaction fee.
258+
/// This takes into account the second-stage HTLC transactions as well.
259+
pub is_dust: bool,
260+
}
261+
262+
impl_writeable_tlv_based!(InboundHTLCDetails, {
263+
(0, htlc_id, required),
264+
(2, amount_msat, required),
265+
(4, cltv_expiry, required),
266+
(6, payment_hash, required),
267+
(8, state, required),
268+
(10, is_dust, required),
269+
});
270+
163271
enum OutboundHTLCState {
164272
/// Added by us and included in a commitment_signed (if we were AwaitingRemoteRevoke when we
165273
/// created it we would have put it in the holding cell instead). When they next revoke_and_ack
@@ -192,6 +300,82 @@ enum OutboundHTLCState {
192300
AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome),
193301
}
194302

303+
/// Exposes the state of pending outbound HTLCs.
304+
///
305+
/// Failure and success suffixes indicate the resolution of the HTLC.
306+
#[derive(Clone, Debug, PartialEq)]
307+
pub enum OutboundHTLCStateDetails {
308+
/// THe AwaitingRemoteRevokeToAnnounce state indicates that this HTLC will be added by us sending
309+
/// update_add_htlc and commitment_signed, but the remote has not sent revoke_and_ack for the
310+
/// previous commitment_signed yet. The HTLC is not on any commitment transactions yet.
311+
AwaitingRemoteRevokeToAnnounce,
312+
/// The LocalAnnounced state indicates that this HTLC has been added by us and included in a
313+
/// commitment_signed, but the HTLC is not on any commitment transactions yet. When the remote
314+
/// sends the next revoke_and_ack, it will be included in the remote's commitment transaction.
315+
LocalAnnounced,
316+
/// The Committed state indicates that this HTLC has been included in a commitment_signed sent by
317+
/// us and we have received a corresponding revoke_and_ack. The HTLC is therefore included in the
318+
/// remote's commitment transaction. Note that this includes the subsequent state where we
319+
/// receive the remote's commitment_signed with this HTLC, respond with the corresponding
320+
/// revoke_and_ack, and include it in the local commitment transaction, as:
321+
/// * they've revoked, so worst case we can announce an old state and get our (option on)
322+
/// money back (though we won't), and,
323+
/// * we'll send them a revoke when they send a commitment_signed, and since only they're
324+
/// allowed to remove it, the "can only be removed once committed on both sides" requirement
325+
/// doesn't matter to us and it's up to them to enforce it, worst-case they jump ahead but
326+
/// we'll never get out of sync).
327+
Committed,
328+
/// RemoteRemoved states indicate that this HTLC has been removed by the remote with
329+
/// update_*_htlc. The HTLC is still on both commitment transactions and we are waiting on their
330+
/// commitment_signed mesage.
331+
RemoteRemovedSuccess,
332+
/// See above.
333+
RemoteRemovedFailure,
334+
/// AwaitingRemoteRevokeToRemove states indicate that the remote removed this HTLC and sent a
335+
/// commitment_signed and we've revoke_and_ack'ed it. It is removed from the local commitment
336+
/// transaction but the remote side hasn't yet revoked their previous state and therefore we
337+
/// haven't yet removed this HTLC in our latest commitment_signed. This HTLC is still included in
338+
/// the remote's commitment transaction.
339+
AwaitingRemoteRevokeToRemoveSuccess,
340+
/// See above.
341+
AwaitingRemoteRevokeToRemoveFailure,
342+
/// AwaitingRemovedRemoteRevoke states indicate that the remote removed this and sent a
343+
/// commitment_signed and we've revoke_and_ack'ed it. It is therefore removed from the local
344+
/// commitment transaction. This HTLC has also been removed in our latest commitment_signed and
345+
/// will be removed from the remote's commitment transaction when we receive their
346+
/// revoke_and_ack, after which we can do any backwards failing.
347+
AwaitingRemovedRemoteRevokeSuccess,
348+
/// See above.
349+
AwaitingRemovedRemoteRevokeFailure,
350+
}
351+
352+
impl From<&OutboundHTLCState> for OutboundHTLCStateDetails {
353+
fn from(state: &OutboundHTLCState) -> OutboundHTLCStateDetails {
354+
match state {
355+
OutboundHTLCState::LocalAnnounced(_) => OutboundHTLCStateDetails::LocalAnnounced,
356+
OutboundHTLCState::Committed => OutboundHTLCStateDetails::Committed,
357+
OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_)) => OutboundHTLCStateDetails::RemoteRemovedSuccess,
358+
OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Failure(_)) => OutboundHTLCStateDetails::RemoteRemovedFailure,
359+
OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_)) => OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveSuccess,
360+
OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Failure(_)) => OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFailure,
361+
OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) => OutboundHTLCStateDetails::AwaitingRemovedRemoteRevokeSuccess,
362+
OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Failure(_)) => OutboundHTLCStateDetails::AwaitingRemovedRemoteRevokeFailure,
363+
}
364+
}
365+
}
366+
367+
impl_writeable_tlv_based_enum!(OutboundHTLCStateDetails,
368+
(0, AwaitingRemoteRevokeToAnnounce) => {},
369+
(2, LocalAnnounced) => {},
370+
(4, Committed) => {},
371+
(6, RemoteRemovedSuccess) => {},
372+
(8, RemoteRemovedFailure) => {},
373+
(10, AwaitingRemoteRevokeToRemoveSuccess) => {},
374+
(12, AwaitingRemoteRevokeToRemoveFailure) => {},
375+
(14, AwaitingRemovedRemoteRevokeSuccess) => {},
376+
(16, AwaitingRemovedRemoteRevokeFailure) => {};
377+
);
378+
195379
#[derive(Clone)]
196380
enum OutboundHTLCOutcome {
197381
/// LDK version 0.0.105+ will always fill in the preimage here.
@@ -227,6 +411,39 @@ struct OutboundHTLCOutput {
227411
skimmed_fee_msat: Option<u64>,
228412
}
229413

414+
/// Exposes details around pending outbound HTLCs.
415+
#[derive(Clone, Debug, PartialEq)]
416+
pub struct OutboundHTLCDetails {
417+
/// The corresponding HTLC ID.
418+
/// Not present when we are awaiting a remote revocation and the HTLC is not added yet.
419+
pub htlc_id: Option<u64>,
420+
/// The amount in msat.
421+
pub amount_msat: u64,
422+
/// The CLTV expiry.
423+
pub cltv_expiry: u32,
424+
/// The payment hash.
425+
pub payment_hash: PaymentHash,
426+
/// The state of the HTLC in the update_*_htlc, commitment_signed, revoke_and_ack flow.
427+
/// Informs on which commitment transactions the HTLC is included.
428+
pub state: OutboundHTLCStateDetails,
429+
/// The extra fee being skimmed off the top of this HTLC.
430+
pub skimmed_fee_msat: Option<u64>,
431+
/// Whether the HTLC has an output below the local dust limit. If so, the output will be trimmed
432+
/// from the local commitment transaction and added to the commitment transaction fee.
433+
/// This takes into account the second-stage HTLC transactions as well.
434+
pub is_dust: bool,
435+
}
436+
437+
impl_writeable_tlv_based!(OutboundHTLCDetails, {
438+
(0, htlc_id, required),
439+
(2, amount_msat, required),
440+
(4, cltv_expiry, required),
441+
(6, payment_hash, required),
442+
(8, state, required),
443+
(10, skimmed_fee_msat, required),
444+
(12, is_dust, required),
445+
});
446+
230447
/// See AwaitingRemoteRevoke ChannelState for more info
231448
enum HTLCUpdateAwaitingACK {
232449
AddHTLC { // TODO: Time out if we're getting close to cltv_expiry
@@ -1549,6 +1766,90 @@ impl<Signer: ChannelSigner> ChannelContext<Signer> {
15491766
stats
15501767
}
15511768

1769+
/// Returns information on all pending inbound HTLCs.
1770+
pub fn get_pending_inbound_htlc_details(&self) -> Vec<InboundHTLCDetails> {
1771+
let mut holding_cell_states = HashMap::new();
1772+
for holding_cell_update in self.holding_cell_htlc_updates.iter() {
1773+
match holding_cell_update {
1774+
HTLCUpdateAwaitingACK::ClaimHTLC { htlc_id, .. } => {
1775+
holding_cell_states.insert(
1776+
htlc_id,
1777+
InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFulfill,
1778+
);
1779+
},
1780+
HTLCUpdateAwaitingACK::FailHTLC { htlc_id, .. } => {
1781+
holding_cell_states.insert(
1782+
htlc_id,
1783+
InboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFail,
1784+
);
1785+
},
1786+
_ => {},
1787+
}
1788+
}
1789+
let mut inbound_details = Vec::new();
1790+
let htlc_success_dust_limit = if self.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
1791+
0
1792+
} else {
1793+
let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
1794+
dust_buffer_feerate * htlc_success_tx_weight(self.get_channel_type()) / 1000
1795+
};
1796+
let holder_dust_limit_success_sat = htlc_success_dust_limit + self.holder_dust_limit_satoshis;
1797+
for htlc in self.pending_inbound_htlcs.iter() {
1798+
inbound_details.push(InboundHTLCDetails{
1799+
htlc_id: htlc.htlc_id,
1800+
amount_msat: htlc.amount_msat,
1801+
cltv_expiry: htlc.cltv_expiry,
1802+
payment_hash: htlc.payment_hash,
1803+
state: holding_cell_states.remove(&htlc.htlc_id).unwrap_or((&htlc.state).into()),
1804+
is_dust: htlc.amount_msat / 1000 < holder_dust_limit_success_sat,
1805+
});
1806+
}
1807+
inbound_details
1808+
}
1809+
1810+
/// Returns information on all pending outbound HTLCs.
1811+
pub fn get_pending_outbound_htlc_details(&self) -> Vec<OutboundHTLCDetails> {
1812+
let mut outbound_details = Vec::new();
1813+
let htlc_timeout_dust_limit = if self.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
1814+
0
1815+
} else {
1816+
let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
1817+
dust_buffer_feerate * htlc_success_tx_weight(self.get_channel_type()) / 1000
1818+
};
1819+
let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.holder_dust_limit_satoshis;
1820+
for htlc in self.pending_outbound_htlcs.iter() {
1821+
outbound_details.push(OutboundHTLCDetails{
1822+
htlc_id: Some(htlc.htlc_id),
1823+
amount_msat: htlc.amount_msat,
1824+
cltv_expiry: htlc.cltv_expiry,
1825+
payment_hash: htlc.payment_hash,
1826+
skimmed_fee_msat: htlc.skimmed_fee_msat,
1827+
state: (&htlc.state).into(),
1828+
is_dust: htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat,
1829+
});
1830+
}
1831+
for holding_cell_update in self.holding_cell_htlc_updates.iter() {
1832+
if let HTLCUpdateAwaitingACK::AddHTLC {
1833+
amount_msat,
1834+
cltv_expiry,
1835+
payment_hash,
1836+
skimmed_fee_msat,
1837+
..
1838+
} = *holding_cell_update {
1839+
outbound_details.push(OutboundHTLCDetails{
1840+
htlc_id: None,
1841+
amount_msat: amount_msat,
1842+
cltv_expiry: cltv_expiry,
1843+
payment_hash: payment_hash,
1844+
skimmed_fee_msat: skimmed_fee_msat,
1845+
state: OutboundHTLCStateDetails::AwaitingRemoteRevokeToAnnounce,
1846+
is_dust: amount_msat / 1000 < holder_dust_limit_timeout_sat,
1847+
});
1848+
}
1849+
}
1850+
outbound_details
1851+
}
1852+
15521853
/// Returns a HTLCStats about pending outbound htlcs, *including* pending adds in our holding cell.
15531854
fn get_outbound_pending_htlc_stats(&self, outbound_feerate_update: Option<u32>) -> HTLCStats {
15541855
let context = self;

0 commit comments

Comments
 (0)