@@ -40,7 +40,7 @@ use lightning_net_tokio::SocketDescriptor;
4040
4141use crate :: chain:: bitcoind:: UtxoSourceClient ;
4242use crate :: chain:: ChainSource ;
43- use crate :: config:: ChannelConfig ;
43+ use crate :: config:: { AnchorChannelsConfig , ChannelConfig } ;
4444use crate :: data_store:: DataStore ;
4545use crate :: fee_estimator:: OnchainFeeEstimator ;
4646use crate :: ffi:: maybe_wrap;
@@ -451,6 +451,47 @@ pub struct ChannelCounterparty {
451451 pub outbound_htlc_maximum_msat : Option < u64 > ,
452452}
453453
454+ /// Describes the reserve behavior of a channel based on its type and trust configuration.
455+ ///
456+ /// This captures the combination of the channel's on-chain construction (anchor outputs vs legacy
457+ /// static_remote_key) and whether the counterparty is in our trusted peers list. It tells the
458+ /// user what reserve obligations exist for this channel without exposing internal protocol details.
459+ ///
460+ /// See [`AnchorChannelsConfig`] for how reserve behavior is configured.
461+ ///
462+ /// [`AnchorChannelsConfig`]: crate::config::AnchorChannelsConfig
463+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
464+ #[ cfg_attr( feature = "uniffi" , derive( uniffi:: Enum ) ) ]
465+ pub enum ReserveType {
466+ /// An anchor outputs channel where we maintain a per-channel on-chain reserve for fee
467+ /// bumping force-close transactions.
468+ ///
469+ /// Anchor channels allow either party to fee-bump commitment transactions via CPFP
470+ /// at broadcast time. Because the pre-signed commitment fee may be insufficient under
471+ /// current fee conditions, the broadcaster must supply additional funds (hence adaptive)
472+ /// through an anchor output spend. The reserve ensures sufficient on-chain funds are
473+ /// available to cover this.
474+ ///
475+ /// This is the default for anchor channels when the counterparty is not in
476+ /// [`trusted_peers_no_reserve`].
477+ ///
478+ /// [`trusted_peers_no_reserve`]: crate::config::AnchorChannelsConfig::trusted_peers_no_reserve
479+ Adaptive ,
480+ /// An anchor outputs channel where we do not maintain any reserve, because the counterparty
481+ /// is in our [`trusted_peers_no_reserve`] list.
482+ ///
483+ /// In this mode, we trust the counterparty to broadcast a valid commitment transaction on
484+ /// our behalf and do not set aside funds for fee bumping.
485+ ///
486+ /// [`trusted_peers_no_reserve`]: crate::config::AnchorChannelsConfig::trusted_peers_no_reserve
487+ TrustedPeersNoReserve ,
488+ /// A legacy (pre-anchor) channel using only `option_static_remotekey`.
489+ ///
490+ /// These channels do not use anchor outputs and therefore do not require an on-chain reserve
491+ /// for fee bumping. Commitment transaction fees are pre-committed at channel open time.
492+ Legacy ,
493+ }
494+
454495/// Details of a channel as returned by [`Node::list_channels`].
455496///
456497/// When a channel is spliced, most fields continue to refer to the original pre-splice channel
@@ -615,10 +656,39 @@ pub struct ChannelDetails {
615656 ///
616657 /// Will be `None` for objects serialized with LDK Node v0.1 and earlier.
617658 pub channel_shutdown_state : Option < ChannelShutdownState > ,
659+ /// The type of on-chain reserve maintained for this channel.
660+ ///
661+ /// See [`ReserveType`] for details on how reserves differ between anchor and legacy channels.
662+ pub reserve_type : ReserveType ,
618663}
619664
620- impl From < LdkChannelDetails > for ChannelDetails {
621- fn from ( value : LdkChannelDetails ) -> Self {
665+ impl ChannelDetails {
666+ pub ( crate ) fn from_ldk (
667+ value : LdkChannelDetails , anchor_channels_config : Option < & AnchorChannelsConfig > ,
668+ ) -> Self {
669+ let reserve_type =
670+ if value. channel_type . as_ref ( ) . is_some_and ( |ct| ct. supports_anchors_zero_fee_htlc_tx ( ) )
671+ {
672+ if let Some ( config) = anchor_channels_config {
673+ if config. trusted_peers_no_reserve . contains ( & value. counterparty . node_id ) {
674+ ReserveType :: TrustedPeersNoReserve
675+ } else {
676+ ReserveType :: Adaptive
677+ }
678+ } else {
679+ // Edge case: if `AnchorChannelsConfig` was previously set and later
680+ // removed, we can no longer distinguish whether this anchor channel's
681+ // reserve was `Adaptive` or `TrustedPeersNoReserve`. We default to
682+ // `Adaptive` here, which may incorrectly override a prior
683+ // `TrustedPeersNoReserve` designation. This is acceptable since
684+ // unsetting `AnchorChannelsConfig` on a node with existing anchor
685+ // channels is not an expected operation.
686+ ReserveType :: Adaptive
687+ }
688+ } else {
689+ ReserveType :: Legacy
690+ } ;
691+
622692 ChannelDetails {
623693 channel_id : value. channel_id ,
624694 counterparty : ChannelCounterparty {
@@ -666,6 +736,7 @@ impl From<LdkChannelDetails> for ChannelDetails {
666736 . map ( |c| c. into ( ) )
667737 . expect ( "value is set for objects serialized with LDK v0.0.109+" ) ,
668738 channel_shutdown_state : value. channel_shutdown_state ,
739+ reserve_type,
669740 }
670741 }
671742}
0 commit comments