Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 40 additions & 10 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -3644,8 +3647,14 @@ impl<SP: SignerProvider> ChannelContext<SP> {
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:
Expand Down Expand Up @@ -4426,8 +4435,14 @@ impl<SP: SignerProvider> ChannelContext<SP> {
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)));
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -13267,7 +13285,15 @@ impl<SP: SignerProvider> OutboundV1Channel<SP> {
channel_value_satoshis: u64, push_msat: u64, user_id: u128, config: &UserConfig, current_chain_height: u32,
outbound_scid_alias: u64, temporary_channel_id: Option<ChannelId>, logger: L
) -> Result<OutboundV1Channel<SP>, 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`
Expand Down Expand Up @@ -13649,7 +13675,11 @@ impl<SP: SignerProvider> InboundV1Channel<SP> {
// 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),
Expand Down
7 changes: 3 additions & 4 deletions lightning/src/ln/channel_open_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand All @@ -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!(),
},
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
8 changes: 4 additions & 4 deletions lightning/src/ln/htlc_reserve_unit_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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, _) =
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/payment_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 3 additions & 2 deletions lightning/src/ln/update_fee_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
Loading