Skip to content

Commit 52a9057

Browse files
committed
Consider counterparty commitment tx fees when 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 consider whether one additional HTLC's commitment tx fees would result in the counterparty's commitment tx fees being greater than the reserve we've picked for them and, if so, limit our next HTLC value to only include dust HTLCs. We also add some testing when sending to ensure that send failures are accounted for in our balance calculations. This, and the previous few commits, fixes #1126.
1 parent b09ccd1 commit 52a9057

File tree

2 files changed

+30
-3
lines changed

2 files changed

+30
-3
lines changed

lightning/src/ln/channel.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2676,6 +2676,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
26762676
/// corner case properly.
26772677
pub fn get_available_balances(&self) -> AvailableBalances {
26782678
// Note that we have to handle overflow due to the above case.
2679+
let inbound_stats = self.get_inbound_pending_htlc_stats(None);
26792680
let outbound_stats = self.get_outbound_pending_htlc_stats(None);
26802681

26812682
let mut balance_msat = self.value_to_self_msat;
@@ -2724,6 +2725,26 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
27242725
} else {
27252726
available_capacity_msat = capacity_minus_commitment_fee_msat as u64;
27262727
}
2728+
} else {
2729+
// If the channel is inbound (i.e. counterparty pays the fee), we need to make sure
2730+
// sending a new HTLC won't reduce their balance below our reserve threshold.
2731+
let mut real_dust_limit_success_sat = self.counterparty_dust_limit_satoshis;
2732+
if !self.opt_anchors() {
2733+
real_dust_limit_success_sat += self.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000;
2734+
}
2735+
2736+
let htlc_above_dust = HTLCCandidate::new(real_dust_limit_success_sat * 1000, HTLCInitiator::LocalOffered);
2737+
let max_reserved_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(htlc_above_dust, None);
2738+
2739+
let holder_selected_chan_reserve_msat = self.holder_selected_channel_reserve_satoshis * 1000;
2740+
let remote_balance_msat = (self.channel_value_satoshis * 1000 - self.value_to_self_msat)
2741+
.saturating_sub(inbound_stats.pending_htlcs_value_msat);
2742+
2743+
if remote_balance_msat < max_reserved_commit_tx_fee_msat + holder_selected_chan_reserve_msat {
2744+
// If another HTLC's fee would reduce the remote's balance below the reserve limit
2745+
// we've selected for them, we can only send dust HTLCs.
2746+
available_capacity_msat = cmp::min(available_capacity_msat, real_dust_limit_success_sat * 1000 - 1);
2747+
}
27272748
}
27282749

27292750
available_capacity_msat = cmp::min(available_capacity_msat,
@@ -5906,6 +5927,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
59065927
let holder_selected_chan_reserve_msat = self.holder_selected_channel_reserve_satoshis * 1000;
59075928
let remote_balance_msat = (self.channel_value_satoshis * 1000 - self.value_to_self_msat).saturating_sub(inbound_stats.pending_htlcs_value_msat);
59085929
if remote_balance_msat < counterparty_commit_tx_fee_msat + holder_selected_chan_reserve_msat {
5930+
debug_assert!(amount_msat > self.get_available_balances().next_outbound_htlc_limit_msat);
59095931
return Err(ChannelError::Ignore("Cannot send value that would put counterparty balance under holder-announced channel reserve value".to_owned()));
59105932
}
59115933
}

lightning/src/ln/functional_tests.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,13 +1527,14 @@ fn test_chan_reserve_violation_outbound_htlc_inbound_chan() {
15271527

15281528
let _ = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, push_amt);
15291529

1530+
// Fetch a route in advance as we will be unable to once we're unable to send.
1531+
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 1_000_000);
15301532
// Sending exactly enough to hit the reserve amount should be accepted
15311533
for _ in 0..MIN_AFFORDABLE_HTLC_COUNT {
15321534
let (_, _, _) = route_payment(&nodes[1], &[&nodes[0]], 1_000_000);
15331535
}
15341536

15351537
// However one more HTLC should be significantly over the reserve amount and fail.
1536-
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 1_000_000);
15371538
unwrap_send_err!(nodes[1].node.send_payment_with_route(&route, our_payment_hash,
15381539
RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0)
15391540
), true, APIError::ChannelUnavailable { ref err },
@@ -1565,7 +1566,9 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
15651566
let (_, _, _) = route_payment(&nodes[1], &[&nodes[0]], 1_000_000);
15661567
}
15671568

1568-
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 700_000);
1569+
let (mut route, payment_hash, _, payment_secret) =
1570+
get_route_and_payment_hash!(nodes[1], nodes[0], 1000);
1571+
route.paths[0].hops[0].fee_msat = 700_000;
15691572
// Need to manually create the update_add_htlc message to go around the channel reserve check in send_htlc()
15701573
let secp_ctx = Secp256k1::new();
15711574
let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
@@ -1627,7 +1630,9 @@ fn test_chan_reserve_dust_inbound_htlcs_outbound_chan() {
16271630
}
16281631

16291632
// One more than the dust amt should fail, however.
1630-
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], dust_amt + 1);
1633+
let (mut route, our_payment_hash, _, our_payment_secret) =
1634+
get_route_and_payment_hash!(nodes[1], nodes[0], dust_amt);
1635+
route.paths[0].hops[0].fee_msat += 1;
16311636
unwrap_send_err!(nodes[1].node.send_payment_with_route(&route, our_payment_hash,
16321637
RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0)
16331638
), true, APIError::ChannelUnavailable { ref err },

0 commit comments

Comments
 (0)