@@ -935,6 +935,9 @@ pub(crate) struct ChannelMonitorImpl<Signer: WriteableEcdsaChannelSigner> {
935
935
/// Ordering of tuple data: (their_per_commitment_point, feerate_per_kw, to_broadcaster_sats,
936
936
/// to_countersignatory_sats)
937
937
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 > ,
938
941
}
939
942
940
943
/// Transaction outputs to watch for on-chain spends.
@@ -1145,6 +1148,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signe
1145
1148
( 15 , self . counterparty_fulfilled_htlcs, required) ,
1146
1149
( 17 , self . initial_counterparty_commitment_info, option) ,
1147
1150
( 19 , self . channel_id, required) ,
1151
+ ( 21 , self . balances_empty_height, option) ,
1148
1152
} ) ;
1149
1153
1150
1154
Ok ( ( ) )
@@ -1328,6 +1332,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
1328
1332
best_block,
1329
1333
counterparty_node_id : Some ( counterparty_node_id) ,
1330
1334
initial_counterparty_commitment_info : None ,
1335
+ balances_empty_height : None ,
1331
1336
} )
1332
1337
}
1333
1338
@@ -1856,6 +1861,52 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
1856
1861
spendable_outputs
1857
1862
}
1858
1863
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
+
1859
1910
#[ cfg( test) ]
1860
1911
pub fn get_counterparty_payment_script ( & self ) -> ScriptBuf {
1861
1912
self . inner . lock ( ) . unwrap ( ) . counterparty_payment_script . clone ( )
@@ -4632,6 +4683,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
4632
4683
let mut spendable_txids_confirmed = Some ( Vec :: new ( ) ) ;
4633
4684
let mut counterparty_fulfilled_htlcs = Some ( new_hash_map ( ) ) ;
4634
4685
let mut initial_counterparty_commitment_info = None ;
4686
+ let mut balances_empty_height = None ;
4635
4687
let mut channel_id = None ;
4636
4688
read_tlv_fields ! ( reader, {
4637
4689
( 1 , funding_spend_confirmed, option) ,
@@ -4644,6 +4696,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
4644
4696
( 15 , counterparty_fulfilled_htlcs, option) ,
4645
4697
( 17 , initial_counterparty_commitment_info, option) ,
4646
4698
( 19 , channel_id, option) ,
4699
+ ( 21 , balances_empty_height, option) ,
4647
4700
} ) ;
4648
4701
4649
4702
// `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
4722
4775
best_block,
4723
4776
counterparty_node_id,
4724
4777
initial_counterparty_commitment_info,
4778
+ balances_empty_height,
4725
4779
} ) ) )
4726
4780
}
4727
4781
}
0 commit comments