@@ -36,7 +36,7 @@ use lightning_net_tokio::SocketDescriptor;
3636
3737use crate :: chain:: bitcoind:: UtxoSourceClient ;
3838use crate :: chain:: ChainSource ;
39- use crate :: config:: ChannelConfig ;
39+ use crate :: config:: { AnchorChannelsConfig , ChannelConfig } ;
4040use crate :: data_store:: DataStore ;
4141use crate :: fee_estimator:: OnchainFeeEstimator ;
4242use 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