@@ -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,52 @@ 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 < L : Logger > ( & self , logger : & L ) -> bool {
1870+ let mut 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+ if is_all_funds_claimed {
1875+ if !inner. funding_spend_seen {
1876+ debug_assert ! ( false , "We should see funding spend by the time a monitor clears out" ) ;
1877+ is_all_funds_claimed = false ;
1878+ }
1879+ }
1880+
1881+ match ( inner. balances_empty_height , is_all_funds_claimed) {
1882+ ( Some ( balances_empty_height) , true ) => {
1883+ // Claimed all funds, check if reached the blocks threshold.
1884+ const BLOCKS_THRESHOLD : u32 = 4032 ; // ~four weeks
1885+ return current_height >= balances_empty_height + BLOCKS_THRESHOLD ;
1886+ } ,
1887+ ( Some ( _) , false ) => {
1888+ // previously assumed we claimed all funds, but we have new funds to claim.
1889+ // Should not happen in practice.
1890+ debug_assert ! ( false , "Thought we were done claiming funds, but claimable_balances now has entries" ) ;
1891+ log_error ! ( logger,
1892+ "WARNING: LDK thought it was done claiming all the available funds in the ChannelMonitor for channel {}, but later decided it had more to claim. This is potentially an important bug in LDK, please report it at https://github.com/lightningdevkit/rust-lightning/issues/new" ,
1893+ inner. get_funding_txo( ) . 0 ) ;
1894+ inner. balances_empty_height = None ;
1895+ false
1896+ } ,
1897+ ( None , true ) => {
1898+ // Claimed all funds but `balances_empty_height` is None. It is set to the
1899+ // current block height.
1900+ inner. balances_empty_height = Some ( current_height) ;
1901+ false
1902+ } ,
1903+ ( None , false ) => {
1904+ // Have funds to claim.
1905+ false
1906+ } ,
1907+ }
1908+ }
1909+
18591910 #[ cfg( test) ]
18601911 pub fn get_counterparty_payment_script ( & self ) -> ScriptBuf {
18611912 self . inner . lock ( ) . unwrap ( ) . counterparty_payment_script . clone ( )
@@ -4632,6 +4683,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
46324683 let mut spendable_txids_confirmed = Some ( Vec :: new ( ) ) ;
46334684 let mut counterparty_fulfilled_htlcs = Some ( new_hash_map ( ) ) ;
46344685 let mut initial_counterparty_commitment_info = None ;
4686+ let mut balances_empty_height = None ;
46354687 let mut channel_id = None ;
46364688 read_tlv_fields ! ( reader, {
46374689 ( 1 , funding_spend_confirmed, option) ,
@@ -4644,6 +4696,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
46444696 ( 15 , counterparty_fulfilled_htlcs, option) ,
46454697 ( 17 , initial_counterparty_commitment_info, option) ,
46464698 ( 19 , channel_id, option) ,
4699+ ( 21 , balances_empty_height, option) ,
46474700 } ) ;
46484701
46494702 // `HolderForceClosedWithInfo` replaced `HolderForceClosed` in v0.0.122. If we have both
@@ -4722,6 +4775,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
47224775 best_block,
47234776 counterparty_node_id,
47244777 initial_counterparty_commitment_info,
4778+ balances_empty_height,
47254779 } ) ) )
47264780 }
47274781}
0 commit comments