Skip to content

Commit 8d8b4ea

Browse files
authored
Merge pull request #3556 from TheBlueMatt/2025-01-fail-back-on-expiry
Fail HTLC backwards before upstream claims on-chain
2 parents 0454d45 + a577f32 commit 8d8b4ea

File tree

5 files changed

+271
-91
lines changed

5 files changed

+271
-91
lines changed

lightning/src/chain/channelmonitor.rs

+75-1
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,12 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
10271027

10281028
/// The first block height at which we had no remaining claimable balances.
10291029
balances_empty_height: Option<u32>,
1030+
1031+
/// In-memory only HTLC ids used to track upstream HTLCs that have been failed backwards due to
1032+
/// a downstream channel force-close remaining unconfirmed by the time the upstream timeout
1033+
/// expires. This is used to tell us we already generated an event to fail this HTLC back
1034+
/// during a previous block scan.
1035+
failed_back_htlc_ids: HashSet<SentHTLCId>,
10301036
}
10311037

10321038
/// Transaction outputs to watch for on-chain spends.
@@ -1449,6 +1455,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
14491455
counterparty_node_id: Some(counterparty_node_id),
14501456
initial_counterparty_commitment_info: None,
14511457
balances_empty_height: None,
1458+
1459+
failed_back_htlc_ids: new_hash_set(),
14521460
})
14531461
}
14541462

@@ -3278,7 +3286,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
32783286
}
32793287
}
32803288

3281-
if ret.is_ok() && (self.funding_spend_seen || self.lockdown_from_offchain) && is_pre_close_update {
3289+
if ret.is_ok() && (self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed) && is_pre_close_update {
32823290
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
32833291
Err(())
32843292
} else { ret }
@@ -4225,6 +4233,71 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
42254233
}
42264234
}
42274235

4236+
if self.lockdown_from_offchain || self.funding_spend_seen || self.holder_tx_signed {
4237+
// Fail back HTLCs on backwards channels if they expire within
4238+
// `LATENCY_GRACE_PERIOD_BLOCKS` blocks and the channel is closed (i.e. we're at a
4239+
// point where no further off-chain updates will be accepted). If we haven't seen the
4240+
// preimage for an HTLC by the time the previous hop's timeout expires, we've lost that
4241+
// HTLC, so we might as well fail it back instead of having our counterparty force-close
4242+
// the inbound channel.
4243+
let current_holder_htlcs = self.current_holder_commitment_tx.htlc_outputs.iter()
4244+
.map(|&(ref a, _, ref b)| (a, b.as_ref()));
4245+
4246+
let current_counterparty_htlcs = if let Some(txid) = self.current_counterparty_commitment_txid {
4247+
if let Some(htlc_outputs) = self.counterparty_claimable_outpoints.get(&txid) {
4248+
Some(htlc_outputs.iter().map(|&(ref a, ref b)| (a, b.as_ref().map(|boxed| &**boxed))))
4249+
} else { None }
4250+
} else { None }.into_iter().flatten();
4251+
4252+
let prev_counterparty_htlcs = if let Some(txid) = self.prev_counterparty_commitment_txid {
4253+
if let Some(htlc_outputs) = self.counterparty_claimable_outpoints.get(&txid) {
4254+
Some(htlc_outputs.iter().map(|&(ref a, ref b)| (a, b.as_ref().map(|boxed| &**boxed))))
4255+
} else { None }
4256+
} else { None }.into_iter().flatten();
4257+
4258+
let htlcs = current_holder_htlcs
4259+
.chain(current_counterparty_htlcs)
4260+
.chain(prev_counterparty_htlcs);
4261+
4262+
let height = self.best_block.height;
4263+
for (htlc, source_opt) in htlcs {
4264+
// Only check forwarded HTLCs' previous hops
4265+
let source = match source_opt {
4266+
Some(source) => source,
4267+
None => continue,
4268+
};
4269+
let inbound_htlc_expiry = match source.inbound_htlc_expiry() {
4270+
Some(cltv_expiry) => cltv_expiry,
4271+
None => continue,
4272+
};
4273+
let max_expiry_height = height.saturating_add(LATENCY_GRACE_PERIOD_BLOCKS);
4274+
if inbound_htlc_expiry > max_expiry_height {
4275+
continue;
4276+
}
4277+
let duplicate_event = self.pending_monitor_events.iter().any(
4278+
|update| if let &MonitorEvent::HTLCEvent(ref upd) = update {
4279+
upd.source == *source
4280+
} else { false });
4281+
if duplicate_event {
4282+
continue;
4283+
}
4284+
if !self.failed_back_htlc_ids.insert(SentHTLCId::from_source(source)) {
4285+
continue;
4286+
}
4287+
if !duplicate_event {
4288+
log_error!(logger, "Failing back HTLC {} upstream to preserve the \
4289+
channel as the forward HTLC hasn't resolved and our backward HTLC \
4290+
expires soon at {}", log_bytes!(htlc.payment_hash.0), inbound_htlc_expiry);
4291+
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
4292+
source: source.clone(),
4293+
payment_preimage: None,
4294+
payment_hash: htlc.payment_hash,
4295+
htlc_value_satoshis: Some(htlc.amount_msat / 1000),
4296+
}));
4297+
}
4298+
}
4299+
}
4300+
42284301
let conf_target = self.closure_conf_target();
42294302
self.onchain_tx_handler.update_claims_view_from_requests(claimable_outpoints, conf_height, self.best_block.height, broadcaster, conf_target, fee_estimator, logger);
42304303
self.onchain_tx_handler.update_claims_view_from_matched_txn(&txn_matched, conf_height, conf_hash, self.best_block.height, broadcaster, conf_target, fee_estimator, logger);
@@ -5070,6 +5143,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
50705143
counterparty_node_id,
50715144
initial_counterparty_commitment_info,
50725145
balances_empty_height,
5146+
failed_back_htlc_ids: new_hash_set(),
50735147
})))
50745148
}
50755149
}

lightning/src/ln/channel.rs

+9-77
Original file line numberDiff line numberDiff line change
@@ -1790,15 +1790,6 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
17901790
/// [`msgs::RevokeAndACK`] message from the counterparty.
17911791
sent_message_awaiting_response: Option<usize>,
17921792

1793-
#[cfg(any(test, fuzzing))]
1794-
// When we receive an HTLC fulfill on an outbound path, we may immediately fulfill the
1795-
// corresponding HTLC on the inbound path. If, then, the outbound path channel is
1796-
// disconnected and reconnected (before we've exchange commitment_signed and revoke_and_ack
1797-
// messages), they may re-broadcast their update_fulfill_htlc, causing a duplicate claim. This
1798-
// is fine, but as a sanity check in our failure to generate the second claim, we check here
1799-
// that the original was a claim, and that we aren't now trying to fulfill a failed HTLC.
1800-
historical_inbound_htlc_fulfills: HashSet<u64>,
1801-
18021793
/// This channel's type, as negotiated during channel open
18031794
channel_type: ChannelTypeFeatures,
18041795

@@ -2518,9 +2509,6 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
25182509
funding_tx_broadcast_safe_event_emitted: false,
25192510
channel_ready_event_emitted: false,
25202511

2521-
#[cfg(any(test, fuzzing))]
2522-
historical_inbound_htlc_fulfills: new_hash_set(),
2523-
25242512
channel_type,
25252513
channel_keys_id,
25262514

@@ -2751,9 +2739,6 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
27512739
funding_tx_broadcast_safe_event_emitted: false,
27522740
channel_ready_event_emitted: false,
27532741

2754-
#[cfg(any(test, fuzzing))]
2755-
historical_inbound_htlc_fulfills: new_hash_set(),
2756-
27572742
channel_type,
27582743
channel_keys_id,
27592744

@@ -4784,10 +4769,6 @@ impl<SP: Deref> FundedChannel<SP> where
47844769
}
47854770
}
47864771
if pending_idx == core::usize::MAX {
4787-
#[cfg(any(test, fuzzing))]
4788-
// If we failed to find an HTLC to fulfill, make sure it was previously fulfilled and
4789-
// this is simply a duplicate claim, not previously failed and we lost funds.
4790-
debug_assert!(self.context.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
47914772
return UpdateFulfillFetch::DuplicateClaim {};
47924773
}
47934774

@@ -4817,8 +4798,6 @@ impl<SP: Deref> FundedChannel<SP> where
48174798
if htlc_id_arg == htlc_id {
48184799
// Make sure we don't leave latest_monitor_update_id incremented here:
48194800
self.context.latest_monitor_update_id -= 1;
4820-
#[cfg(any(test, fuzzing))]
4821-
debug_assert!(self.context.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
48224801
return UpdateFulfillFetch::DuplicateClaim {};
48234802
}
48244803
},
@@ -4840,12 +4819,8 @@ impl<SP: Deref> FundedChannel<SP> where
48404819
self.context.holding_cell_htlc_updates.push(HTLCUpdateAwaitingACK::ClaimHTLC {
48414820
payment_preimage: payment_preimage_arg, htlc_id: htlc_id_arg,
48424821
});
4843-
#[cfg(any(test, fuzzing))]
4844-
self.context.historical_inbound_htlc_fulfills.insert(htlc_id_arg);
48454822
return UpdateFulfillFetch::NewClaim { monitor_update, htlc_value_msat, msg: None };
48464823
}
4847-
#[cfg(any(test, fuzzing))]
4848-
self.context.historical_inbound_htlc_fulfills.insert(htlc_id_arg);
48494824

48504825
{
48514826
let htlc = &mut self.context.pending_inbound_htlcs[pending_idx];
@@ -4910,14 +4885,8 @@ impl<SP: Deref> FundedChannel<SP> where
49104885
}
49114886
}
49124887

4913-
/// We can only have one resolution per HTLC. In some cases around reconnect, we may fulfill
4914-
/// an HTLC more than once or fulfill once and then attempt to fail after reconnect. We cannot,
4915-
/// however, fail more than once as we wait for an upstream failure to be irrevocably committed
4916-
/// before we fail backwards.
4917-
///
4918-
/// If we do fail twice, we `debug_assert!(false)` and return `Ok(None)`. Thus, this will always
4919-
/// return `Ok(_)` if preconditions are met. In any case, `Err`s will only be
4920-
/// [`ChannelError::Ignore`].
4888+
/// Returns `Err` (always with [`ChannelError::Ignore`]) if the HTLC could not be failed (e.g.
4889+
/// if it was already resolved). Otherwise returns `Ok`.
49214890
pub fn queue_fail_htlc<L: Deref>(&mut self, htlc_id_arg: u64, err_packet: msgs::OnionErrorPacket, logger: &L)
49224891
-> Result<(), ChannelError> where L::Target: Logger {
49234892
self.fail_htlc(htlc_id_arg, err_packet, true, logger)
@@ -4935,14 +4904,8 @@ impl<SP: Deref> FundedChannel<SP> where
49354904
.map(|msg_opt| assert!(msg_opt.is_none(), "We forced holding cell?"))
49364905
}
49374906

4938-
/// We can only have one resolution per HTLC. In some cases around reconnect, we may fulfill
4939-
/// an HTLC more than once or fulfill once and then attempt to fail after reconnect. We cannot,
4940-
/// however, fail more than once as we wait for an upstream failure to be irrevocably committed
4941-
/// before we fail backwards.
4942-
///
4943-
/// If we do fail twice, we `debug_assert!(false)` and return `Ok(None)`. Thus, this will always
4944-
/// return `Ok(_)` if preconditions are met. In any case, `Err`s will only be
4945-
/// [`ChannelError::Ignore`].
4907+
/// Returns `Err` (always with [`ChannelError::Ignore`]) if the HTLC could not be failed (e.g.
4908+
/// if it was already resolved). Otherwise returns `Ok`.
49464909
fn fail_htlc<L: Deref, E: FailHTLCContents + Clone>(
49474910
&mut self, htlc_id_arg: u64, err_contents: E, mut force_holding_cell: bool,
49484911
logger: &L
@@ -4960,12 +4923,8 @@ impl<SP: Deref> FundedChannel<SP> where
49604923
if htlc.htlc_id == htlc_id_arg {
49614924
match htlc.state {
49624925
InboundHTLCState::Committed => {},
4963-
InboundHTLCState::LocalRemoved(ref reason) => {
4964-
if let &InboundHTLCRemovalReason::Fulfill(_) = reason {
4965-
} else {
4966-
debug_assert!(false, "Tried to fail an HTLC that was already failed");
4967-
}
4968-
return Ok(None);
4926+
InboundHTLCState::LocalRemoved(_) => {
4927+
return Err(ChannelError::Ignore(format!("HTLC {} was already resolved", htlc.htlc_id)));
49694928
},
49704929
_ => {
49714930
debug_assert!(false, "Have an inbound HTLC we tried to claim before it was fully committed to");
@@ -4976,11 +4935,7 @@ impl<SP: Deref> FundedChannel<SP> where
49764935
}
49774936
}
49784937
if pending_idx == core::usize::MAX {
4979-
#[cfg(any(test, fuzzing))]
4980-
// If we failed to find an HTLC to fail, make sure it was previously fulfilled and this
4981-
// is simply a duplicate fail, not previously failed and we failed-back too early.
4982-
debug_assert!(self.context.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
4983-
return Ok(None);
4938+
return Err(ChannelError::Ignore(format!("Unable to find a pending HTLC which matched the given HTLC ID ({})", htlc_id_arg)));
49844939
}
49854940

49864941
if !self.context.channel_state.can_generate_new_commitment() {
@@ -4994,17 +4949,14 @@ impl<SP: Deref> FundedChannel<SP> where
49944949
match pending_update {
49954950
&HTLCUpdateAwaitingACK::ClaimHTLC { htlc_id, .. } => {
49964951
if htlc_id_arg == htlc_id {
4997-
#[cfg(any(test, fuzzing))]
4998-
debug_assert!(self.context.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
4999-
return Ok(None);
4952+
return Err(ChannelError::Ignore(format!("HTLC {} was already claimed!", htlc_id)));
50004953
}
50014954
},
50024955
&HTLCUpdateAwaitingACK::FailHTLC { htlc_id, .. } |
50034956
&HTLCUpdateAwaitingACK::FailMalformedHTLC { htlc_id, .. } =>
50044957
{
50054958
if htlc_id_arg == htlc_id {
5006-
debug_assert!(false, "Tried to fail an HTLC that was already failed");
5007-
return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID".to_owned()));
4959+
return Err(ChannelError::Ignore(format!("HTLC {} was already pending failure", htlc_id)));
50084960
}
50094961
},
50104962
_ => {}
@@ -9795,13 +9747,6 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
97959747

97969748
self.context.channel_update_status.write(writer)?;
97979749

9798-
#[cfg(any(test, fuzzing))]
9799-
(self.context.historical_inbound_htlc_fulfills.len() as u64).write(writer)?;
9800-
#[cfg(any(test, fuzzing))]
9801-
for htlc in self.context.historical_inbound_htlc_fulfills.iter() {
9802-
htlc.write(writer)?;
9803-
}
9804-
98059750
// If the channel type is something other than only-static-remote-key, then we need to have
98069751
// older clients fail to deserialize this channel at all. If the type is
98079752
// only-static-remote-key, we simply consider it "default" and don't write the channel type
@@ -10135,16 +10080,6 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
1013510080

1013610081
let channel_update_status = Readable::read(reader)?;
1013710082

10138-
#[cfg(any(test, fuzzing))]
10139-
let mut historical_inbound_htlc_fulfills = new_hash_set();
10140-
#[cfg(any(test, fuzzing))]
10141-
{
10142-
let htlc_fulfills_len: u64 = Readable::read(reader)?;
10143-
for _ in 0..htlc_fulfills_len {
10144-
assert!(historical_inbound_htlc_fulfills.insert(Readable::read(reader)?));
10145-
}
10146-
}
10147-
1014810083
let pending_update_fee = if let Some(feerate) = pending_update_fee_value {
1014910084
Some((feerate, if channel_parameters.is_outbound_from_holder {
1015010085
FeeUpdateState::Outbound
@@ -10485,9 +10420,6 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
1048510420
channel_pending_event_emitted: channel_pending_event_emitted.unwrap_or(true),
1048610421
channel_ready_event_emitted: channel_ready_event_emitted.unwrap_or(true),
1048710422

10488-
#[cfg(any(test, fuzzing))]
10489-
historical_inbound_htlc_fulfills,
10490-
1049110423
channel_type: channel_type.unwrap(),
1049210424
channel_keys_id,
1049310425

0 commit comments

Comments
 (0)