Skip to content

Commit 32c3eef

Browse files
committed
Expose ChannelDetails::channel_shutdown_state
Add a `ChannelShutdownState` enum mirroring LDK's own type, and expose it as an `Option<ChannelShutdownState>` field on `ChannelDetails`.
1 parent b0e159a commit 32c3eef

File tree

5 files changed

+161
-59
lines changed

5 files changed

+161
-59
lines changed

src/ffi/types.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub use bitcoin::{Address, BlockHash, FeeRate, Network, OutPoint, ScriptBuf, Txi
2525
pub use lightning::chain::channelmonitor::BalanceSource;
2626
use lightning::events::PaidBolt12Invoice as LdkPaidBolt12Invoice;
2727
pub use lightning::events::{ClosureReason, PaymentFailureReason};
28+
pub use lightning::ln::channel_state::ChannelShutdownState;
2829
use lightning::ln::channelmanager::PaymentId;
2930
use lightning::ln::msgs::DecodeError;
3031
pub use lightning::ln::types::ChannelId;
@@ -1408,6 +1409,26 @@ uniffi::custom_type!(LSPSDateTime, String, {
14081409
},
14091410
});
14101411

1412+
/// The shutdown state of a channel as returned in [`ChannelDetails::channel_shutdown_state`].
1413+
///
1414+
/// [`ChannelDetails::channel_shutdown_state`]: crate::ChannelDetails::channel_shutdown_state
1415+
#[uniffi::remote(Enum)]
1416+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1417+
pub enum ChannelShutdownState {
1418+
/// Channel has not sent or received a shutdown message.
1419+
NotShuttingDown,
1420+
/// Local node has sent a shutdown message for this channel.
1421+
ShutdownInitiated,
1422+
/// Shutdown message exchanges have concluded and the channels are in the midst of
1423+
/// resolving all existing open HTLCs before closing can continue.
1424+
ResolvingHTLCs,
1425+
/// All HTLCs have been resolved, nodes are currently negotiating channel close onchain fee rates.
1426+
NegotiatingClosingFee,
1427+
/// We've successfully negotiated a closing_signed dance. At this point `ChannelManager` is about
1428+
/// to drop the channel.
1429+
ShutdownComplete,
1430+
}
1431+
14111432
/// The reason the channel was closed. See individual variants for more details.
14121433
#[uniffi::remote(Enum)]
14131434
#[derive(Clone, Debug, PartialEq, Eq)]

src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ pub use lightning;
145145
use lightning::chain::BestBlock;
146146
use lightning::impl_writeable_tlv_based;
147147
use lightning::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT;
148-
use lightning::ln::channel_state::{ChannelDetails as LdkChannelDetails, ChannelShutdownState};
148+
use lightning::ln::channel_state::ChannelDetails as LdkChannelDetails;
149149
use lightning::ln::channelmanager::PaymentId;
150150
use lightning::ln::msgs::SocketAddress;
151151
use lightning::routing::gossip::NodeAlias;
@@ -173,7 +173,10 @@ use types::{
173173
HRNResolver, KeysManager, OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper,
174174
Wallet,
175175
};
176-
pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, SyncAndAsyncKVStore, UserChannelId};
176+
pub use types::{
177+
ChannelDetails, ChannelShutdownState, CustomTlvRecord, PeerDetails, SyncAndAsyncKVStore,
178+
UserChannelId,
179+
};
177180
pub use vss_client;
178181

179182
use crate::scoring::setup_background_pathfinding_scores_sync;

src/types.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use bitcoin_payment_instructions::onion_message_resolver::LDKOnionMessageDNSSECH
1616
use lightning::chain::chainmonitor;
1717
use lightning::impl_writeable_tlv_based;
1818
use lightning::ln::channel_state::ChannelDetails as LdkChannelDetails;
19+
pub use lightning::ln::channel_state::ChannelShutdownState;
1920
use lightning::ln::msgs::{RoutingMessageHandler, SocketAddress};
2021
use lightning::ln::peer_handler::IgnoringMessageHandler;
2122
use lightning::ln::types::ChannelId;
@@ -529,6 +530,10 @@ pub struct ChannelDetails {
529530
pub inbound_htlc_maximum_msat: Option<u64>,
530531
/// Set of configurable parameters that affect channel operation.
531532
pub config: ChannelConfig,
533+
/// The current shutdown state of the channel, if any.
534+
///
535+
/// Returns `None` for `ChannelDetails` serialized on LDK versions prior to 0.0.116.
536+
pub channel_shutdown_state: Option<ChannelShutdownState>,
532537
}
533538

534539
impl From<LdkChannelDetails> for ChannelDetails {
@@ -584,6 +589,7 @@ impl From<LdkChannelDetails> for ChannelDetails {
584589
inbound_htlc_maximum_msat: value.inbound_htlc_maximum_msat,
585590
// unwrap safety: `config` is only `None` for LDK objects serialized prior to 0.0.109.
586591
config: value.config.map(|c| c.into()).unwrap(),
592+
channel_shutdown_state: value.channel_shutdown_state,
587593
}
588594
}
589595
}

tests/common/mod.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ use ldk_node::entropy::{generate_entropy_mnemonic, NodeEntropy};
3131
use ldk_node::io::sqlite_store::SqliteStore;
3232
use ldk_node::payment::{PaymentDirection, PaymentKind, PaymentStatus};
3333
use ldk_node::{
34-
Builder, CustomTlvRecord, Event, LightningBalance, Node, NodeError, PendingSweepBalance,
35-
UserChannelId,
34+
Builder, ChannelShutdownState, CustomTlvRecord, Event, LightningBalance, Node, NodeError,
35+
PendingSweepBalance, UserChannelId,
3636
};
3737
use lightning::io;
3838
use lightning::ln::msgs::SocketAddress;
@@ -918,6 +918,28 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
918918
let user_channel_id_a = expect_channel_ready_event!(node_a, node_b.node_id());
919919
let user_channel_id_b = expect_channel_ready_event!(node_b, node_a.node_id());
920920

921+
// After channel_ready, no shutdown should be in progress on either side.
922+
for channel in node_a.list_channels() {
923+
assert!(
924+
matches!(
925+
channel.channel_shutdown_state,
926+
None | Some(ChannelShutdownState::NotShuttingDown)
927+
),
928+
"Expected no shutdown in progress on node_a, got {:?}",
929+
channel.channel_shutdown_state,
930+
);
931+
}
932+
for channel in node_b.list_channels() {
933+
assert!(
934+
matches!(
935+
channel.channel_shutdown_state,
936+
None | Some(ChannelShutdownState::NotShuttingDown)
937+
),
938+
"Expected no shutdown in progress on node_b, got {:?}",
939+
channel.channel_shutdown_state,
940+
);
941+
}
942+
921943
println!("\nB receive");
922944
let invoice_amount_1_msat = 2500_000;
923945
let invoice_description: Bolt11InvoiceDescription =
@@ -1233,6 +1255,20 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
12331255
expect_channel_ready_event!(node_a, node_b.node_id());
12341256
expect_channel_ready_event!(node_b, node_a.node_id());
12351257

1258+
// After the splice-out, the channel must still report no shutdown in progress.
1259+
for channel in node_a.list_channels() {
1260+
assert!(matches!(
1261+
channel.channel_shutdown_state,
1262+
None | Some(ChannelShutdownState::NotShuttingDown)
1263+
));
1264+
}
1265+
for channel in node_b.list_channels() {
1266+
assert!(matches!(
1267+
channel.channel_shutdown_state,
1268+
None | Some(ChannelShutdownState::NotShuttingDown)
1269+
));
1270+
}
1271+
12361272
assert_eq!(
12371273
node_a
12381274
.list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound
@@ -1255,6 +1291,20 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
12551291
expect_channel_ready_event!(node_a, node_b.node_id());
12561292
expect_channel_ready_event!(node_b, node_a.node_id());
12571293

1294+
// After the splice-in, the channel must still report no shutdown in progress.
1295+
for channel in node_a.list_channels() {
1296+
assert!(matches!(
1297+
channel.channel_shutdown_state,
1298+
None | Some(ChannelShutdownState::NotShuttingDown)
1299+
));
1300+
}
1301+
for channel in node_b.list_channels() {
1302+
assert!(matches!(
1303+
channel.channel_shutdown_state,
1304+
None | Some(ChannelShutdownState::NotShuttingDown)
1305+
));
1306+
}
1307+
12581308
assert_eq!(
12591309
node_a
12601310
.list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound

tests/integration_tests_rust.rs

Lines changed: 77 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use ldk_node::payment::{
3333
ConfirmationStatus, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus,
3434
UnifiedPaymentResult,
3535
};
36-
use ldk_node::{Builder, Event, NodeError};
36+
use ldk_node::{Builder, ChannelShutdownState, Event, NodeError};
3737
use lightning::ln::channelmanager::PaymentId;
3838
use lightning::routing::gossip::{NodeAlias, NodeId};
3939
use lightning::routing::router::RouteParametersConfig;
@@ -2108,24 +2108,26 @@ async fn lsps2_client_trusts_lsp() {
21082108

21092109
service_node.sync_wallets().unwrap();
21102110
client_node.sync_wallets().unwrap();
2111-
assert_eq!(
2112-
client_node
2113-
.list_channels()
2114-
.iter()
2115-
.find(|c| c.counterparty_node_id == service_node_id)
2116-
.unwrap()
2117-
.confirmations,
2118-
Some(0)
2119-
);
2120-
assert_eq!(
2121-
service_node
2122-
.list_channels()
2123-
.iter()
2124-
.find(|c| c.counterparty_node_id == client_node_id)
2125-
.unwrap()
2126-
.confirmations,
2127-
Some(0)
2128-
);
2111+
let client_channel = client_node
2112+
.list_channels()
2113+
.into_iter()
2114+
.find(|c| c.counterparty_node_id == service_node_id)
2115+
.unwrap();
2116+
assert_eq!(client_channel.confirmations, Some(0));
2117+
assert!(matches!(
2118+
client_channel.channel_shutdown_state,
2119+
None | Some(ChannelShutdownState::NotShuttingDown)
2120+
));
2121+
let service_channel = service_node
2122+
.list_channels()
2123+
.into_iter()
2124+
.find(|c| c.counterparty_node_id == client_node_id)
2125+
.unwrap();
2126+
assert_eq!(service_channel.confirmations, Some(0));
2127+
assert!(matches!(
2128+
service_channel.channel_shutdown_state,
2129+
None | Some(ChannelShutdownState::NotShuttingDown)
2130+
));
21292131

21302132
// Now claim the JIT payment, which should release the funding transaction
21312133
let service_fee_msat = (jit_amount_msat * channel_opening_fee_ppm as u64) / 1_000_000;
@@ -2152,24 +2154,26 @@ async fn lsps2_client_trusts_lsp() {
21522154
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await;
21532155
service_node.sync_wallets().unwrap();
21542156
client_node.sync_wallets().unwrap();
2155-
assert_eq!(
2156-
client_node
2157-
.list_channels()
2158-
.iter()
2159-
.find(|c| c.counterparty_node_id == service_node_id)
2160-
.unwrap()
2161-
.confirmations,
2162-
Some(6)
2163-
);
2164-
assert_eq!(
2165-
service_node
2166-
.list_channels()
2167-
.iter()
2168-
.find(|c| c.counterparty_node_id == client_node_id)
2169-
.unwrap()
2170-
.confirmations,
2171-
Some(6)
2172-
);
2157+
let client_channel = client_node
2158+
.list_channels()
2159+
.into_iter()
2160+
.find(|c| c.counterparty_node_id == service_node_id)
2161+
.unwrap();
2162+
assert_eq!(client_channel.confirmations, Some(6));
2163+
assert!(matches!(
2164+
client_channel.channel_shutdown_state,
2165+
None | Some(ChannelShutdownState::NotShuttingDown)
2166+
));
2167+
let service_channel = service_node
2168+
.list_channels()
2169+
.into_iter()
2170+
.find(|c| c.counterparty_node_id == client_node_id)
2171+
.unwrap();
2172+
assert_eq!(service_channel.confirmations, Some(6));
2173+
assert!(matches!(
2174+
service_channel.channel_shutdown_state,
2175+
None | Some(ChannelShutdownState::NotShuttingDown)
2176+
));
21732177
}
21742178

21752179
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
@@ -2280,24 +2284,26 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() {
22802284
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await;
22812285
service_node.sync_wallets().unwrap();
22822286
client_node.sync_wallets().unwrap();
2283-
assert_eq!(
2284-
client_node
2285-
.list_channels()
2286-
.iter()
2287-
.find(|c| c.counterparty_node_id == service_node_id)
2288-
.unwrap()
2289-
.confirmations,
2290-
Some(6)
2291-
);
2292-
assert_eq!(
2293-
service_node
2294-
.list_channels()
2295-
.iter()
2296-
.find(|c| c.counterparty_node_id == client_node_id)
2297-
.unwrap()
2298-
.confirmations,
2299-
Some(6)
2300-
);
2287+
let client_channel = client_node
2288+
.list_channels()
2289+
.into_iter()
2290+
.find(|c| c.counterparty_node_id == service_node_id)
2291+
.unwrap();
2292+
assert_eq!(client_channel.confirmations, Some(6));
2293+
assert!(matches!(
2294+
client_channel.channel_shutdown_state,
2295+
None | Some(ChannelShutdownState::NotShuttingDown)
2296+
));
2297+
let service_channel = service_node
2298+
.list_channels()
2299+
.into_iter()
2300+
.find(|c| c.counterparty_node_id == client_node_id)
2301+
.unwrap();
2302+
assert_eq!(service_channel.confirmations, Some(6));
2303+
assert!(matches!(
2304+
service_channel.channel_shutdown_state,
2305+
None | Some(ChannelShutdownState::NotShuttingDown)
2306+
));
23012307
}
23022308

23032309
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
@@ -2672,6 +2678,10 @@ async fn open_channel_with_all_with_anchors() {
26722678
assert!(channel.channel_value_sats > premine_amount_sat - anchor_reserve_sat - 500);
26732679
assert_eq!(channel.counterparty_node_id, node_b.node_id());
26742680
assert_eq!(channel.funding_txo.unwrap(), funding_txo);
2681+
assert!(matches!(
2682+
channel.channel_shutdown_state,
2683+
None | Some(ChannelShutdownState::NotShuttingDown)
2684+
));
26752685

26762686
node_a.stop().unwrap();
26772687
node_b.stop().unwrap();
@@ -2723,6 +2733,10 @@ async fn open_channel_with_all_without_anchors() {
27232733
assert!(channel.channel_value_sats > premine_amount_sat - 500);
27242734
assert_eq!(channel.counterparty_node_id, node_b.node_id());
27252735
assert_eq!(channel.funding_txo.unwrap(), funding_txo);
2736+
assert!(matches!(
2737+
channel.channel_shutdown_state,
2738+
None | Some(ChannelShutdownState::NotShuttingDown)
2739+
));
27262740

27272741
node_a.stop().unwrap();
27282742
node_b.stop().unwrap();
@@ -2766,6 +2780,10 @@ async fn splice_in_with_all_balance() {
27662780
assert_eq!(channels.len(), 1);
27672781
assert_eq!(channels[0].channel_value_sats, channel_amount_sat);
27682782
assert_eq!(channels[0].funding_txo.unwrap(), funding_txo);
2783+
assert!(matches!(
2784+
channels[0].channel_shutdown_state,
2785+
None | Some(ChannelShutdownState::NotShuttingDown)
2786+
));
27692787

27702788
let balance_before_splice = node_a.list_balances().spendable_onchain_balance_sats;
27712789
assert!(balance_before_splice > 0);
@@ -2787,6 +2805,10 @@ async fn splice_in_with_all_balance() {
27872805
let channels = node_a.list_channels();
27882806
assert_eq!(channels.len(), 1);
27892807
let channel = &channels[0];
2808+
assert!(matches!(
2809+
channel.channel_shutdown_state,
2810+
None | Some(ChannelShutdownState::NotShuttingDown)
2811+
));
27902812
assert!(
27912813
channel.channel_value_sats > premine_amount_sat - anchor_reserve_sat - 1000,
27922814
"Channel value {} should be close to premined amount {} minus anchor reserve {} and fees",

0 commit comments

Comments
 (0)