Skip to content

Commit cf994a3

Browse files
Matt CoralloTheBlueMatt
authored andcommitted
Include recent blocks in the synchronize_listeners-returned cache
When `synchronize_listeners` runs, it returns a cache of the headers it needed when doing chain difference-finding. This allows us to ensure that when we start running normally we have all the recent headers in case we need them to reorg. Sadly, in some cases it was returning a mostly-empty cache. Because it was only being filled during block difference reconciliation it would only get a block around each listener's fork point. Worse, because we were calling `disconnect_blocks` with the cache the cache would assume we were reorging against the main chain and drop blocks we actually want. Instead, we avoid dropping blocks on `disconnect_blocks` calls and ensure we always add connected blocks to the cache.
1 parent b1596f9 commit cf994a3

File tree

1 file changed

+56
-6
lines changed

1 file changed

+56
-6
lines changed

lightning-block-sync/src/init.rs

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
use crate::async_poll::{MultiResultFuturePoller, ResultFuture};
55
use crate::poll::{ChainPoller, Poll, Validate, ValidatedBlockHeader};
6-
use crate::{BlockData, BlockSource, BlockSourceResult, ChainNotifier, HeaderCache};
6+
use crate::{BlockData, BlockSource, BlockSourceResult, Cache, ChainNotifier, HeaderCache};
77

88
use bitcoin::block::Header;
99
use 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)]
238264
mod 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

Comments
 (0)