Skip to content

Commit 6e6e4dc

Browse files
authored
Merge pull request #3932 from TheBlueMatt/2025-07-0.1.4-backports
0.1.5 backports
2 parents 63f41fd + 382e71b commit 6e6e4dc

13 files changed

+1079
-285
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3008,23 +3008,26 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
30083008
(payment_preimage.clone(), payment_info.clone().into_iter().collect())
30093009
});
30103010

3011-
let confirmed_spend_txid = self.funding_spend_confirmed.or_else(|| {
3012-
self.onchain_events_awaiting_threshold_conf.iter().find_map(|event| match event.event {
3013-
OnchainEvent::FundingSpendConfirmation { .. } => Some(event.txid),
3014-
_ => None,
3015-
})
3016-
});
3017-
let confirmed_spend_txid = if let Some(txid) = confirmed_spend_txid {
3018-
txid
3019-
} else {
3020-
return;
3021-
};
3011+
let confirmed_spend_info = self.funding_spend_confirmed
3012+
.map(|txid| (txid, None))
3013+
.or_else(|| {
3014+
self.onchain_events_awaiting_threshold_conf.iter().find_map(|event| match event.event {
3015+
OnchainEvent::FundingSpendConfirmation { .. } => Some((event.txid, Some(event.height))),
3016+
_ => None,
3017+
})
3018+
});
3019+
let (confirmed_spend_txid, confirmed_spend_height) =
3020+
if let Some((txid, height)) = confirmed_spend_info {
3021+
(txid, height)
3022+
} else {
3023+
return;
3024+
};
30223025

30233026
// If the channel is force closed, try to claim the output from this preimage.
30243027
// First check if a counterparty commitment transaction has been broadcasted:
30253028
macro_rules! claim_htlcs {
30263029
($commitment_number: expr, $txid: expr, $htlcs: expr) => {
3027-
let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None, $htlcs);
3030+
let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None, $htlcs, confirmed_spend_height);
30283031
let conf_target = self.closure_conf_target();
30293032
self.onchain_tx_handler.update_claims_view_from_requests(htlc_claim_reqs, self.best_block.height, self.best_block.height, broadcaster, conf_target, fee_estimator, logger);
30303033
}
@@ -3542,7 +3545,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
35423545
// First, process non-htlc outputs (to_holder & to_counterparty)
35433546
for (idx, outp) in tx.output.iter().enumerate() {
35443547
if outp.script_pubkey == revokeable_p2wsh {
3545-
let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv, self.onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx());
3548+
let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv, self.onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx(), height);
35463549
let justice_package = PackageTemplate::build_package(
35473550
commitment_txid, idx as u32,
35483551
PackageSolvingData::RevokedOutput(revk_outp),
@@ -3563,7 +3566,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
35633566
// per_commitment_data is corrupt or our commitment signing key leaked!
35643567
return (claimable_outpoints, to_counterparty_output_info);
35653568
}
3566-
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features);
3569+
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features, height);
35673570
let counterparty_spendable_height = if htlc.offered {
35683571
htlc.cltv_expiry
35693572
} else {
@@ -3617,7 +3620,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36173620
(htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
36183621
), logger);
36193622
let (htlc_claim_reqs, counterparty_output_info) =
3620-
self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx), per_commitment_option);
3623+
self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx), per_commitment_option, Some(height));
36213624
to_counterparty_output_info = counterparty_output_info;
36223625
for req in htlc_claim_reqs {
36233626
claimable_outpoints.push(req);
@@ -3628,7 +3631,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36283631
}
36293632

36303633
/// Returns the HTLC claim package templates and the counterparty output info
3631-
fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>, per_commitment_option: Option<&Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>>)
3634+
fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>, per_commitment_option: Option<&Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>>, confirmation_height: Option<u32>)
36323635
-> (Vec<PackageTemplate>, CommitmentTxCounterpartyOutputInfo) {
36333636
let mut claimable_outpoints = Vec::new();
36343637
let mut to_counterparty_output_info: CommitmentTxCounterpartyOutputInfo = None;
@@ -3688,13 +3691,15 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36883691
CounterpartyOfferedHTLCOutput::build(*per_commitment_point,
36893692
self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
36903693
self.counterparty_commitment_params.counterparty_htlc_base_key,
3691-
preimage.unwrap(), htlc.clone(), self.onchain_tx_handler.channel_type_features().clone()))
3694+
preimage.unwrap(), htlc.clone(), self.onchain_tx_handler.channel_type_features().clone(),
3695+
confirmation_height))
36923696
} else {
36933697
PackageSolvingData::CounterpartyReceivedHTLCOutput(
36943698
CounterpartyReceivedHTLCOutput::build(*per_commitment_point,
36953699
self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
36963700
self.counterparty_commitment_params.counterparty_htlc_base_key,
3697-
htlc.clone(), self.onchain_tx_handler.channel_type_features().clone()))
3701+
htlc.clone(), self.onchain_tx_handler.channel_type_features().clone(),
3702+
confirmation_height))
36983703
};
36993704
let counterparty_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, counterparty_htlc_outp, htlc.cltv_expiry);
37003705
claimable_outpoints.push(counterparty_package);
@@ -3736,7 +3741,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
37363741
per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
37373742
self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key,
37383743
tx.output[idx].value, self.counterparty_commitment_params.on_counterparty_tx_csv,
3739-
false
3744+
false, height,
37403745
);
37413746
let justice_package = PackageTemplate::build_package(
37423747
htlc_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp),
@@ -3765,7 +3770,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
37653770
if let Some(transaction_output_index) = htlc.transaction_output_index {
37663771
let (htlc_output, counterparty_spendable_height) = if htlc.offered {
37673772
let htlc_output = HolderHTLCOutput::build_offered(
3768-
htlc.amount_msat, htlc.cltv_expiry, self.onchain_tx_handler.channel_type_features().clone()
3773+
htlc.amount_msat, htlc.cltv_expiry, self.onchain_tx_handler.channel_type_features().clone(), conf_height
37693774
);
37703775
(htlc_output, conf_height)
37713776
} else {
@@ -3776,7 +3781,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
37763781
continue;
37773782
};
37783783
let htlc_output = HolderHTLCOutput::build_accepted(
3779-
payment_preimage, htlc.amount_msat, self.onchain_tx_handler.channel_type_features().clone()
3784+
payment_preimage, htlc.amount_msat, self.onchain_tx_handler.channel_type_features().clone(), conf_height
37803785
);
37813786
(htlc_output, htlc.cltv_expiry)
37823787
};

lightning/src/chain/onchaintx.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ pub struct OnchainTxHandler<ChannelSigner: EcdsaChannelSigner> {
278278
#[cfg(not(test))]
279279
claimable_outpoints: HashMap<BitcoinOutPoint, (ClaimId, u32)>,
280280

281+
#[cfg(any(test, feature = "_test_utils"))]
282+
pub(crate) locktimed_packages: BTreeMap<u32, Vec<PackageTemplate>>,
283+
#[cfg(not(any(test, feature = "_test_utils")))]
281284
locktimed_packages: BTreeMap<u32, Vec<PackageTemplate>>,
282285

283286
onchain_events_awaiting_threshold_conf: Vec<OnchainEventEntry>,
@@ -862,9 +865,10 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
862865
// Because fuzzing can cause hash collisions, we can end up with conflicting claim
863866
// ids here, so we only assert when not fuzzing.
864867
debug_assert!(cfg!(fuzzing) || self.pending_claim_requests.get(&claim_id).is_none());
865-
for k in req.outpoints() {
866-
log_info!(logger, "Registering claiming request for {}:{}", k.txid, k.vout);
867-
self.claimable_outpoints.insert(k.clone(), (claim_id, conf_height));
868+
for (k, outpoint_confirmation_height) in req.outpoints_and_creation_heights() {
869+
let creation_height = outpoint_confirmation_height.unwrap_or(conf_height);
870+
log_info!(logger, "Registering claiming request for {}:{}, which exists as of height {creation_height}", k.txid, k.vout);
871+
self.claimable_outpoints.insert(k.clone(), (claim_id, creation_height));
868872
}
869873
self.pending_claim_requests.insert(claim_id, req);
870874
}
@@ -969,6 +973,17 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
969973
panic!("Inconsistencies between pending_claim_requests map and claimable_outpoints map");
970974
}
971975
}
976+
977+
// Also remove/split any locktimed packages whose inputs have been spent by this transaction.
978+
self.locktimed_packages.retain(|_locktime, packages|{
979+
packages.retain_mut(|package| {
980+
if let Some(p) = package.split_package(&inp.previous_output) {
981+
claimed_outputs_material.push(p);
982+
}
983+
!package.outpoints().is_empty()
984+
});
985+
!packages.is_empty()
986+
});
972987
}
973988
for package in claimed_outputs_material.drain(..) {
974989
let entry = OnchainEventEntry {
@@ -1104,6 +1119,13 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
11041119
//- resurect outpoint back in its claimable set and regenerate tx
11051120
match entry.event {
11061121
OnchainEvent::ContentiousOutpoint { package } => {
1122+
// We pass 0 to `package_locktime` to get the actual required locktime.
1123+
let package_locktime = package.package_locktime(0);
1124+
if package_locktime >= height {
1125+
self.locktimed_packages.entry(package_locktime).or_default().push(package);
1126+
continue;
1127+
}
1128+
11071129
if let Some(pending_claim) = self.claimable_outpoints.get(package.outpoints()[0]) {
11081130
if let Some(request) = self.pending_claim_requests.get_mut(&pending_claim.0) {
11091131
assert!(request.merge_package(package, height).is_ok());
@@ -1408,6 +1430,7 @@ mod tests {
14081430
htlc.amount_msat,
14091431
htlc.cltv_expiry,
14101432
ChannelTypeFeatures::only_static_remote_key(),
1433+
0,
14111434
)),
14121435
0,
14131436
));

0 commit comments

Comments
 (0)