Expand the fields exposed to users in `ChannelDetails`
authorMatt Corallo <git@bluematt.me>
Fri, 2 Jul 2021 23:54:57 +0000 (23:54 +0000)
committerMatt Corallo <git@bluematt.me>
Tue, 6 Jul 2021 00:18:27 +0000 (00:18 +0000)
This adds four new fields in `ChannelDetails`:
1. holder_selected_ and counterparty_selected_channel_reserve_delay
   are useful to determine what amount of the channel is
   unavailable for payments.
2. confirmations_required is useful when awaiting funding
   confirmation to determine how long you will need to wait.
3. to_self_delay is useful to determine how long it will take to
   receive funds after a force-close.

Fixes #983.

fuzz/src/router.rs
lightning/src/ln/channel.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_tests.rs
lightning/src/routing/router.rs

index e8e025e247b2ec7c7442ce05c72363946d9bc85b..fabad11ac75d974f9ca4c92b7b5cd5cb841dbcb9 100644 (file)
@@ -212,12 +212,13 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
                                                                remote_network_id: *rnid,
                                                                counterparty_features: InitFeatures::known(),
                                                                channel_value_satoshis: slice_to_be64(get_slice!(8)),
-                                                               user_id: 0,
-                                                               inbound_capacity_msat: 0,
-                                                               is_outbound: true,
-                                                               is_funding_locked: true,
-                                                               is_usable: true,
-                                                               is_public: true,
+                                                               user_id: 0, inbound_capacity_msat: 0,
+                                                               to_self_reserve_satoshis: None,
+                                                               to_remote_reserve_satoshis: 0,
+                                                               confirmations_required: None,
+                                                               spend_csv_on_our_commitment_funds: None,
+                                                               is_outbound: true, is_funding_locked: true,
+                                                               is_usable: true, is_public: true,
                                                                outbound_capacity_msat: 0,
                                                                counterparty_forwarding_info: None,
                                                        });
index 92a9ab6ab9ad4cd19836ddd8e66ecfc66c5104de..0cda76fd5e4306396bd72c3240eb74a4ec83e2fb 100644 (file)
@@ -1784,8 +1784,22 @@ impl<Signer: Sign> Channel<Signer> {
        /// corner case properly.
        pub fn get_inbound_outbound_available_balance_msat(&self) -> (u64, u64) {
                // Note that we have to handle overflow due to the above case.
-               (cmp::max(self.channel_value_satoshis as i64 * 1000 - self.value_to_self_msat as i64 - self.get_inbound_pending_htlc_stats().1 as i64, 0) as u64,
-               cmp::max(self.value_to_self_msat as i64 - self.get_outbound_pending_htlc_stats().1 as i64, 0) as u64)
+               (
+                       cmp::max(self.channel_value_satoshis as i64 * 1000
+                               - self.value_to_self_msat as i64
+                               - self.get_inbound_pending_htlc_stats().1 as i64
+                               - Self::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis) as i64 * 1000,
+                       0) as u64,
+                       cmp::max(self.value_to_self_msat as i64
+                               - self.get_outbound_pending_htlc_stats().1 as i64
+                               - self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000,
+                       0) as u64
+               )
+       }
+
+       pub fn get_holder_counterparty_selected_channel_reserve_satoshis(&self) -> (u64, Option<u64>) {
+               (Self::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis),
+               self.counterparty_selected_channel_reserve_satoshis)
        }
 
        // Get the fee cost of a commitment tx with a given number of HTLC outputs.
@@ -3338,6 +3352,10 @@ impl<Signer: Sign> Channel<Signer> {
                self.channel_id
        }
 
+       pub fn minimum_depth(&self) -> Option<u32> {
+               self.minimum_depth
+       }
+
        /// Gets the "user_id" value passed into the construction of this channel. It has no special
        /// meaning and exists only to allow users to have a persistent identifier of a channel.
        pub fn get_user_id(&self) -> u64 {
@@ -3365,7 +3383,7 @@ impl<Signer: Sign> Channel<Signer> {
                &self.channel_transaction_parameters.holder_pubkeys
        }
 
-       fn get_counterparty_selected_contest_delay(&self) -> Option<u16> {
+       pub fn get_counterparty_selected_contest_delay(&self) -> Option<u16> {
                self.channel_transaction_parameters.counterparty_parameters
                        .as_ref().map(|params| params.selected_contest_delay)
        }
index 1f16781603ab43e5dc6f6ed0ae8a65b567a1c2d3..c147ed1c4f137cd0519defe457327b3ce753a240 100644 (file)
@@ -656,25 +656,74 @@ pub struct ChannelDetails {
        pub counterparty_features: InitFeatures,
        /// The value, in satoshis, of this channel as appears in the funding output
        pub channel_value_satoshis: u64,
+       /// The value, in satoshis, that must always be held in the channel for us. This value ensures
+       /// that if we broadcast a revoked state, our counterparty can punish us by claiming at least
+       /// this value on chain.
+       ///
+       /// This value is not included in [`outbound_capacity_msat`] as it can never be spent.
+       ///
+       /// This value will be `None` for outbound channels until the counterparty accepts the channel.
+       ///
+       /// [`outbound_capacity_msat`]: ChannelDetails::outbound_capacity_msat
+       pub to_self_reserve_satoshis: Option<u64>,
+       /// The value, in satoshis, that must always be held in the channel for our counterparty. This
+       /// value ensures that if our counterparty broadcasts a revoked state, we can punish them by
+       /// claiming at least this value on chain.
+       ///
+       /// This value is not included in [`inbound_capacity_msat`] as it can never be spent.
+       ///
+       /// [`inbound_capacity_msat`]: ChannelDetails::inbound_capacity_msat
+       pub to_remote_reserve_satoshis: u64,
        /// The user_id passed in to create_channel, or 0 if the channel was inbound.
        pub user_id: u64,
        /// The available outbound capacity for sending HTLCs to the remote peer. This does not include
        /// any pending HTLCs which are not yet fully resolved (and, thus, who's balance is not
        /// available for inclusion in new outbound HTLCs). This further does not include any pending
        /// outgoing HTLCs which are awaiting some other resolution to be sent.
+       ///
+       /// This value is not exact. Due to various in-flight changes, feerate changes, and our
+       /// conflict-avoidance policy, exactly this amount is not likely to be spendable. However, we
+       /// should be able to spend nearly this amount.
        pub outbound_capacity_msat: u64,
        /// The available inbound capacity for the remote peer to send HTLCs to us. This does not
        /// include any pending HTLCs which are not yet fully resolved (and, thus, who's balance is not
        /// available for inclusion in new inbound HTLCs).
        /// Note that there are some corner cases not fully handled here, so the actual available
        /// inbound capacity may be slightly higher than this.
+       ///
+       /// This value is not exact. Due to various in-flight changes, feerate changes, and our
+       /// counterparty's conflict-avoidance policy, exactly this amount is not likely to be spendable.
+       /// However, our counterparty should be able to spend nearly this amount.
        pub inbound_capacity_msat: u64,
+       /// The number of required confirmations on the funding transaction before the funding will be
+       /// considered "locked". This number is selected by the channel fundee (i.e. us if
+       /// [`is_outbound`] is *not* set), and can be selected for inbound channels with
+       /// [`ChannelHandshakeConfig::minimum_depth`] or limited for outbound channels with
+       /// [`ChannelHandshakeLimits::max_minimum_depth`].
+       ///
+       /// This value will be `None` for outbound channels until the counterparty accepts the channel.
+       ///
+       /// [`is_outbound`]: ChannelDetails::is_outbound
+       /// [`ChannelHandshakeConfig::minimum_depth`]: crate::util::config::ChannelHandshakeConfig::minimum_depth
+       /// [`ChannelHandshakeLimits::max_minimum_depth`]: crate::util::config::ChannelHandshakeLimits::max_minimum_depth
+       pub confirmations_required: Option<u32>,
+       /// The number of blocks (after our commitment transaction confirms) that we will need to wait
+       /// until we can claim our funds after we force-close the channel. During this time our
+       /// counterparty is allowed to punish us if we broadcasted a stale state. If our counterparty
+       /// force-closes the channel and broadcasts a commitment transaction we do not have to wait any
+       /// time to claim our non-HTLC-encumbered funds.
+       ///
+       /// This value will be `None` for outbound channels until the counterparty accepts the channel.
+       pub spend_csv_on_our_commitment_funds: Option<u16>,
        /// True if the channel was initiated (and thus funded) by us.
        pub is_outbound: bool,
        /// True if the channel is confirmed, funding_locked messages have been exchanged, and the
        /// channel is not currently being shut down. `funding_locked` message exchange implies the
        /// required confirmation count has been reached (and we were connected to the peer at some
-       /// point after the funding transaction received enough confirmations).
+       /// point after the funding transaction received enough confirmations). The required
+       /// confirmation count is provided in [`confirmations_required`].
+       ///
+       /// [`confirmations_required`]: ChannelDetails::confirmations_required
        pub is_funding_locked: bool,
        /// True if the channel is (a) confirmed and funding_locked messages have been exchanged, (b)
        /// the peer is connected, and (c) the channel is not currently negotiating a shutdown.
@@ -1146,6 +1195,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                        res.reserve(channel_state.by_id.len());
                        for (channel_id, channel) in channel_state.by_id.iter().filter(f) {
                                let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
+                               let (to_remote_reserve_satoshis, to_self_reserve_satoshis) =
+                                       channel.get_holder_counterparty_selected_channel_reserve_satoshis();
                                res.push(ChannelDetails {
                                        channel_id: (*channel_id).clone(),
                                        funding_txo: channel.get_funding_txo(),
@@ -1153,9 +1204,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                        remote_network_id: channel.get_counterparty_node_id(),
                                        counterparty_features: InitFeatures::empty(),
                                        channel_value_satoshis: channel.get_value_satoshis(),
+                                       to_self_reserve_satoshis,
+                                       to_remote_reserve_satoshis,
                                        inbound_capacity_msat,
                                        outbound_capacity_msat,
                                        user_id: channel.get_user_id(),
+                                       confirmations_required: channel.minimum_depth(),
+                                       spend_csv_on_our_commitment_funds: channel.get_counterparty_selected_contest_delay(),
                                        is_outbound: channel.is_outbound(),
                                        is_funding_locked: channel.is_usable(),
                                        is_usable: channel.is_live(),
index df3c64ae8ff67ac0e429d720bfe304ba628341db..4e51f6b4bf5fe9dcdd08d393e31461b05aa5ff9b 100644 (file)
@@ -1878,11 +1878,12 @@ fn test_inbound_outbound_capacity_is_not_zero() {
        assert_eq!(channels0.len(), 1);
        assert_eq!(channels1.len(), 1);
 
-       assert_eq!(channels0[0].inbound_capacity_msat, 95000000);
-       assert_eq!(channels1[0].outbound_capacity_msat, 95000000);
+       let reserve = Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100000);
+       assert_eq!(channels0[0].inbound_capacity_msat, 95000000 - reserve*1000);
+       assert_eq!(channels1[0].outbound_capacity_msat, 95000000 - reserve*1000);
 
-       assert_eq!(channels0[0].outbound_capacity_msat, 100000 * 1000 - 95000000);
-       assert_eq!(channels1[0].inbound_capacity_msat, 100000 * 1000 - 95000000);
+       assert_eq!(channels0[0].outbound_capacity_msat, 100000 * 1000 - 95000000 - reserve*1000);
+       assert_eq!(channels1[0].inbound_capacity_msat, 100000 * 1000 - 95000000 - reserve*1000);
 }
 
 fn commit_tx_fee_msat(feerate: u32, num_htlcs: u64) -> u64 {
index b12b960d5d3a5d34374205b4eedfcc8cee79e46e..aa1c3c1176c70e45d6db1b0a0ca903e2b7e470d0 100644 (file)
@@ -1642,6 +1642,10 @@ mod tests {
                        user_id: 0,
                        outbound_capacity_msat: 100000,
                        inbound_capacity_msat: 100000,
+                       to_self_reserve_satoshis: None,
+                       to_remote_reserve_satoshis: 0,
+                       confirmations_required: None,
+                       spend_csv_on_our_commitment_funds: None,
                        is_outbound: true, is_funding_locked: true,
                        is_usable: true, is_public: true,
                        counterparty_forwarding_info: None,
@@ -1962,6 +1966,10 @@ mod tests {
                        user_id: 0,
                        outbound_capacity_msat: 250_000_000,
                        inbound_capacity_msat: 0,
+                       to_self_reserve_satoshis: None,
+                       to_remote_reserve_satoshis: 0,
+                       confirmations_required: None,
+                       spend_csv_on_our_commitment_funds: None,
                        is_outbound: true, is_funding_locked: true,
                        is_usable: true, is_public: true,
                        counterparty_forwarding_info: None,
@@ -2012,6 +2020,10 @@ mod tests {
                        user_id: 0,
                        outbound_capacity_msat: 250_000_000,
                        inbound_capacity_msat: 0,
+                       to_self_reserve_satoshis: None,
+                       to_remote_reserve_satoshis: 0,
+                       confirmations_required: None,
+                       spend_csv_on_our_commitment_funds: None,
                        is_outbound: true, is_funding_locked: true,
                        is_usable: true, is_public: true,
                        counterparty_forwarding_info: None,
@@ -2079,6 +2091,10 @@ mod tests {
                        user_id: 0,
                        outbound_capacity_msat: 250_000_000,
                        inbound_capacity_msat: 0,
+                       to_self_reserve_satoshis: None,
+                       to_remote_reserve_satoshis: 0,
+                       confirmations_required: None,
+                       spend_csv_on_our_commitment_funds: None,
                        is_outbound: true, is_funding_locked: true,
                        is_usable: true, is_public: true,
                        counterparty_forwarding_info: None,
@@ -2218,6 +2234,10 @@ mod tests {
                        user_id: 0,
                        outbound_capacity_msat: 250_000_000,
                        inbound_capacity_msat: 0,
+                       to_self_reserve_satoshis: None,
+                       to_remote_reserve_satoshis: 0,
+                       confirmations_required: None,
+                       spend_csv_on_our_commitment_funds: None,
                        is_outbound: true, is_funding_locked: true,
                        is_usable: true, is_public: true,
                        counterparty_forwarding_info: None,
@@ -2349,6 +2369,10 @@ mod tests {
                        user_id: 0,
                        outbound_capacity_msat: 100000,
                        inbound_capacity_msat: 100000,
+                       to_self_reserve_satoshis: None,
+                       to_remote_reserve_satoshis: 0,
+                       confirmations_required: None,
+                       spend_csv_on_our_commitment_funds: None,
                        is_outbound: true, is_funding_locked: true,
                        is_usable: true, is_public: true,
                        counterparty_forwarding_info: None,
@@ -2483,6 +2507,10 @@ mod tests {
                        user_id: 0,
                        outbound_capacity_msat: 200_000_000,
                        inbound_capacity_msat: 0,
+                       to_self_reserve_satoshis: None,
+                       to_remote_reserve_satoshis: 0,
+                       confirmations_required: None,
+                       spend_csv_on_our_commitment_funds: None,
                        is_outbound: true, is_funding_locked: true,
                        is_usable: true, is_public: true,
                        counterparty_forwarding_info: None,