Skip to content

Add persistent closed channel history and list_closed_channels()#882

Open
f3r10 wants to merge 1 commit intolightningdevkit:mainfrom
f3r10:feat/persist_historical_channels
Open

Add persistent closed channel history and list_closed_channels()#882
f3r10 wants to merge 1 commit intolightningdevkit:mainfrom
f3r10:feat/persist_historical_channels

Conversation

@f3r10
Copy link
Copy Markdown
Contributor

@f3r10 f3r10 commented Apr 20, 2026

Summary

Closes #851.

Adds persistent historical closed channel records so applications can query all channels that have ever closed on this node, surviving restarts.

New public API

  • Node::list_closed_channels() -> Vec<ClosedChannelDetails>
  • ClosedChannelDetails struct: channel_id, user_channel_id,
    counterparty_node_id, funding_txo, channel_capacity_sats,
    last_local_balance_msat, is_outbound, closure_reason, closed_at
  • Event::ChannelClosed gains three new fields: channel_capacity_sats,
    channel_funding_txo, last_local_balance_msat

Implementation

Persistence (src/closed_channel.rs, src/io/): ClosedChannelDetails implements StorableObject with insert-only semantics (closed records are immutable). Records are stored under the "closed_channels" KV namespace keyed by UserChannelId.

Startup (src/builder.rs): read_closed_channels() joins the existing tokio::join! block so historical records are loaded in parallel with payments, pending payments, and node metrics — no added sequential startup cost as history gronws.

Event handlinng (src/event.rs): On ChannelClosed, the handler writes a ClosedChannelDetails record to the store and emits the enriched public event. Channel direction (is_outbound) is tracked via an in-memory outbound_channel_ids set, seeded from channel_manager.list_channels() at startup and updated on ChannelPending, since ChannelClosed does not carry that information directly.

Introduce `ClosedChannelDetails`, a new record type persisted to the KV
store under the `"closed_channels"` namespace whenever a channel closes.
Records are written in the `ChannelClosed` event handler and loaded back
at startup in parallel with other stores via `tokio::join!`.

Add `Node::list_closed_channels()` to expose the full history of closed
channels across restarts.

 Track outbound channel direction via an in-memory `outbound_channel_ids`
set seeded from `channel_manager.list_channels()` at startup and updated
on `ChannelPending` events, since `ChannelClosed` does not carry that
information directly.
@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Apr 20, 2026

I've assigned @tnull as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@ldk-reviews-bot ldk-reviews-bot requested a review from tnull April 20, 2026 16:42
Copy link
Copy Markdown
Contributor

@Camillarhi Camillarhi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this. I dropped some comments. Also, CI seems to be failing due to a formatting issue. Kindly run cargo fmt --all to resolve this

Comment thread src/closed_channel.rs
closed_at: closed_at.0.ok_or(DecodeError::InvalidValue)?,
})
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use impl_writeable_tlv_based! here instead? Matches the rest of the StorableObject types in the codebase

Comment thread src/lib.rs
/// Retrieve a list of closed channels.
///
/// Channels are added to this list when they are closed and will be persisted across restarts.
pub fn list_closed_channels(&self) -> Vec<ClosedChannelDetails> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new list_closed_channels() API isn't exposed in bindings.

Comment thread src/closed_channel.rs
/// [`Node::list_closed_channels`]: crate::Node::list_closed_channels
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
pub struct ClosedChannelDetails {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClosedChannelDetails isn't exposed in bindings

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Persist historical channels

3 participants