From: Matt Corallo Date: Fri, 2 Jul 2021 23:54:57 +0000 (+0000) Subject: Expand the fields exposed to users in `ChannelDetails` X-Git-Tag: v0.0.99~5^2~2 X-Git-Url: http://git.bitcoin.ninja/index.cgi?p=rust-lightning;a=commitdiff_plain;h=0882655680040a0226b1790b6558150324a288f4 Expand the fields exposed to users in `ChannelDetails` 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. --- diff --git a/fuzz/src/router.rs b/fuzz/src/router.rs index e8e025e2..fabad11a 100644 --- a/fuzz/src/router.rs +++ b/fuzz/src/router.rs @@ -212,12 +212,13 @@ pub fn do_test(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, }); diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 92a9ab6a..0cda76fd 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -1784,8 +1784,22 @@ impl Channel { /// 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) { + (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 Channel { self.channel_id } + pub fn minimum_depth(&self) -> Option { + 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 Channel { &self.channel_transaction_parameters.holder_pubkeys } - fn get_counterparty_selected_contest_delay(&self) -> Option { + pub fn get_counterparty_selected_contest_delay(&self) -> Option { self.channel_transaction_parameters.counterparty_parameters .as_ref().map(|params| params.selected_contest_delay) } diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 1f167816..c147ed1c 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -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, + /// 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, + /// 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, /// 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 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 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(), diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index df3c64ae..4e51f6b4 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -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::::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 { diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index b12b960d..aa1c3c11 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -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,