Skip to content

Commit 9fe1a36

Browse files
committed
Validate reserved fees on both commitments
The local and remote commitments may have different dust limits, which can cause each commitment to have a different transaction fee. Therefore when we reserve commitment transaction fees in `get_available_balances`, we must ensure that we read the maximum of the transaction fees on the local and the remote commitments. Otherwise, we may have not reserved enough fees to ensure that our next proposed channel state update is onside.
1 parent 69d2f08 commit 9fe1a36

2 files changed

Lines changed: 355 additions & 81 deletions

File tree

lightning/src/ln/htlc_reserve_unit_tests.rs

Lines changed: 267 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,10 +1053,10 @@ pub fn test_chan_reserve_dust_inbound_htlcs_outbound_chan() {
10531053
* 1000;
10541054
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, push_amt);
10551055

1056-
let (htlc_success_tx_fee_sat, _) =
1056+
let (_htlc_success_tx_fee_sat, htlc_timeout_tx_fee_sat) =
10571057
second_stage_tx_fees_sat(&channel_type_features, feerate_per_kw);
10581058
let dust_amt = crate::ln::channel::MIN_CHAN_DUST_LIMIT_SATOSHIS * 1000
1059-
+ htlc_success_tx_fee_sat * 1000
1059+
+ htlc_timeout_tx_fee_sat * 1000
10601060
- 1;
10611061
// In the previous code, routing this dust payment would cause nodes[0] to perceive a channel
10621062
// reserve violation even though it's a dust HTLC and therefore shouldn't count towards the
@@ -3528,3 +3528,268 @@ fn test_fail_cannot_afford_dust_htlcs_at_spike_multiple_if_nondust_at_base_feera
35283528
true,
35293529
);
35303530
}
3531+
3532+
#[xtest(feature = "_externalize_tests")]
3533+
fn test_available_balances_both_commitments_dust_on_funder_commitment() {
3534+
let mut config = test_default_channel_config();
3535+
3536+
let chanmon_cfgs = create_chanmon_cfgs(2);
3537+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
3538+
config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage =
3539+
100;
3540+
3541+
let channel_type = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
3542+
3543+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]);
3544+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
3545+
3546+
const FEERATE: u32 = 253;
3547+
const TOTAL_ANCHORS_MSAT: u64 = 2 * 330_000;
3548+
const NODE_0_DUST_LIMIT_MSAT: u64 = 10_000 * 1000;
3549+
const NODE_1_DUST_LIMIT_MSAT: u64 = 354 * 1000;
3550+
const CHANNEL_VALUE_MSAT: u64 = 50_000 * 1000;
3551+
const NODE_0_VALUE_TO_SELF_MSAT: u64 = 25_000 * 1000;
3552+
const NODE_1_VALUE_TO_SELF_MSAT: u64 = 25_000 * 1000;
3553+
const NODE_0_SELECTED_CHANNEL_RESERVE_MSAT: u64 = 1_000 * 1_000;
3554+
const NODE_1_SELECTED_CHANNEL_RESERVE_MSAT: u64 = 10_000 * 1_000;
3555+
3556+
let channel_id = create_announced_chan_between_nodes_with_value(
3557+
&nodes,
3558+
0,
3559+
1,
3560+
CHANNEL_VALUE_MSAT / 1000,
3561+
NODE_1_VALUE_TO_SELF_MSAT,
3562+
)
3563+
.2;
3564+
assert_eq!(nodes[0].node.list_channels()[0].channel_type.as_ref().unwrap(), &channel_type);
3565+
3566+
{
3567+
let per_peer_state_lock;
3568+
let mut peer_state_lock;
3569+
let chan =
3570+
get_channel_ref!(nodes[0], nodes[1], per_peer_state_lock, peer_state_lock, channel_id);
3571+
chan.context_mut().holder_dust_limit_satoshis = NODE_0_DUST_LIMIT_MSAT / 1000;
3572+
chan.funding_mut().counterparty_selected_channel_reserve_satoshis =
3573+
Some(NODE_1_SELECTED_CHANNEL_RESERVE_MSAT / 1000);
3574+
assert_eq!(chan.context().counterparty_dust_limit_satoshis, NODE_1_DUST_LIMIT_MSAT / 1000);
3575+
assert_eq!(
3576+
chan.funding().holder_selected_channel_reserve_satoshis,
3577+
NODE_0_SELECTED_CHANNEL_RESERVE_MSAT / 1000
3578+
);
3579+
}
3580+
3581+
{
3582+
let per_peer_state_lock;
3583+
let mut peer_state_lock;
3584+
let chan =
3585+
get_channel_ref!(nodes[1], nodes[0], per_peer_state_lock, peer_state_lock, channel_id);
3586+
chan.context_mut().counterparty_dust_limit_satoshis = NODE_0_DUST_LIMIT_MSAT / 1000;
3587+
chan.funding_mut().holder_selected_channel_reserve_satoshis =
3588+
NODE_1_SELECTED_CHANNEL_RESERVE_MSAT / 1000;
3589+
assert_eq!(chan.context().holder_dust_limit_satoshis, NODE_1_DUST_LIMIT_MSAT / 1000);
3590+
assert_eq!(
3591+
chan.funding().counterparty_selected_channel_reserve_satoshis,
3592+
Some(NODE_0_SELECTED_CHANNEL_RESERVE_MSAT / 1000)
3593+
);
3594+
}
3595+
3596+
// This HTLC is only present on node 1's commitment
3597+
const SNEAKY_HTLC_MSAT: u64 = 5_000_000;
3598+
3599+
route_payment(&nodes[1], &[&nodes[0]], SNEAKY_HTLC_MSAT);
3600+
3601+
let node_1_details = &nodes[1].node.list_channels()[0];
3602+
let expected_outbound_capacity_msat =
3603+
NODE_1_VALUE_TO_SELF_MSAT - SNEAKY_HTLC_MSAT - NODE_0_SELECTED_CHANNEL_RESERVE_MSAT;
3604+
assert_eq!(node_1_details.outbound_capacity_msat, expected_outbound_capacity_msat);
3605+
let expected_available_capacity_msat = expected_outbound_capacity_msat;
3606+
assert_eq!(node_1_details.next_outbound_htlc_limit_msat, expected_available_capacity_msat);
3607+
let expected_splice_out_max =
3608+
NODE_1_VALUE_TO_SELF_MSAT / 1000 - SNEAKY_HTLC_MSAT / 1000 - NODE_1_DUST_LIMIT_MSAT / 1000;
3609+
assert_eq!(node_1_details.next_splice_out_maximum_sat, expected_splice_out_max);
3610+
3611+
let node_0_details = &nodes[0].node.list_channels()[0];
3612+
let expected_outbound_capacity_msat =
3613+
NODE_0_VALUE_TO_SELF_MSAT - NODE_1_SELECTED_CHANNEL_RESERVE_MSAT - TOTAL_ANCHORS_MSAT;
3614+
assert_eq!(node_0_details.outbound_capacity_msat, expected_outbound_capacity_msat);
3615+
let expected_available_capacity_msat =
3616+
expected_outbound_capacity_msat - commit_tx_fee_sat(FEERATE, 3, &channel_type) * 1000;
3617+
assert_eq!(node_0_details.next_outbound_htlc_limit_msat, expected_available_capacity_msat);
3618+
let expected_splice_out_max = NODE_0_VALUE_TO_SELF_MSAT / 1000
3619+
- TOTAL_ANCHORS_MSAT / 1000
3620+
- commit_tx_fee_sat(FEERATE, 2, &channel_type)
3621+
- NODE_0_DUST_LIMIT_MSAT / 1000;
3622+
assert_eq!(node_0_details.next_splice_out_maximum_sat, expected_splice_out_max);
3623+
3624+
let node_0_payment_msat = expected_available_capacity_msat;
3625+
send_payment(&nodes[0], &[&nodes[1]], node_0_payment_msat);
3626+
3627+
route_payment(&nodes[1], &[&nodes[0]], SNEAKY_HTLC_MSAT);
3628+
route_payment(&nodes[1], &[&nodes[0]], SNEAKY_HTLC_MSAT);
3629+
3630+
let node_0_details = &nodes[0].node.list_channels()[0];
3631+
let expected_outbound_capacity_msat = NODE_0_VALUE_TO_SELF_MSAT
3632+
- node_0_payment_msat
3633+
- NODE_1_SELECTED_CHANNEL_RESERVE_MSAT
3634+
- TOTAL_ANCHORS_MSAT;
3635+
assert_eq!(node_0_details.outbound_capacity_msat, expected_outbound_capacity_msat);
3636+
assert_eq!(
3637+
node_0_details.outbound_capacity_msat,
3638+
commit_tx_fee_sat(FEERATE, 3, &channel_type) * 1000
3639+
);
3640+
assert_eq!(node_0_details.next_outbound_htlc_limit_msat, 0);
3641+
assert_eq!(node_0_details.next_splice_out_maximum_sat, 0);
3642+
3643+
let node_1_details = &nodes[1].node.list_channels()[0];
3644+
let expected_outbound_capacity_msat = NODE_1_VALUE_TO_SELF_MSAT + node_0_payment_msat
3645+
- 3 * SNEAKY_HTLC_MSAT
3646+
- NODE_0_SELECTED_CHANNEL_RESERVE_MSAT;
3647+
assert_eq!(node_1_details.outbound_capacity_msat, expected_outbound_capacity_msat);
3648+
let (_htlc_success_tx_fee_sat, htlc_timeout_tx_fee_sat) =
3649+
second_stage_tx_fees_sat(&channel_type, FEERATE);
3650+
let expected_available_capacity_msat =
3651+
(NODE_1_DUST_LIMIT_MSAT / 1000 + htlc_timeout_tx_fee_sat) * 1000 - 1;
3652+
assert_eq!(node_1_details.next_outbound_htlc_limit_msat, expected_available_capacity_msat);
3653+
let expected_splice_out_max = NODE_1_VALUE_TO_SELF_MSAT / 1000 + node_0_payment_msat / 1000
3654+
- 3 * SNEAKY_HTLC_MSAT / 1000
3655+
- NODE_1_DUST_LIMIT_MSAT / 1000;
3656+
assert_eq!(node_1_details.next_splice_out_maximum_sat, expected_splice_out_max);
3657+
}
3658+
3659+
#[xtest(feature = "_externalize_tests")]
3660+
fn test_available_balances_both_commitments_dust_on_fundee_commitment() {
3661+
let mut config = test_default_channel_config();
3662+
3663+
let chanmon_cfgs = create_chanmon_cfgs(2);
3664+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
3665+
config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage =
3666+
100;
3667+
3668+
let channel_type = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
3669+
3670+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]);
3671+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
3672+
3673+
const FEERATE: u32 = 253;
3674+
const TOTAL_ANCHORS_MSAT: u64 = 2 * 330_000;
3675+
const NODE_0_DUST_LIMIT_MSAT: u64 = 354 * 1000;
3676+
const NODE_1_DUST_LIMIT_MSAT: u64 = 10_000 * 1000;
3677+
const CHANNEL_VALUE_MSAT: u64 = 50_000 * 1000;
3678+
const NODE_0_VALUE_TO_SELF_MSAT: u64 = 25_000 * 1000;
3679+
const NODE_1_VALUE_TO_SELF_MSAT: u64 = 25_000 * 1000;
3680+
const NODE_0_SELECTED_CHANNEL_RESERVE_MSAT: u64 = 10_000 * 1_000;
3681+
const NODE_1_SELECTED_CHANNEL_RESERVE_MSAT: u64 = 1_000 * 1_000;
3682+
3683+
let channel_id = create_announced_chan_between_nodes_with_value(
3684+
&nodes,
3685+
0,
3686+
1,
3687+
CHANNEL_VALUE_MSAT / 1000,
3688+
NODE_1_VALUE_TO_SELF_MSAT,
3689+
)
3690+
.2;
3691+
assert_eq!(nodes[0].node.list_channels()[0].channel_type.as_ref().unwrap(), &channel_type);
3692+
3693+
{
3694+
let per_peer_state_lock;
3695+
let mut peer_state_lock;
3696+
let chan =
3697+
get_channel_ref!(nodes[0], nodes[1], per_peer_state_lock, peer_state_lock, channel_id);
3698+
chan.context_mut().counterparty_dust_limit_satoshis = NODE_1_DUST_LIMIT_MSAT / 1000;
3699+
chan.funding_mut().holder_selected_channel_reserve_satoshis =
3700+
NODE_0_SELECTED_CHANNEL_RESERVE_MSAT / 1000;
3701+
assert_eq!(chan.context().holder_dust_limit_satoshis, NODE_0_DUST_LIMIT_MSAT / 1000);
3702+
assert_eq!(
3703+
chan.funding().counterparty_selected_channel_reserve_satoshis,
3704+
Some(NODE_1_SELECTED_CHANNEL_RESERVE_MSAT / 1000)
3705+
);
3706+
}
3707+
3708+
{
3709+
let per_peer_state_lock;
3710+
let mut peer_state_lock;
3711+
let chan =
3712+
get_channel_ref!(nodes[1], nodes[0], per_peer_state_lock, peer_state_lock, channel_id);
3713+
chan.context_mut().holder_dust_limit_satoshis = NODE_1_DUST_LIMIT_MSAT / 1000;
3714+
chan.funding_mut().counterparty_selected_channel_reserve_satoshis =
3715+
Some(NODE_0_SELECTED_CHANNEL_RESERVE_MSAT / 1000);
3716+
assert_eq!(chan.context().counterparty_dust_limit_satoshis, NODE_0_DUST_LIMIT_MSAT / 1000);
3717+
assert_eq!(
3718+
chan.funding().holder_selected_channel_reserve_satoshis,
3719+
NODE_1_SELECTED_CHANNEL_RESERVE_MSAT / 1000
3720+
);
3721+
}
3722+
3723+
// This HTLC is only present on node 0's commitment
3724+
const SNEAKY_HTLC_MSAT: u64 = 5_000_000;
3725+
3726+
route_payment(&nodes[1], &[&nodes[0]], SNEAKY_HTLC_MSAT);
3727+
3728+
let node_1_details = &nodes[1].node.list_channels()[0];
3729+
let expected_outbound_capacity_msat =
3730+
NODE_1_VALUE_TO_SELF_MSAT - SNEAKY_HTLC_MSAT - NODE_0_SELECTED_CHANNEL_RESERVE_MSAT;
3731+
assert_eq!(node_1_details.outbound_capacity_msat, expected_outbound_capacity_msat);
3732+
let expected_available_capacity_msat = expected_outbound_capacity_msat;
3733+
assert_eq!(node_1_details.next_outbound_htlc_limit_msat, expected_available_capacity_msat);
3734+
let expected_splice_out_max =
3735+
NODE_1_VALUE_TO_SELF_MSAT / 1000 - SNEAKY_HTLC_MSAT / 1000 - NODE_1_DUST_LIMIT_MSAT / 1000;
3736+
assert_eq!(node_1_details.next_splice_out_maximum_sat, expected_splice_out_max);
3737+
3738+
let node_0_details = &nodes[0].node.list_channels()[0];
3739+
3740+
let expected_outbound_capacity_msat =
3741+
NODE_0_VALUE_TO_SELF_MSAT - NODE_1_SELECTED_CHANNEL_RESERVE_MSAT - TOTAL_ANCHORS_MSAT;
3742+
assert_eq!(node_0_details.outbound_capacity_msat, expected_outbound_capacity_msat);
3743+
3744+
let expected_splice_out_max = NODE_0_VALUE_TO_SELF_MSAT / 1000
3745+
- TOTAL_ANCHORS_MSAT / 1000
3746+
- commit_tx_fee_sat(FEERATE, 2, &channel_type)
3747+
- NODE_0_DUST_LIMIT_MSAT / 1000;
3748+
assert_eq!(node_0_details.next_splice_out_maximum_sat, expected_splice_out_max);
3749+
3750+
let expected_available_capacity_msat =
3751+
expected_outbound_capacity_msat - commit_tx_fee_sat(FEERATE, 3, &channel_type) * 1000;
3752+
assert_eq!(node_0_details.next_outbound_htlc_limit_msat, expected_available_capacity_msat);
3753+
3754+
let node_0_payment_msat = expected_available_capacity_msat;
3755+
send_payment(&nodes[0], &[&nodes[1]], node_0_payment_msat);
3756+
3757+
route_payment(&nodes[1], &[&nodes[0]], SNEAKY_HTLC_MSAT);
3758+
route_payment(&nodes[1], &[&nodes[0]], SNEAKY_HTLC_MSAT);
3759+
3760+
let node_0_details = &nodes[0].node.list_channels()[0];
3761+
let expected_outbound_capacity_msat = NODE_0_VALUE_TO_SELF_MSAT
3762+
- node_0_payment_msat
3763+
- NODE_1_SELECTED_CHANNEL_RESERVE_MSAT
3764+
- TOTAL_ANCHORS_MSAT;
3765+
assert_eq!(node_0_details.outbound_capacity_msat, expected_outbound_capacity_msat);
3766+
assert_eq!(
3767+
node_0_details.outbound_capacity_msat,
3768+
commit_tx_fee_sat(FEERATE, 3, &channel_type) * 1000
3769+
);
3770+
assert_eq!(node_0_details.next_outbound_htlc_limit_msat, 0);
3771+
3772+
let local_balance_before_fee_sat =
3773+
NODE_0_VALUE_TO_SELF_MSAT / 1000 - node_0_payment_msat / 1000 - TOTAL_ANCHORS_MSAT / 1000;
3774+
let post_splice_delta_above_reserve = commit_tx_fee_sat(FEERATE, 4, &channel_type);
3775+
let divident_sat = local_balance_before_fee_sat * 100 + 100
3776+
- (post_splice_delta_above_reserve * 100)
3777+
- CHANNEL_VALUE_MSAT / 1000;
3778+
let expected_splice_out_max = (divident_sat - 1) / 99;
3779+
assert_eq!(node_0_details.next_splice_out_maximum_sat, expected_splice_out_max);
3780+
3781+
let node_1_details = &nodes[1].node.list_channels()[0];
3782+
let expected_outbound_capacity_msat = NODE_1_VALUE_TO_SELF_MSAT + node_0_payment_msat
3783+
- 3 * SNEAKY_HTLC_MSAT
3784+
- NODE_0_SELECTED_CHANNEL_RESERVE_MSAT;
3785+
assert_eq!(node_1_details.outbound_capacity_msat, expected_outbound_capacity_msat);
3786+
let (htlc_success_tx_fee_sat, _htlc_timeout_tx_fee_sat) =
3787+
second_stage_tx_fees_sat(&channel_type, FEERATE);
3788+
let expected_available_capacity_msat =
3789+
(NODE_0_DUST_LIMIT_MSAT / 1000 + htlc_success_tx_fee_sat) * 1000 - 1;
3790+
assert_eq!(node_1_details.next_outbound_htlc_limit_msat, expected_available_capacity_msat);
3791+
let expected_splice_out_max = NODE_1_VALUE_TO_SELF_MSAT / 1000 + node_0_payment_msat / 1000
3792+
- 3 * SNEAKY_HTLC_MSAT / 1000
3793+
- NODE_1_DUST_LIMIT_MSAT / 1000;
3794+
assert_eq!(node_1_details.next_splice_out_maximum_sat, expected_splice_out_max);
3795+
}

0 commit comments

Comments
 (0)