Skip to content

Commit df624db

Browse files
committed
Don't trim HTLCs when calculating the reserved commit tx fee
We previously accounted for HTLC trims at the spiked feerate when calculating the reserved commitment transaction fees. This could cause an underestimate of the real current commitment fee at the current channel feerate. This is because a 2x increase in the feerate could trim enough HTLCs to result in a smaller commitment transaction fee. Also, the previous code only reserved the fee for an exact 2x increase in the feerate, instead of reserving the fee for any increase in the feerate between 1x to 2x. Fixes #4563.
1 parent 3fa2974 commit df624db

2 files changed

Lines changed: 167 additions & 8 deletions

File tree

lightning/src/ln/htlc_reserve_unit_tests.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::ln::channel::{
1111
FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT,
1212
MIN_CHAN_DUST_LIMIT_SATOSHIS,
1313
};
14+
use crate::ln::channel_state::ChannelDetails;
1415
use crate::ln::channelmanager::{PaymentId, RAACommitmentOrder, TrustedChannelFeatures};
1516
use crate::ln::functional_test_utils::*;
1617
use crate::ln::msgs::{self, BaseMessageHandler, ChannelMessageHandler, MessageSendEvent};
@@ -3406,3 +3407,145 @@ fn test_0reserve_zero_conf_combined() {
34063407
assert_eq!(node_1_max_htlc, node_0_max_htlc - node_1_reserve * 1000);
34073408
send_payment(&nodes[1], &[&nodes[0]], node_1_max_htlc);
34083409
}
3410+
3411+
#[xtest(feature = "_externalize_tests")]
3412+
fn test_outbound_vs_available_capacity_outbound_htlc_limit_spiked_feerate() {
3413+
let mut config = test_default_channel_config();
3414+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = false;
3415+
config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = false;
3416+
3417+
let chanmon_cfgs = create_chanmon_cfgs(2);
3418+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
3419+
config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage =
3420+
100;
3421+
3422+
let channel_type = ChannelTypeFeatures::only_static_remote_key();
3423+
3424+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]);
3425+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
3426+
3427+
let _node_a_id = nodes[0].node.get_our_node_id();
3428+
let _node_b_id = nodes[1].node.get_our_node_id();
3429+
3430+
const FEERATE: u32 = 253;
3431+
const MULTIPLE: u32 = FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32;
3432+
const SPIKED_FEERATE: u32 = FEERATE * MULTIPLE;
3433+
const DUST_LIMIT_MSAT: u64 = 354 * 1000;
3434+
const CHANNEL_VALUE_MSAT: u64 = 10_000 * 1000;
3435+
const NODE_0_VALUE_TO_SELF_MSAT: u64 = 5000 * 1000;
3436+
const NODE_1_VALUE_TO_SELF_MSAT: u64 = 5000 * 1000;
3437+
const CHANNEL_RESERVE_MSAT: u64 = 1000 * 1000;
3438+
3439+
// Find the HTLC amount that will be non-dust at the current feerate, but dust at the spiked feerate
3440+
const SPIKED_DUST_HTLC_MSAT: u64 = 688 * 1000;
3441+
const HTLC_SPIKE_DUST_LIMIT_MSAT: u64 = 689 * 1000;
3442+
let htlc_timeout_spike_tx_fee_msat =
3443+
second_stage_tx_fees_sat(&channel_type, SPIKED_FEERATE).1 * 1000;
3444+
assert_eq!(HTLC_SPIKE_DUST_LIMIT_MSAT, DUST_LIMIT_MSAT + htlc_timeout_spike_tx_fee_msat);
3445+
3446+
let channel_id =
3447+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, CHANNEL_VALUE_MSAT / 1000, 0)
3448+
.2;
3449+
assert_eq!(nodes[0].node.list_channels()[0].channel_type.as_ref().unwrap(), &channel_type);
3450+
{
3451+
// Quick double-check on the dust limit to make sure HTLCs would be dust at 2x the
3452+
// feerate...
3453+
let mut per_peer_lock;
3454+
let mut peer_state_lock;
3455+
3456+
let channel =
3457+
get_channel_ref!(nodes[0], nodes[1], per_peer_lock, peer_state_lock, channel_id);
3458+
assert_eq!(channel.context().holder_dust_limit_satoshis * 1000, DUST_LIMIT_MSAT);
3459+
}
3460+
3461+
// Balance the channel so each side has 5_000 sats
3462+
send_payment(&nodes[0], &[&nodes[1]], NODE_1_VALUE_TO_SELF_MSAT);
3463+
3464+
let count_total_htlcs = |details: &ChannelDetails| {
3465+
details.pending_outbound_htlcs.len() + details.pending_inbound_htlcs.len()
3466+
};
3467+
let count_node_0_nondust_htlcs = || {
3468+
let mut txs = get_local_commitment_txn!(nodes[0], channel_id);
3469+
let commitment_tx = &txs[0];
3470+
commitment_tx
3471+
.output
3472+
.iter()
3473+
.filter(|output| output.value.to_sat() * 1000 == SPIKED_DUST_HTLC_MSAT)
3474+
.count()
3475+
};
3476+
let count_node_1_nondust_htlcs = || {
3477+
let mut txs = get_local_commitment_txn!(nodes[1], channel_id);
3478+
let commitment_tx = &txs[0];
3479+
commitment_tx
3480+
.output
3481+
.iter()
3482+
.filter(|output| output.value.to_sat() * 1000 == SPIKED_DUST_HTLC_MSAT)
3483+
.count()
3484+
};
3485+
3486+
// Sanity check
3487+
{
3488+
let reserved_fee_sat = commit_tx_fee_sat(SPIKED_FEERATE, 2, &channel_type);
3489+
let node_0_outbound_capacity_msat = NODE_0_VALUE_TO_SELF_MSAT - CHANNEL_RESERVE_MSAT;
3490+
let node_0_available_capacity_msat =
3491+
node_0_outbound_capacity_msat - reserved_fee_sat * 1000;
3492+
let node_0_details = &nodes[0].node.list_channels()[0];
3493+
assert_eq!(node_0_details.outbound_capacity_msat, node_0_outbound_capacity_msat);
3494+
assert_eq!(node_0_details.next_outbound_htlc_limit_msat, node_0_available_capacity_msat);
3495+
assert_eq!(count_total_htlcs(&node_0_details), 0);
3496+
assert_eq!(count_node_0_nondust_htlcs(), 0);
3497+
}
3498+
3499+
// Route 2 688sat HTLCs from node 0 to node 1
3500+
for i in 1..3 {
3501+
route_payment(&nodes[0], &[&nodes[1]], SPIKED_DUST_HTLC_MSAT);
3502+
3503+
let max_reserved_fee_msat = commit_tx_fee_sat(SPIKED_FEERATE, 2 + i, &channel_type) * 1000;
3504+
let node_0_outbound_capacity_msat =
3505+
NODE_0_VALUE_TO_SELF_MSAT - SPIKED_DUST_HTLC_MSAT * i as u64 - CHANNEL_RESERVE_MSAT;
3506+
let node_0_available_capacity_msat = node_0_outbound_capacity_msat - max_reserved_fee_msat;
3507+
// Node 0 can send non-dust HTLCs throughout
3508+
assert!(node_0_available_capacity_msat >= HTLC_SPIKE_DUST_LIMIT_MSAT);
3509+
let node_0_details = &nodes[0].node.list_channels()[0];
3510+
assert_eq!(node_0_details.outbound_capacity_msat, node_0_outbound_capacity_msat);
3511+
assert_eq!(node_0_details.next_outbound_htlc_limit_msat, node_0_available_capacity_msat);
3512+
assert_eq!(count_total_htlcs(&node_0_details), i);
3513+
assert_eq!(count_node_0_nondust_htlcs(), i);
3514+
}
3515+
3516+
let node_0_details = &nodes[0].node.list_channels()[0];
3517+
let local_nondust_htlc_count = 2;
3518+
assert_eq!(count_total_htlcs(&node_0_details), local_nondust_htlc_count);
3519+
assert_eq!(count_node_0_nondust_htlcs(), local_nondust_htlc_count);
3520+
assert_eq!(count_node_1_nondust_htlcs(), local_nondust_htlc_count);
3521+
3522+
let node_0_outbound_capacity_msat = node_0_details.outbound_capacity_msat;
3523+
3524+
// Route 2 688sat HTLCs from node 1 to node 0
3525+
for i in 1..3 {
3526+
route_payment(&nodes[1], &[&nodes[0]], SPIKED_DUST_HTLC_MSAT);
3527+
3528+
let node_1_outbound_capacity_msat =
3529+
NODE_1_VALUE_TO_SELF_MSAT - SPIKED_DUST_HTLC_MSAT * i as u64 - CHANNEL_RESERVE_MSAT;
3530+
assert!(node_1_outbound_capacity_msat >= HTLC_SPIKE_DUST_LIMIT_MSAT);
3531+
let node_1_details = &nodes[1].node.list_channels()[0];
3532+
assert_eq!(node_1_details.outbound_capacity_msat, node_1_outbound_capacity_msat);
3533+
assert_eq!(node_1_details.next_outbound_htlc_limit_msat, node_1_outbound_capacity_msat);
3534+
3535+
let nondust_htlc_count = 2 + i;
3536+
// At the current feerate, 688sat HTLCs are present on both commitments
3537+
assert_eq!(count_node_0_nondust_htlcs(), nondust_htlc_count);
3538+
assert_eq!(count_node_1_nondust_htlcs(), nondust_htlc_count);
3539+
3540+
assert_eq!(
3541+
nodes[0].node.list_channels()[0].outbound_capacity_msat,
3542+
node_0_outbound_capacity_msat
3543+
);
3544+
let max_reserved_fee_msat =
3545+
commit_tx_fee_sat(SPIKED_FEERATE, nondust_htlc_count + 2, &channel_type) * 1000;
3546+
assert_eq!(
3547+
nodes[0].node.list_channels()[0].next_outbound_htlc_limit_msat,
3548+
node_0_outbound_capacity_msat - max_reserved_fee_msat
3549+
);
3550+
}
3551+
}

lightning/src/sign/tx_builder.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,17 @@ fn get_available_balances(
455455
);
456456

457457
let local_nondust_htlc_count = pending_htlcs
458+
.iter()
459+
.filter(|htlc| {
460+
!htlc.is_dust(
461+
true,
462+
feerate_per_kw,
463+
channel_constraints.holder_dust_limit_satoshis,
464+
channel_type,
465+
)
466+
})
467+
.count();
468+
let local_spiked_nondust_htlc_count = pending_htlcs
458469
.iter()
459470
.filter(|htlc| {
460471
!htlc.is_dust(
@@ -465,6 +476,10 @@ fn get_available_balances(
465476
)
466477
})
467478
.count();
479+
480+
// Note here we use the htlc count at the current feerate together with the spiked feerate;
481+
// this makes sure that the holder can afford any fee bump between 1x to 2x from the current
482+
// feerate.
468483
let local_max_commit_tx_fee_sat = commit_tx_fee_sat(
469484
spiked_feerate,
470485
local_nondust_htlc_count + fee_spike_buffer_htlc + 1,
@@ -528,7 +543,7 @@ fn get_available_balances(
528543
remote_balance_before_fee_msat,
529544
spiked_feerate,
530545
// The number of non-dust HTLCs on the local commitment at the spiked feerate
531-
local_nondust_htlc_count,
546+
local_spiked_nondust_htlc_count,
532547
// The post-splice minimum balance of the holder
533548
if is_outbound_from_holder { local_min_commit_tx_fee_sat } else { 0 },
534549
&channel_constraints,
@@ -661,7 +676,7 @@ fn get_available_balances(
661676
// Now adjust our min and max size HTLC to make sure both the local and the remote commitments still have
662677
// at least one output at the spiked feerate.
663678

664-
let remote_nondust_htlc_count = pending_htlcs
679+
let remote_spiked_nondust_htlc_count = pending_htlcs
665680
.iter()
666681
.filter(|htlc| {
667682
!htlc.is_dust(
@@ -679,8 +694,8 @@ fn get_available_balances(
679694
is_outbound_from_holder,
680695
local_balance_before_fee_msat,
681696
remote_balance_before_fee_msat,
682-
local_nondust_htlc_count,
683697
spiked_feerate,
698+
local_spiked_nondust_htlc_count,
684699
channel_constraints.holder_dust_limit_satoshis,
685700
channel_type,
686701
next_outbound_htlc_minimum_msat,
@@ -693,8 +708,8 @@ fn get_available_balances(
693708
is_outbound_from_holder,
694709
local_balance_before_fee_msat,
695710
remote_balance_before_fee_msat,
696-
remote_nondust_htlc_count,
697711
spiked_feerate,
712+
remote_spiked_nondust_htlc_count,
698713
channel_constraints.counterparty_dust_limit_satoshis,
699714
channel_type,
700715
next_outbound_htlc_minimum_msat,
@@ -715,9 +730,10 @@ fn get_available_balances(
715730

716731
fn adjust_boundaries_if_max_dust_htlc_produces_no_output(
717732
local: bool, is_outbound_from_holder: bool, holder_balance_before_fee_msat: u64,
718-
counterparty_balance_before_fee_msat: u64, nondust_htlc_count: usize, spiked_feerate: u32,
719-
dust_limit_satoshis: u64, channel_type: &ChannelTypeFeatures,
720-
next_outbound_htlc_minimum_msat: u64, available_capacity_msat: u64,
733+
counterparty_balance_before_fee_msat: u64, spiked_feerate: u32,
734+
spiked_feerate_nondust_htlc_count: usize, dust_limit_satoshis: u64,
735+
channel_type: &ChannelTypeFeatures, next_outbound_htlc_minimum_msat: u64,
736+
available_capacity_msat: u64,
721737
) -> (u64, u64) {
722738
// First, determine the biggest dust HTLC we could send
723739
let (htlc_success_tx_fee_sat, htlc_timeout_tx_fee_sat) =
@@ -733,7 +749,7 @@ fn adjust_boundaries_if_max_dust_htlc_produces_no_output(
733749
holder_balance_before_fee_msat.saturating_sub(max_dust_htlc_msat),
734750
counterparty_balance_before_fee_msat,
735751
spiked_feerate,
736-
nondust_htlc_count,
752+
spiked_feerate_nondust_htlc_count,
737753
dust_limit_satoshis,
738754
channel_type,
739755
) {

0 commit comments

Comments
 (0)