Skip to content

Commit 2290141

Browse files
committed
Disallow sending an HTLC when the balance needed is pending removal
While its nice to be able to push an HTLC which spends balance that is removed in our local commitment transaction but awaiting an RAA from our peer for final removal its by no means a critical feature. Because peers should really be sending RAAs quickly after we send a commitment, this should be an exceedingly rare case, and we already don't expose this as available balance when routing, so this isn't even made available when sending, only forwarding. Note that `test_pending_claimed_htlc_no_balance_underflow` is removed as it tested a case which was only possible because of this and now is no longer possible.
1 parent 6775b95 commit 2290141

File tree

2 files changed

+11
-44
lines changed

2 files changed

+11
-44
lines changed

lightning/src/ln/channel.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5857,14 +5857,13 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
58575857
return Err(ChannelError::Ignore(format!("Cannot send value that would put us over the max HTLC value in flight our peer will accept ({})", self.counterparty_max_htlc_value_in_flight_msat)));
58585858
}
58595859

5860-
let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number);
5861-
let commitment_stats = self.build_commitment_transaction(self.cur_holder_commitment_transaction_number, &keys, true, true, logger);
58625860
if !self.is_outbound() {
58635861
// Check that we won't violate the remote channel reserve by adding this HTLC.
58645862
let htlc_candidate = HTLCCandidate::new(amount_msat, HTLCInitiator::LocalOffered);
58655863
let counterparty_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(htlc_candidate, None);
58665864
let holder_selected_chan_reserve_msat = self.holder_selected_channel_reserve_satoshis * 1000;
5867-
if commitment_stats.remote_balance_msat < counterparty_commit_tx_fee_msat + holder_selected_chan_reserve_msat {
5865+
let remote_balance_msat = (self.channel_value_satoshis * 1000 - self.value_to_self_msat).saturating_sub(inbound_stats.pending_htlcs_value_msat);
5866+
if remote_balance_msat < counterparty_commit_tx_fee_msat + holder_selected_chan_reserve_msat {
58685867
return Err(ChannelError::Ignore("Cannot send value that would put counterparty balance under holder-announced channel reserve value".to_owned()));
58695868
}
58705869
}
@@ -5894,7 +5893,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
58945893
}
58955894
}
58965895

5897-
let holder_balance_msat = commitment_stats.local_balance_msat - outbound_stats.holding_cell_msat;
5896+
let holder_balance_msat = self.value_to_self_msat
5897+
.saturating_sub(outbound_stats.pending_htlcs_value_msat);
58985898
if holder_balance_msat < amount_msat {
58995899
return Err(ChannelError::Ignore(format!("Cannot send value that would overdraw remaining funds. Amount: {}, pending value to self {}", amount_msat, holder_balance_msat)));
59005900
}
@@ -5915,7 +5915,13 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
59155915
return Err(ChannelError::Ignore(format!("Cannot send value that would put our balance under counterparty-announced channel reserve value ({})", chan_reserve_msat)));
59165916
}
59175917

5918-
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::MonitorUpdateInProgress as u32)) != 0 {
5918+
let need_holding_cell = (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::MonitorUpdateInProgress as u32)) != 0;
5919+
log_debug!(logger, "Pushing new outbound HTLC for {} msat {}", amount_msat,
5920+
if force_holding_cell { "into holding cell" }
5921+
else if need_holding_cell { "into holding cell as we're awaiting an RAA or monitor" }
5922+
else { "to peer" });
5923+
5924+
if need_holding_cell {
59195925
force_holding_cell = true;
59205926
}
59215927

lightning/src/ln/functional_tests.rs

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7618,45 +7618,6 @@ fn test_bump_txn_sanitize_tracking_maps() {
76187618
}
76197619
}
76207620

7621-
#[test]
7622-
fn test_pending_claimed_htlc_no_balance_underflow() {
7623-
// Tests that if we have a pending outbound HTLC as well as a claimed-but-not-fully-removed
7624-
// HTLC we will not underflow when we call `Channel::get_balance_msat()`.
7625-
let chanmon_cfgs = create_chanmon_cfgs(2);
7626-
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
7627-
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
7628-
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
7629-
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, 0);
7630-
7631-
let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &[&nodes[1]], 1_010_000);
7632-
nodes[1].node.claim_funds(payment_preimage);
7633-
expect_payment_claimed!(nodes[1], payment_hash, 1_010_000);
7634-
check_added_monitors!(nodes[1], 1);
7635-
let fulfill_ev = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
7636-
7637-
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &fulfill_ev.update_fulfill_htlcs[0]);
7638-
expect_payment_sent_without_paths!(nodes[0], payment_preimage);
7639-
nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &fulfill_ev.commitment_signed);
7640-
check_added_monitors!(nodes[0], 1);
7641-
let (_raa, _cs) = get_revoke_commit_msgs!(nodes[0], nodes[1].node.get_our_node_id());
7642-
7643-
// At this point nodes[1] has received 1,010k msat (10k msat more than their reserve) and can
7644-
// send an HTLC back (though it will go in the holding cell). Send an HTLC back and check we
7645-
// can get our balance.
7646-
7647-
// Get a route from nodes[1] to nodes[0] by getting a route going the other way and then flip
7648-
// the public key of the only hop. This works around ChannelDetails not showing the
7649-
// almost-claimed HTLC as available balance.
7650-
let (mut route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[1], 10_000);
7651-
route.payment_params = None; // This is all wrong, but unnecessary
7652-
route.paths[0].hops[0].pubkey = nodes[0].node.get_our_node_id();
7653-
let (_, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[0]);
7654-
nodes[1].node.send_payment_with_route(&route, payment_hash_2,
7655-
RecipientOnionFields::secret_only(payment_secret_2), PaymentId(payment_hash_2.0)).unwrap();
7656-
7657-
assert_eq!(nodes[1].node.list_channels()[0].balance_msat, 1_000_000);
7658-
}
7659-
76607621
#[test]
76617622
fn test_channel_conf_timeout() {
76627623
// Tests that, for inbound channels, we give up on them if the funding transaction does not

0 commit comments

Comments
 (0)