33
44use crate :: async_poll:: { MultiResultFuturePoller , ResultFuture } ;
55use crate :: poll:: { ChainPoller , Poll , Validate , ValidatedBlockHeader } ;
6- use crate :: { BlockData , BlockSource , BlockSourceResult , ChainNotifier , HeaderCache } ;
6+ use crate :: { BlockData , BlockSource , BlockSourceResult , Cache , ChainNotifier , HeaderCache } ;
77
88use bitcoin:: block:: Header ;
99use bitcoin:: network:: Network ;
@@ -153,8 +153,9 @@ where
153153 // Disconnect any stale blocks, but keep them in the cache for the next iteration.
154154 let ( common_ancestor, connected_blocks) = {
155155 let chain_listener = & DynamicChainListener ( chain_listener) ;
156+ let mut cache_wrapper = HeaderCacheNoDisconnect ( & mut header_cache) ;
156157 let mut chain_notifier =
157- ChainNotifier { header_cache : & mut header_cache , chain_listener } ;
158+ ChainNotifier { header_cache : & mut cache_wrapper , chain_listener } ;
158159 let difference = chain_notifier
159160 . find_difference_from_best_block ( best_header, old_best_block, & mut chain_poller)
160161 . await ?;
@@ -188,7 +189,9 @@ where
188189
189190 let mut fetched_blocks = [ const { None } ; MAX_BLOCKS_AT_ONCE ] ;
190191 for ( ( header, block_res) , result) in results. into_iter ( ) . zip ( fetched_blocks. iter_mut ( ) ) {
191- * result = Some ( ( header. height , block_res?) ) ;
192+ let block = block_res?;
193+ header_cache. block_connected ( header. block_hash , * header) ;
194+ * result = Some ( ( header. height , block) ) ;
192195 }
193196 debug_assert ! ( fetched_blocks. iter( ) . take( most_connected_blocks. len( ) ) . all( |r| r. is_some( ) ) ) ;
194197 debug_assert ! ( fetched_blocks
@@ -234,10 +237,34 @@ impl<'a, L: chain::Listen + ?Sized> chain::Listen for DynamicChainListener<'a, L
234237 }
235238}
236239
240+ /// Wrapper around HeaderCache that ignores `blocks_disconnected` calls, retaining disconnected
241+ /// blocks in the cache. This is useful during initial sync to keep headers available across
242+ /// multiple listeners.
243+ struct HeaderCacheNoDisconnect < ' a > ( & ' a mut HeaderCache ) ;
244+
245+ impl < ' a > crate :: Cache for & mut HeaderCacheNoDisconnect < ' a > {
246+ fn look_up ( & self , block_hash : & bitcoin:: hash_types:: BlockHash ) -> Option < & ValidatedBlockHeader > {
247+ self . 0 . look_up ( block_hash)
248+ }
249+
250+ fn insert_during_diff ( & mut self , block_hash : bitcoin:: hash_types:: BlockHash , block_header : ValidatedBlockHeader ) {
251+ self . 0 . insert_during_diff ( block_hash, block_header) ;
252+ }
253+
254+ fn block_connected ( & mut self , block_hash : bitcoin:: hash_types:: BlockHash , block_header : ValidatedBlockHeader ) {
255+ self . 0 . block_connected ( block_hash, block_header) ;
256+ }
257+
258+ fn blocks_disconnected ( & mut self , _fork_point : & ValidatedBlockHeader ) {
259+ // Intentionally ignore disconnections to retain blocks in cache
260+ }
261+ }
262+
237263#[ cfg( test) ]
238264mod tests {
239265 use super :: * ;
240266 use crate :: test_utils:: { Blockchain , MockChainListener } ;
267+ use crate :: Cache ;
241268
242269 #[ tokio:: test]
243270 async fn sync_from_same_chain ( ) {
@@ -258,7 +285,13 @@ mod tests {
258285 ( chain. best_block_at_height( 3 ) , & listener_3 as & dyn chain:: Listen ) ,
259286 ] ;
260287 match synchronize_listeners ( & chain, Network :: Bitcoin , listeners) . await {
261- Ok ( ( _, header) ) => assert_eq ! ( header, chain. tip( ) ) ,
288+ Ok ( ( cache, header) ) => {
289+ assert_eq ! ( header, chain. tip( ) ) ;
290+ assert ! ( cache. look_up( & chain. at_height( 1 ) . block_hash) . is_some( ) ) ;
291+ assert ! ( cache. look_up( & chain. at_height( 2 ) . block_hash) . is_some( ) ) ;
292+ assert ! ( cache. look_up( & chain. at_height( 3 ) . block_hash) . is_some( ) ) ;
293+ assert ! ( cache. look_up( & chain. at_height( 4 ) . block_hash) . is_some( ) ) ;
294+ } ,
262295 Err ( e) => panic ! ( "Unexpected error: {:?}" , e) ,
263296 }
264297 }
@@ -289,7 +322,15 @@ mod tests {
289322 ( fork_chain_3. best_block( ) , & listener_3 as & dyn chain:: Listen ) ,
290323 ] ;
291324 match synchronize_listeners ( & main_chain, Network :: Bitcoin , listeners) . await {
292- Ok ( ( _, header) ) => assert_eq ! ( header, main_chain. tip( ) ) ,
325+ Ok ( ( cache, header) ) => {
326+ assert_eq ! ( header, main_chain. tip( ) ) ;
327+ assert ! ( cache. look_up( & main_chain. at_height( 1 ) . block_hash) . is_some( ) ) ;
328+ assert ! ( cache. look_up( & main_chain. at_height( 2 ) . block_hash) . is_some( ) ) ;
329+ assert ! ( cache. look_up( & main_chain. at_height( 3 ) . block_hash) . is_some( ) ) ;
330+ assert ! ( cache. look_up( & fork_chain_1. at_height( 2 ) . block_hash) . is_none( ) ) ;
331+ assert ! ( cache. look_up( & fork_chain_2. at_height( 3 ) . block_hash) . is_none( ) ) ;
332+ assert ! ( cache. look_up( & fork_chain_3. at_height( 4 ) . block_hash) . is_none( ) ) ;
333+ } ,
293334 Err ( e) => panic ! ( "Unexpected error: {:?}" , e) ,
294335 }
295336 }
@@ -323,7 +364,16 @@ mod tests {
323364 ( fork_chain_3. best_block( ) , & listener_3 as & dyn chain:: Listen ) ,
324365 ] ;
325366 match synchronize_listeners ( & main_chain, Network :: Bitcoin , listeners) . await {
326- Ok ( ( _, header) ) => assert_eq ! ( header, main_chain. tip( ) ) ,
367+ Ok ( ( cache, header) ) => {
368+ assert_eq ! ( header, main_chain. tip( ) ) ;
369+ assert ! ( cache. look_up( & main_chain. at_height( 1 ) . block_hash) . is_some( ) ) ;
370+ assert ! ( cache. look_up( & main_chain. at_height( 2 ) . block_hash) . is_some( ) ) ;
371+ assert ! ( cache. look_up( & main_chain. at_height( 3 ) . block_hash) . is_some( ) ) ;
372+ assert ! ( cache. look_up( & main_chain. at_height( 4 ) . block_hash) . is_some( ) ) ;
373+ assert ! ( cache. look_up( & fork_chain_1. at_height( 2 ) . block_hash) . is_none( ) ) ;
374+ assert ! ( cache. look_up( & fork_chain_1. at_height( 3 ) . block_hash) . is_none( ) ) ;
375+ assert ! ( cache. look_up( & fork_chain_1. at_height( 4 ) . block_hash) . is_none( ) ) ;
376+ } ,
327377 Err ( e) => panic ! ( "Unexpected error: {:?}" , e) ,
328378 }
329379 }
0 commit comments