Skip to content

Commit e66b458

Browse files
Matt CoralloTheBlueMatt
authored andcommitted
Make UnboundedCache bounded
In the previous commit we moved to hard-coding `UnboundedCache` in the `lightning-block-sync` interface. This is great, except that its an unbounded cache that can use arbitrary amounts of memory (though never really all that much - its just headers that come in while we're running). Here we simply limit the size, and while we're at it give it a more generic `HeaderCache` name.
1 parent 51b56fc commit e66b458

File tree

3 files changed

+39
-26
lines changed

3 files changed

+39
-26
lines changed

lightning-block-sync/src/init.rs

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

77
use bitcoin::block::Header;
8-
use bitcoin::hash_types::BlockHash;
98
use bitcoin::network::Network;
109

1110
use lightning::chain;
@@ -142,7 +141,7 @@ pub async fn synchronize_listeners<
142141
>(
143142
block_source: B, network: Network,
144143
mut chain_listeners: Vec<(BestBlock, &L)>,
145-
) -> BlockSourceResult<(UnboundedCache, ValidatedBlockHeader)>
144+
) -> BlockSourceResult<(HeaderCache, ValidatedBlockHeader)>
146145
where
147146
B::Target: BlockSource,
148147
{
@@ -153,7 +152,7 @@ where
153152
let mut chain_listeners_at_height = Vec::new();
154153
let mut most_common_ancestor = None;
155154
let mut most_connected_blocks = Vec::new();
156-
let mut header_cache = UnboundedCache::new();
155+
let mut header_cache = HeaderCache::new();
157156
for (old_best_block, chain_listener) in chain_listeners.drain(..) {
158157
// Disconnect any stale blocks, but keep them in the cache for the next iteration.
159158
let (common_ancestor, connected_blocks) = {

lightning-block-sync/src/lib.rs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ where
176176
{
177177
chain_tip: ValidatedBlockHeader,
178178
chain_poller: P,
179-
chain_notifier: ChainNotifier<UnboundedCache, L>,
179+
chain_notifier: ChainNotifier<HeaderCache, L>,
180180
}
181181

182182
/// The `Cache` trait defines behavior for managing a block header cache, where block headers are
@@ -204,34 +204,47 @@ pub(crate) trait Cache {
204204
fn blocks_disconnected(&mut self, fork_point: &ValidatedBlockHeader);
205205
}
206206

207-
/// Unbounded cache of block headers keyed by block hash.
208-
pub type UnboundedCache = std::collections::HashMap<BlockHash, ValidatedBlockHeader>;
207+
/// Bounded cache of block headers keyed by block hash.
208+
///
209+
/// Retains only the last `ANTI_REORG_DELAY * 2` block headers based on height.
210+
pub struct HeaderCache(std::collections::HashMap<BlockHash, ValidatedBlockHeader>);
209211

210-
impl Cache for UnboundedCache {
212+
impl HeaderCache {
213+
/// Creates a new empty header cache.
214+
pub fn new() -> Self {
215+
Self(std::collections::HashMap::new())
216+
}
217+
}
218+
219+
impl Cache for HeaderCache {
211220
fn look_up(&self, block_hash: &BlockHash) -> Option<&ValidatedBlockHeader> {
212-
self.get(block_hash)
221+
self.0.get(block_hash)
213222
}
214223

215224
fn block_connected(&mut self, block_hash: BlockHash, block_header: ValidatedBlockHeader) {
216-
self.insert(block_hash, block_header);
225+
self.0.insert(block_hash, block_header);
226+
227+
// Remove headers older than a week.
228+
let cutoff_height = block_header.height.saturating_sub(6 * 24 * 7);
229+
self.0.retain(|_, header| header.height >= cutoff_height);
217230
}
218231

219232
fn blocks_disconnected(&mut self, fork_point: &ValidatedBlockHeader) {
220-
self.retain(|_, block_info| block_info.height < fork_point.height);
233+
self.0.retain(|_, block_info| block_info.height < fork_point.height);
221234
}
222235
}
223236

224-
impl Cache for &mut UnboundedCache {
237+
impl Cache for &mut HeaderCache {
225238
fn look_up(&self, block_hash: &BlockHash) -> Option<&ValidatedBlockHeader> {
226-
self.get(block_hash)
239+
self.0.get(block_hash)
227240
}
228241

229242
fn block_connected(&mut self, block_hash: BlockHash, block_header: ValidatedBlockHeader) {
230-
self.insert(block_hash, block_header);
243+
(*self).block_connected(block_hash, block_header);
231244
}
232245

233246
fn blocks_disconnected(&mut self, fork_point: &ValidatedBlockHeader) {
234-
self.retain(|_, block_info| block_info.height < fork_point.height);
247+
self.0.retain(|_, block_info| block_info.height < fork_point.height);
235248
}
236249
}
237250

@@ -250,7 +263,7 @@ where
250263
///
251264
/// [`poll_best_tip`]: SpvClient::poll_best_tip
252265
pub fn new(
253-
chain_tip: ValidatedBlockHeader, chain_poller: P, header_cache: UnboundedCache,
266+
chain_tip: ValidatedBlockHeader, chain_poller: P, header_cache: HeaderCache,
254267
chain_listener: L,
255268
) -> Self {
256269
let chain_notifier = ChainNotifier { header_cache, chain_listener };
@@ -489,7 +502,7 @@ mod spv_client_tests {
489502
let best_tip = chain.at_height(1);
490503

491504
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
492-
let cache = UnboundedCache::new();
505+
let cache = HeaderCache::new();
493506
let mut listener = NullChainListener {};
494507
let mut client = SpvClient::new(best_tip, poller, cache, &mut listener);
495508
match client.poll_best_tip().await {
@@ -508,7 +521,7 @@ mod spv_client_tests {
508521
let common_tip = chain.tip();
509522

510523
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
511-
let cache = UnboundedCache::new();
524+
let cache = HeaderCache::new();
512525
let mut listener = NullChainListener {};
513526
let mut client = SpvClient::new(common_tip, poller, cache, &mut listener);
514527
match client.poll_best_tip().await {
@@ -528,7 +541,7 @@ mod spv_client_tests {
528541
let old_tip = chain.at_height(1);
529542

530543
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
531-
let cache = UnboundedCache::new();
544+
let cache = HeaderCache::new();
532545
let mut listener = NullChainListener {};
533546
let mut client = SpvClient::new(old_tip, poller, cache, &mut listener);
534547
match client.poll_best_tip().await {
@@ -548,7 +561,7 @@ mod spv_client_tests {
548561
let old_tip = chain.at_height(1);
549562

550563
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
551-
let cache = UnboundedCache::new();
564+
let cache = HeaderCache::new();
552565
let mut listener = NullChainListener {};
553566
let mut client = SpvClient::new(old_tip, poller, cache, &mut listener);
554567
match client.poll_best_tip().await {
@@ -568,7 +581,7 @@ mod spv_client_tests {
568581
let old_tip = chain.at_height(1);
569582

570583
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
571-
let cache = UnboundedCache::new();
584+
let cache = HeaderCache::new();
572585
let mut listener = NullChainListener {};
573586
let mut client = SpvClient::new(old_tip, poller, cache, &mut listener);
574587
match client.poll_best_tip().await {
@@ -589,7 +602,7 @@ mod spv_client_tests {
589602
let worse_tip = chain.tip();
590603

591604
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
592-
let cache = UnboundedCache::new();
605+
let cache = HeaderCache::new();
593606
let mut listener = NullChainListener {};
594607
let mut client = SpvClient::new(best_tip, poller, cache, &mut listener);
595608
match client.poll_best_tip().await {

lightning-block-sync/src/test_utils.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::poll::{Validate, ValidatedBlockHeader};
22
use crate::{
3-
BlockData, BlockHeaderData, BlockSource, BlockSourceError, BlockSourceResult, UnboundedCache,
3+
BlockData, BlockHeaderData, BlockSource, BlockSourceError, BlockSourceResult, Cache,
4+
HeaderCache,
45
};
56

67
use bitcoin::block::{Block, Header, Version};
@@ -144,12 +145,12 @@ impl Blockchain {
144145
self.blocks.pop()
145146
}
146147

147-
pub fn header_cache(&self, heights: std::ops::RangeInclusive<usize>) -> UnboundedCache {
148-
let mut cache = UnboundedCache::new();
148+
pub fn header_cache(&self, heights: std::ops::RangeInclusive<usize>) -> HeaderCache {
149+
let mut cache = HeaderCache::new();
149150
for i in heights {
150151
let value = self.at_height(i);
151152
let key = value.header.block_hash();
152-
assert!(cache.insert(key, value).is_none());
153+
cache.block_connected(key, value);
153154
}
154155
cache
155156
}

0 commit comments

Comments
 (0)