Skip to content

Commit 79267d3

Browse files
authored
Merge pull request #3559 from tnull/2025-01-sweeper-improvements
`OutputSweeper`: Delay pruning until monitors have likely been archived
2 parents 3718da0 + 84412cc commit 79267d3

File tree

5 files changed

+44
-28
lines changed

5 files changed

+44
-28
lines changed

lightning-background-processor/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1099,7 +1099,7 @@ mod tests {
10991099
SCORER_PERSISTENCE_SECONDARY_NAMESPACE,
11001100
};
11011101
use lightning::util::ser::Writeable;
1102-
use lightning::util::sweep::{OutputSpendStatus, OutputSweeper};
1102+
use lightning::util::sweep::{OutputSpendStatus, OutputSweeper, PRUNE_DELAY_BLOCKS};
11031103
use lightning::util::test_utils;
11041104
use lightning::{get_event, get_event_msg};
11051105
use lightning_persister::fs_store::FilesystemStore;
@@ -2282,8 +2282,8 @@ mod tests {
22822282
}
22832283

22842284
// Check we stop tracking the spendable outputs when one of the txs reaches
2285-
// ANTI_REORG_DELAY confirmations.
2286-
confirm_transaction_depth(&mut nodes[0], &sweep_tx_0, ANTI_REORG_DELAY);
2285+
// PRUNE_DELAY_BLOCKS confirmations.
2286+
confirm_transaction_depth(&mut nodes[0], &sweep_tx_0, PRUNE_DELAY_BLOCKS);
22872287
assert_eq!(nodes[0].sweeper.tracked_spendable_outputs().len(), 0);
22882288

22892289
if !std::thread::panicking() {

lightning/src/chain/channelmonitor.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ pub(crate) const LATENCY_GRACE_PERIOD_BLOCKS: u32 = 3;
256256
// solved by a previous claim tx. What we want to avoid is reorg evicting our claim tx and us not
257257
// keep bumping another claim tx to solve the outpoint.
258258
pub const ANTI_REORG_DELAY: u32 = 6;
259+
/// Number of blocks we wait before assuming a [`ChannelMonitor`] to be fully resolved and
260+
/// considering it be safely archived.
261+
// 4032 blocks are roughly four weeks
262+
pub const ARCHIVAL_DELAY_BLOCKS: u32 = 4032;
259263
/// Number of blocks before confirmation at which we fail back an un-relayed HTLC or at which we
260264
/// refuse to accept a new HTLC.
261265
///
@@ -2015,10 +2019,11 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
20152019
///
20162020
/// This function returns a tuple of two booleans, the first indicating whether the monitor is
20172021
/// fully resolved, and the second whether the monitor needs persistence to ensure it is
2018-
/// reliably marked as resolved within 4032 blocks.
2022+
/// reliably marked as resolved within [`ARCHIVAL_DELAY_BLOCKS`] blocks.
20192023
///
2020-
/// The first boolean is true only if [`Self::get_claimable_balances`] has been empty for at least
2021-
/// 4032 blocks as an additional protection against any bugs resulting in spuriously empty balance sets.
2024+
/// The first boolean is true only if [`Self::get_claimable_balances`] has been empty for at
2025+
/// least [`ARCHIVAL_DELAY_BLOCKS`] blocks as an additional protection against any bugs
2026+
/// resulting in spuriously empty balance sets.
20222027
pub fn check_and_update_full_resolution_status<L: Logger>(&self, logger: &L) -> (bool, bool) {
20232028
let mut is_all_funds_claimed = self.get_claimable_balances().is_empty();
20242029
let current_height = self.current_best_block().height;
@@ -2034,11 +2039,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
20342039
// once processed, implies the preimage exists in the corresponding inbound channel.
20352040
let preimages_not_needed_elsewhere = inner.pending_monitor_events.is_empty();
20362041

2037-
const BLOCKS_THRESHOLD: u32 = 4032; // ~four weeks
20382042
match (inner.balances_empty_height, is_all_funds_claimed, preimages_not_needed_elsewhere) {
20392043
(Some(balances_empty_height), true, true) => {
20402044
// Claimed all funds, check if reached the blocks threshold.
2041-
(current_height >= balances_empty_height + BLOCKS_THRESHOLD, false)
2045+
(current_height >= balances_empty_height + ARCHIVAL_DELAY_BLOCKS, false)
20422046
},
20432047
(Some(_), false, _)|(Some(_), _, false) => {
20442048
// previously assumed we claimed all funds, but we have new funds to claim or
@@ -2058,7 +2062,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
20582062
// None. It is set to the current block height.
20592063
log_debug!(logger,
20602064
"ChannelMonitor funded at {} is now fully resolved. It will become archivable in {} blocks",
2061-
inner.get_funding_txo().0, BLOCKS_THRESHOLD);
2065+
inner.get_funding_txo().0, ARCHIVAL_DELAY_BLOCKS);
20622066
inner.balances_empty_height = Some(current_height);
20632067
(false, true)
20642068
},

lightning/src/ln/monitor_tests.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
//! Further functional tests which test blockchain reorganizations.
1111
1212
use crate::sign::{ecdsa::EcdsaChannelSigner, OutputSpender, SpendableOutputDescriptor};
13-
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS, Balance, BalanceSource, ChannelMonitorUpdateStep};
13+
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, ARCHIVAL_DELAY_BLOCKS,LATENCY_GRACE_PERIOD_BLOCKS, Balance, BalanceSource, ChannelMonitorUpdateStep};
1414
use crate::chain::transaction::OutPoint;
1515
use crate::chain::chaininterface::{ConfirmationTarget, LowerBoundedFeeEstimator, compute_feerate_sat_per_1000_weight};
1616
use crate::events::bump_transaction::{BumpTransactionEvent, WalletSource};
@@ -246,31 +246,31 @@ fn archive_fully_resolved_monitors() {
246246

247247
// At this point, both nodes have no more `Balance`s, but nodes[0]'s `ChannelMonitor` still
248248
// hasn't had the `MonitorEvent` that contains the preimage claimed by the `ChannelManager`.
249-
// Thus, calling `archive_fully_resolved_channel_monitors` and waiting 4032 blocks will not
250-
// result in the `ChannelMonitor` being archived.
249+
// Thus, calling `archive_fully_resolved_channel_monitors` and waiting `ARCHIVAL_DELAY_BLOCKS`
250+
// blocks will not result in the `ChannelMonitor` being archived.
251251
nodes[0].chain_monitor.chain_monitor.archive_fully_resolved_channel_monitors();
252252
assert_eq!(nodes[0].chain_monitor.chain_monitor.list_monitors().len(), 1);
253-
connect_blocks(&nodes[0], 4032);
253+
connect_blocks(&nodes[0], ARCHIVAL_DELAY_BLOCKS);
254254
nodes[0].chain_monitor.chain_monitor.archive_fully_resolved_channel_monitors();
255255
assert_eq!(nodes[0].chain_monitor.chain_monitor.list_monitors().len(), 1);
256256

257-
// ...however, nodes[1]'s `ChannelMonitor` is ready to be archived, and will be in exactly 4032
258-
// blocks.
257+
// ...however, nodes[1]'s `ChannelMonitor` is ready to be archived, and will be in exactly
258+
// `ARCHIVAL_DELAY_BLOCKS` blocks.
259259
nodes[1].chain_monitor.chain_monitor.archive_fully_resolved_channel_monitors();
260260
assert_eq!(nodes[1].chain_monitor.chain_monitor.list_monitors().len(), 1);
261-
connect_blocks(&nodes[1], 4031);
261+
connect_blocks(&nodes[1], ARCHIVAL_DELAY_BLOCKS - 1);
262262
nodes[1].chain_monitor.chain_monitor.archive_fully_resolved_channel_monitors();
263263
assert_eq!(nodes[1].chain_monitor.chain_monitor.list_monitors().len(), 1);
264264
connect_blocks(&nodes[1], 1);
265265
nodes[1].chain_monitor.chain_monitor.archive_fully_resolved_channel_monitors();
266266
assert_eq!(nodes[1].chain_monitor.chain_monitor.list_monitors().len(), 0);
267267

268268
// Finally, we process the pending `MonitorEvent` from nodes[0], allowing the `ChannelMonitor`
269-
// to be archived 4032 blocks later.
269+
// to be archived `ARCHIVAL_DELAY_BLOCKS` blocks later.
270270
expect_payment_sent(&nodes[0], payment_preimage, None, true, false);
271271
nodes[0].chain_monitor.chain_monitor.archive_fully_resolved_channel_monitors();
272272
assert_eq!(nodes[0].chain_monitor.chain_monitor.list_monitors().len(), 1);
273-
connect_blocks(&nodes[0], 4031);
273+
connect_blocks(&nodes[0], ARCHIVAL_DELAY_BLOCKS - 1);
274274
nodes[0].chain_monitor.chain_monitor.archive_fully_resolved_channel_monitors();
275275
assert_eq!(nodes[0].chain_monitor.chain_monitor.list_monitors().len(), 1);
276276
connect_blocks(&nodes[0], 1);

lightning/src/sign/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,15 @@ impl SpendableOutputDescriptor {
538538
};
539539
Ok((psbt, expected_max_weight))
540540
}
541+
542+
/// Returns the outpoint of the spendable output.
543+
pub fn outpoint(&self) -> OutPoint {
544+
match self {
545+
Self::StaticOutput { outpoint, .. } => *outpoint,
546+
Self::StaticPaymentOutput(descriptor) => descriptor.outpoint,
547+
Self::DelayedPaymentOutput(descriptor) => descriptor.outpoint,
548+
}
549+
}
541550
}
542551

543552
/// The parameters required to derive a channel signer via [`SignerProvider`].

lightning/src/util/sweep.rs

+13-10
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//! sweeping them.
1010
1111
use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
12-
use crate::chain::channelmonitor::ANTI_REORG_DELAY;
12+
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, ARCHIVAL_DELAY_BLOCKS};
1313
use crate::chain::{self, BestBlock, Confirm, Filter, Listen, WatchedOutput};
1414
use crate::io;
1515
use crate::ln::msgs::DecodeError;
@@ -32,6 +32,9 @@ use bitcoin::{BlockHash, Transaction, Txid};
3232

3333
use core::ops::Deref;
3434

35+
/// The number of blocks we wait before we prune the tracked spendable outputs.
36+
pub const PRUNE_DELAY_BLOCKS: u32 = ARCHIVAL_DELAY_BLOCKS + ANTI_REORG_DELAY;
37+
3538
/// The state of a spendable output currently tracked by an [`OutputSweeper`].
3639
#[derive(Clone, Debug, PartialEq, Eq)]
3740
pub struct TrackedSpendableOutput {
@@ -71,13 +74,7 @@ impl TrackedSpendableOutput {
7174

7275
/// Returns whether the output is spent in the given transaction.
7376
pub fn is_spent_in(&self, tx: &Transaction) -> bool {
74-
let prev_outpoint = match &self.descriptor {
75-
SpendableOutputDescriptor::StaticOutput { outpoint, .. } => *outpoint,
76-
SpendableOutputDescriptor::DelayedPaymentOutput(output) => output.outpoint,
77-
SpendableOutputDescriptor::StaticPaymentOutput(output) => output.outpoint,
78-
}
79-
.into_bitcoin_outpoint();
80-
77+
let prev_outpoint = self.descriptor.outpoint().into_bitcoin_outpoint();
8178
tx.input.iter().any(|input| input.previous_output == prev_outpoint)
8279
}
8380
}
@@ -107,7 +104,11 @@ pub enum OutputSpendStatus {
107104
latest_spending_tx: Transaction,
108105
},
109106
/// A transaction spending the output has been confirmed on-chain but will be tracked until it
110-
/// reaches [`ANTI_REORG_DELAY`] confirmations.
107+
/// reaches at least [`PRUNE_DELAY_BLOCKS`] confirmations to ensure [`Event::SpendableOutputs`]
108+
/// stemming from lingering [`ChannelMonitor`]s can safely be replayed.
109+
///
110+
/// [`Event::SpendableOutputs`]: crate::events::Event::SpendableOutputs
111+
/// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
111112
PendingThresholdConfirmations {
112113
/// The hash of the chain tip when we first broadcast a transaction spending this output.
113114
first_broadcast_hash: BlockHash,
@@ -530,7 +531,9 @@ where
530531
// Prune all outputs that have sufficient depth by now.
531532
sweeper_state.outputs.retain(|o| {
532533
if let Some(confirmation_height) = o.status.confirmation_height() {
533-
if cur_height >= confirmation_height + ANTI_REORG_DELAY - 1 {
534+
// We wait at least `PRUNE_DELAY_BLOCKS` as before that
535+
// `Event::SpendableOutputs` from lingering monitors might get replayed.
536+
if cur_height >= confirmation_height + PRUNE_DELAY_BLOCKS - 1 {
534537
log_debug!(self.logger,
535538
"Pruning swept output as sufficiently confirmed via spend in transaction {:?}. Pruned descriptor: {:?}",
536539
o.status.latest_spending_tx().map(|t| t.compute_txid()), o.descriptor

0 commit comments

Comments
 (0)