Skip to content

Commit 0c57018

Browse files
authored
Merge pull request #970 from TheBlueMatt/2021-06-no-confirmed-csv-delay
Create SpendableOutputs events no matter the chain::Confirm order
2 parents e7d3781 + 412cc9f commit 0c57018

File tree

6 files changed

+209
-110
lines changed

6 files changed

+209
-110
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,8 @@ impl OnchainEventEntry {
370370
conf_threshold
371371
}
372372

373-
fn has_reached_confirmation_threshold(&self, height: u32) -> bool {
374-
height >= self.confirmation_threshold()
373+
fn has_reached_confirmation_threshold(&self, best_block: &BestBlock) -> bool {
374+
best_block.height() >= self.confirmation_threshold()
375375
}
376376
}
377377

@@ -1331,7 +1331,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
13311331
macro_rules! claim_htlcs {
13321332
($commitment_number: expr, $txid: expr) => {
13331333
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs($commitment_number, $txid, None);
1334-
self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, self.best_block.height(), broadcaster, fee_estimator, logger);
1334+
self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
13351335
}
13361336
}
13371337
if let Some(txid) = self.current_counterparty_commitment_txid {
@@ -1353,11 +1353,14 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
13531353
// *we* sign a holder commitment transaction, not when e.g. a watchtower broadcasts one of our
13541354
// holder commitment transactions.
13551355
if self.broadcasted_holder_revokable_script.is_some() {
1356-
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, 0);
1357-
self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, self.best_block.height(), broadcaster, fee_estimator, logger);
1356+
// Assume that the broadcasted commitment transaction confirmed in the current best
1357+
// block. Even if not, its a reasonable metric for the bump criteria on the HTLC
1358+
// transactions.
1359+
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, self.best_block.height());
1360+
self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
13581361
if let Some(ref tx) = self.prev_holder_signed_commitment_tx {
1359-
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&tx, 0);
1360-
self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, self.best_block.height(), broadcaster, fee_estimator, logger);
1362+
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&tx, self.best_block.height());
1363+
self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
13611364
}
13621365
}
13631366
}
@@ -1724,7 +1727,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
17241727
// Returns (1) `PackageTemplate`s that can be given to the OnChainTxHandler, so that the handler can
17251728
// broadcast transactions claiming holder HTLC commitment outputs and (2) a holder revokable
17261729
// script so we can detect whether a holder transaction has been seen on-chain.
1727-
fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx, height: u32) -> (Vec<PackageTemplate>, Option<(Script, PublicKey, PublicKey)>) {
1730+
fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx, conf_height: u32) -> (Vec<PackageTemplate>, Option<(Script, PublicKey, PublicKey)>) {
17281731
let mut claim_requests = Vec::with_capacity(holder_tx.htlc_outputs.len());
17291732

17301733
let redeemscript = chan_utils::get_revokeable_redeemscript(&holder_tx.revocation_key, self.on_holder_tx_csv, &holder_tx.delayed_payment_key);
@@ -1743,7 +1746,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
17431746
};
17441747
HolderHTLCOutput::build_accepted(payment_preimage, htlc.amount_msat)
17451748
};
1746-
let htlc_package = PackageTemplate::build_package(holder_tx.txid, transaction_output_index, PackageSolvingData::HolderHTLCOutput(htlc_output), height, false, height);
1749+
let htlc_package = PackageTemplate::build_package(holder_tx.txid, transaction_output_index, PackageSolvingData::HolderHTLCOutput(htlc_output), htlc.cltv_expiry, false, conf_height);
17471750
claim_requests.push(htlc_package);
17481751
}
17491752
}
@@ -1856,7 +1859,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
18561859
} else if htlc.0.cltv_expiry > self.best_block.height() + 1 {
18571860
// Don't broadcast HTLC-Timeout transactions immediately as they don't meet the
18581861
// current locktime requirements on-chain. We will broadcast them in
1859-
// `block_confirmed` when `would_broadcast_at_height` returns true.
1862+
// `block_confirmed` when `should_broadcast_holder_commitment_txn` returns true.
18601863
// Note that we add + 1 as transactions are broadcastable when they can be
18611864
// confirmed in the next block.
18621865
continue;
@@ -1926,13 +1929,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
19261929

19271930
if height > self.best_block.height() {
19281931
self.best_block = BestBlock::new(block_hash, height);
1929-
self.block_confirmed(height, vec![], vec![], vec![], broadcaster, fee_estimator, logger)
1930-
} else {
1932+
self.block_confirmed(height, vec![], vec![], vec![], &broadcaster, &fee_estimator, &logger)
1933+
} else if block_hash != self.best_block.block_hash() {
19311934
self.best_block = BestBlock::new(block_hash, height);
19321935
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= height);
19331936
self.onchain_tx_handler.block_disconnected(height + 1, broadcaster, fee_estimator, logger);
19341937
Vec::new()
1935-
}
1938+
} else { Vec::new() }
19361939
}
19371940

19381941
fn transactions_confirmed<B: Deref, F: Deref, L: Deref>(
@@ -2004,33 +2007,49 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
20042007
self.is_paying_spendable_output(&tx, height, &logger);
20052008
}
20062009

2007-
self.block_confirmed(height, txn_matched, watch_outputs, claimable_outpoints, broadcaster, fee_estimator, logger)
2010+
if height > self.best_block.height() {
2011+
self.best_block = BestBlock::new(block_hash, height);
2012+
}
2013+
2014+
self.block_confirmed(height, txn_matched, watch_outputs, claimable_outpoints, &broadcaster, &fee_estimator, &logger)
20082015
}
20092016

2017+
/// Update state for new block(s)/transaction(s) confirmed. Note that the caller must update
2018+
/// `self.best_block` before calling if a new best blockchain tip is available. More
2019+
/// concretely, `self.best_block` must never be at a lower height than `conf_height`, avoiding
2020+
/// complexity especially in `OnchainTx::update_claims_view`.
2021+
///
2022+
/// `conf_height` should be set to the height at which any new transaction(s)/block(s) were
2023+
/// confirmed at, even if it is not the current best height.
20102024
fn block_confirmed<B: Deref, F: Deref, L: Deref>(
20112025
&mut self,
2012-
height: u32,
2026+
conf_height: u32,
20132027
txn_matched: Vec<&Transaction>,
20142028
mut watch_outputs: Vec<TransactionOutputs>,
20152029
mut claimable_outpoints: Vec<PackageTemplate>,
2016-
broadcaster: B,
2017-
fee_estimator: F,
2018-
logger: L,
2030+
broadcaster: &B,
2031+
fee_estimator: &F,
2032+
logger: &L,
20192033
) -> Vec<TransactionOutputs>
20202034
where
20212035
B::Target: BroadcasterInterface,
20222036
F::Target: FeeEstimator,
20232037
L::Target: Logger,
20242038
{
2025-
let should_broadcast = self.would_broadcast_at_height(height, &logger);
2039+
debug_assert!(self.best_block.height() >= conf_height);
2040+
2041+
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
20262042
if should_broadcast {
20272043
let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone());
2028-
let commitment_package = PackageTemplate::build_package(self.funding_info.0.txid.clone(), self.funding_info.0.index as u32, PackageSolvingData::HolderFundingOutput(funding_outp), height, false, height);
2044+
let commitment_package = PackageTemplate::build_package(self.funding_info.0.txid.clone(), self.funding_info.0.index as u32, PackageSolvingData::HolderFundingOutput(funding_outp), self.best_block.height(), false, self.best_block.height());
20292045
claimable_outpoints.push(commitment_package);
20302046
self.pending_monitor_events.push(MonitorEvent::CommitmentTxBroadcasted(self.funding_info.0));
20312047
let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
20322048
self.holder_tx_signed = true;
2033-
let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, height);
2049+
// Because we're broadcasting a commitment transaction, we should construct the package
2050+
// assuming it gets confirmed in the next block. Sadly, we have code which considers
2051+
// "not yet confirmed" things as discardable, so we cannot do that here.
2052+
let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, self.best_block.height());
20342053
let new_outputs = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, &commitment_tx);
20352054
if !new_outputs.is_empty() {
20362055
watch_outputs.push((self.current_holder_commitment_tx.txid.clone(), new_outputs));
@@ -2043,7 +2062,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
20432062
self.onchain_events_awaiting_threshold_conf.drain(..).collect::<Vec<_>>();
20442063
let mut onchain_events_reaching_threshold_conf = Vec::new();
20452064
for entry in onchain_events_awaiting_threshold_conf {
2046-
if entry.has_reached_confirmation_threshold(height) {
2065+
if entry.has_reached_confirmation_threshold(&self.best_block) {
20472066
onchain_events_reaching_threshold_conf.push(entry);
20482067
} else {
20492068
self.onchain_events_awaiting_threshold_conf.push(entry);
@@ -2098,7 +2117,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
20982117
}
20992118
}
21002119

2101-
self.onchain_tx_handler.update_claims_view(&txn_matched, claimable_outpoints, height, &&*broadcaster, &&*fee_estimator, &&*logger);
2120+
self.onchain_tx_handler.update_claims_view(&txn_matched, claimable_outpoints, conf_height, self.best_block.height(), broadcaster, fee_estimator, logger);
21022121

21032122
// Determine new outputs to watch by comparing against previously known outputs to watch,
21042123
// updating the latter in the process.
@@ -2200,7 +2219,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
22002219
false
22012220
}
22022221

2203-
fn would_broadcast_at_height<L: Deref>(&self, height: u32, logger: &L) -> bool where L::Target: Logger {
2222+
fn should_broadcast_holder_commitment_txn<L: Deref>(&self, logger: &L) -> bool where L::Target: Logger {
22042223
// We need to consider all HTLCs which are:
22052224
// * in any unrevoked counterparty commitment transaction, as they could broadcast said
22062225
// transactions and we'd end up in a race, or
@@ -2211,6 +2230,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
22112230
// to the source, and if we don't fail the channel we will have to ensure that the next
22122231
// updates that peer sends us are update_fails, failing the channel if not. It's probably
22132232
// easier to just fail the channel as this case should be rare enough anyway.
2233+
let height = self.best_block.height();
22142234
macro_rules! scan_commitment {
22152235
($htlcs: expr, $holder_tx: expr) => {
22162236
for ref htlc in $htlcs {

lightning/src/chain/onchaintx.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -343,15 +343,15 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
343343
/// (CSV or CLTV following cases). In case of high-fee spikes, claim tx may stuck in the mempool, so you need to bump its feerate quickly using Replace-By-Fee or Child-Pay-For-Parent.
344344
/// Panics if there are signing errors, because signing operations in reaction to on-chain events
345345
/// are not expected to fail, and if they do, we may lose funds.
346-
fn generate_claim_tx<F: Deref, L: Deref>(&mut self, height: u32, cached_request: &PackageTemplate, fee_estimator: &F, logger: &L) -> Option<(Option<u32>, u64, Transaction)>
346+
fn generate_claim_tx<F: Deref, L: Deref>(&mut self, cur_height: u32, cached_request: &PackageTemplate, fee_estimator: &F, logger: &L) -> Option<(Option<u32>, u64, Transaction)>
347347
where F::Target: FeeEstimator,
348348
L::Target: Logger,
349349
{
350350
if cached_request.outpoints().len() == 0 { return None } // But don't prune pending claiming request yet, we may have to resurrect HTLCs
351351

352352
// Compute new height timer to decide when we need to regenerate a new bumped version of the claim tx (if we
353353
// didn't receive confirmation of it before, or not enough reorg-safe depth on top of it).
354-
let new_timer = Some(cached_request.get_height_timer(height));
354+
let new_timer = Some(cached_request.get_height_timer(cur_height));
355355
if cached_request.is_malleable() {
356356
let predicted_weight = cached_request.package_weight(&self.destination_script);
357357
if let Some((output_value, new_feerate)) = cached_request.compute_package_output(predicted_weight, fee_estimator, logger) {
@@ -377,12 +377,15 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
377377
/// for this channel, provide new relevant on-chain transactions and/or new claim requests.
378378
/// Formerly this was named `block_connected`, but it is now also used for claiming an HTLC output
379379
/// if we receive a preimage after force-close.
380-
pub(crate) fn update_claims_view<B: Deref, F: Deref, L: Deref>(&mut self, txn_matched: &[&Transaction], requests: Vec<PackageTemplate>, height: u32, broadcaster: &B, fee_estimator: &F, logger: &L)
380+
/// `conf_height` represents the height at which the transactions in `txn_matched` were
381+
/// confirmed. This does not need to equal the current blockchain tip height, which should be
382+
/// provided via `cur_height`, however it must never be higher than `cur_height`.
383+
pub(crate) fn update_claims_view<B: Deref, F: Deref, L: Deref>(&mut self, txn_matched: &[&Transaction], requests: Vec<PackageTemplate>, conf_height: u32, cur_height: u32, broadcaster: &B, fee_estimator: &F, logger: &L)
381384
where B::Target: BroadcasterInterface,
382385
F::Target: FeeEstimator,
383386
L::Target: Logger,
384387
{
385-
log_debug!(logger, "Updating claims view at height {} with {} matched transactions and {} claim requests", height, txn_matched.len(), requests.len());
388+
log_debug!(logger, "Updating claims view at height {} with {} matched transactions in block {} and {} claim requests", cur_height, txn_matched.len(), conf_height, requests.len());
386389
let mut preprocessed_requests = Vec::with_capacity(requests.len());
387390
let mut aggregated_request = None;
388391

@@ -401,17 +404,17 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
401404
continue;
402405
}
403406

404-
if req.package_timelock() > height + 1 {
405-
log_info!(logger, "Delaying claim of package until its timelock at {} (current height {}), the following outpoints are spent:", req.package_timelock(), height);
407+
if req.package_timelock() > cur_height + 1 {
408+
log_info!(logger, "Delaying claim of package until its timelock at {} (current height {}), the following outpoints are spent:", req.package_timelock(), cur_height);
406409
for outpoint in req.outpoints() {
407410
log_info!(logger, " Outpoint {}", outpoint);
408411
}
409412
self.locktimed_packages.entry(req.package_timelock()).or_insert(Vec::new()).push(req);
410413
continue;
411414
}
412415

413-
log_trace!(logger, "Test if outpoint can be aggregated with expiration {} against {}", req.timelock(), height + CLTV_SHARED_CLAIM_BUFFER);
414-
if req.timelock() <= height + CLTV_SHARED_CLAIM_BUFFER || !req.aggregable() {
416+
log_trace!(logger, "Test if outpoint can be aggregated with expiration {} against {}", req.timelock(), cur_height + CLTV_SHARED_CLAIM_BUFFER);
417+
if req.timelock() <= cur_height + CLTV_SHARED_CLAIM_BUFFER || !req.aggregable() {
415418
// Don't aggregate if outpoint package timelock is soon or marked as non-aggregable
416419
preprocessed_requests.push(req);
417420
} else if aggregated_request.is_none() {
@@ -425,8 +428,8 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
425428
preprocessed_requests.push(req);
426429
}
427430

428-
// Claim everything up to and including height + 1
429-
let remaining_locked_packages = self.locktimed_packages.split_off(&(height + 2));
431+
// Claim everything up to and including cur_height + 1
432+
let remaining_locked_packages = self.locktimed_packages.split_off(&(cur_height + 2));
430433
for (pop_height, mut entry) in self.locktimed_packages.iter_mut() {
431434
log_trace!(logger, "Restoring delayed claim of package(s) at their timelock at {}.", pop_height);
432435
preprocessed_requests.append(&mut entry);
@@ -436,13 +439,13 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
436439
// Generate claim transactions and track them to bump if necessary at
437440
// height timer expiration (i.e in how many blocks we're going to take action).
438441
for mut req in preprocessed_requests {
439-
if let Some((new_timer, new_feerate, tx)) = self.generate_claim_tx(height, &req, &*fee_estimator, &*logger) {
442+
if let Some((new_timer, new_feerate, tx)) = self.generate_claim_tx(cur_height, &req, &*fee_estimator, &*logger) {
440443
req.set_timer(new_timer);
441444
req.set_feerate(new_feerate);
442445
let txid = tx.txid();
443446
for k in req.outpoints() {
444447
log_info!(logger, "Registering claiming request for {}:{}", k.txid, k.vout);
445-
self.claimable_outpoints.insert(k.clone(), (txid, height));
448+
self.claimable_outpoints.insert(k.clone(), (txid, conf_height));
446449
}
447450
self.pending_claim_requests.insert(txid, req);
448451
log_info!(logger, "Broadcasting onchain {}", log_tx!(tx));
@@ -476,7 +479,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
476479
() => {
477480
let entry = OnchainEventEntry {
478481
txid: tx.txid(),
479-
height,
482+
height: conf_height,
480483
event: OnchainEvent::Claim { claim_request: first_claim_txid_height.0.clone() }
481484
};
482485
if !self.onchain_events_awaiting_threshold_conf.contains(&entry) {
@@ -516,7 +519,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
516519
for package in claimed_outputs_material.drain(..) {
517520
let entry = OnchainEventEntry {
518521
txid: tx.txid(),
519-
height,
522+
height: conf_height,
520523
event: OnchainEvent::ContentiousOutpoint { package },
521524
};
522525
if !self.onchain_events_awaiting_threshold_conf.contains(&entry) {
@@ -529,7 +532,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
529532
let onchain_events_awaiting_threshold_conf =
530533
self.onchain_events_awaiting_threshold_conf.drain(..).collect::<Vec<_>>();
531534
for entry in onchain_events_awaiting_threshold_conf {
532-
if entry.has_reached_confirmation_threshold(height) {
535+
if entry.has_reached_confirmation_threshold(cur_height) {
533536
match entry.event {
534537
OnchainEvent::Claim { claim_request } => {
535538
// We may remove a whole set of claim outpoints here, as these one may have
@@ -555,7 +558,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
555558
// Check if any pending claim request must be rescheduled
556559
for (first_claim_txid, ref request) in self.pending_claim_requests.iter() {
557560
if let Some(h) = request.timer() {
558-
if height >= h {
561+
if cur_height >= h {
559562
bump_candidates.insert(*first_claim_txid, (*request).clone());
560563
}
561564
}
@@ -564,7 +567,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
564567
// Build, bump and rebroadcast tx accordingly
565568
log_trace!(logger, "Bumping {} candidates", bump_candidates.len());
566569
for (first_claim_txid, request) in bump_candidates.iter() {
567-
if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(height, &request, &*fee_estimator, &*logger) {
570+
if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(cur_height, &request, &*fee_estimator, &*logger) {
568571
log_info!(logger, "Broadcasting RBF-bumped onchain {}", log_tx!(bump_tx));
569572
broadcaster.broadcast_transaction(&bump_tx);
570573
if let Some(request) = self.pending_claim_requests.get_mut(first_claim_txid) {

lightning/src/ln/channel.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ enum InboundHTLCState {
114114
/// commitment transaction without it as otherwise we'll have to force-close the channel to
115115
/// claim it before the timeout (obviously doesn't apply to revoked HTLCs that we can't claim
116116
/// anyway). That said, ChannelMonitor does this for us (see
117-
/// ChannelMonitor::would_broadcast_at_height) so we actually remove the HTLC from our own
118-
/// local state before then, once we're sure that the next commitment_signed and
117+
/// ChannelMonitor::should_broadcast_holder_commitment_txn) so we actually remove the HTLC from
118+
/// our own local state before then, once we're sure that the next commitment_signed and
119119
/// ChannelMonitor::provide_latest_local_commitment_tx will not include this HTLC.
120120
LocalRemoved(InboundHTLCRemovalReason),
121121
}

0 commit comments

Comments
 (0)