@@ -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,42 @@ 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 ( & 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
+
1859
1900
#[ cfg( test) ]
1860
1901
pub fn get_counterparty_payment_script ( & self ) -> ScriptBuf {
1861
1902
self . inner . lock ( ) . unwrap ( ) . counterparty_payment_script . clone ( )
@@ -4632,6 +4673,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
4632
4673
let mut spendable_txids_confirmed = Some ( Vec :: new ( ) ) ;
4633
4674
let mut counterparty_fulfilled_htlcs = Some ( new_hash_map ( ) ) ;
4634
4675
let mut initial_counterparty_commitment_info = None ;
4676
+ let mut balances_empty_height = None ;
4635
4677
let mut channel_id = None ;
4636
4678
read_tlv_fields ! ( reader, {
4637
4679
( 1 , funding_spend_confirmed, option) ,
@@ -4644,6 +4686,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
4644
4686
( 15 , counterparty_fulfilled_htlcs, option) ,
4645
4687
( 17 , initial_counterparty_commitment_info, option) ,
4646
4688
( 19 , channel_id, option) ,
4689
+ ( 21 , balances_empty_height, option) ,
4647
4690
} ) ;
4648
4691
4649
4692
// `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
4722
4765
best_block,
4723
4766
counterparty_node_id,
4724
4767
initial_counterparty_commitment_info,
4768
+ balances_empty_height,
4725
4769
} ) ) )
4726
4770
}
4727
4771
}
0 commit comments