Skip to content

Commit e43cfbd

Browse files
naumenkogsTheBlueMatt
authored andcommitted
Consider commitment tx fee while assembling a route
When calculating the amount available to send for the next HTLC, if we over-count we may create routes which are not actually usable. Historically this has been an issue, which we resolve over a few commits. Here we include the cost of the commitment transaction fee in our calculation, subtracting the commitment tx fee cost from the available as we do in `send_payment`. We also add some testing when sending to ensure that send failures are accounted for in our balance calculations. This commit is based on original work by Gleb Naumenko <[email protected]> and modified by Matt Corallo <[email protected]>.
1 parent 4b900b7 commit e43cfbd

File tree

2 files changed

+62
-12
lines changed

2 files changed

+62
-12
lines changed

lightning/src/ln/channel.rs

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2686,18 +2686,53 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
26862686
}
26872687
balance_msat -= outbound_stats.pending_htlcs_value_msat;
26882688

2689-
let outbound_capacity_msat = cmp::max(self.value_to_self_msat as i64
2690-
- outbound_stats.pending_htlcs_value_msat as i64
2691-
- self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000,
2692-
0) as u64;
2689+
let outbound_capacity_msat = self.value_to_self_msat
2690+
.saturating_sub(outbound_stats.pending_htlcs_value_msat)
2691+
.saturating_sub(
2692+
self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) * 1000);
2693+
2694+
let mut available_capacity_msat = outbound_capacity_msat;
2695+
2696+
if self.is_outbound() {
2697+
// We should mind channel commit tx fee when computing how much of the available capacity
2698+
// can be used in the next htlc. Mirrors the logic in send_htlc.
2699+
//
2700+
// The fee depends on whether the amount we will be sending is above dust or not,
2701+
// and the answer will in turn change the amount itself — making it a circular
2702+
// dependency.
2703+
// This complicates the computation around dust-values, up to the one-htlc-value.
2704+
let mut real_dust_limit_timeout_sat = self.holder_dust_limit_satoshis;
2705+
if !self.opt_anchors() {
2706+
real_dust_limit_timeout_sat += self.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000;
2707+
}
2708+
2709+
let htlc_above_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000, HTLCInitiator::LocalOffered);
2710+
let max_reserved_commit_tx_fee_msat = FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE * self.next_local_commit_tx_fee_msat(htlc_above_dust, Some(()));
2711+
let htlc_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000 - 1, HTLCInitiator::LocalOffered);
2712+
let min_reserved_commit_tx_fee_msat = FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE * self.next_local_commit_tx_fee_msat(htlc_dust, Some(()));
2713+
2714+
// We will first subtract the fee as if we were above-dust. Then, if the resulting
2715+
// value ends up being below dust, we have this fee available again. In that case,
2716+
// match the value to right-below-dust.
2717+
let mut capacity_minus_commitment_fee_msat: i64 = (available_capacity_msat as i64) - (max_reserved_commit_tx_fee_msat as i64);
2718+
if capacity_minus_commitment_fee_msat < (real_dust_limit_timeout_sat as i64) * 1000 {
2719+
let one_htlc_difference_msat = max_reserved_commit_tx_fee_msat - min_reserved_commit_tx_fee_msat;
2720+
debug_assert!(one_htlc_difference_msat != 0);
2721+
capacity_minus_commitment_fee_msat += one_htlc_difference_msat as i64;
2722+
capacity_minus_commitment_fee_msat = cmp::min(real_dust_limit_timeout_sat as i64 * 1000 - 1, capacity_minus_commitment_fee_msat);
2723+
available_capacity_msat = cmp::max(0, cmp::min(capacity_minus_commitment_fee_msat, available_capacity_msat as i64)) as u64;
2724+
} else {
2725+
available_capacity_msat = capacity_minus_commitment_fee_msat as u64;
2726+
}
2727+
}
26932728
AvailableBalances {
26942729
inbound_capacity_msat: cmp::max(self.channel_value_satoshis as i64 * 1000
26952730
- self.value_to_self_msat as i64
26962731
- self.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
26972732
- self.holder_selected_channel_reserve_satoshis as i64 * 1000,
26982733
0) as u64,
26992734
outbound_capacity_msat,
2700-
next_outbound_htlc_limit_msat: cmp::max(cmp::min(outbound_capacity_msat as i64,
2735+
next_outbound_htlc_limit_msat: cmp::max(cmp::min(available_capacity_msat as i64,
27012736
self.counterparty_max_htlc_value_in_flight_msat as i64
27022737
- outbound_stats.pending_htlcs_value_msat as i64),
27032738
0) as u64,
@@ -5896,6 +5931,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
58965931
let holder_balance_msat = self.value_to_self_msat
58975932
.saturating_sub(outbound_stats.pending_htlcs_value_msat);
58985933
if holder_balance_msat < amount_msat {
5934+
debug_assert!(amount_msat > self.get_available_balances().next_outbound_htlc_limit_msat);
58995935
return Err(ChannelError::Ignore(format!("Cannot send value that would overdraw remaining funds. Amount: {}, pending value to self {}", amount_msat, holder_balance_msat)));
59005936
}
59015937

@@ -5905,13 +5941,15 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
59055941
FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE * self.next_local_commit_tx_fee_msat(htlc_candidate, Some(()))
59065942
} else { 0 };
59075943
if holder_balance_msat - amount_msat < commit_tx_fee_msat {
5944+
debug_assert!(amount_msat > self.get_available_balances().next_outbound_htlc_limit_msat);
59085945
return Err(ChannelError::Ignore(format!("Cannot send value that would not leave enough to pay for fees. Pending value to self: {}. local_commit_tx_fee {}", holder_balance_msat, commit_tx_fee_msat)));
59095946
}
59105947

59115948
// Check self.counterparty_selected_channel_reserve_satoshis (the amount we must keep as
59125949
// reserve for the remote to have something to claim if we misbehave)
59135950
let chan_reserve_msat = self.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000;
59145951
if holder_balance_msat - amount_msat - commit_tx_fee_msat < chan_reserve_msat {
5952+
debug_assert!(amount_msat > self.get_available_balances().next_outbound_htlc_limit_msat);
59155953
return Err(ChannelError::Ignore(format!("Cannot send value that would put our balance under counterparty-announced channel reserve value ({})", chan_reserve_msat)));
59165954
}
59175955

lightning/src/ln/functional_tests.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,7 +1342,9 @@ fn test_basic_channel_reserve() {
13421342
// The 2* and +1 are for the fee spike reserve.
13431343
let commit_tx_fee = 2 * commit_tx_fee_msat(get_feerate!(nodes[0], nodes[1], chan.2), 1 + 1, get_opt_anchors!(nodes[0], nodes[1], chan.2));
13441344
let max_can_send = 5000000 - channel_reserve - commit_tx_fee;
1345-
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], max_can_send + 1);
1345+
let (mut route, our_payment_hash, _, our_payment_secret) =
1346+
get_route_and_payment_hash!(nodes[0], nodes[1], max_can_send);
1347+
route.paths[0].hops.last_mut().unwrap().fee_msat += 1;
13461348
let err = nodes[0].node.send_payment_with_route(&route, our_payment_hash,
13471349
RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0)).err().unwrap();
13481350
match err {
@@ -1369,7 +1371,9 @@ fn test_fee_spike_violation_fails_htlc() {
13691371
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
13701372
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000);
13711373

1372-
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 3460001);
1374+
let (mut route, payment_hash, _, payment_secret) =
1375+
get_route_and_payment_hash!(nodes[0], nodes[1], 3460000);
1376+
route.paths[0].hops[0].fee_msat += 1;
13731377
// Need to manually create the update_add_htlc message to go around the channel reserve check in send_htlc()
13741378
let secp_ctx = Secp256k1::new();
13751379
let session_priv = SecretKey::from_slice(&[42; 32]).expect("RNG is bad!");
@@ -1732,7 +1736,8 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
17321736
let commit_tx_fee_2_htlcs = commit_tx_fee_msat(feerate, 2, opt_anchors);
17331737
let recv_value_2 = chan_stat.value_to_self_msat - amt_msat_1 - chan_stat.channel_reserve_msat - total_routing_fee_msat - commit_tx_fee_2_htlcs + 1;
17341738
let amt_msat_2 = recv_value_2 + total_routing_fee_msat;
1735-
let (route_2, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[2], amt_msat_2);
1739+
let mut route_2 = route_1.clone();
1740+
route_2.paths[0].hops.last_mut().unwrap().fee_msat = amt_msat_2;
17361741

17371742
// Need to manually create the update_add_htlc message to go around the channel reserve check in send_htlc()
17381743
let secp_ctx = Secp256k1::new();
@@ -1901,7 +1906,9 @@ fn test_channel_reserve_holding_cell_htlcs() {
19011906
// channel reserve test with htlc pending output > 0
19021907
let recv_value_2 = stat01.value_to_self_msat - amt_msat_1 - stat01.channel_reserve_msat - total_fee_msat - commit_tx_fee_2_htlcs;
19031908
{
1904-
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], recv_value_2 + 1);
1909+
let mut route = route_1.clone();
1910+
route.paths[0].hops.last_mut().unwrap().fee_msat = recv_value_2 + 1;
1911+
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[2]);
19051912
unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, our_payment_hash,
19061913
RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0)
19071914
), true, APIError::ChannelUnavailable { ref err },
@@ -1930,7 +1937,9 @@ fn test_channel_reserve_holding_cell_htlcs() {
19301937

19311938
// test with outbound holding cell amount > 0
19321939
{
1933-
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], recv_value_22+1);
1940+
let (mut route, our_payment_hash, _, our_payment_secret) =
1941+
get_route_and_payment_hash!(nodes[0], nodes[2], recv_value_22);
1942+
route.paths[0].hops.last_mut().unwrap().fee_msat += 1;
19341943
unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, our_payment_hash,
19351944
RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0)
19361945
), true, APIError::ChannelUnavailable { ref err },
@@ -6241,12 +6250,15 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
62416250
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
62426251
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000);
62436252

6244-
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 3999999);
6253+
let send_amt = 3999999;
6254+
let (mut route, our_payment_hash, _, our_payment_secret) =
6255+
get_route_and_payment_hash!(nodes[0], nodes[1], 1000);
6256+
route.paths[0].hops[0].fee_msat = send_amt;
62456257
let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
62466258
let cur_height = nodes[0].node.best_block.read().unwrap().height() + 1;
62476259
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::signing_only(), &route.paths[0], &session_priv).unwrap();
62486260
let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(
6249-
&route.paths[0], 3999999, RecipientOnionFields::secret_only(our_payment_secret), cur_height, &None).unwrap();
6261+
&route.paths[0], send_amt, RecipientOnionFields::secret_only(our_payment_secret), cur_height, &None).unwrap();
62506262
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash).unwrap();
62516263

62526264
let mut msg = msgs::UpdateAddHTLC {

0 commit comments

Comments
 (0)