@@ -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