Skip to content

Commit 8080d50

Browse files
committed
Add ReserveType to ChannelDetails
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. Additionally, We replace the From<LdkChannelDetails> impl with an explicit from_ldk method that takes the anchor channels config.
1 parent 7efc3d6 commit 8080d50

2 files changed

Lines changed: 76 additions & 5 deletions

File tree

src/lib.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,9 @@ use types::{
177177
HRNResolver, KeysManager, OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper,
178178
Wallet,
179179
};
180-
pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, SyncAndAsyncKVStore, UserChannelId};
180+
pub use types::{
181+
ChannelDetails, CustomTlvRecord, PeerDetails, ReserveType, SyncAndAsyncKVStore, UserChannelId,
182+
};
181183
pub use vss_client;
182184

183185
use crate::scoring::setup_background_pathfinding_scores_sync;
@@ -1069,7 +1071,11 @@ impl Node {
10691071

10701072
/// Retrieve a list of known channels.
10711073
pub fn list_channels(&self) -> Vec<ChannelDetails> {
1072-
self.channel_manager.list_channels().into_iter().map(|c| c.into()).collect()
1074+
self.channel_manager
1075+
.list_channels()
1076+
.into_iter()
1077+
.map(|c| ChannelDetails::from_ldk(c, self.config.anchor_channels_config.as_ref()))
1078+
.collect()
10731079
}
10741080

10751081
/// Connect to a node on the peer-to-peer network.

src/types.rs

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use lightning_net_tokio::SocketDescriptor;
3636

3737
use crate::chain::bitcoind::UtxoSourceClient;
3838
use crate::chain::ChainSource;
39-
use crate::config::ChannelConfig;
39+
use crate::config::{AnchorChannelsConfig, ChannelConfig};
4040
use crate::data_store::DataStore;
4141
use crate::fee_estimator::OnchainFeeEstimator;
4242
use crate::logger::Logger;
@@ -399,6 +399,48 @@ pub struct ChannelCounterparty {
399399
pub outbound_htlc_maximum_msat: Option<u64>,
400400
}
401401

402+
/// Describes the reserve behavior of a channel based on its type and trust configuration.
403+
///
404+
/// This captures the combination of the channel's on-chain construction (anchor outputs vs legacy
405+
/// static_remote_key) and whether the counterparty is in our trusted peers list. It tells the
406+
/// user what reserve obligations exist for this channel without exposing internal protocol details.
407+
///
408+
/// See [`AnchorChannelsConfig`] for how reserve behavior is configured.
409+
///
410+
/// [`AnchorChannelsConfig`]: crate::config::AnchorChannelsConfig
411+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
412+
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
413+
pub enum ReserveType {
414+
/// An anchor outputs channel where we maintain a per-channel on-chain reserve for fee
415+
/// bumping force-close transactions.
416+
///
417+
/// Anchor channels (using `option_anchors_zero_fee_htlc_tx` as defined in
418+
/// [BOLT 3](https://github.com/lightning/bolts/03-transactions.md#to_local_anchor-and-to_remote_anchor-output-option_anchors))
419+
/// allow either party to fee-bump commitment transactions via CPFP (Child-Pays-For-Parent)
420+
/// at broadcast time. Because the pre-signed commitment fee may be insufficient under
421+
/// current fee conditions, the broadcaster must supply additional funds (hence adaptive) through an
422+
/// anchor output spend. The reserve ensures sufficient on-chain funds are available to cover this.
423+
///
424+
/// This is the default for anchor channels when the counterparty is not in
425+
/// [`AnchorChannelsConfig::trusted_peers_no_reserve`].
426+
///
427+
/// [`AnchorChannelsConfig::trusted_peers_no_reserve`]: crate::config::AnchorChannelsConfig::trusted_peers_no_reserve
428+
Adaptive,
429+
/// An anchor outputs channel where we do not maintain any reserve, because the counterparty
430+
/// is in our [`AnchorChannelsConfig::trusted_peers_no_reserve`] list.
431+
///
432+
/// In this mode, we trust the counterparty to broadcast a valid commitment transaction on
433+
/// our behalf and do not set aside funds for fee bumping.
434+
///
435+
/// [`AnchorChannelsConfig::trusted_peers_no_reserve`]: crate::config::AnchorChannelsConfig::trusted_peers_no_reserve
436+
TrustedPeersNoReserve,
437+
/// A legacy (pre-anchor) channel using only `option_static_remotekey`.
438+
///
439+
/// These channels do not use anchor outputs and therefore do not require an on-chain reserve
440+
/// for fee bumping. Commitment transaction fees are pre-committed at channel open time.
441+
Legacy,
442+
}
443+
402444
/// Details of a channel as returned by [`Node::list_channels`].
403445
///
404446
/// When a channel is spliced, most fields continue to refer to the original pre-splice channel
@@ -559,10 +601,32 @@ pub struct ChannelDetails {
559601
pub inbound_htlc_maximum_msat: Option<u64>,
560602
/// Set of configurable parameters that affect channel operation.
561603
pub config: ChannelConfig,
604+
/// The type of on-chain reserve maintained for this channel.
605+
///
606+
/// See [`ReserveType`] for details on how reserves differ between anchor and legacy channels.
607+
pub reserve_type: ReserveType,
562608
}
563609

564-
impl From<LdkChannelDetails> for ChannelDetails {
565-
fn from(value: LdkChannelDetails) -> Self {
610+
impl ChannelDetails {
611+
pub(crate) fn from_ldk(
612+
value: LdkChannelDetails, anchor_channels_config: Option<&AnchorChannelsConfig>,
613+
) -> Self {
614+
let is_anchor_channel =
615+
value.channel_type.as_ref().map_or(false, |ct| ct.supports_anchors_zero_fee_htlc_tx());
616+
617+
let reserve_type = if is_anchor_channel {
618+
let is_trusted = anchor_channels_config.map_or(false, |c| {
619+
c.trusted_peers_no_reserve.contains(&value.counterparty.node_id)
620+
});
621+
if is_trusted {
622+
ReserveType::TrustedPeersNoReserve
623+
} else {
624+
ReserveType::Adaptive
625+
}
626+
} else {
627+
ReserveType::Legacy
628+
};
629+
566630
ChannelDetails {
567631
channel_id: value.channel_id,
568632
counterparty: ChannelCounterparty {
@@ -601,6 +665,7 @@ impl From<LdkChannelDetails> for ChannelDetails {
601665
inbound_htlc_maximum_msat: value.inbound_htlc_maximum_msat,
602666
// unwrap safety: `config` is only `None` for LDK objects serialized prior to 0.0.109.
603667
config: value.config.map(|c| c.into()).unwrap(),
668+
reserve_type,
604669
}
605670
}
606671
}

0 commit comments

Comments
 (0)