Skip to content

Commit a85b089

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 84a589e commit a85b089

1 file changed

Lines changed: 62 additions & 6 deletions

File tree

lightning-block-sync/src/init.rs

Lines changed: 62 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?;
@@ -189,7 +190,9 @@ where
189190
const NO_BLOCK: Option<(u32, crate::poll::ValidatedBlock)> = None;
190191
let mut fetched_blocks = [NO_BLOCK; MAX_BLOCKS_AT_ONCE];
191192
for ((header, block_res), result) in results.into_iter().zip(fetched_blocks.iter_mut()) {
192-
*result = Some((header.height, block_res?));
193+
let block = block_res?;
194+
header_cache.block_connected(header.block_hash, *header);
195+
*result = Some((header.height, block));
193196
}
194197
debug_assert!(fetched_blocks.iter().take(most_connected_blocks.len()).all(|r| r.is_some()));
195198
// TODO: When our MSRV is 1.82, use is_sorted_by_key
@@ -242,10 +245,40 @@ impl<'a, L: chain::Listen + ?Sized> chain::Listen for DynamicChainListener<'a, L
242245
}
243246
}
244247

248+
/// Wrapper around HeaderCache that ignores `blocks_disconnected` calls, retaining disconnected
249+
/// blocks in the cache. This is useful during initial sync to keep headers available across
250+
/// multiple listeners.
251+
struct HeaderCacheNoDisconnect<'a>(&'a mut HeaderCache);
252+
253+
impl<'a> crate::Cache for &mut HeaderCacheNoDisconnect<'a> {
254+
fn look_up(
255+
&self, block_hash: &bitcoin::hash_types::BlockHash,
256+
) -> Option<&ValidatedBlockHeader> {
257+
self.0.look_up(block_hash)
258+
}
259+
260+
fn insert_during_diff(
261+
&mut self, block_hash: bitcoin::hash_types::BlockHash, block_header: ValidatedBlockHeader,
262+
) {
263+
self.0.insert_during_diff(block_hash, block_header);
264+
}
265+
266+
fn block_connected(
267+
&mut self, block_hash: bitcoin::hash_types::BlockHash, block_header: ValidatedBlockHeader,
268+
) {
269+
self.0.block_connected(block_hash, block_header);
270+
}
271+
272+
fn blocks_disconnected(&mut self, _fork_point: &ValidatedBlockHeader) {
273+
// Intentionally ignore disconnections to retain blocks in cache
274+
}
275+
}
276+
245277
#[cfg(test)]
246278
mod tests {
247279
use super::*;
248280
use crate::test_utils::{Blockchain, MockChainListener};
281+
use crate::Cache;
249282

250283
#[tokio::test]
251284
async fn sync_from_same_chain() {
@@ -266,7 +299,13 @@ mod tests {
266299
(chain.best_block_at_height(3), &listener_3 as &dyn chain::Listen),
267300
];
268301
match synchronize_listeners(&chain, Network::Bitcoin, listeners).await {
269-
Ok((_, header)) => assert_eq!(header, chain.tip()),
302+
Ok((cache, header)) => {
303+
assert_eq!(header, chain.tip());
304+
assert!(cache.look_up(&chain.at_height(1).block_hash).is_some());
305+
assert!(cache.look_up(&chain.at_height(2).block_hash).is_some());
306+
assert!(cache.look_up(&chain.at_height(3).block_hash).is_some());
307+
assert!(cache.look_up(&chain.at_height(4).block_hash).is_some());
308+
},
270309
Err(e) => panic!("Unexpected error: {:?}", e),
271310
}
272311
}
@@ -297,7 +336,15 @@ mod tests {
297336
(fork_chain_3.best_block(), &listener_3 as &dyn chain::Listen),
298337
];
299338
match synchronize_listeners(&main_chain, Network::Bitcoin, listeners).await {
300-
Ok((_, header)) => assert_eq!(header, main_chain.tip()),
339+
Ok((cache, header)) => {
340+
assert_eq!(header, main_chain.tip());
341+
assert!(cache.look_up(&main_chain.at_height(1).block_hash).is_some());
342+
assert!(cache.look_up(&main_chain.at_height(2).block_hash).is_some());
343+
assert!(cache.look_up(&main_chain.at_height(3).block_hash).is_some());
344+
assert!(cache.look_up(&fork_chain_1.at_height(2).block_hash).is_none());
345+
assert!(cache.look_up(&fork_chain_2.at_height(3).block_hash).is_none());
346+
assert!(cache.look_up(&fork_chain_3.at_height(4).block_hash).is_none());
347+
},
301348
Err(e) => panic!("Unexpected error: {:?}", e),
302349
}
303350
}
@@ -331,7 +378,16 @@ mod tests {
331378
(fork_chain_3.best_block(), &listener_3 as &dyn chain::Listen),
332379
];
333380
match synchronize_listeners(&main_chain, Network::Bitcoin, listeners).await {
334-
Ok((_, header)) => assert_eq!(header, main_chain.tip()),
381+
Ok((cache, header)) => {
382+
assert_eq!(header, main_chain.tip());
383+
assert!(cache.look_up(&main_chain.at_height(1).block_hash).is_some());
384+
assert!(cache.look_up(&main_chain.at_height(2).block_hash).is_some());
385+
assert!(cache.look_up(&main_chain.at_height(3).block_hash).is_some());
386+
assert!(cache.look_up(&main_chain.at_height(4).block_hash).is_some());
387+
assert!(cache.look_up(&fork_chain_1.at_height(2).block_hash).is_none());
388+
assert!(cache.look_up(&fork_chain_1.at_height(3).block_hash).is_none());
389+
assert!(cache.look_up(&fork_chain_1.at_height(4).block_hash).is_none());
390+
},
335391
Err(e) => panic!("Unexpected error: {:?}", e),
336392
}
337393
}

0 commit comments

Comments
 (0)