Skip to content

Commit 2454f0c

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 f7b403c commit 2454f0c

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?;
@@ -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,34 @@ 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(&self, block_hash: &bitcoin::hash_types::BlockHash) -> Option<&ValidatedBlockHeader> {
255+
self.0.look_up(block_hash)
256+
}
257+
258+
fn insert_during_diff(&mut self, block_hash: bitcoin::hash_types::BlockHash, block_header: ValidatedBlockHeader) {
259+
self.0.insert_during_diff(block_hash, block_header);
260+
}
261+
262+
fn block_connected(&mut self, block_hash: bitcoin::hash_types::BlockHash, block_header: ValidatedBlockHeader) {
263+
self.0.block_connected(block_hash, block_header);
264+
}
265+
266+
fn blocks_disconnected(&mut self, _fork_point: &ValidatedBlockHeader) {
267+
// Intentionally ignore disconnections to retain blocks in cache
268+
}
269+
}
270+
245271
#[cfg(test)]
246272
mod tests {
247273
use super::*;
248274
use crate::test_utils::{Blockchain, MockChainListener};
275+
use crate::Cache;
249276

250277
#[tokio::test]
251278
async fn sync_from_same_chain() {
@@ -266,7 +293,13 @@ mod tests {
266293
(chain.best_block_at_height(3), &listener_3 as &dyn chain::Listen),
267294
];
268295
match synchronize_listeners(&chain, Network::Bitcoin, listeners).await {
269-
Ok((_, header)) => assert_eq!(header, chain.tip()),
296+
Ok((cache, header)) => {
297+
assert_eq!(header, chain.tip());
298+
assert!(cache.look_up(&chain.at_height(1).block_hash).is_some());
299+
assert!(cache.look_up(&chain.at_height(2).block_hash).is_some());
300+
assert!(cache.look_up(&chain.at_height(3).block_hash).is_some());
301+
assert!(cache.look_up(&chain.at_height(4).block_hash).is_some());
302+
},
270303
Err(e) => panic!("Unexpected error: {:?}", e),
271304
}
272305
}
@@ -297,7 +330,15 @@ mod tests {
297330
(fork_chain_3.best_block(), &listener_3 as &dyn chain::Listen),
298331
];
299332
match synchronize_listeners(&main_chain, Network::Bitcoin, listeners).await {
300-
Ok((_, header)) => assert_eq!(header, main_chain.tip()),
333+
Ok((cache, header)) => {
334+
assert_eq!(header, main_chain.tip());
335+
assert!(cache.look_up(&main_chain.at_height(1).block_hash).is_some());
336+
assert!(cache.look_up(&main_chain.at_height(2).block_hash).is_some());
337+
assert!(cache.look_up(&main_chain.at_height(3).block_hash).is_some());
338+
assert!(cache.look_up(&fork_chain_1.at_height(2).block_hash).is_none());
339+
assert!(cache.look_up(&fork_chain_2.at_height(3).block_hash).is_none());
340+
assert!(cache.look_up(&fork_chain_3.at_height(4).block_hash).is_none());
341+
},
301342
Err(e) => panic!("Unexpected error: {:?}", e),
302343
}
303344
}
@@ -331,7 +372,16 @@ mod tests {
331372
(fork_chain_3.best_block(), &listener_3 as &dyn chain::Listen),
332373
];
333374
match synchronize_listeners(&main_chain, Network::Bitcoin, listeners).await {
334-
Ok((_, header)) => assert_eq!(header, main_chain.tip()),
375+
Ok((cache, header)) => {
376+
assert_eq!(header, main_chain.tip());
377+
assert!(cache.look_up(&main_chain.at_height(1).block_hash).is_some());
378+
assert!(cache.look_up(&main_chain.at_height(2).block_hash).is_some());
379+
assert!(cache.look_up(&main_chain.at_height(3).block_hash).is_some());
380+
assert!(cache.look_up(&main_chain.at_height(4).block_hash).is_some());
381+
assert!(cache.look_up(&fork_chain_1.at_height(2).block_hash).is_none());
382+
assert!(cache.look_up(&fork_chain_1.at_height(3).block_hash).is_none());
383+
assert!(cache.look_up(&fork_chain_1.at_height(4).block_hash).is_none());
384+
},
335385
Err(e) => panic!("Unexpected error: {:?}", e),
336386
}
337387
}

0 commit comments

Comments
 (0)