@@ -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;
@@ -380,6 +380,47 @@ pub struct ChannelCounterparty {
380380 pub outbound_htlc_maximum_msat : Option < u64 > ,
381381}
382382
383+ /// Describes the reserve behavior of a channel based on its type and trust configuration.
384+ ///
385+ /// This captures the combination of the channel's on-chain construction (anchor outputs vs legacy
386+ /// static_remote_key) and whether the counterparty is in our trusted peers list. It tells the
387+ /// user what reserve obligations exist for this channel without exposing internal protocol details.
388+ ///
389+ /// See [`AnchorChannelsConfig`] for how reserve behavior is configured.
390+ ///
391+ /// [`AnchorChannelsConfig`]: crate::config::AnchorChannelsConfig
392+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
393+ #[ cfg_attr( feature = "uniffi" , derive( uniffi:: Enum ) ) ]
394+ pub enum ReserveType {
395+ /// An anchor outputs channel where we maintain a per-channel on-chain reserve for fee
396+ /// bumping force-close transactions.
397+ ///
398+ /// Anchor channels allow either party to fee-bump commitment transactions via CPFP
399+ /// at broadcast time. Because the pre-signed commitment fee may be insufficient under
400+ /// current fee conditions, the broadcaster must supply additional funds (hence adaptive)
401+ /// through an anchor output spend. The reserve ensures sufficient on-chain funds are
402+ /// available to cover this.
403+ ///
404+ /// This is the default for anchor channels when the counterparty is not in
405+ /// [`trusted_peers_no_reserve`].
406+ ///
407+ /// [`trusted_peers_no_reserve`]: crate::config::AnchorChannelsConfig::trusted_peers_no_reserve
408+ Adaptive ,
409+ /// An anchor outputs channel where we do not maintain any reserve, because the counterparty
410+ /// is in our [`trusted_peers_no_reserve`] list.
411+ ///
412+ /// In this mode, we trust the counterparty to broadcast a valid commitment transaction on
413+ /// our behalf and do not set aside funds for fee bumping.
414+ ///
415+ /// [`trusted_peers_no_reserve`]: crate::config::AnchorChannelsConfig::trusted_peers_no_reserve
416+ TrustedPeersNoReserve ,
417+ /// A legacy (pre-anchor) channel using only `option_static_remotekey`.
418+ ///
419+ /// These channels do not use anchor outputs and therefore do not require an on-chain reserve
420+ /// for fee bumping. Commitment transaction fees are pre-committed at channel open time.
421+ Legacy ,
422+ }
423+
383424/// Details of a channel as returned by [`Node::list_channels`].
384425///
385426/// When a channel is spliced, most fields continue to refer to the original pre-splice channel
@@ -544,10 +585,42 @@ pub struct ChannelDetails {
544585 ///
545586 /// Will be `None` for objects serialized with LDK Node v0.1 and earlier.
546587 pub channel_shutdown_state : Option < ChannelShutdownState > ,
588+ /// The type of on-chain reserve maintained for this channel.
589+ ///
590+ /// Will be `None` until channel negotiation has completed and determined whether
591+ /// this channel uses anchor or legacy reserve behavior.
592+ ///
593+ /// See [`ReserveType`] for details on how reserves differ between anchor and legacy channels.
594+ pub reserve_type : Option < ReserveType > ,
547595}
548596
549- impl From < LdkChannelDetails > for ChannelDetails {
550- fn from ( value : LdkChannelDetails ) -> Self {
597+ impl ChannelDetails {
598+ pub ( crate ) fn from_ldk (
599+ value : LdkChannelDetails , anchor_channels_config : Option < & AnchorChannelsConfig > ,
600+ ) -> Self {
601+ let reserve_type = value. channel_type . as_ref ( ) . map ( |channel_type| {
602+ if channel_type. supports_anchors_zero_fee_htlc_tx ( ) {
603+ if let Some ( config) = anchor_channels_config {
604+ if config. trusted_peers_no_reserve . contains ( & value. counterparty . node_id ) {
605+ ReserveType :: TrustedPeersNoReserve
606+ } else {
607+ ReserveType :: Adaptive
608+ }
609+ } else {
610+ // Edge case: if `AnchorChannelsConfig` was previously set and later
611+ // removed, we can no longer distinguish whether this anchor channel's
612+ // reserve was `Adaptive` or `TrustedPeersNoReserve`. We default to
613+ // `Adaptive` here, which may incorrectly override a prior
614+ // `TrustedPeersNoReserve` designation. This is acceptable since
615+ // unsetting `AnchorChannelsConfig` on a node with existing anchor
616+ // channels is not an expected operation.
617+ ReserveType :: Adaptive
618+ }
619+ } else {
620+ ReserveType :: Legacy
621+ }
622+ } ) ;
623+
551624 ChannelDetails {
552625 channel_id : value. channel_id ,
553626 counterparty : ChannelCounterparty {
@@ -590,6 +663,7 @@ impl From<LdkChannelDetails> for ChannelDetails {
590663 . map ( |c| c. into ( ) )
591664 . expect ( "value is set for objects serialized with LDK v0.0.109+" ) ,
592665 channel_shutdown_state : value. channel_shutdown_state ,
666+ reserve_type,
593667 }
594668 }
595669}
0 commit comments