diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 9361cd3c749..ab3627225d3 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -982,8 +982,11 @@ pub const TOTAL_BITCOIN_SUPPLY_SATOSHIS: u64 = 21_000_000 * 1_0000_0000; /// implementations use this value for their dust limit today. pub const MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS: u64 = 546; +/// The maximum channel dust limit we will accept from our counterparty for non-anchor channels. +pub const MAX_LEGACY_CHAN_DUST_LIMIT_SATOSHIS: u64 = MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS; + /// The maximum channel dust limit we will accept from our counterparty. -pub const MAX_CHAN_DUST_LIMIT_SATOSHIS: u64 = MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS; +pub const MAX_CHAN_DUST_LIMIT_SATOSHIS: u64 = 10_000; /// The dust limit is used for both the commitment transaction outputs as well as the closing /// transactions. For cooperative closing transactions, we require segwit outputs, though accept @@ -3644,8 +3647,14 @@ impl ChannelContext { if open_channel_fields.dust_limit_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS { return Err(ChannelError::close(format!("dust_limit_satoshis ({}) is less than the implementation limit ({})", open_channel_fields.dust_limit_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS))); } - if open_channel_fields.dust_limit_satoshis > MAX_CHAN_DUST_LIMIT_SATOSHIS { - return Err(ChannelError::close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", open_channel_fields.dust_limit_satoshis, MAX_CHAN_DUST_LIMIT_SATOSHIS))); + + let max_chan_dust_limit_satoshis = if channel_type.supports_anchors_zero_fee_htlc_tx() || channel_type.supports_anchor_zero_fee_commitments() { + MAX_CHAN_DUST_LIMIT_SATOSHIS + } else { + MAX_LEGACY_CHAN_DUST_LIMIT_SATOSHIS + }; + if open_channel_fields.dust_limit_satoshis > max_chan_dust_limit_satoshis { + return Err(ChannelError::close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", open_channel_fields.dust_limit_satoshis, max_chan_dust_limit_satoshis))); } // Convert things into internal flags and prep our state: @@ -4426,8 +4435,14 @@ impl ChannelContext { if common_fields.dust_limit_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS { return Err(ChannelError::close(format!("dust_limit_satoshis ({}) is less than the implementation limit ({})", common_fields.dust_limit_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS))); } - if common_fields.dust_limit_satoshis > MAX_CHAN_DUST_LIMIT_SATOSHIS { - return Err(ChannelError::close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", common_fields.dust_limit_satoshis, MAX_CHAN_DUST_LIMIT_SATOSHIS))); + + let max_chan_dust_limit_satoshis = if channel_type.supports_anchors_zero_fee_htlc_tx() || channel_type.supports_anchor_zero_fee_commitments() { + MAX_CHAN_DUST_LIMIT_SATOSHIS + } else { + MAX_LEGACY_CHAN_DUST_LIMIT_SATOSHIS + }; + if common_fields.dust_limit_satoshis > max_chan_dust_limit_satoshis { + return Err(ChannelError::close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", common_fields.dust_limit_satoshis, max_chan_dust_limit_satoshis))); } if common_fields.minimum_depth > peer_limits.max_minimum_depth { return Err(ChannelError::close(format!("We consider the minimum depth to be unreasonably large. Expected minimum: ({}). Actual: ({})", peer_limits.max_minimum_depth, common_fields.minimum_depth))); @@ -6280,15 +6295,18 @@ fn get_holder_max_htlc_value_in_flight_msat( /// Guaranteed to return a value no larger than channel_value_satoshis /// /// This is used both for outbound and inbound channels and has lower bound -/// of `MIN_THEIR_CHAN_RESERVE_SATOSHIS`. +/// of `MIN_THEIR_CHAN_RESERVE_SATOSHIS`, and the `dust_limit_satoshis` of +/// the counterparty. pub(crate) fn get_holder_selected_channel_reserve_satoshis( - channel_value_satoshis: u64, config: &UserConfig, + channel_value_satoshis: u64, their_dust_limit_satoshis: u64, config: &UserConfig, ) -> u64 { let counterparty_chan_reserve_prop_mil = config.channel_handshake_config.their_channel_reserve_proportional_millionths as u64; let calculated_reserve = channel_value_satoshis.saturating_mul(counterparty_chan_reserve_prop_mil) / 1_000_000; - cmp::min(channel_value_satoshis, cmp::max(calculated_reserve, MIN_THEIR_CHAN_RESERVE_SATOSHIS)) + let channel_reserve_satoshis = cmp::max(calculated_reserve, MIN_THEIR_CHAN_RESERVE_SATOSHIS); + let channel_reserve_satoshis = cmp::max(channel_reserve_satoshis, their_dust_limit_satoshis); + cmp::min(channel_value_satoshis, channel_reserve_satoshis) } /// This is for legacy reasons, present for forward-compatibility. @@ -13267,7 +13285,15 @@ impl OutboundV1Channel { channel_value_satoshis: u64, push_msat: u64, user_id: u128, config: &UserConfig, current_chain_height: u32, outbound_scid_alias: u64, temporary_channel_id: Option, logger: L ) -> Result, APIError> { - let holder_selected_channel_reserve_satoshis = get_holder_selected_channel_reserve_satoshis(channel_value_satoshis, config); + // At this point, we do not know what `dust_limit_satoshis` the counterparty will want for themselves, + // so we set the channel reserve with no regard for their dust limit, and fail the channel if they want + // a dust limit higher than our selected reserve. + let their_dust_limit_satoshis = 0; + let holder_selected_channel_reserve_satoshis = get_holder_selected_channel_reserve_satoshis( + channel_value_satoshis, + their_dust_limit_satoshis, + config + ); if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS { // Protocol level safety check in place, although it should never happen because // of `MIN_THEIR_CHAN_RESERVE_SATOSHIS` @@ -13649,7 +13675,11 @@ impl InboundV1Channel { // support this channel type. let channel_type = channel_type_from_open_channel(&msg.common_fields, our_supported_features)?; - let holder_selected_channel_reserve_satoshis = get_holder_selected_channel_reserve_satoshis(msg.common_fields.funding_satoshis, config); + let holder_selected_channel_reserve_satoshis = get_holder_selected_channel_reserve_satoshis( + msg.common_fields.funding_satoshis, + msg.common_fields.dust_limit_satoshis, + config + ); let counterparty_pubkeys = ChannelPublicKeys { funding_pubkey: msg.common_fields.funding_pubkey, revocation_basepoint: RevocationBasepoint::from(msg.common_fields.revocation_basepoint), diff --git a/lightning/src/ln/channel_open_tests.rs b/lightning/src/ln/channel_open_tests.rs index 08cabc053c5..e13343ade76 100644 --- a/lightning/src/ln/channel_open_tests.rs +++ b/lightning/src/ln/channel_open_tests.rs @@ -470,7 +470,7 @@ pub fn test_insane_channel_opens() { // funding satoshis let channel_value_sat = 31337; // same as funding satoshis let channel_reserve_satoshis = - get_holder_selected_channel_reserve_satoshis(channel_value_sat, &legacy_cfg); + get_holder_selected_channel_reserve_satoshis(channel_value_sat, 0, &legacy_cfg); let push_msat = (channel_value_sat - channel_reserve_satoshis) * 1000; // Have node0 initiate a channel to node1 with aforementioned parameters @@ -880,8 +880,7 @@ pub fn bolt2_open_channel_sane_dust_limit() { nodes[0].node.create_channel(node_b_id, value_sats, push_msat, 42, None, None).unwrap(); let mut node0_to_1_send_open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, node_b_id); - node0_to_1_send_open_channel.common_fields.dust_limit_satoshis = 547; - node0_to_1_send_open_channel.channel_reserve_satoshis = 100001; + node0_to_1_send_open_channel.common_fields.dust_limit_satoshis = 10_001; nodes[1].node.handle_open_channel(node_a_id, &node0_to_1_send_open_channel); let events = nodes[1].node.get_and_clear_pending_events(); @@ -893,7 +892,7 @@ pub fn bolt2_open_channel_sane_dust_limit() { { Err(APIError::ChannelUnavailable { err }) => assert_eq!( err, - "dust_limit_satoshis (547) is greater than the implementation limit (546)" + "dust_limit_satoshis (10001) is greater than the implementation limit (10000)" ), _ => panic!(), }, diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 17fbc1fce28..c98cfa53b86 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -413,7 +413,7 @@ pub fn test_inbound_outbound_capacity_is_not_zero() { assert_eq!(channels0.len(), 1); assert_eq!(channels1.len(), 1); - let reserve = get_holder_selected_channel_reserve_satoshis(100_000, &default_config); + let reserve = get_holder_selected_channel_reserve_satoshis(100_000, 0, &default_config); assert_eq!(channels0[0].inbound_capacity_msat, 95000000 - reserve * 1000); assert_eq!(channels1[0].outbound_capacity_msat, 95000000 - reserve * 1000); diff --git a/lightning/src/ln/htlc_reserve_unit_tests.rs b/lightning/src/ln/htlc_reserve_unit_tests.rs index d88b9a2dc3f..2dfd191d9ea 100644 --- a/lightning/src/ln/htlc_reserve_unit_tests.rs +++ b/lightning/src/ln/htlc_reserve_unit_tests.rs @@ -50,7 +50,7 @@ fn do_test_counterparty_no_reserve(send_from_initiator: bool) { push_amt -= feerate_per_kw as u64 * (commitment_tx_base_weight(&channel_type_features) + 4 * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000 * 1000; - push_amt -= get_holder_selected_channel_reserve_satoshis(100_000, &default_config) * 1000; + push_amt -= get_holder_selected_channel_reserve_satoshis(100_000, 0, &default_config) * 1000; let push = if send_from_initiator { 0 } else { push_amt }; let temp_channel_id = @@ -1008,7 +1008,7 @@ pub fn test_chan_reserve_violation_outbound_htlc_inbound_chan() { &channel_type_features, ); - push_amt -= get_holder_selected_channel_reserve_satoshis(100_000, &default_config) * 1000; + push_amt -= get_holder_selected_channel_reserve_satoshis(100_000, 0, &default_config) * 1000; let _ = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, push_amt); @@ -1052,7 +1052,7 @@ pub fn test_chan_reserve_violation_inbound_htlc_outbound_channel() { MIN_AFFORDABLE_HTLC_COUNT as u64, &channel_type_features, ); - push_amt -= get_holder_selected_channel_reserve_satoshis(100_000, &default_config) * 1000; + push_amt -= get_holder_selected_channel_reserve_satoshis(100_000, 0, &default_config) * 1000; let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, push_amt); // Send four HTLCs to cover the initial push_msat buffer we're required to include @@ -1130,7 +1130,7 @@ pub fn test_chan_reserve_dust_inbound_htlcs_outbound_chan() { MIN_AFFORDABLE_HTLC_COUNT as u64, &channel_type_features, ); - push_amt -= get_holder_selected_channel_reserve_satoshis(100_000, &default_config) * 1000; + push_amt -= get_holder_selected_channel_reserve_satoshis(100_000, 0, &default_config) * 1000; create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, push_amt); let (htlc_success_tx_fee_sat, _) = diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index b5cbe0fee98..7d198d2d70d 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -4985,7 +4985,7 @@ fn test_htlc_forward_considers_anchor_outputs_value() { create_announced_chan_between_nodes_with_value(&nodes, 1, 2, CHAN_AMT, PUSH_MSAT); let channel_reserve_msat = - get_holder_selected_channel_reserve_satoshis(CHAN_AMT, &config) * 1000; + get_holder_selected_channel_reserve_satoshis(CHAN_AMT, 0, &config) * 1000; let commitment_fee_msat = chan_utils::commit_tx_fee_sat( *nodes[1].fee_estimator.sat_per_kw.lock().unwrap(), 2, diff --git a/lightning/src/ln/update_fee_tests.rs b/lightning/src/ln/update_fee_tests.rs index ac566393bdb..5a50120d764 100644 --- a/lightning/src/ln/update_fee_tests.rs +++ b/lightning/src/ln/update_fee_tests.rs @@ -408,7 +408,8 @@ pub fn do_test_update_fee_that_funder_cannot_afford(channel_type_features: Chann ); let channel_id = chan.2; let secp_ctx = Secp256k1::new(); - let bs_channel_reserve_sats = get_holder_selected_channel_reserve_satoshis(channel_value, &cfg); + let bs_channel_reserve_sats = + get_holder_selected_channel_reserve_satoshis(channel_value, 0, &cfg); let (anchor_outputs_value_sats, outputs_num_no_htlcs) = if channel_type_features.supports_anchors_zero_fee_htlc_tx() { (ANCHOR_OUTPUT_VALUE_SATOSHI * 2, 4) @@ -892,7 +893,7 @@ pub fn test_chan_init_feerate_unaffordability() { // During open, we don't have a "counterparty channel reserve" to check against, so that // requirement only comes into play on the open_channel handling side. - push_amt -= get_holder_selected_channel_reserve_satoshis(100_000, &default_config) * 1000; + push_amt -= get_holder_selected_channel_reserve_satoshis(100_000, 0, &default_config) * 1000; nodes[0].node.create_channel(node_b_id, 100_000, push_amt, 42, None, None).unwrap(); let mut open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, node_b_id);