Skip to content

Commit 55aefd5

Browse files
committed
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 5a67ebb commit 55aefd5

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;
@@ -138,7 +137,7 @@ where
138137
/// [`ChannelMonitor`]: lightning::chain::channelmonitor::ChannelMonitor
139138
pub async fn synchronize_listeners<B: Deref + Sized + Send + Sync, L: chain::Listen + ?Sized>(
140139
block_source: B, network: Network, mut chain_listeners: Vec<(BestBlock, &L)>,
141-
) -> BlockSourceResult<(UnboundedCache, ValidatedBlockHeader)>
140+
) -> BlockSourceResult<(HeaderCache, ValidatedBlockHeader)>
142141
where
143142
B::Target: BlockSource,
144143
{
@@ -149,7 +148,7 @@ where
149148
let mut chain_listeners_at_height = Vec::new();
150149
let mut most_common_ancestor = None;
151150
let mut most_connected_blocks = Vec::new();
152-
let mut header_cache = UnboundedCache::new();
151+
let mut header_cache = HeaderCache::new();
153152
for (old_best_block, chain_listener) in chain_listeners.drain(..) {
154153
// Disconnect any stale blocks, but keep them in the cache for the next iteration.
155154
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 };
@@ -490,7 +503,7 @@ mod spv_client_tests {
490503
let best_tip = chain.at_height(1);
491504

492505
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
493-
let cache = UnboundedCache::new();
506+
let cache = HeaderCache::new();
494507
let mut listener = NullChainListener {};
495508
let mut client = SpvClient::new(best_tip, poller, cache, &mut listener);
496509
match client.poll_best_tip().await {
@@ -509,7 +522,7 @@ mod spv_client_tests {
509522
let common_tip = chain.tip();
510523

511524
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
512-
let cache = UnboundedCache::new();
525+
let cache = HeaderCache::new();
513526
let mut listener = NullChainListener {};
514527
let mut client = SpvClient::new(common_tip, poller, cache, &mut listener);
515528
match client.poll_best_tip().await {
@@ -529,7 +542,7 @@ mod spv_client_tests {
529542
let old_tip = chain.at_height(1);
530543

531544
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
532-
let cache = UnboundedCache::new();
545+
let cache = HeaderCache::new();
533546
let mut listener = NullChainListener {};
534547
let mut client = SpvClient::new(old_tip, poller, cache, &mut listener);
535548
match client.poll_best_tip().await {
@@ -549,7 +562,7 @@ mod spv_client_tests {
549562
let old_tip = chain.at_height(1);
550563

551564
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
552-
let cache = UnboundedCache::new();
565+
let cache = HeaderCache::new();
553566
let mut listener = NullChainListener {};
554567
let mut client = SpvClient::new(old_tip, poller, cache, &mut listener);
555568
match client.poll_best_tip().await {
@@ -569,7 +582,7 @@ mod spv_client_tests {
569582
let old_tip = chain.at_height(1);
570583

571584
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
572-
let cache = UnboundedCache::new();
585+
let cache = HeaderCache::new();
573586
let mut listener = NullChainListener {};
574587
let mut client = SpvClient::new(old_tip, poller, cache, &mut listener);
575588
match client.poll_best_tip().await {
@@ -590,7 +603,7 @@ mod spv_client_tests {
590603
let worse_tip = chain.tip();
591604

592605
let poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
593-
let cache = UnboundedCache::new();
606+
let cache = HeaderCache::new();
594607
let mut listener = NullChainListener {};
595608
let mut client = SpvClient::new(best_tip, poller, cache, &mut listener);
596609
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)