@@ -5,7 +5,7 @@ use crate::wallet::{num_blocks_from_conf_target, Wallet};
55use crate :: { Error , KeysManager } ;
66
77use lightning:: chain:: chaininterface:: { BroadcasterInterface , ConfirmationTarget , FeeEstimator } ;
8- use lightning:: chain:: BestBlock ;
8+ use lightning:: chain:: { self , BestBlock , Confirm , Filter , Listen } ;
99use lightning:: impl_writeable_tlv_based;
1010use lightning:: sign:: { EntropySource , SpendableOutputDescriptor } ;
1111use lightning:: util:: ser:: Writeable ;
@@ -16,6 +16,8 @@ use bitcoin::{BlockHash, BlockHeader, LockTime, PackedLockTime, Script, Transact
1616use std:: ops:: Deref ;
1717use std:: sync:: { Arc , Mutex } ;
1818
19+ const CONSIDERED_SPENT_THRESHOLD_CONF : u32 = 6 ;
20+
1921#[ derive( Clone , Debug , PartialEq , Eq ) ]
2022pub ( crate ) struct SpendableOutputInfo {
2123 id : [ u8 ; 32 ] ,
@@ -33,29 +35,39 @@ impl_writeable_tlv_based!(SpendableOutputInfo, {
3335 ( 8 , confirmed_in_block, option) ,
3436} ) ;
3537
36- pub ( crate ) struct OutputSweeper < K : KVStore + Sync + Send , L : Deref >
38+ pub ( crate ) struct OutputSweeper < K : KVStore + Sync + Send , F : Deref , L : Deref >
3739where
40+ F :: Target : Filter ,
3841 L :: Target : Logger ,
3942{
4043 outputs : Mutex < Vec < SpendableOutputInfo > > ,
4144 wallet : Arc < Wallet < bdk:: database:: SqliteDatabase , L > > ,
4245 keys_manager : Arc < KeysManager > ,
4346 kv_store : Arc < K > ,
4447 best_block : Mutex < BestBlock > ,
48+ chain_source : Option < F > ,
4549 logger : L ,
4650}
4751
48- impl < K : KVStore + Sync + Send , L : Deref > OutputSweeper < K , L >
52+ impl < K : KVStore + Sync + Send , F : Deref , L : Deref > OutputSweeper < K , F , L >
4953where
54+ F :: Target : Filter ,
5055 L :: Target : Logger ,
5156{
5257 pub ( crate ) fn new (
5358 outputs : Vec < SpendableOutputInfo > , wallet : Arc < Wallet < bdk:: database:: SqliteDatabase , L > > ,
54- keys_manager : Arc < KeysManager > , kv_store : Arc < K > , best_block : BestBlock , logger : L ,
59+ keys_manager : Arc < KeysManager > , kv_store : Arc < K > , best_block : BestBlock ,
60+ chain_source : Option < F > , logger : L ,
5561 ) -> Self {
62+ for o in & outputs {
63+ if let Some ( filter) = chain_source. as_ref ( ) {
64+ filter. register_tx ( & o. spending_tx . txid ( ) , & Script :: new ( ) )
65+ }
66+ }
67+
5668 let outputs = Mutex :: new ( outputs) ;
5769 let best_block = Mutex :: new ( best_block) ;
58- Self { outputs, wallet, keys_manager, kv_store, best_block, logger }
70+ Self { outputs, wallet, keys_manager, kv_store, best_block, chain_source , logger }
5971 }
6072
6173 pub ( crate ) fn add_outputs ( & self , output_descriptors : Vec < SpendableOutputDescriptor > ) {
6476 let spending_tx = match self . get_spending_tx ( & output_descriptors) {
6577 Ok ( Some ( spending_tx) ) => {
6678 self . wallet . broadcast_transactions ( & [ & spending_tx] ) ;
79+ if let Some ( filter) = self . chain_source . as_ref ( ) {
80+ filter. register_tx ( & spending_tx. txid ( ) , & Script :: new ( ) )
81+ }
6782 spending_tx
6883 }
6984 Ok ( None ) => {
@@ -138,3 +153,145 @@ where
138153 } )
139154 }
140155}
156+
157+ impl < K : KVStore + Sync + Send , F : Deref , L : Deref > Listen for OutputSweeper < K , F , L >
158+ where
159+ F :: Target : Filter ,
160+ L :: Target : Logger ,
161+ {
162+ fn filtered_block_connected (
163+ & self , header : & BlockHeader , txdata : & chain:: transaction:: TransactionData , height : u32 ,
164+ ) {
165+ {
166+ let best_block = self . best_block . lock ( ) . unwrap ( ) ;
167+ assert_eq ! ( best_block. block_hash( ) , header. prev_blockhash,
168+ "Blocks must be connected in chain-order - the connected header must build on the last connected header" ) ;
169+ assert_eq ! ( best_block. height( ) , height - 1 ,
170+ "Blocks must be connected in chain-order - the connected block height must be one greater than the previous height" ) ;
171+ }
172+
173+ self . transactions_confirmed ( header, txdata, height) ;
174+ self . best_block_updated ( header, height) ;
175+ }
176+
177+ fn block_disconnected ( & self , header : & BlockHeader , height : u32 ) {
178+ let new_height = height - 1 ;
179+ {
180+ let mut best_block = self . best_block . lock ( ) . unwrap ( ) ;
181+ assert_eq ! ( best_block. block_hash( ) , header. block_hash( ) ,
182+ "Blocks must be disconnected in chain-order - the disconnected header must be the last connected header" ) ;
183+ assert_eq ! ( best_block. height( ) , height,
184+ "Blocks must be disconnected in chain-order - the disconnected block must have the correct height" ) ;
185+ * best_block = BestBlock :: new ( header. prev_blockhash , new_height)
186+ }
187+
188+ let mut locked_outputs = self . outputs . lock ( ) . unwrap ( ) ;
189+ for output_info in locked_outputs. iter_mut ( ) {
190+ if output_info. confirmed_in_block == Some ( ( height, header. block_hash ( ) ) ) {
191+ output_info. confirmed_in_block = None ;
192+ }
193+ }
194+ }
195+ }
196+
197+ impl < K : KVStore + Sync + Send , F : Deref , L : Deref > Confirm for OutputSweeper < K , F , L >
198+ where
199+ F :: Target : Filter ,
200+ L :: Target : Logger ,
201+ {
202+ fn transactions_confirmed (
203+ & self , header : & BlockHeader , txdata : & chain:: transaction:: TransactionData , height : u32 ,
204+ ) {
205+ let mut locked_outputs = self . outputs . lock ( ) . unwrap ( ) ;
206+ for ( _, tx) in txdata {
207+ locked_outputs
208+ . iter_mut ( )
209+ . filter ( |o| o. spending_tx . txid ( ) == tx. txid ( ) )
210+ . for_each ( |o| o. confirmed_in_block = Some ( ( height, header. block_hash ( ) ) ) ) ;
211+ }
212+ }
213+
214+ fn transaction_unconfirmed ( & self , txid : & Txid ) {
215+ let mut locked_outputs = self . outputs . lock ( ) . unwrap ( ) ;
216+
217+ // Get what height was unconfirmed.
218+ let unconf_height = locked_outputs
219+ . iter ( )
220+ . find ( |o| o. spending_tx . txid ( ) == * txid)
221+ . and_then ( |o| o. confirmed_in_block )
222+ . map ( |t| t. 0 ) ;
223+
224+ // Unconfirm all >= this height.
225+ locked_outputs
226+ . iter_mut ( )
227+ . filter ( |o| o. confirmed_in_block . map ( |t| t. 0 ) >= unconf_height)
228+ . for_each ( |o| o. confirmed_in_block = None ) ;
229+ }
230+
231+ fn best_block_updated ( & self , header : & BlockHeader , height : u32 ) {
232+ * self . best_block . lock ( ) . unwrap ( ) = BestBlock :: new ( header. block_hash ( ) , height) ;
233+
234+ let mut locked_outputs = self . outputs . lock ( ) . unwrap ( ) ;
235+
236+ // Rebroadcast all outputs that didn't get confirmed by now.
237+ for output_info in locked_outputs. iter_mut ( ) . filter ( |o| o. confirmed_in_block . is_none ( ) ) {
238+ if height
239+ >= output_info. broadcast_height
240+ + num_blocks_from_conf_target ( ConfirmationTarget :: Background )
241+ {
242+ let output_descriptors = vec ! [ output_info. descriptor. clone( ) ] ;
243+ match self . get_spending_tx ( & output_descriptors) {
244+ Ok ( Some ( spending_tx) ) => {
245+ self . wallet . broadcast_transactions ( & [ & spending_tx] ) ;
246+ if let Some ( filter) = self . chain_source . as_ref ( ) {
247+ filter. register_tx ( & spending_tx. txid ( ) , & Script :: new ( ) )
248+ }
249+ output_info. spending_tx = spending_tx;
250+ output_info. broadcast_height = height;
251+ }
252+ Ok ( None ) => {
253+ log_debug ! (
254+ self . logger,
255+ "Omitted spending static outputs: {:?}" ,
256+ output_descriptors
257+ ) ;
258+ }
259+ Err ( err) => {
260+ log_error ! ( self . logger, "Error spending outputs: {:?}" , err) ;
261+ }
262+ } ;
263+ }
264+ }
265+
266+ // Prune all outputs that have sufficient depth by now.
267+ locked_outputs. retain ( |o| {
268+ if let Some ( ( conf_height, _) ) = o. confirmed_in_block {
269+ if height >= conf_height + CONSIDERED_SPENT_THRESHOLD_CONF {
270+ let key = hex_utils:: to_string ( & o. id ) ;
271+ match self . kv_store . remove ( SPENDABLE_OUTPUT_INFO_PERSISTENCE_NAMESPACE , & key) {
272+ Ok ( _) => return false ,
273+ Err ( e) => {
274+ log_error ! (
275+ self . logger,
276+ "Removal of key {}/{} failed due to: {}" ,
277+ SPENDABLE_OUTPUT_INFO_PERSISTENCE_NAMESPACE ,
278+ key,
279+ e
280+ ) ;
281+ return true ;
282+ }
283+ }
284+ }
285+ }
286+ true
287+ } ) ;
288+ }
289+
290+ fn get_relevant_txids ( & self ) -> Vec < ( Txid , Option < BlockHash > ) > {
291+ let locked_outputs = self . outputs . lock ( ) . unwrap ( ) ;
292+ locked_outputs
293+ . iter ( )
294+ . map ( |o| ( o. spending_tx . txid ( ) , o. confirmed_in_block . map ( |c| c. 1 ) ) )
295+ . collect :: < Vec < _ > > ( )
296+ }
297+ }
0 commit comments