Skip to content

Commit 0cd16eb

Browse files
committed
Do not dip into the funder's reserve to cover the two anchors
At all times, the funder's balance should cover the commitment transaction fee, any non-zero-value anchors, and the fundee-selected channel reserve. Prior to this commit, we would allow the funder to dip into its reserve to pay for the two 330sat anchors. LDK sets reserves to at least 1000sat, so two 330 sat anchors would never overdraw this reserve. We now prevent any such dips, and ensure that the funder can pay for the complete sum of the transaction fee, the anchors, and the reserve.
1 parent 1156efb commit 0cd16eb

File tree

2 files changed

+43
-20
lines changed

2 files changed

+43
-20
lines changed

lightning/src/ln/channel.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3808,7 +3808,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38083808
if update_fee {
38093809
debug_assert!(!funding.is_outbound());
38103810
let counterparty_reserve_we_require_msat = funding.holder_selected_channel_reserve_satoshis * 1000;
3811-
if commitment_data.stats.remote_balance_before_fee_anchors_msat < commitment_data.stats.total_fee_sat * 1000 + counterparty_reserve_we_require_msat {
3811+
if commitment_data.stats.remote_balance_before_fee_anchors_msat < commitment_data.stats.total_fee_sat * 1000 + commitment_data.stats.total_anchors_sat * 1000 + counterparty_reserve_we_require_msat {
38123812
return Err(ChannelError::close("Funding remote cannot afford proposed new fee".to_owned()));
38133813
}
38143814
}
@@ -6724,7 +6724,7 @@ impl<SP: Deref> FundedChannel<SP> where
67246724
);
67256725
let buffer_fee_msat = commit_tx_fee_sat(feerate_per_kw, commitment_data.tx.nondust_htlcs().len() + htlc_stats.on_holder_tx_outbound_holding_cell_htlcs_count as usize + CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize, self.funding.get_channel_type()) * 1000;
67266726
let holder_balance_msat = commitment_data.stats.local_balance_before_fee_anchors_msat - htlc_stats.outbound_holding_cell_msat;
6727-
if holder_balance_msat < buffer_fee_msat + self.funding.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 {
6727+
if holder_balance_msat < buffer_fee_msat + commitment_data.stats.total_anchors_sat * 1000 + self.funding.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 {
67286728
//TODO: auto-close after a number of failures?
67296729
log_debug!(logger, "Cannot afford to send new feerate at {}", feerate_per_kw);
67306730
return None;

lightning/src/ln/update_fee_tests.rs

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use crate::ln::chan_utils::{
66
COMMITMENT_TX_WEIGHT_PER_HTLC,
77
};
88
use crate::ln::channel::{
9-
get_holder_selected_channel_reserve_satoshis, CONCURRENT_INBOUND_HTLC_FEE_BUFFER,
10-
MIN_AFFORDABLE_HTLC_COUNT,
9+
get_holder_selected_channel_reserve_satoshis, ANCHOR_OUTPUT_VALUE_SATOSHI,
10+
CONCURRENT_INBOUND_HTLC_FEE_BUFFER, MIN_AFFORDABLE_HTLC_COUNT,
1111
};
1212
use crate::ln::channelmanager::PaymentId;
1313
use crate::ln::functional_test_utils::*;
@@ -388,11 +388,22 @@ pub fn test_update_fee_vanilla() {
388388
check_added_monitors(&nodes[1], 1);
389389
}
390390

391-
#[xtest(feature = "_externalize_tests")]
392-
pub fn test_update_fee_that_funder_cannot_afford() {
391+
pub fn do_test_update_fee_that_funder_cannot_afford(channel_type_features: ChannelTypeFeatures) {
393392
let chanmon_cfgs = create_chanmon_cfgs(2);
394393
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
395-
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
394+
395+
let mut default_config = test_default_channel_config();
396+
if channel_type_features == ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies() {
397+
default_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
398+
// this setting is also needed to create an anchor channel
399+
default_config.manually_accept_inbound_channels = true;
400+
}
401+
402+
let node_chanmgrs = create_node_chanmgrs(
403+
2,
404+
&node_cfgs,
405+
&[Some(default_config.clone()), Some(default_config.clone())],
406+
);
396407
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
397408

398409
let node_a_id = nodes[0].node.get_our_node_id();
@@ -409,22 +420,26 @@ pub fn test_update_fee_that_funder_cannot_afford() {
409420
);
410421
let channel_id = chan.2;
411422
let secp_ctx = Secp256k1::new();
412-
let default_config = UserConfig::default();
413423
let bs_channel_reserve_sats =
414424
get_holder_selected_channel_reserve_satoshis(channel_value, &default_config);
415-
416-
let channel_type_features = ChannelTypeFeatures::only_static_remote_key();
425+
let (anchor_outputs_value_sats, outputs_num_no_htlcs) =
426+
if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
427+
(ANCHOR_OUTPUT_VALUE_SATOSHI * 2, 4)
428+
} else {
429+
(0, 2)
430+
};
417431

418432
// Calculate the maximum feerate that A can afford. Note that we don't send an update_fee
419433
// CONCURRENT_INBOUND_HTLC_FEE_BUFFER HTLCs before actually running out of local balance, so we
420434
// calculate two different feerates here - the expected local limit as well as the expected
421435
// remote limit.
422-
let feerate = ((channel_value - bs_channel_reserve_sats - push_sats) * 1000
423-
/ (commitment_tx_base_weight(&channel_type_features)
424-
+ CONCURRENT_INBOUND_HTLC_FEE_BUFFER as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC))
425-
as u32;
426-
let non_buffer_feerate = ((channel_value - bs_channel_reserve_sats - push_sats) * 1000
427-
/ commitment_tx_base_weight(&channel_type_features)) as u32;
436+
let feerate =
437+
((channel_value - bs_channel_reserve_sats - push_sats - anchor_outputs_value_sats) * 1000
438+
/ (commitment_tx_base_weight(&channel_type_features)
439+
+ CONCURRENT_INBOUND_HTLC_FEE_BUFFER as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC)) as u32;
440+
let non_buffer_feerate =
441+
((channel_value - bs_channel_reserve_sats - push_sats - anchor_outputs_value_sats) * 1000
442+
/ commitment_tx_base_weight(&channel_type_features)) as u32;
428443
{
429444
let mut feerate_lock = chanmon_cfgs[0].fee_estimator.sat_per_kw.lock().unwrap();
430445
*feerate_lock = feerate;
@@ -441,8 +456,8 @@ pub fn test_update_fee_that_funder_cannot_afford() {
441456
{
442457
let commitment_tx = get_local_commitment_txn!(nodes[1], channel_id)[0].clone();
443458

444-
//We made sure neither party's funds are below the dust limit and there are no HTLCs here
445-
assert_eq!(commitment_tx.output.len(), 2);
459+
// We made sure neither party's funds are below the dust limit and there are no HTLCs here
460+
assert_eq!(commitment_tx.output.len(), outputs_num_no_htlcs);
446461
let total_fee: u64 = commit_tx_fee_msat(feerate, 0, &channel_type_features) / 1000;
447462
let mut actual_fee =
448463
commitment_tx.output.iter().fold(0, |acc, output| acc + output.value.to_sat());
@@ -486,8 +501,8 @@ pub fn test_update_fee_that_funder_cannot_afford() {
486501
&remote_point,
487502
push_sats,
488503
channel_value
489-
- push_sats - commit_tx_fee_msat(non_buffer_feerate + 4, 0, &channel_type_features)
490-
/ 1000,
504+
- push_sats - anchor_outputs_value_sats
505+
- commit_tx_fee_msat(non_buffer_feerate + 4, 0, &channel_type_features) / 1000,
491506
non_buffer_feerate + 4,
492507
nondust_htlcs,
493508
&local_chan.funding().channel_transaction_parameters.as_counterparty_broadcastable(),
@@ -526,6 +541,14 @@ pub fn test_update_fee_that_funder_cannot_afford() {
526541
check_closed_event!(nodes[1], 1, reason, [node_a_id], channel_value);
527542
}
528543

544+
#[xtest(feature = "_externalize_tests")]
545+
pub fn test_update_fee_that_funder_cannot_afford() {
546+
do_test_update_fee_that_funder_cannot_afford(ChannelTypeFeatures::only_static_remote_key());
547+
do_test_update_fee_that_funder_cannot_afford(
548+
ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(),
549+
);
550+
}
551+
529552
#[xtest(feature = "_externalize_tests")]
530553
pub fn test_update_fee_that_saturates_subs() {
531554
// Check that when a remote party sends us an `update_fee` message that results in a total fee

0 commit comments

Comments
 (0)