Skip to content

Commit

Permalink
Include pending HTLC's in ChannelDetails
Browse files Browse the repository at this point in the history
  • Loading branch information
wvanlint committed Aug 5, 2023
1 parent 6f58072 commit 529da61
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 1 deletion.
3 changes: 3 additions & 0 deletions fuzz/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
config: None,
feerate_sat_per_1000_weight: None,
channel_shutdown_state: Some(channelmanager::ChannelShutdownState::NotShuttingDown),
pending_inbound_htlcs: Vec::new(),
pending_outbound_htlcs: Vec::new(),
holding_cell_updates: Vec::new(),
});
}
Some(&$first_hops_vec[..])
Expand Down
243 changes: 243 additions & 0 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,50 @@ enum InboundHTLCState {
LocalRemoved(InboundHTLCRemovalReason),
}

#[derive(Clone, Debug, PartialEq)]
pub enum InboundHTLCStateDetails {
RemoteAnnouncedForward,
RemoteAnnouncedFail,
AwaitingRemoteRevokeToAnnounceForward,
AwaitingRemoteRevokeToAnnounceFail,
AwaitingAnnouncedRemoteRevokeForward,
AwaitingAnnouncedRemoteRevokeFail,
Committed,
LocalRemovedFailRelay,
LocalRemovedFailMalformed,
LocalRemovedFulfill,
}

impl From<&InboundHTLCState> for InboundHTLCStateDetails {
fn from(state: &InboundHTLCState) -> InboundHTLCStateDetails {
match state {
InboundHTLCState::RemoteAnnounced(PendingHTLCStatus::Forward(_)) => InboundHTLCStateDetails::RemoteAnnouncedForward,
InboundHTLCState::RemoteAnnounced(PendingHTLCStatus::Fail(_)) => InboundHTLCStateDetails::RemoteAnnouncedFail,
InboundHTLCState::AwaitingRemoteRevokeToAnnounce(PendingHTLCStatus::Forward(_)) => InboundHTLCStateDetails::AwaitingRemoteRevokeToAnnounceForward,
InboundHTLCState::AwaitingRemoteRevokeToAnnounce(PendingHTLCStatus::Fail(_)) => InboundHTLCStateDetails::AwaitingRemoteRevokeToAnnounceFail,
InboundHTLCState::AwaitingAnnouncedRemoteRevoke(PendingHTLCStatus::Forward(_)) => InboundHTLCStateDetails::AwaitingAnnouncedRemoteRevokeForward,
InboundHTLCState::AwaitingAnnouncedRemoteRevoke(PendingHTLCStatus::Fail(_)) => InboundHTLCStateDetails::AwaitingAnnouncedRemoteRevokeFail,
InboundHTLCState::Committed => InboundHTLCStateDetails::Committed,
InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(_)) => InboundHTLCStateDetails::LocalRemovedFailRelay,
InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailMalformed(_)) => InboundHTLCStateDetails::LocalRemovedFailMalformed,
InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_)) => InboundHTLCStateDetails::LocalRemovedFulfill,
}
}
}

impl_writeable_tlv_based_enum!(InboundHTLCStateDetails,
(0, RemoteAnnouncedForward) => {},
(1, RemoteAnnouncedFail) => {},
(2, AwaitingRemoteRevokeToAnnounceForward) => {},
(3, AwaitingRemoteRevokeToAnnounceFail) => {},
(4, AwaitingAnnouncedRemoteRevokeForward) => {},
(5, AwaitingAnnouncedRemoteRevokeFail) => {},
(6, Committed) => {},
(7, LocalRemovedFailRelay) => {},
(8, LocalRemovedFailMalformed) => {},
(9, LocalRemovedFulfill) => {};
);

struct InboundHTLCOutput {
htlc_id: u64,
amount_msat: u64,
Expand All @@ -160,6 +204,25 @@ struct InboundHTLCOutput {
state: InboundHTLCState,
}

#[derive(Clone, Debug, PartialEq)]
pub struct InboundHTLCDetails {
pub htlc_id: u64,
pub amount_msat: u64,
pub cltv_expiry: u32,
pub payment_hash: PaymentHash,
pub state: InboundHTLCStateDetails,
pub is_dust: bool,
}

impl_writeable_tlv_based!(InboundHTLCDetails, {
(0, htlc_id, required),
(2, amount_msat, required),
(4, cltv_expiry, required),
(6, payment_hash, required),
(8, state, required),
(10, is_dust, required),
});

enum OutboundHTLCState {
/// Added by us and included in a commitment_signed (if we were AwaitingRemoteRevoke when we
/// created it we would have put it in the holding cell instead). When they next revoke_and_ack
Expand Down Expand Up @@ -192,6 +255,44 @@ enum OutboundHTLCState {
AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome),
}

#[derive(Clone, Debug, PartialEq)]
pub enum OutboundHTLCStateDetails {
LocalAnnounced,
Committed,
RemoteRemovedSuccess,
RemoteRemovedFailure,
AwaitingRemoteRevokeToRemoveSuccess,
AwaitingRemoteRevokeToRemoveFailure,
AwaitingRemovedRemoteRevokeSuccess,
AwaitingRemovedRemoteRevokeFailure,
}

impl From<&OutboundHTLCState> for OutboundHTLCStateDetails {
fn from(state: &OutboundHTLCState) -> OutboundHTLCStateDetails {
match state {
OutboundHTLCState::LocalAnnounced(_) => OutboundHTLCStateDetails::LocalAnnounced,
OutboundHTLCState::Committed => OutboundHTLCStateDetails::Committed,
OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_)) => OutboundHTLCStateDetails::RemoteRemovedSuccess,
OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Failure(_)) => OutboundHTLCStateDetails::RemoteRemovedFailure,
OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_)) => OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveSuccess,
OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Failure(_)) => OutboundHTLCStateDetails::AwaitingRemoteRevokeToRemoveFailure,
OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) => OutboundHTLCStateDetails::AwaitingRemovedRemoteRevokeSuccess,
OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Failure(_)) => OutboundHTLCStateDetails::AwaitingRemovedRemoteRevokeFailure,
}
}
}

impl_writeable_tlv_based_enum!(OutboundHTLCStateDetails,
(0, LocalAnnounced) => {},
(1, Committed) => {},
(2, RemoteRemovedSuccess) => {},
(3, RemoteRemovedFailure) => {},
(4, AwaitingRemoteRevokeToRemoveSuccess) => {},
(5, AwaitingRemoteRevokeToRemoveFailure) => {},
(6, AwaitingRemovedRemoteRevokeSuccess) => {},
(7, AwaitingRemovedRemoteRevokeFailure) => {};
);

#[derive(Clone)]
enum OutboundHTLCOutcome {
/// LDK version 0.0.105+ will always fill in the preimage here.
Expand Down Expand Up @@ -227,6 +328,27 @@ struct OutboundHTLCOutput {
skimmed_fee_msat: Option<u64>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct OutboundHTLCDetails {
pub htlc_id: u64,
pub amount_msat: u64,
pub cltv_expiry: u32,
pub payment_hash: PaymentHash,
pub state: OutboundHTLCStateDetails,
pub skimmed_fee_msat: Option<u64>,
pub is_dust: bool,
}

impl_writeable_tlv_based!(OutboundHTLCDetails, {
(0, htlc_id, required),
(2, amount_msat, required),
(4, cltv_expiry, required),
(6, payment_hash, required),
(8, state, required),
(10, skimmed_fee_msat, required),
(12, is_dust, required),
});

/// See AwaitingRemoteRevoke ChannelState for more info
enum HTLCUpdateAwaitingACK {
AddHTLC { // TODO: Time out if we're getting close to cltv_expiry
Expand All @@ -249,6 +371,39 @@ enum HTLCUpdateAwaitingACK {
},
}

#[derive(Clone, Debug, PartialEq)]
pub enum HTLCUpdateAwaitingACKDetails {
AddHTLC {
amount_msat: u64,
cltv_expiry: u32,
payment_hash: PaymentHash,
skimmed_fee_msat: Option<u64>,
is_dust: bool,
},
ClaimHTLC {
htlc_id: u64,
},
FailHTLC {
htlc_id: u64,
},
}

impl_writeable_tlv_based_enum!(HTLCUpdateAwaitingACKDetails,
(0, AddHTLC) => {
(0, amount_msat, required),
(2, cltv_expiry, required),
(4, payment_hash, required),
(6, skimmed_fee_msat, required),
(8, is_dust, required),
},
(1, ClaimHTLC) => {
(0, htlc_id, required),
},
(2, FailHTLC) => {
(0, htlc_id, required),
};
);

/// There are a few "states" and then a number of flags which can be applied:
/// We first move through init with `OurInitSent` -> `TheirInitSent` -> `FundingCreated` -> `FundingSent`.
/// `TheirChannelReady` and `OurChannelReady` then get set on `FundingSent`, and when both are set we
Expand Down Expand Up @@ -1549,6 +1704,94 @@ impl<Signer: ChannelSigner> ChannelContext<Signer> {
stats
}

/// Returns information on all pending inbound HTLCs.
pub fn get_pending_inbound_htlc_details(&self) -> Vec<InboundHTLCDetails> {
let mut inbound_details = Vec::new();
let htlc_success_dust_limit = if self.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
0
} else {
let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
dust_buffer_feerate * htlc_success_tx_weight(self.get_channel_type()) / 1000
};
let holder_dust_limit_success_sat = htlc_success_dust_limit + self.holder_dust_limit_satoshis;
for ref htlc in self.pending_inbound_htlcs.iter() {
inbound_details.push(InboundHTLCDetails{
htlc_id: htlc.htlc_id,
amount_msat: htlc.amount_msat,
cltv_expiry: htlc.cltv_expiry,
payment_hash: htlc.payment_hash,
state: (&htlc.state).into(),
is_dust: htlc.amount_msat / 1000 < holder_dust_limit_success_sat,
});
}
inbound_details
}

/// Returns information on all pending outbound HTLCs.
pub fn get_pending_outbound_htlc_details(&self) -> Vec<OutboundHTLCDetails> {
let mut outbound_details = Vec::new();
let htlc_timeout_dust_limit = if self.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
0
} else {
let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
dust_buffer_feerate * htlc_success_tx_weight(self.get_channel_type()) / 1000
};
let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.holder_dust_limit_satoshis;
for ref htlc in self.pending_outbound_htlcs.iter() {
outbound_details.push(OutboundHTLCDetails{
htlc_id: htlc.htlc_id,
amount_msat: htlc.amount_msat,
cltv_expiry: htlc.cltv_expiry,
payment_hash: htlc.payment_hash,
skimmed_fee_msat: htlc.skimmed_fee_msat,
state: (&htlc.state).into(),
is_dust: htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat,
});
}
outbound_details
}

pub fn get_holding_cell_details(&self) -> Vec<HTLCUpdateAwaitingACKDetails> {
let mut holding_cell_details = Vec::new();
let htlc_timeout_dust_limit = if self.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
0
} else {
let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
dust_buffer_feerate * htlc_success_tx_weight(self.get_channel_type()) / 1000
};
let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.holder_dust_limit_satoshis;
for ref htlc_update in self.holding_cell_htlc_updates.iter() {
holding_cell_details.push(match htlc_update {
HTLCUpdateAwaitingACK::AddHTLC {
amount_msat,
cltv_expiry,
payment_hash,
skimmed_fee_msat,
..
} => HTLCUpdateAwaitingACKDetails::AddHTLC {
amount_msat: *amount_msat,
cltv_expiry: *cltv_expiry,
payment_hash: *payment_hash,
skimmed_fee_msat: *skimmed_fee_msat,
is_dust: amount_msat / 1000 < holder_dust_limit_timeout_sat,
},
HTLCUpdateAwaitingACK::ClaimHTLC {
htlc_id,
..
} => HTLCUpdateAwaitingACKDetails::ClaimHTLC {
htlc_id: *htlc_id,
},
HTLCUpdateAwaitingACK::FailHTLC {
htlc_id,
..
} => HTLCUpdateAwaitingACKDetails::FailHTLC {
htlc_id: *htlc_id,
},
});
}
holding_cell_details
}

/// Returns a HTLCStats about pending outbound htlcs, *including* pending adds in our holding cell.
fn get_outbound_pending_htlc_stats(&self, outbound_feerate_update: Option<u32>) -> HTLCStats {
let context = self;
Expand Down
26 changes: 25 additions & 1 deletion lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use crate::events::{Event, EventHandler, EventsProvider, MessageSendEvent, Messa
// Since this struct is returned in `list_channels` methods, expose it here in case users want to
// construct one themselves.
use crate::ln::{inbound_payment, PaymentHash, PaymentPreimage, PaymentSecret};
use crate::ln::channel::{Channel, ChannelContext, ChannelError, ChannelUpdateStatus, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel};
use crate::ln::channel::{Channel, ChannelContext, ChannelError, ChannelUpdateStatus, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel, InboundHTLCDetails, OutboundHTLCDetails, HTLCUpdateAwaitingACKDetails};
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
#[cfg(any(feature = "_test_utils", test))]
use crate::ln::features::Bolt11InvoiceFeatures;
Expand Down Expand Up @@ -1505,6 +1505,18 @@ pub struct ChannelDetails {
///
/// This field is only `None` for `ChannelDetails` objects serialized prior to LDK 0.0.109.
pub config: Option<ChannelConfig>,
/// Pending inbound HTLCs.
///
/// This field is empty for objects serialized with LDK versions prior to 0.0.117.
pub pending_inbound_htlcs: Vec<InboundHTLCDetails>,
/// Pending outbound HTLCs.
///
/// This field is empty for objects serialized with LDK versions prior to 0.0.117.
pub pending_outbound_htlcs: Vec<OutboundHTLCDetails>,
/// Pending HTLC updates that are held awaiting a revoke_and_ack.
///
/// This field is empty for objects serialized with LDK versions prior to 0.0.117.
pub holding_cell_updates: Vec<HTLCUpdateAwaitingACKDetails>,
}

impl ChannelDetails {
Expand Down Expand Up @@ -1580,6 +1592,9 @@ impl ChannelDetails {
inbound_htlc_maximum_msat: context.get_holder_htlc_maximum_msat(),
config: Some(context.config()),
channel_shutdown_state: Some(context.shutdown_state()),
pending_inbound_htlcs: context.get_pending_inbound_htlc_details(),
pending_outbound_htlcs: context.get_pending_outbound_htlc_details(),
holding_cell_updates: context.get_holding_cell_details(),
}
}
}
Expand Down Expand Up @@ -7530,6 +7545,9 @@ impl Writeable for ChannelDetails {
(37, user_channel_id_high_opt, option),
(39, self.feerate_sat_per_1000_weight, option),
(41, self.channel_shutdown_state, option),
(43, self.pending_inbound_htlcs, optional_vec),
(45, self.pending_outbound_htlcs, optional_vec),
(47, self.holding_cell_updates, optional_vec),
});
Ok(())
}
Expand Down Expand Up @@ -7568,6 +7586,9 @@ impl Readable for ChannelDetails {
(37, user_channel_id_high_opt, option),
(39, feerate_sat_per_1000_weight, option),
(41, channel_shutdown_state, option),
(43, pending_inbound_htlcs, optional_vec),
(45, pending_outbound_htlcs, optional_vec),
(47, holding_cell_updates, optional_vec),
});

// `user_channel_id` used to be a single u64 value. In order to remain backwards compatible with
Expand Down Expand Up @@ -7604,6 +7625,9 @@ impl Readable for ChannelDetails {
inbound_htlc_maximum_msat,
feerate_sat_per_1000_weight,
channel_shutdown_state,
pending_inbound_htlcs: pending_inbound_htlcs.unwrap_or(Vec::new()),
pending_outbound_htlcs: pending_outbound_htlcs.unwrap_or(Vec::new()),
holding_cell_updates: holding_cell_updates.unwrap_or(Vec::new()),
})
}
}
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/routing/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2741,6 +2741,9 @@ mod tests {
config: None,
feerate_sat_per_1000_weight: None,
channel_shutdown_state: Some(channelmanager::ChannelShutdownState::NotShuttingDown),
pending_inbound_htlcs: Vec::new(),
pending_outbound_htlcs: Vec::new(),
holding_cell_updates: Vec::new(),
}
}

Expand Down Expand Up @@ -6812,6 +6815,9 @@ pub(crate) mod bench_utils {
config: None,
feerate_sat_per_1000_weight: None,
channel_shutdown_state: Some(channelmanager::ChannelShutdownState::NotShuttingDown),
pending_inbound_htlcs: Vec::new(),
pending_outbound_htlcs: Vec::new(),
holding_cell_updates: Vec::new(),
}
}

Expand Down

0 comments on commit 529da61

Please sign in to comment.