From 30b610c323c66b1ad950fc93151c5ab5ee07b505 Mon Sep 17 00:00:00 2001 From: Fernando Ledesma Date: Sat, 14 Mar 2026 13:58:43 -0500 Subject: [PATCH] Expose `ChannelDetails::channel_shutdown_state` Add a `ChannelShutdownState` enum mirroring LDK's own type, and expose it as an `Option` field on `ChannelDetails`. --- bindings/ldk_node.udl | 2 ++ src/lib.rs | 11 ++++++++--- src/types.rs | 43 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 3ec2919e7..1452937a6 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -316,6 +316,8 @@ dictionary OutPoint { typedef dictionary ChannelDetails; +typedef enum ChannelShutdownState; + typedef dictionary PeerDetails; [Remote] diff --git a/src/lib.rs b/src/lib.rs index 109ade0ae..8ba700bef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,7 +145,9 @@ pub use lightning; use lightning::chain::BestBlock; use lightning::impl_writeable_tlv_based; use lightning::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT; -use lightning::ln::channel_state::{ChannelDetails as LdkChannelDetails, ChannelShutdownState}; +use lightning::ln::channel_state::{ + ChannelDetails as LdkChannelDetails, ChannelShutdownState as LdkChannelShutdownState, +}; use lightning::ln::channelmanager::PaymentId; use lightning::ln::msgs::SocketAddress; use lightning::routing::gossip::NodeAlias; @@ -173,7 +175,10 @@ use types::{ HRNResolver, KeysManager, OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper, Wallet, }; -pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, SyncAndAsyncKVStore, UserChannelId}; +pub use types::{ + ChannelDetails, ChannelShutdownState, CustomTlvRecord, PeerDetails, SyncAndAsyncKVStore, + UserChannelId, +}; pub use vss_client; use crate::scoring::setup_background_pathfinding_scores_sync; @@ -2021,7 +2026,7 @@ pub(crate) fn total_anchor_channels_reserve_sats( .filter(|c| { !anchor_channels_config.trusted_peers_no_reserve.contains(&c.counterparty.node_id) && c.channel_shutdown_state - .map_or(true, |s| s != ChannelShutdownState::ShutdownComplete) + .map_or(true, |s| s != LdkChannelShutdownState::ShutdownComplete) && c.channel_type .as_ref() .map_or(false, |t| t.requires_anchors_zero_fee_htlc_tx()) diff --git a/src/types.rs b/src/types.rs index 381bfbd21..88b8bbece 100644 --- a/src/types.rs +++ b/src/types.rs @@ -15,7 +15,9 @@ use bitcoin::{OutPoint, ScriptBuf}; use bitcoin_payment_instructions::onion_message_resolver::LDKOnionMessageDNSSECHrnResolver; use lightning::chain::chainmonitor; use lightning::impl_writeable_tlv_based; -use lightning::ln::channel_state::ChannelDetails as LdkChannelDetails; +use lightning::ln::channel_state::{ + ChannelDetails as LdkChannelDetails, ChannelShutdownState as LdkChannelShutdownState, +}; use lightning::ln::msgs::{RoutingMessageHandler, SocketAddress}; use lightning::ln::peer_handler::IgnoringMessageHandler; use lightning::ln::types::ChannelId; @@ -347,6 +349,40 @@ impl fmt::Display for UserChannelId { } } +/// The shutdown state of a channel as returned in [`ChannelDetails::channel_shutdown_state`]. +/// +/// [`ChannelDetails::channel_shutdown_state`]: crate::ChannelDetails::channel_shutdown_state +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))] +pub enum ChannelShutdownState { + /// Channel has not sent or received a shutdown message. + NotShuttingDown, + /// Local node has sent a shutdown message for this channel. + ShutdownInitiated, + /// Shutdown message exchanges have concluded and the channels are in the midst of + /// resolving all existing open HTLCs before closing can continue. + ResolvingHTLCs, + /// All HTLCs have been resolved, nodes are currently negotiating channel close onchain fee rates. + NegotiatingClosingFee, + /// We've successfully negotiated a closing_signed dance. At this point `ChannelManager` is about + /// to drop the channel. + ShutdownComplete, +} + +impl From for ChannelShutdownState { + fn from(value: LdkChannelShutdownState) -> Self { + match value { + LdkChannelShutdownState::NotShuttingDown => ChannelShutdownState::NotShuttingDown, + LdkChannelShutdownState::ShutdownInitiated => ChannelShutdownState::ShutdownInitiated, + LdkChannelShutdownState::ResolvingHTLCs => ChannelShutdownState::ResolvingHTLCs, + LdkChannelShutdownState::NegotiatingClosingFee => { + ChannelShutdownState::NegotiatingClosingFee + }, + LdkChannelShutdownState::ShutdownComplete => ChannelShutdownState::ShutdownComplete, + } + } +} + /// Details of a channel as returned by [`Node::list_channels`]. /// /// When a channel is spliced, most fields continue to refer to the original pre-splice channel @@ -529,6 +565,10 @@ pub struct ChannelDetails { pub inbound_htlc_maximum_msat: Option, /// Set of configurable parameters that affect channel operation. pub config: ChannelConfig, + /// The current shutdown state of the channel, if any. + /// + /// Returns `None` for channels that have not yet started the shutdown process. + pub channel_shutdown_state: Option, } impl From for ChannelDetails { @@ -584,6 +624,7 @@ impl From for ChannelDetails { inbound_htlc_maximum_msat: value.inbound_htlc_maximum_msat, // unwrap safety: `config` is only `None` for LDK objects serialized prior to 0.0.109. config: value.config.map(|c| c.into()).unwrap(), + channel_shutdown_state: value.channel_shutdown_state.map(|s| s.into()), } } }