@@ -934,6 +934,9 @@ pub(crate) struct ChannelMonitorImpl<Signer: WriteableEcdsaChannelSigner> {
934
934
/// Ordering of tuple data: (their_per_commitment_point, feerate_per_kw, to_broadcaster_sats,
935
935
/// to_countersignatory_sats)
936
936
initial_counterparty_commitment_info : Option < ( PublicKey , u32 , u64 , u64 ) > ,
937
+
938
+ /// The first block height at which we had no remaining claimable balances.
939
+ blanaces_empty_height : Option < u32 > ,
937
940
}
938
941
939
942
/// Transaction outputs to watch for on-chain spends.
@@ -1327,6 +1330,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
1327
1330
best_block,
1328
1331
counterparty_node_id : Some ( counterparty_node_id) ,
1329
1332
initial_counterparty_commitment_info : None ,
1333
+ blanaces_empty_height : None ,
1330
1334
} )
1331
1335
}
1332
1336
@@ -1855,6 +1859,45 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
1855
1859
spendable_outputs
1856
1860
}
1857
1861
1862
+ /// Checks if the monitor is stale, meaning it has not been updated with a new block data in a
1863
+ /// substantial amount of time and thus has no outputs to watch, ie
1864
+ /// this monitor is not expected to be able to claim any funds on chain.
1865
+ ///
1866
+ /// The first time this method is called it will save the current known height of the monitor
1867
+ /// if all funds are claimed. Otherwise, if we yet to claim all funds, it will return false. If all funds
1868
+ /// are claimed, it will return true if the monitor has not been updated with new block data in
1869
+ /// a substantial amount of time referred as `threshold` and `balances_empty_height` is set.
1870
+ pub ( crate ) fn is_stale ( & self ) -> bool {
1871
+ let threshold = 2016 ; // TODO: Should this be configurable?
1872
+ let is_all_funds_claimed = self . get_claimable_balances ( ) . is_empty ( ) ;
1873
+ let current_height = self . current_best_block ( ) . height ;
1874
+ let inner = self . inner . lock ( ) . unwrap ( ) ;
1875
+ let blanaces_empty_height = inner. blanaces_empty_height ;
1876
+ if let Some ( blanaces_empty_height) = blanaces_empty_height {
1877
+ // This if can be ture at least in the second time this method is called. we check if
1878
+ // the monitor has not been updated with new block data to watch new ouputs in a
1879
+ // substantial amount(2016 blocks) of time meaning the channel is probably closed and
1880
+ // this monitor is not expected to be able to claim any funds on chain.
1881
+ debug_assert ! ( is_all_funds_claimed, "Trying to check if monitor is stale before all funds are claimed. Aborting." ) ;
1882
+ return current_height > blanaces_empty_height + threshold;
1883
+ } else if is_all_funds_claimed {
1884
+ // If we claimed all funds, but `balances_empty_height` is None, we set it to the
1885
+ // current height and start counting from there.
1886
+ // This is to to make sure we don't consider the monitor stale on the first call to
1887
+ // `is_stale` after all funds are claimed.
1888
+ let mut inner = inner;
1889
+ inner. blanaces_empty_height = Some ( current_height) ;
1890
+ return false ;
1891
+ }
1892
+ // We still have funds to claim.
1893
+ return false ;
1894
+ }
1895
+
1896
+ #[ cfg( test) ]
1897
+ pub fn balances_empty_height ( & self ) -> Option < u32 > {
1898
+ self . inner . lock ( ) . unwrap ( ) . blanaces_empty_height
1899
+ }
1900
+
1858
1901
#[ cfg( test) ]
1859
1902
pub fn get_counterparty_payment_script ( & self ) -> ScriptBuf {
1860
1903
self . inner . lock ( ) . unwrap ( ) . counterparty_payment_script . clone ( )
@@ -4721,6 +4764,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
4721
4764
best_block,
4722
4765
counterparty_node_id,
4723
4766
initial_counterparty_commitment_info,
4767
+ blanaces_empty_height : None ,
4724
4768
} ) ) )
4725
4769
}
4726
4770
}
0 commit comments