Make our in-flight limit percentage configurable
[rust-lightning] / lightning / src / ln / channel.rs
index 12fd6958348f07fed591be0eb79252ede1c30bb2..f6767e8a7d46555cecb48592ee52200aa4b53ded 100644 (file)
@@ -39,7 +39,7 @@ use util::events::ClosureReason;
 use util::ser::{Readable, ReadableArgs, Writeable, Writer, VecWriter};
 use util::logger::Logger;
 use util::errors::APIError;
-use util::config::{UserConfig, ChannelConfig, ChannelHandshakeLimits};
+use util::config::{UserConfig, ChannelConfig, ChannelHandshakeConfig, ChannelHandshakeLimits};
 use util::scid_utils::scid_from_parts;
 
 use io;
@@ -62,6 +62,17 @@ pub struct ChannelValueStat {
        pub counterparty_dust_limit_msat: u64,
 }
 
+pub struct AvailableBalances {
+       /// The amount that would go to us if we close the channel, ignoring any on-chain fees.
+       pub balance_msat: u64,
+       /// Total amount available for our counterparty to send to us.
+       pub inbound_capacity_msat: u64,
+       /// Total amount available for us to send to our counterparty.
+       pub outbound_capacity_msat: u64,
+       /// The maximum value we can assign to the next outbound HTLC
+       pub next_outbound_htlc_limit_msat: u64,
+}
+
 #[derive(Debug, Clone, Copy, PartialEq)]
 enum FeeUpdateState {
        // Inbound states mirroring InboundHTLCState
@@ -734,9 +745,19 @@ pub const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
 
 pub const ANCHOR_OUTPUT_VALUE_SATOSHI: u64 = 330;
 
-/// Maximum `funding_satoshis` value, according to the BOLT #2 specification
-/// it's 2^24.
-pub const MAX_FUNDING_SATOSHIS: u64 = 1 << 24;
+/// The percentage of the channel value `holder_max_htlc_value_in_flight_msat` used to be set to,
+/// before this was made configurable. The percentage was made configurable in LDK 0.0.107,
+/// although LDK 0.0.104+ enabled serialization of channels with a different value set for
+/// `holder_max_htlc_value_in_flight_msat`.
+pub const MAX_IN_FLIGHT_PERCENT_LEGACY: u8 = 10;
+
+/// Maximum `funding_satoshis` value according to the BOLT #2 specification, if
+/// `option_support_large_channel` (aka wumbo channels) is not supported.
+/// It's 2^24 - 1.
+pub const MAX_FUNDING_SATOSHIS_NO_WUMBO: u64 = (1 << 24) - 1;
+
+/// Total bitcoin supply in satoshis.
+pub const TOTAL_BITCOIN_SUPPLY_SATOSHIS: u64 = 21_000_000 * 1_0000_0000;
 
 /// The maximum network dust limit for standard script formats. This currently represents the
 /// minimum output value for a P2SH output before Bitcoin Core 22 considers the entire
@@ -788,9 +809,22 @@ macro_rules! secp_check {
 }
 
 impl<Signer: Sign> Channel<Signer> {
-       // Convert constants + channel value to limits:
-       fn get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis: u64) -> u64 {
-               channel_value_satoshis * 1000 / 10 //TODO
+       /// Returns the value to use for `holder_max_htlc_value_in_flight_msat` as a percentage of the
+       /// `channel_value_satoshis` in msat, set through
+       /// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`]
+       ///
+       /// The effective percentage is lower bounded by 1% and upper bounded by 100%.
+       ///
+       /// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`]: crate::util::config::ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel
+       fn get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis: u64, config: &ChannelHandshakeConfig) -> u64 {
+               let configured_percent = if config.max_inbound_htlc_value_in_flight_percent_of_channel < 1 {
+                       1
+               } else if config.max_inbound_htlc_value_in_flight_percent_of_channel > 100 {
+                       100
+               } else {
+                       config.max_inbound_htlc_value_in_flight_percent_of_channel as u64
+               };
+               channel_value_satoshis * 10 * configured_percent
        }
 
        /// Returns a minimum channel reserve value the remote needs to maintain,
@@ -850,8 +884,11 @@ impl<Signer: Sign> Channel<Signer> {
                let holder_signer = keys_provider.get_channel_signer(false, channel_value_satoshis);
                let pubkeys = holder_signer.pubkeys().clone();
 
-               if channel_value_satoshis >= MAX_FUNDING_SATOSHIS {
-                       return Err(APIError::APIMisuseError{err: format!("funding_value must be smaller than {}, it was {}", MAX_FUNDING_SATOSHIS, channel_value_satoshis)});
+               if !their_features.supports_wumbo() && channel_value_satoshis > MAX_FUNDING_SATOSHIS_NO_WUMBO {
+                       return Err(APIError::APIMisuseError{err: format!("funding_value must not exceed {}, it was {}", MAX_FUNDING_SATOSHIS_NO_WUMBO, channel_value_satoshis)});
+               }
+               if channel_value_satoshis >= TOTAL_BITCOIN_SUPPLY_SATOSHIS {
+                       return Err(APIError::APIMisuseError{err: format!("funding_value must be smaller than the total bitcoin supply, it was {}", channel_value_satoshis)});
                }
                let channel_value_msat = channel_value_satoshis * 1000;
                if push_msat > channel_value_msat {
@@ -946,7 +983,7 @@ impl<Signer: Sign> Channel<Signer> {
                        counterparty_dust_limit_satoshis: 0,
                        holder_dust_limit_satoshis: MIN_CHAN_DUST_LIMIT_SATOSHIS,
                        counterparty_max_htlc_value_in_flight_msat: 0,
-                       holder_max_htlc_value_in_flight_msat: Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis),
+                       holder_max_htlc_value_in_flight_msat: Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis, &config.own_channel_config),
                        counterparty_selected_channel_reserve_satoshis: None, // Filled in in accept_channel
                        holder_selected_channel_reserve_satoshis,
                        counterparty_htlc_minimum_msat: 0,
@@ -1076,20 +1113,22 @@ impl<Signer: Sign> Channel<Signer> {
                }
 
                // Check sanity of message fields:
-               if msg.funding_satoshis >= MAX_FUNDING_SATOSHIS {
-                       return Err(ChannelError::Close(format!("Funding must be smaller than {}. It was {}", MAX_FUNDING_SATOSHIS, msg.funding_satoshis)));
+               if msg.funding_satoshis > config.peer_channel_config_limits.max_funding_satoshis {
+                       return Err(ChannelError::Close(format!("Per our config, funding must be at most {}. It was {}", config.peer_channel_config_limits.max_funding_satoshis, msg.funding_satoshis)));
+               }
+               if msg.funding_satoshis >= TOTAL_BITCOIN_SUPPLY_SATOSHIS {
+                       return Err(ChannelError::Close(format!("Funding must be smaller than the total bitcoin supply. It was {}", msg.funding_satoshis)));
                }
                if msg.channel_reserve_satoshis > msg.funding_satoshis {
                        return Err(ChannelError::Close(format!("Bogus channel_reserve_satoshis ({}). Must be not greater than funding_satoshis: {}", msg.channel_reserve_satoshis, msg.funding_satoshis)));
                }
-               let funding_value = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
-               if msg.push_msat > funding_value {
-                       return Err(ChannelError::Close(format!("push_msat {} was larger than funding value {}", msg.push_msat, funding_value)));
+               let full_channel_value_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
+               if msg.push_msat > full_channel_value_msat {
+                       return Err(ChannelError::Close(format!("push_msat {} was larger than channel amount minus reserve ({})", msg.push_msat, full_channel_value_msat)));
                }
                if msg.dust_limit_satoshis > msg.funding_satoshis {
                        return Err(ChannelError::Close(format!("dust_limit_satoshis {} was larger than funding_satoshis {}. Peer never wants payout outputs?", msg.dust_limit_satoshis, msg.funding_satoshis)));
                }
-               let full_channel_value_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
                if msg.htlc_minimum_msat >= full_channel_value_msat {
                        return Err(ChannelError::Close(format!("Minimum htlc value ({}) was larger than full channel value ({})", msg.htlc_minimum_msat, full_channel_value_msat)));
                }
@@ -1143,6 +1182,9 @@ impl<Signer: Sign> Channel<Signer> {
                if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
                        return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). dust_limit_satoshis is ({}).", holder_selected_channel_reserve_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
                }
+               if holder_selected_channel_reserve_satoshis * 1000 >= full_channel_value_msat {
+                       return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). Channel value is ({} - {}).", holder_selected_channel_reserve_satoshis, full_channel_value_msat, msg.push_msat)));
+               }
                if msg.channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
                        log_debug!(logger, "channel_reserve_satoshis ({}) is smaller than our dust limit ({}). We can broadcast stale states without any risk, implying this channel is very insecure for our counterparty.",
                                msg.channel_reserve_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS);
@@ -1259,7 +1301,7 @@ impl<Signer: Sign> Channel<Signer> {
                        counterparty_dust_limit_satoshis: msg.dust_limit_satoshis,
                        holder_dust_limit_satoshis: MIN_CHAN_DUST_LIMIT_SATOSHIS,
                        counterparty_max_htlc_value_in_flight_msat: cmp::min(msg.max_htlc_value_in_flight_msat, msg.funding_satoshis * 1000),
-                       holder_max_htlc_value_in_flight_msat: Self::get_holder_max_htlc_value_in_flight_msat(msg.funding_satoshis),
+                       holder_max_htlc_value_in_flight_msat: Self::get_holder_max_htlc_value_in_flight_msat(msg.funding_satoshis, &config.own_channel_config),
                        counterparty_selected_channel_reserve_satoshis: Some(msg.channel_reserve_satoshis),
                        holder_selected_channel_reserve_satoshis,
                        counterparty_htlc_minimum_msat: msg.htlc_minimum_msat,
@@ -2330,40 +2372,39 @@ impl<Signer: Sign> Channel<Signer> {
                stats
        }
 
-       /// Get the available (ie not including pending HTLCs) inbound and outbound balance in msat.
+       /// Get the available balances, see [`AvailableBalances`]'s fields for more info.
        /// Doesn't bother handling the
        /// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
        /// corner case properly.
-       /// The channel reserve is subtracted from each balance.
-       /// See also [`Channel::get_balance_msat`]
-       pub fn get_inbound_outbound_available_balance_msat(&self) -> (u64, u64) {
+       pub fn get_available_balances(&self) -> AvailableBalances {
                // 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(None).pending_htlcs_value_msat as i64
-                               - self.holder_selected_channel_reserve_satoshis as i64 * 1000,
-                       0) as u64,
-                       cmp::max(self.value_to_self_msat as i64
-                               - self.get_outbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
-                               - self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000,
-                       0) as u64
-               )
-       }
+               let outbound_stats = self.get_outbound_pending_htlc_stats(None);
 
-       /// Get our total balance in msat.
-       /// This is the amount that would go to us if we close the channel, ignoring any on-chain fees.
-       /// See also [`Channel::get_inbound_outbound_available_balance_msat`]
-       pub fn get_balance_msat(&self) -> u64 {
-               // Include our local balance, plus any inbound HTLCs we know the preimage for, minus any
-               // HTLCs sent or which will be sent after commitment signed's are exchanged.
                let mut balance_msat = self.value_to_self_msat;
                for ref htlc in self.pending_inbound_htlcs.iter() {
                        if let InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_)) = htlc.state {
                                balance_msat += htlc.amount_msat;
                        }
                }
-               balance_msat - self.get_outbound_pending_htlc_stats(None).pending_htlcs_value_msat
+               balance_msat -= outbound_stats.pending_htlcs_value_msat;
+
+               let outbound_capacity_msat = cmp::max(self.value_to_self_msat as i64
+                               - outbound_stats.pending_htlcs_value_msat as i64
+                               - self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000,
+                       0) as u64;
+               AvailableBalances {
+                       inbound_capacity_msat: cmp::max(self.channel_value_satoshis as i64 * 1000
+                                       - self.value_to_self_msat as i64
+                                       - self.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
+                                       - self.holder_selected_channel_reserve_satoshis as i64 * 1000,
+                               0) as u64,
+                       outbound_capacity_msat,
+                       next_outbound_htlc_limit_msat: cmp::max(cmp::min(outbound_capacity_msat as i64,
+                                       self.counterparty_max_htlc_value_in_flight_msat as i64
+                                               - outbound_stats.pending_htlcs_value_msat as i64),
+                               0) as u64,
+                       balance_msat,
+               }
        }
 
        pub fn get_holder_counterparty_selected_channel_reserve_satoshis(&self) -> (u64, Option<u64>) {
@@ -4110,7 +4151,7 @@ impl<Signer: Sign> Channel<Signer> {
                if !self.pending_inbound_htlcs.is_empty() || !self.pending_outbound_htlcs.is_empty() {
                        return Err(ChannelError::Close("Remote end sent us a closing_signed while there were still pending HTLCs".to_owned()));
                }
-               if msg.fee_satoshis > 21_000_000 * 1_0000_0000 { //this is required to stop potential overflow in build_closing_transaction
+               if msg.fee_satoshis > TOTAL_BITCOIN_SUPPLY_SATOSHIS { // this is required to stop potential overflow in build_closing_transaction
                        return Err(ChannelError::Close("Remote tried to send us a closing tx with > 21 million BTC fee".to_owned()));
                }
 
@@ -4347,6 +4388,11 @@ impl<Signer: Sign> Channel<Signer> {
                self.counterparty_htlc_minimum_msat
        }
 
+       /// Allowed in any state (including after shutdown), but will return none before TheirInitSent
+       pub fn get_counterparty_htlc_maximum_msat(&self) -> Option<u64> {
+               self.get_htlc_maximum_msat(self.counterparty_max_htlc_value_in_flight_msat)
+       }
+
        fn get_htlc_maximum_msat(&self, party_max_htlc_value_in_flight_msat: u64) -> Option<u64> {
                self.counterparty_selected_channel_reserve_satoshis.map(|counterparty_reserve| {
                        let holder_reserve = self.holder_selected_channel_reserve_satoshis;
@@ -5850,13 +5896,18 @@ impl<Signer: Sign> Writeable for Channel<Signer> {
                let chan_type = if self.channel_type != ChannelTypeFeatures::only_static_remote_key() {
                        Some(&self.channel_type) } else { None };
 
-               // The same logic applies for `holder_selected_channel_reserve_satoshis` and
-               // `holder_max_htlc_value_in_flight_msat` values other than the defaults.
+               // The same logic applies for `holder_selected_channel_reserve_satoshis` values other than
+               // the default, and when `holder_max_htlc_value_in_flight_msat` is configured to be set to
+               // a different percentage of the channel value then 10%, which older versions of LDK used
+               // to set it to before the percentage was made configurable.
                let serialized_holder_selected_reserve =
                        if self.holder_selected_channel_reserve_satoshis != Self::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis)
                        { Some(self.holder_selected_channel_reserve_satoshis) } else { None };
+
+               let mut old_max_in_flight_percent_config = UserConfig::default().own_channel_config;
+               old_max_in_flight_percent_config.max_inbound_htlc_value_in_flight_percent_of_channel = MAX_IN_FLIGHT_PERCENT_LEGACY;
                let serialized_holder_htlc_max_in_flight =
-                       if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis)
+                       if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis, &old_max_in_flight_percent_config)
                        { Some(self.holder_max_htlc_value_in_flight_msat) } else { None };
 
                write_tlv_fields!(writer, {
@@ -6126,7 +6177,7 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel<Signer>
                let mut target_closing_feerate_sats_per_kw = None;
                let mut monitor_pending_finalized_fulfills = Some(Vec::new());
                let mut holder_selected_channel_reserve_satoshis = Some(Self::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis));
-               let mut holder_max_htlc_value_in_flight_msat = Some(Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis));
+               let mut holder_max_htlc_value_in_flight_msat = Some(Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis, &UserConfig::default().own_channel_config));
                // Prior to supporting channel type negotiation, all of our channels were static_remotekey
                // only, so we default to that if none was written.
                let mut channel_type = Some(ChannelTypeFeatures::only_static_remote_key());
@@ -6312,7 +6363,7 @@ mod tests {
        use ln::PaymentHash;
        use ln::channelmanager::{HTLCSource, PaymentId};
        use ln::channel::{Channel, InboundHTLCOutput, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator};
-       use ln::channel::MAX_FUNDING_SATOSHIS;
+       use ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS};
        use ln::features::InitFeatures;
        use ln::msgs::{ChannelUpdate, DataLossProtect, DecodeError, OptionalField, UnsignedChannelUpdate};
        use ln::script::ShutdownScript;
@@ -6348,9 +6399,10 @@ mod tests {
        }
 
        #[test]
-       fn test_max_funding_satoshis() {
-               assert!(MAX_FUNDING_SATOSHIS <= 21_000_000 * 100_000_000,
-                       "MAX_FUNDING_SATOSHIS is greater than all satoshis in existence");
+       fn test_max_funding_satoshis_no_wumbo() {
+               assert_eq!(TOTAL_BITCOIN_SUPPLY_SATOSHIS, 21_000_000 * 100_000_000);
+               assert!(MAX_FUNDING_SATOSHIS_NO_WUMBO <= TOTAL_BITCOIN_SUPPLY_SATOSHIS,
+                       "MAX_FUNDING_SATOSHIS_NO_WUMBO is greater than all satoshis in existence");
        }
 
        #[test]