Skip to content

Commit 51b56fc

Browse files
Matt CoralloTheBlueMatt
authored andcommitted
Make the Cache trait priv, just use UnboundedCache publicly
In the previous commit, we moved to relying on `BestBlock::previous_blocks` to find the fork point in `lightning-block-sync`'s `init::synchronize_listeners`. Here we now drop the `Cache` parameter as we no longer rely on it. Because we now have no reason to want a persistent `Cache`, we remove the trait from the public interface. However, to keep disconnections reliable we return the `UnboundedCache` we built up during initial sync from `init::synchronize_listeners` which we expect developers to pass to `SpvClient::new`.
1 parent a3c3af9 commit 51b56fc

File tree

2 files changed

+54
-94
lines changed

2 files changed

+54
-94
lines changed

lightning-block-sync/src/init.rs

Lines changed: 19 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! from disk.
33
44
use crate::poll::{ChainPoller, Validate, ValidatedBlockHeader};
5-
use crate::{BlockSource, BlockSourceResult, Cache, ChainNotifier};
5+
use crate::{BlockSource, BlockSourceResult, Cache, ChainNotifier, UnboundedCache};
66

77
use bitcoin::block::Header;
88
use bitcoin::hash_types::BlockHash;
@@ -32,9 +32,9 @@ where
3232
/// Performs a one-time sync of chain listeners using a single *trusted* block source, bringing each
3333
/// listener's view of the chain from its paired block hash to `block_source`'s best chain tip.
3434
///
35-
/// Upon success, the returned header can be used to initialize [`SpvClient`]. In the case of
36-
/// failure, each listener may be left at a different block hash than the one it was originally
37-
/// paired with.
35+
/// Upon success, the returned header and header cache can be used to initialize [`SpvClient`]. In
36+
/// the case of failure, each listener may be left at a different block hash than the one it was
37+
/// originally paired with.
3838
///
3939
/// Useful during startup to bring the [`ChannelManager`] and each [`ChannelMonitor`] in sync before
4040
/// switching to [`SpvClient`]. For example:
@@ -114,14 +114,13 @@ where
114114
/// };
115115
///
116116
/// // Synchronize any channel monitors and the channel manager to be on the best block.
117-
/// let mut cache = UnboundedCache::new();
118117
/// let mut monitor_listener = (monitor, &*tx_broadcaster, &*fee_estimator, &*logger);
119118
/// let listeners = vec![
120119
/// (monitor_best_block, &monitor_listener as &dyn chain::Listen),
121120
/// (manager_best_block, &manager as &dyn chain::Listen),
122121
/// ];
123-
/// let chain_tip = init::synchronize_listeners(
124-
/// block_source, Network::Bitcoin, &mut cache, listeners).await.unwrap();
122+
/// let (chain_cache, chain_tip) = init::synchronize_listeners(
123+
/// block_source, Network::Bitcoin, listeners).await.unwrap();
125124
///
126125
/// // Allow the chain monitor to watch any channels.
127126
/// let monitor = monitor_listener.0;
@@ -130,7 +129,7 @@ where
130129
/// // Create an SPV client to notify the chain monitor and channel manager of block events.
131130
/// let chain_poller = poll::ChainPoller::new(block_source, Network::Bitcoin);
132131
/// let mut chain_listener = (chain_monitor, &manager);
133-
/// let spv_client = SpvClient::new(chain_tip, chain_poller, &mut cache, &chain_listener);
132+
/// let spv_client = SpvClient::new(chain_tip, chain_poller, chain_cache, &chain_listener);
134133
/// }
135134
/// ```
136135
///
@@ -139,12 +138,11 @@ where
139138
/// [`ChannelMonitor`]: lightning::chain::channelmonitor::ChannelMonitor
140139
pub async fn synchronize_listeners<
141140
B: Deref + Sized + Send + Sync,
142-
C: Cache,
143141
L: chain::Listen + ?Sized,
144142
>(
145-
block_source: B, network: Network, header_cache: &mut C,
143+
block_source: B, network: Network,
146144
mut chain_listeners: Vec<(BestBlock, &L)>,
147-
) -> BlockSourceResult<ValidatedBlockHeader>
145+
) -> BlockSourceResult<(UnboundedCache, ValidatedBlockHeader)>
148146
where
149147
B::Target: BlockSource,
150148
{
@@ -155,12 +153,12 @@ where
155153
let mut chain_listeners_at_height = Vec::new();
156154
let mut most_common_ancestor = None;
157155
let mut most_connected_blocks = Vec::new();
156+
let mut header_cache = UnboundedCache::new();
158157
for (old_best_block, chain_listener) in chain_listeners.drain(..) {
159158
// Disconnect any stale blocks, but keep them in the cache for the next iteration.
160-
let header_cache = &mut ReadOnlyCache(header_cache);
161159
let (common_ancestor, connected_blocks) = {
162160
let chain_listener = &DynamicChainListener(chain_listener);
163-
let mut chain_notifier = ChainNotifier { header_cache, chain_listener };
161+
let mut chain_notifier = ChainNotifier { header_cache: &mut header_cache, chain_listener };
164162
let difference =
165163
chain_notifier.find_difference_from_best_block(best_header, old_best_block, &mut chain_poller).await?;
166164
if difference.common_ancestor.block_hash != old_best_block.block_hash {
@@ -180,32 +178,14 @@ where
180178
// Connect new blocks for all listeners at once to avoid re-fetching blocks.
181179
if let Some(common_ancestor) = most_common_ancestor {
182180
let chain_listener = &ChainListenerSet(chain_listeners_at_height);
183-
let mut chain_notifier = ChainNotifier { header_cache, chain_listener };
181+
let mut chain_notifier = ChainNotifier { header_cache: &mut header_cache, chain_listener };
184182
chain_notifier
185183
.connect_blocks(common_ancestor, most_connected_blocks, &mut chain_poller)
186184
.await
187185
.map_err(|(e, _)| e)?;
188186
}
189187

190-
Ok(best_header)
191-
}
192-
193-
/// A wrapper to make a cache read-only.
194-
///
195-
/// Used to prevent losing headers that may be needed to disconnect blocks common to more than one
196-
/// listener.
197-
struct ReadOnlyCache<'a, C: Cache>(&'a mut C);
198-
199-
impl<'a, C: Cache> Cache for ReadOnlyCache<'a, C> {
200-
fn look_up(&self, block_hash: &BlockHash) -> Option<&ValidatedBlockHeader> {
201-
self.0.look_up(block_hash)
202-
}
203-
204-
fn block_connected(&mut self, _block_hash: BlockHash, _block_header: ValidatedBlockHeader) {
205-
unreachable!()
206-
}
207-
208-
fn blocks_disconnected(&mut self, _fork_point: &ValidatedBlockHeader) {}
188+
Ok((header_cache, best_header))
209189
}
210190

211191
/// Wrapper for supporting dynamically sized chain listeners.
@@ -273,9 +253,8 @@ mod tests {
273253
(chain.best_block_at_height(2), &listener_2 as &dyn chain::Listen),
274254
(chain.best_block_at_height(3), &listener_3 as &dyn chain::Listen),
275255
];
276-
let mut cache = chain.header_cache(0..=4);
277-
match synchronize_listeners(&chain, Network::Bitcoin, &mut cache, listeners).await {
278-
Ok(header) => assert_eq!(header, chain.tip()),
256+
match synchronize_listeners(&chain, Network::Bitcoin, listeners).await {
257+
Ok((_, header)) => assert_eq!(header, chain.tip()),
279258
Err(e) => panic!("Unexpected error: {:?}", e),
280259
}
281260
}
@@ -305,11 +284,8 @@ mod tests {
305284
(fork_chain_2.best_block(), &listener_2 as &dyn chain::Listen),
306285
(fork_chain_3.best_block(), &listener_3 as &dyn chain::Listen),
307286
];
308-
let mut cache = fork_chain_1.header_cache(2..=4);
309-
cache.extend(fork_chain_2.header_cache(3..=4));
310-
cache.extend(fork_chain_3.header_cache(4..=4));
311-
match synchronize_listeners(&main_chain, Network::Bitcoin, &mut cache, listeners).await {
312-
Ok(header) => assert_eq!(header, main_chain.tip()),
287+
match synchronize_listeners(&main_chain, Network::Bitcoin, listeners).await {
288+
Ok((_, header)) => assert_eq!(header, main_chain.tip()),
313289
Err(e) => panic!("Unexpected error: {:?}", e),
314290
}
315291
}
@@ -342,33 +318,8 @@ mod tests {
342318
(fork_chain_2.best_block(), &listener_2 as &dyn chain::Listen),
343319
(fork_chain_3.best_block(), &listener_3 as &dyn chain::Listen),
344320
];
345-
let mut cache = fork_chain_1.header_cache(2..=4);
346-
cache.extend(fork_chain_2.header_cache(3..=4));
347-
cache.extend(fork_chain_3.header_cache(4..=4));
348-
match synchronize_listeners(&main_chain, Network::Bitcoin, &mut cache, listeners).await {
349-
Ok(header) => assert_eq!(header, main_chain.tip()),
350-
Err(e) => panic!("Unexpected error: {:?}", e),
351-
}
352-
}
353-
354-
#[tokio::test]
355-
async fn cache_connected_and_keep_disconnected_blocks() {
356-
let main_chain = Blockchain::default().with_height(2);
357-
let fork_chain = main_chain.fork_at_height(1);
358-
let new_tip = main_chain.tip();
359-
let old_best_block = fork_chain.best_block();
360-
361-
let listener = MockChainListener::new()
362-
.expect_blocks_disconnected(*fork_chain.at_height(1))
363-
.expect_block_connected(*new_tip);
364-
365-
let listeners = vec![(old_best_block, &listener as &dyn chain::Listen)];
366-
let mut cache = fork_chain.header_cache(2..=2);
367-
match synchronize_listeners(&main_chain, Network::Bitcoin, &mut cache, listeners).await {
368-
Ok(_) => {
369-
assert!(cache.contains_key(&new_tip.block_hash));
370-
assert!(cache.contains_key(&old_best_block.block_hash));
371-
},
321+
match synchronize_listeners(&main_chain, Network::Bitcoin, listeners).await {
322+
Ok((_, header)) => assert_eq!(header, main_chain.tip()),
372323
Err(e) => panic!("Unexpected error: {:?}", e),
373324
}
374325
}

lightning-block-sync/src/lib.rs

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -170,18 +170,13 @@ pub enum BlockData {
170170
/// sources for the best chain tip. During this process it detects any chain forks, determines which
171171
/// constitutes the best chain, and updates the listener accordingly with any blocks that were
172172
/// connected or disconnected since the last poll.
173-
///
174-
/// Block headers for the best chain are maintained in the parameterized cache, allowing for a
175-
/// custom cache eviction policy. This offers flexibility to those sensitive to resource usage.
176-
/// Hence, there is a trade-off between a lower memory footprint and potentially increased network
177-
/// I/O as headers are re-fetched during fork detection.
178-
pub struct SpvClient<'a, P: Poll, C: Cache, L: Deref>
173+
pub struct SpvClient<P: Poll, L: Deref>
179174
where
180175
L::Target: chain::Listen,
181176
{
182177
chain_tip: ValidatedBlockHeader,
183178
chain_poller: P,
184-
chain_notifier: ChainNotifier<'a, C, L>,
179+
chain_notifier: ChainNotifier<UnboundedCache, L>,
185180
}
186181

187182
/// The `Cache` trait defines behavior for managing a block header cache, where block headers are
@@ -194,7 +189,7 @@ where
194189
/// Implementations may define how long to retain headers such that it's unlikely they will ever be
195190
/// needed to disconnect a block. In cases where block sources provide access to headers on stale
196191
/// forks reliably, caches may be entirely unnecessary.
197-
pub trait Cache {
192+
pub(crate) trait Cache {
198193
/// Retrieves the block header keyed by the given block hash.
199194
fn look_up(&self, block_hash: &BlockHash) -> Option<&ValidatedBlockHeader>;
200195

@@ -226,7 +221,21 @@ impl Cache for UnboundedCache {
226221
}
227222
}
228223

229-
impl<'a, P: Poll, C: Cache, L: Deref> SpvClient<'a, P, C, L>
224+
impl Cache for &mut UnboundedCache {
225+
fn look_up(&self, block_hash: &BlockHash) -> Option<&ValidatedBlockHeader> {
226+
self.get(block_hash)
227+
}
228+
229+
fn block_connected(&mut self, block_hash: BlockHash, block_header: ValidatedBlockHeader) {
230+
self.insert(block_hash, block_header);
231+
}
232+
233+
fn blocks_disconnected(&mut self, fork_point: &ValidatedBlockHeader) {
234+
self.retain(|_, block_info| block_info.height < fork_point.height);
235+
}
236+
}
237+
238+
impl<P: Poll, L: Deref> SpvClient<P, L>
230239
where
231240
L::Target: chain::Listen,
232241
{
@@ -241,7 +250,7 @@ where
241250
///
242251
/// [`poll_best_tip`]: SpvClient::poll_best_tip
243252
pub fn new(
244-
chain_tip: ValidatedBlockHeader, chain_poller: P, header_cache: &'a mut C,
253+
chain_tip: ValidatedBlockHeader, chain_poller: P, header_cache: UnboundedCache,
245254
chain_listener: L,
246255
) -> Self {
247256
let chain_notifier = ChainNotifier { header_cache, chain_listener };
@@ -295,15 +304,15 @@ where
295304
/// Notifies [listeners] of blocks that have been connected or disconnected from the chain.
296305
///
297306
/// [listeners]: lightning::chain::Listen
298-
pub struct ChainNotifier<'a, C: Cache, L: Deref>
307+
pub(crate) struct ChainNotifier<C: Cache, L: Deref>
299308
where
300309
L::Target: chain::Listen,
301310
{
302311
/// Cache for looking up headers before fetching from a block source.
303-
header_cache: &'a mut C,
312+
pub(crate) header_cache: C,
304313

305314
/// Listener that will be notified of connected or disconnected blocks.
306-
chain_listener: L,
315+
pub(crate) chain_listener: L,
307316
}
308317

309318
/// Changes made to the chain between subsequent polls that transformed it from having one chain tip
@@ -321,7 +330,7 @@ struct ChainDifference {
321330
connected_blocks: Vec<ValidatedBlockHeader>,
322331
}
323332

324-
impl<'a, C: Cache, L: Deref> ChainNotifier<'a, C, L>
333+
impl<C: Cache, L: Deref> ChainNotifier<C, L>
325334
where
326335
L::Target: chain::Listen,
327336
{
@@ -480,9 +489,9 @@ mod spv_client_tests {
480489
let best_tip = chain.at_height(1);
481490

482491
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
483-
let mut cache = UnboundedCache::new();
492+
let cache = UnboundedCache::new();
484493
let mut listener = NullChainListener {};
485-
let mut client = SpvClient::new(best_tip, poller, &mut cache, &mut listener);
494+
let mut client = SpvClient::new(best_tip, poller, cache, &mut listener);
486495
match client.poll_best_tip().await {
487496
Err(e) => {
488497
assert_eq!(e.kind(), BlockSourceErrorKind::Persistent);
@@ -499,9 +508,9 @@ mod spv_client_tests {
499508
let common_tip = chain.tip();
500509

501510
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
502-
let mut cache = UnboundedCache::new();
511+
let cache = UnboundedCache::new();
503512
let mut listener = NullChainListener {};
504-
let mut client = SpvClient::new(common_tip, poller, &mut cache, &mut listener);
513+
let mut client = SpvClient::new(common_tip, poller, cache, &mut listener);
505514
match client.poll_best_tip().await {
506515
Err(e) => panic!("Unexpected error: {:?}", e),
507516
Ok((chain_tip, blocks_connected)) => {
@@ -519,9 +528,9 @@ mod spv_client_tests {
519528
let old_tip = chain.at_height(1);
520529

521530
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
522-
let mut cache = UnboundedCache::new();
531+
let cache = UnboundedCache::new();
523532
let mut listener = NullChainListener {};
524-
let mut client = SpvClient::new(old_tip, poller, &mut cache, &mut listener);
533+
let mut client = SpvClient::new(old_tip, poller, cache, &mut listener);
525534
match client.poll_best_tip().await {
526535
Err(e) => panic!("Unexpected error: {:?}", e),
527536
Ok((chain_tip, blocks_connected)) => {
@@ -539,9 +548,9 @@ mod spv_client_tests {
539548
let old_tip = chain.at_height(1);
540549

541550
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
542-
let mut cache = UnboundedCache::new();
551+
let cache = UnboundedCache::new();
543552
let mut listener = NullChainListener {};
544-
let mut client = SpvClient::new(old_tip, poller, &mut cache, &mut listener);
553+
let mut client = SpvClient::new(old_tip, poller, cache, &mut listener);
545554
match client.poll_best_tip().await {
546555
Err(e) => panic!("Unexpected error: {:?}", e),
547556
Ok((chain_tip, blocks_connected)) => {
@@ -559,9 +568,9 @@ mod spv_client_tests {
559568
let old_tip = chain.at_height(1);
560569

561570
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
562-
let mut cache = UnboundedCache::new();
571+
let cache = UnboundedCache::new();
563572
let mut listener = NullChainListener {};
564-
let mut client = SpvClient::new(old_tip, poller, &mut cache, &mut listener);
573+
let mut client = SpvClient::new(old_tip, poller, cache, &mut listener);
565574
match client.poll_best_tip().await {
566575
Err(e) => panic!("Unexpected error: {:?}", e),
567576
Ok((chain_tip, blocks_connected)) => {
@@ -580,9 +589,9 @@ mod spv_client_tests {
580589
let worse_tip = chain.tip();
581590

582591
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
583-
let mut cache = UnboundedCache::new();
592+
let cache = UnboundedCache::new();
584593
let mut listener = NullChainListener {};
585-
let mut client = SpvClient::new(best_tip, poller, &mut cache, &mut listener);
594+
let mut client = SpvClient::new(best_tip, poller, cache, &mut listener);
586595
match client.poll_best_tip().await {
587596
Err(e) => panic!("Unexpected error: {:?}", e),
588597
Ok((chain_tip, blocks_connected)) => {

0 commit comments

Comments
 (0)