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;
pub const ANCHOR_OUTPUT_VALUE_SATOSHI: u64 = 330;
+/// 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.
}
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,
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,
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,
// channel might have been used to route very small values (either by honest users or as DoS).
self.channel_value_satoshis * 1000 * 9 / 10,
- self.holder_max_htlc_value_in_flight_msat
+ self.counterparty_max_htlc_value_in_flight_msat
);
}
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, {
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());
}
}
+ #[test]
+ fn test_configured_holder_max_htlc_value_in_flight() {
+ let feeest = TestFeeEstimator{fee_est: 15000};
+ let logger = test_utils::TestLogger::new();
+ let secp_ctx = Secp256k1::new();
+ let seed = [42; 32];
+ let network = Network::Testnet;
+ let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
+ let outbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+ let inbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
+
+ let mut config_2_percent = UserConfig::default();
+ config_2_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 2;
+ let mut config_99_percent = UserConfig::default();
+ config_99_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 99;
+ let mut config_0_percent = UserConfig::default();
+ config_0_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 0;
+ let mut config_101_percent = UserConfig::default();
+ config_101_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 101;
+
+ // Test that `new_outbound` creates a channel with the correct value for
+ // `holder_max_htlc_value_in_flight_msat`, when configured with a valid percentage value,
+ // which is set to the lower bound + 1 (2%) of the `channel_value`.
+ let chan_1 = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, outbound_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config_2_percent, 0, 42).unwrap();
+ let chan_1_value_msat = chan_1.channel_value_satoshis * 1000;
+ assert_eq!(chan_1.holder_max_htlc_value_in_flight_msat, (chan_1_value_msat as f64 * 0.02) as u64);
+
+ // Test with the upper bound - 1 of valid values (99%).
+ let chan_2 = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, outbound_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config_99_percent, 0, 42).unwrap();
+ let chan_2_value_msat = chan_2.channel_value_satoshis * 1000;
+ assert_eq!(chan_2.holder_max_htlc_value_in_flight_msat, (chan_2_value_msat as f64 * 0.99) as u64);
+
+ let chan_1_open_channel_msg = chan_1.get_open_channel(genesis_block(network).header.block_hash());
+
+ // Test that `new_from_req` creates a channel with the correct value for
+ // `holder_max_htlc_value_in_flight_msat`, when configured with a valid percentage value,
+ // which is set to the lower bound - 1 (2%) of the `channel_value`.
+ let chan_3 = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_1_open_channel_msg, 7, &config_2_percent, 0, &&logger, 42).unwrap();
+ let chan_3_value_msat = chan_3.channel_value_satoshis * 1000;
+ assert_eq!(chan_3.holder_max_htlc_value_in_flight_msat, (chan_3_value_msat as f64 * 0.02) as u64);
+
+ // Test with the upper bound - 1 of valid values (99%).
+ let chan_4 = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_1_open_channel_msg, 7, &config_99_percent, 0, &&logger, 42).unwrap();
+ let chan_4_value_msat = chan_4.channel_value_satoshis * 1000;
+ assert_eq!(chan_4.holder_max_htlc_value_in_flight_msat, (chan_4_value_msat as f64 * 0.99) as u64);
+
+ // Test that `new_outbound` uses the lower bound of the configurable percentage values (1%)
+ // if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a value less than 1.
+ let chan_5 = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, outbound_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config_0_percent, 0, 42).unwrap();
+ let chan_5_value_msat = chan_5.channel_value_satoshis * 1000;
+ assert_eq!(chan_5.holder_max_htlc_value_in_flight_msat, (chan_5_value_msat as f64 * 0.01) as u64);
+
+ // Test that `new_outbound` uses the upper bound of the configurable percentage values
+ // (100%) if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a larger value
+ // than 100.
+ let chan_6 = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, outbound_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config_101_percent, 0, 42).unwrap();
+ let chan_6_value_msat = chan_6.channel_value_satoshis * 1000;
+ assert_eq!(chan_6.holder_max_htlc_value_in_flight_msat, chan_6_value_msat);
+
+ // Test that `new_from_req` uses the lower bound of the configurable percentage values (1%)
+ // if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a value less than 1.
+ let chan_7 = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_1_open_channel_msg, 7, &config_0_percent, 0, &&logger, 42).unwrap();
+ let chan_7_value_msat = chan_7.channel_value_satoshis * 1000;
+ assert_eq!(chan_7.holder_max_htlc_value_in_flight_msat, (chan_7_value_msat as f64 * 0.01) as u64);
+
+ // Test that `new_from_req` uses the upper bound of the configurable percentage values
+ // (100%) if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a larger value
+ // than 100.
+ let chan_8 = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_1_open_channel_msg, 7, &config_101_percent, 0, &&logger, 42).unwrap();
+ let chan_8_value_msat = chan_8.channel_value_satoshis * 1000;
+ assert_eq!(chan_8.holder_max_htlc_value_in_flight_msat, chan_8_value_msat);
+ }
+
#[test]
fn channel_update() {
let feeest = TestFeeEstimator{fee_est: 15000};
use routing::router::{PaymentParameters, Route, RouteHop, RouteParameters, find_route, get_route};
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
use ln::msgs;
-use ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
+use ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, OptionalField, ErrorAction};
use util::enforcing_trait_impls::EnforcingSigner;
use util::{byte_utils, test_utils};
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose, ClosureReason};
assert_eq!(res.htlc_minimum_msat, 1);
}
+#[test]
+fn test_channel_update_has_correct_htlc_maximum_msat() {
+ // Tests that the `ChannelUpdate` message has the correct values for `htlc_maximum_msat` set.
+ // Bolt 7 specifies that if present `htlc_maximum_msat`:
+ // 1. MUST be set to less than or equal to the channel capacity. In LDK, this is capped to
+ // 90% of the `channel_value`.
+ // 2. MUST be set to less than or equal to the `max_htlc_value_in_flight_msat` received from the peer.
+
+ let mut config_30_percent = UserConfig::default();
+ config_30_percent.channel_options.announced_channel = true;
+ config_30_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 30;
+ let mut config_50_percent = UserConfig::default();
+ config_50_percent.channel_options.announced_channel = true;
+ config_50_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 50;
+ let mut config_95_percent = UserConfig::default();
+ config_95_percent.channel_options.announced_channel = true;
+ config_95_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 95;
+ let mut config_100_percent = UserConfig::default();
+ config_100_percent.channel_options.announced_channel = true;
+ config_100_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100;
+
+ let chanmon_cfgs = create_chanmon_cfgs(4);
+ let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[Some(config_30_percent), Some(config_50_percent), Some(config_95_percent), Some(config_100_percent)]);
+ let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
+
+ let channel_value_satoshis = 100000;
+ let channel_value_msat = channel_value_satoshis * 1000;
+ let channel_value_30_percent_msat = (channel_value_msat as f64 * 0.3) as u64;
+ let channel_value_50_percent_msat = (channel_value_msat as f64 * 0.5) as u64;
+ let channel_value_90_percent_msat = (channel_value_msat as f64 * 0.9) as u64;
+
+ let (node_0_chan_update, node_1_chan_update, _, _) = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, channel_value_satoshis, 10001, InitFeatures::known(), InitFeatures::known());
+ let (node_2_chan_update, node_3_chan_update, _, _) = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, channel_value_satoshis, 10001, InitFeatures::known(), InitFeatures::known());
+
+ // Assert that `node[0]`'s `ChannelUpdate` is capped at 50 percent of the `channel_value`, as
+ // that's the value of `node[1]`'s `holder_max_htlc_value_in_flight_msat`.
+ assert_eq!(node_0_chan_update.contents.htlc_maximum_msat, OptionalField::Present(channel_value_50_percent_msat));
+ // Assert that `node[1]`'s `ChannelUpdate` is capped at 30 percent of the `channel_value`, as
+ // that's the value of `node[0]`'s `holder_max_htlc_value_in_flight_msat`.
+ assert_eq!(node_1_chan_update.contents.htlc_maximum_msat, OptionalField::Present(channel_value_30_percent_msat));
+
+ // Assert that `node[2]`'s `ChannelUpdate` is capped at 90 percent of the `channel_value`, as
+ // the value of `node[3]`'s `holder_max_htlc_value_in_flight_msat` (100%), exceeds 90% of the
+ // `channel_value`.
+ assert_eq!(node_2_chan_update.contents.htlc_maximum_msat, OptionalField::Present(channel_value_90_percent_msat));
+ // Assert that `node[3]`'s `ChannelUpdate` is capped at 90 percent of the `channel_value`, as
+ // the value of `node[2]`'s `holder_max_htlc_value_in_flight_msat` (95%), exceeds 90% of the
+ // `channel_value`.
+ assert_eq!(node_3_chan_update.contents.htlc_maximum_msat, OptionalField::Present(channel_value_90_percent_msat));
+}
+
#[test]
fn test_manually_accept_inbound_channel_request() {
let mut manually_accept_conf = UserConfig::default();