Skip to content

Commit 905ead3

Browse files
committed
Add ChannelMonitor::is_fully_resolved function
Checks if the monitor is fully resolved. Resolved monitor is one that has claimed all of its ouputs and balacnes. This function returns true only if `get_claimable_balances` has been empty for at least 2016 blocks.
1 parent 1d2a27d commit 905ead3

File tree

1 file changed

+44
-0
lines changed

1 file changed

+44
-0
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,9 @@ pub(crate) struct ChannelMonitorImpl<Signer: WriteableEcdsaChannelSigner> {
935935
/// Ordering of tuple data: (their_per_commitment_point, feerate_per_kw, to_broadcaster_sats,
936936
/// to_countersignatory_sats)
937937
initial_counterparty_commitment_info: Option<(PublicKey, u32, u64, u64)>,
938+
939+
/// The first block height at which we had no remaining claimable balances.
940+
balances_empty_height: Option<u32>,
938941
}
939942

940943
/// Transaction outputs to watch for on-chain spends.
@@ -1145,6 +1148,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signe
11451148
(15, self.counterparty_fulfilled_htlcs, required),
11461149
(17, self.initial_counterparty_commitment_info, option),
11471150
(19, self.channel_id, required),
1151+
(21, self.balances_empty_height, option),
11481152
});
11491153

11501154
Ok(())
@@ -1328,6 +1332,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
13281332
best_block,
13291333
counterparty_node_id: Some(counterparty_node_id),
13301334
initial_counterparty_commitment_info: None,
1335+
balances_empty_height: None,
13311336
})
13321337
}
13331338

@@ -1856,6 +1861,42 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
18561861
spendable_outputs
18571862
}
18581863

1864+
/// Checks if the monitor is fully resolved. Resolved monitor is one that has claimed all of
1865+
/// its outputs and balances (i.e. [`Self::get_claimable_balances`] returns an empty set).
1866+
///
1867+
/// This function returns true only if [`Self::get_claimable_balances`] has been empty for at least
1868+
/// 2016 blocks as an additional protection against any bugs resulting in spuriously empty balance sets.
1869+
pub fn is_fully_resolved(&self) -> bool {
1870+
let is_all_funds_claimed = self.get_claimable_balances().is_empty();
1871+
let current_height = self.current_best_block().height;
1872+
let mut inner = self.inner.lock().unwrap();
1873+
1874+
match (inner.balances_empty_height, is_all_funds_claimed) {
1875+
(Some(balances_empty_height), true) => {
1876+
// Claimed all funds, check if reached the blocks threshold.
1877+
const BLOCKS_THRESHOLD: u32 = 2016; // ~two weeks
1878+
return current_height >= balances_empty_height + BLOCKS_THRESHOLD;
1879+
},
1880+
(Some(_), false) => {
1881+
// previously assumed we claimed all funds, but we have new funds to claim.
1882+
// Should not happen in practice.
1883+
debug_assert!(is_all_funds_claimed, "Trying to check if monitor is fully resolved before all funds are claimed.");
1884+
inner.balances_empty_height = None;
1885+
return false;
1886+
},
1887+
(None, true) => {
1888+
// Claimed all funds but `balances_empty_height` is None. It is set to the
1889+
// current block height.
1890+
inner.balances_empty_height = Some(current_height);
1891+
return false;
1892+
},
1893+
(None, false) => {
1894+
// Have funds to claim.
1895+
return false;
1896+
},
1897+
}
1898+
}
1899+
18591900
#[cfg(test)]
18601901
pub fn get_counterparty_payment_script(&self) -> ScriptBuf {
18611902
self.inner.lock().unwrap().counterparty_payment_script.clone()
@@ -4632,6 +4673,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
46324673
let mut spendable_txids_confirmed = Some(Vec::new());
46334674
let mut counterparty_fulfilled_htlcs = Some(new_hash_map());
46344675
let mut initial_counterparty_commitment_info = None;
4676+
let mut balances_empty_height = None;
46354677
let mut channel_id = None;
46364678
read_tlv_fields!(reader, {
46374679
(1, funding_spend_confirmed, option),
@@ -4644,6 +4686,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
46444686
(15, counterparty_fulfilled_htlcs, option),
46454687
(17, initial_counterparty_commitment_info, option),
46464688
(19, channel_id, option),
4689+
(21, balances_empty_height, option),
46474690
});
46484691

46494692
// `HolderForceClosedWithInfo` replaced `HolderForceClosed` in v0.0.122. If we have both
@@ -4722,6 +4765,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
47224765
best_block,
47234766
counterparty_node_id,
47244767
initial_counterparty_commitment_info,
4768+
balances_empty_height,
47254769
})))
47264770
}
47274771
}

0 commit comments

Comments
 (0)