Skip to content

Commit 9eab8f1

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 a06de8f commit 9eab8f1

File tree

1 file changed

+62
-6
lines changed

1 file changed

+62
-6
lines changed

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;
@@ -156,8 +156,9 @@ where
156156
// Disconnect any stale blocks, but keep them in the cache for the next iteration.
157157
let (common_ancestor, connected_blocks) = {
158158
let chain_listener = &DynamicChainListener(chain_listener);
159+
let mut cache_wrapper = HeaderCacheNoDisconnect(&mut header_cache);
159160
let mut chain_notifier =
160-
ChainNotifier { header_cache: &mut header_cache, chain_listener };
161+
ChainNotifier { header_cache: &mut cache_wrapper, chain_listener };
161162
let difference = chain_notifier
162163
.find_difference_from_best_block(best_header, old_best_block, &mut chain_poller)
163164
.await?;
@@ -192,7 +193,9 @@ where
192193
const NO_BLOCK: Option<(u32, crate::poll::ValidatedBlock)> = None;
193194
let mut fetched_blocks = [NO_BLOCK; MAX_BLOCKS_AT_ONCE];
194195
for ((header, block_res), result) in results.into_iter().zip(fetched_blocks.iter_mut()) {
195-
*result = Some((header.height, block_res?));
196+
let block = block_res?;
197+
header_cache.block_connected(header.block_hash, *header);
198+
*result = Some((header.height, block));
196199
}
197200
debug_assert!(fetched_blocks.iter().take(most_connected_blocks.len()).all(|r| r.is_some()));
198201
// TODO: When our MSRV is 1.82, use is_sorted_by_key
@@ -243,10 +246,40 @@ impl<'a, L: chain::Listen + ?Sized> chain::Listen for DynamicChainListener<'a, L
243246
}
244247
}
245248

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

251284
#[tokio::test]
252285
async fn sync_from_same_chain() {
@@ -267,7 +300,13 @@ mod tests {
267300
(chain.best_block_at_height(3), &listener_3 as &dyn chain::Listen),
268301
];
269302
match synchronize_listeners(&chain, Network::Bitcoin, listeners).await {
270-
Ok((_, header)) => assert_eq!(header, chain.tip()),
303+
Ok((cache, header)) => {
304+
assert_eq!(header, chain.tip());
305+
assert!(cache.look_up(&chain.at_height(1).block_hash).is_some());
306+
assert!(cache.look_up(&chain.at_height(2).block_hash).is_some());
307+
assert!(cache.look_up(&chain.at_height(3).block_hash).is_some());
308+
assert!(cache.look_up(&chain.at_height(4).block_hash).is_some());
309+
},
271310
Err(e) => panic!("Unexpected error: {:?}", e),
272311
}
273312
}
@@ -298,7 +337,15 @@ mod tests {
298337
(fork_chain_3.best_block(), &listener_3 as &dyn chain::Listen),
299338
];
300339
match synchronize_listeners(&main_chain, Network::Bitcoin, listeners).await {
301-
Ok((_, header)) => assert_eq!(header, main_chain.tip()),
340+
Ok((cache, header)) => {
341+
assert_eq!(header, main_chain.tip());
342+
assert!(cache.look_up(&main_chain.at_height(1).block_hash).is_some());
343+
assert!(cache.look_up(&main_chain.at_height(2).block_hash).is_some());
344+
assert!(cache.look_up(&main_chain.at_height(3).block_hash).is_some());
345+
assert!(cache.look_up(&fork_chain_1.at_height(2).block_hash).is_none());
346+
assert!(cache.look_up(&fork_chain_2.at_height(3).block_hash).is_none());
347+
assert!(cache.look_up(&fork_chain_3.at_height(4).block_hash).is_none());
348+
},
302349
Err(e) => panic!("Unexpected error: {:?}", e),
303350
}
304351
}
@@ -332,7 +379,16 @@ mod tests {
332379
(fork_chain_3.best_block(), &listener_3 as &dyn chain::Listen),
333380
];
334381
match synchronize_listeners(&main_chain, Network::Bitcoin, listeners).await {
335-
Ok((_, header)) => assert_eq!(header, main_chain.tip()),
382+
Ok((cache, header)) => {
383+
assert_eq!(header, main_chain.tip());
384+
assert!(cache.look_up(&main_chain.at_height(1).block_hash).is_some());
385+
assert!(cache.look_up(&main_chain.at_height(2).block_hash).is_some());
386+
assert!(cache.look_up(&main_chain.at_height(3).block_hash).is_some());
387+
assert!(cache.look_up(&main_chain.at_height(4).block_hash).is_some());
388+
assert!(cache.look_up(&fork_chain_1.at_height(2).block_hash).is_none());
389+
assert!(cache.look_up(&fork_chain_1.at_height(3).block_hash).is_none());
390+
assert!(cache.look_up(&fork_chain_1.at_height(4).block_hash).is_none());
391+
},
336392
Err(e) => panic!("Unexpected error: {:?}", e),
337393
}
338394
}

0 commit comments

Comments
 (0)