Skip to content

Expose ChannelCounterparty and ReserveType in ChannelDetails#841

Open
enigbe wants to merge 3 commits into
lightningdevkit:mainfrom
enigbe:2026-03-channel-reserve-type-exposure
Open

Expose ChannelCounterparty and ReserveType in ChannelDetails#841
enigbe wants to merge 3 commits into
lightningdevkit:mainfrom
enigbe:2026-03-channel-reserve-type-exposure

Conversation

@enigbe

@enigbe enigbe commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

What this PR does

In #305, we needed a way to expose channel type and the channel reserve without leaking implementation details not useful to users. The related discussion in #141 proposed a ReserveType abstraction that bakes in both in a user-relevant way. This PR introduces the said enumeration to ChannelDetails with the following variants:

  • Legacy: signifying pre-anchor channel types where on-chain fees paying for broadcast transactions following channel closure were pre-determined
  • TrustedPeersNoReserve: for anchor type channels with trusted peers
  • Adaptive: indicating anchor channel with adaptive reserve, reflecting dynamic best-effort attempt at fee-bumping.
    (see @jkczyz's suggestion)

We modify the ChannelDetails constructor to accept an optional anchor channels config to address the challenge of users cross-referencing the channel details and config to determine if counterparties are trusted.

Additionally, following recommendation in #810 about negotiated features, this PR exposes per-peer InitFeatures, and as a consequence, modifies ChannelDetails by replacing the flattened counterparty_* with ChannelCounterparty that encapsulates them and mirror LDK's.

Issue Addressed

Closes #305.
Builds on discussions and recommendations from #141 and #810.

@ldk-reviews-bot

ldk-reviews-bot commented Mar 24, 2026

Copy link
Copy Markdown

👋 I see @tnull was un-assigned.
If you'd like another reviewer assignment, please click here.

enigbe added a commit to enigbe/ldk-node that referenced this pull request Mar 24, 2026
This fixup moves node feature exposure from freestanding APIs
to NodeStatus, as suggested in review. Rather than exposing
init_features(), channel_features(), bolt11_invoice_features(),
and node_features() as separate public methods on Node, this
embeds NodeFeatures in the NodeStatus struct returned by
Node::status().

Additionally, channel and invoice features at node level are
confusing. Users would expect negotiated per-peer/channel/invoice
features, not what the node generally supports. Access to
negotiated features are addressed in lightningdevkit#841
@ldk-reviews-bot ldk-reviews-bot requested a review from tnull March 24, 2026 21:13

@tnull tnull left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Looks pretty good, have a few initial comments.

Comment thread src/types.rs Outdated
Comment thread src/types.rs Outdated
Comment thread src/types.rs Outdated
Comment thread src/types.rs Outdated
@enigbe enigbe force-pushed the 2026-03-channel-reserve-type-exposure branch 4 times, most recently from 33a36bd to 63f32ab Compare March 30, 2026 06:34

@tnull tnull left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

One comment, otherwise looks good. Feel free to squash (also the new fixup)

Comment thread src/types.rs Outdated
@enigbe enigbe force-pushed the 2026-03-channel-reserve-type-exposure branch from 63f32ab to 56d86f7 Compare March 30, 2026 09:57
@enigbe

enigbe commented Mar 30, 2026

Copy link
Copy Markdown
Contributor Author

Squashed all fix-ups.

Comment thread src/types.rs
@enigbe enigbe force-pushed the 2026-03-channel-reserve-type-exposure branch from 56d86f7 to 77dcbc4 Compare April 3, 2026 09:24
Comment thread src/ffi/types.rs
Comment thread src/ffi/types.rs
@enigbe enigbe force-pushed the 2026-03-channel-reserve-type-exposure branch from fe4597a to eb7f021 Compare April 9, 2026 08:01
@enigbe enigbe force-pushed the 2026-03-channel-reserve-type-exposure branch 2 times, most recently from 86e9936 to 863d5e7 Compare April 22, 2026 09:04
@enigbe enigbe force-pushed the 2026-03-channel-reserve-type-exposure branch from 863d5e7 to b5736f1 Compare May 11, 2026 08:14
@enigbe enigbe requested a review from tnull May 11, 2026 09:24
@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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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.

@tnull tnull left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good I think. One nit.

Comment thread src/ffi/types.rs Outdated

@tnull tnull left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This needs a minor rebase by now.

@enigbe enigbe force-pushed the 2026-03-channel-reserve-type-exposure branch 4 times, most recently from d67aaa8 to fdbd4ff Compare June 4, 2026 17:31
@enigbe

enigbe commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review @tnull.

I've rebased and also simplified the documentation on InitFeatures to match the lean style used with NodeFeatures.

Comment thread src/types.rs Outdated
Comment thread src/types.rs
/// to better separate parameters.
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
pub struct ChannelCounterparty {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This needs to be re-exported via a pub use in src/ffi/types.rs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Why does this need to be re-exported in src/ffi/types.rs? We don't do this for other uniffi::Records. Do you mean src/lib.rs? If it's the latter, I've re-exported in the fix-up commit.

@enigbe enigbe force-pushed the 2026-03-channel-reserve-type-exposure branch 4 times, most recently from aa631a4 to b4dcc99 Compare June 12, 2026 11:00
@enigbe enigbe force-pushed the 2026-03-channel-reserve-type-exposure branch from b4dcc99 to 3b06d8e Compare June 16, 2026 08:35
Comment thread src/ffi/types.rs
pub(crate) inner: LdkInitFeatures,
}

impl InitFeatures {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This seems to be missing #[uniffi::export]?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This has been added.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No, the one you added is also fine to keep, but we'll also need to add uniffi::export to the impl block, as otherwise the methods won't be available in bindings.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's right. I've added this and extended the coverage in the python test to check the now-exposed methods. I noticed NodeFeatures had the same issue, so I fixed and covered that as well.

Comment thread src/types.rs Outdated
pub(crate) fn from_ldk(
value: LdkChannelDetails, anchor_channels_config: Option<&AnchorChannelsConfig>,
) -> Self {
let reserve_type =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, the LDK docs on channel_type say: "None until negotiation completes and the channel type is finalized."

This means we'll erroneously report all channels as Legacy util the negotiation is fully done, no? Probably will need to make this an optional field, too, or have a state for 'not ready' or similar?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Leaning more towards making it optional. Also, I updated the documentation to highlight this.

@enigbe enigbe requested a review from tnull June 17, 2026 08:07
@tnull tnull removed their request for review June 17, 2026 08:09
@enigbe enigbe force-pushed the 2026-03-channel-reserve-type-exposure branch from 84095d9 to 2613151 Compare June 19, 2026 09:08
enigbe added 3 commits June 19, 2026 11:35
We previously flattened ChannelCounterparty fields
into ChannelDetails as individual counterparty_*
fields, and InitFeatures was entirely omitted.
This made it impossible for consumers to access per-peer
feature flags, and awkward to access counterparty
forwarding information without navigating the flattened
field names.

This commit replaces the flattened fields with a
structured ChannelCounterparty type that mirrors
LDK's ChannelCounterparty, exposing InitFeatures
and CounterpartyForwardingInfo that were previously
inaccessible.

We keep outbound_htlc_minimum_msat optional because
it is unavailable before receiving OpenChannel or
AcceptChannel, and re-export ChannelCounterparty so
Rust consumers can name the type.
We expose the reserve type of each channel through
a new ReserveType enum on ChannelDetails. This tells
users whether a channel uses adaptive anchor reserves,
has no reserve due to a trusted peer, or is a legacy
pre-anchor channel.

The reserve type is derived at query time in list_channels
by checking the channel's type features against
trusted_peers_no_reserve.

We replace the From<LdkChannelDetails> implementation with
an explicit from_ldk method that takes the anchor channels
config.

Additionally, we document the rationale behind selecting
adaptive reserve type in the unlikely event the anchor
channels config was previously set and then later removed.
We add requires_* counterparts for every supports_* method on
InitFeatures, completing the BOLT 9 feature flag coverage
for FFI consumers.

We export the existing feature helper methods for both InitFeatures
and NodeFeatures through UniFFI. NodeFeatures had the same missing
export path as InitFeatures and was noticed while addressing the
InitFeatures FFI exposure.

Additionally, we extend the Python full-cycle test to cover node
features and init features on their real runtime paths.
@enigbe enigbe force-pushed the 2026-03-channel-reserve-type-exposure branch from 2613151 to fee7a73 Compare June 19, 2026 18:53
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.

Expose a way to access channel or reserve type

3 participants