Skip to content

Commit 897ffe3

Browse files
committed
Add a simple naive block cache in gossip sync lookups
1 parent e51a789 commit 897ffe3

File tree

1 file changed

+69
-24
lines changed

1 file changed

+69
-24
lines changed

lightning-block-sync/src/gossip.rs

+69-24
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
use crate::{AsyncBlockSourceResult, BlockData, BlockSource};
66

7+
use bitcoin::blockdata::block::Block;
78
use bitcoin::blockdata::transaction::{TxOut, OutPoint};
89
use bitcoin::hash_types::BlockHash;
910

@@ -17,7 +18,8 @@ use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoResult, UtxoLookupErr
1718

1819
use lightning::util::logger::Logger;
1920

20-
use std::sync::Arc;
21+
use std::sync::{Arc, Mutex};
22+
use std::collections::VecDeque;
2123
use std::future::Future;
2224
use std::ops::Deref;
2325

@@ -27,9 +29,6 @@ use std::ops::Deref;
2729
/// Note that while this is implementable for a [`BlockSource`] which returns filtered block data
2830
/// (i.e. [`BlockData::HeaderOnly`] for [`BlockSource::get_block`] requests), such an
2931
/// implementation will reject all gossip as it is not fully able to verify the UTXOs referenced.
30-
///
31-
/// For efficiency, an implementation may consider caching some set of blocks, as many redundant
32-
/// calls may be made.
3332
pub trait UtxoSource : BlockSource + 'static {
3433
/// Fetches the block hash of the block at the given height.
3534
///
@@ -91,8 +90,11 @@ pub struct GossipVerifier<S: FutureSpawner,
9190
peer_manager: Arc<PeerManager<Descriptor, CM, Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>, OM, L, CMH, NS>>,
9291
gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>,
9392
spawn: S,
93+
block_cache: Arc<Mutex<VecDeque<(u32, Block)>>>,
9494
}
9595

96+
const BLOCK_CACHE_SIZE: usize = 5;
97+
9698
impl<S: FutureSpawner,
9799
Blocks: Deref + Send + Sync + Clone,
98100
L: Deref + Send + Sync,
@@ -114,34 +116,76 @@ impl<S: FutureSpawner,
114116
/// This is expected to be given to a [`P2PGossipSync`] (initially constructed with `None` for
115117
/// the UTXO lookup) via [`P2PGossipSync::add_utxo_lookup`].
116118
pub fn new(source: Blocks, spawn: S, gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>, peer_manager: Arc<PeerManager<Descriptor, CM, Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>, OM, L, CMH, NS>>) -> Self {
117-
Self { source, spawn, gossiper, peer_manager }
119+
Self {
120+
source, spawn, gossiper, peer_manager,
121+
block_cache: Arc::new(Mutex::new(VecDeque::with_capacity(BLOCK_CACHE_SIZE))),
122+
}
118123
}
119124

120-
async fn retrieve_utxo(source: Blocks, short_channel_id: u64) -> Result<TxOut, UtxoLookupError> {
125+
async fn retrieve_utxo(
126+
source: Blocks, block_cache: Arc<Mutex<VecDeque<(u32, Block)>>>, short_channel_id: u64
127+
) -> Result<TxOut, UtxoLookupError> {
121128
let block_height = (short_channel_id >> 5 * 8) as u32; // block height is most significant three bytes
122129
let transaction_index = ((short_channel_id >> 2 * 8) & 0xffffff) as u32;
123130
let output_index = (short_channel_id & 0xffff) as u16;
124131

125-
let block_hash = source.get_block_hash_by_height(block_height).await
126-
.map_err(|_| UtxoLookupError::UnknownTx)?;
127-
let block_data = source.get_block(&block_hash).await
128-
.map_err(|_| UtxoLookupError::UnknownTx)?;
129-
let mut block = match block_data {
130-
BlockData::HeaderOnly(_) => return Err(UtxoLookupError::UnknownTx),
131-
BlockData::FullBlock(block) => block,
132+
let (outpoint, output);
133+
134+
'tx_found: loop { // Used as a simple goto
135+
macro_rules! process_block {
136+
($block: expr) => { {
137+
if transaction_index as usize >= $block.txdata.len() {
138+
return Err(UtxoLookupError::UnknownTx);
139+
}
140+
let transaction = &$block.txdata[transaction_index as usize];
141+
if output_index as usize >= transaction.output.len() {
142+
return Err(UtxoLookupError::UnknownTx);
143+
}
144+
145+
outpoint = OutPoint::new(transaction.txid(), output_index.into());
146+
output = transaction.output[output_index as usize].clone();
147+
} }
148+
}
149+
{
150+
let recent_blocks = block_cache.lock().unwrap();
151+
for (height, block) in recent_blocks.iter() {
152+
if *height == block_height {
153+
process_block!(block);
154+
break 'tx_found;
155+
}
156+
}
157+
}
158+
159+
let block_hash = source.get_block_hash_by_height(block_height).await
160+
.map_err(|_| UtxoLookupError::UnknownTx)?;
161+
let block_data = source.get_block(&block_hash).await
162+
.map_err(|_| UtxoLookupError::UnknownTx)?;
163+
let block = match block_data {
164+
BlockData::HeaderOnly(_) => return Err(UtxoLookupError::UnknownTx),
165+
BlockData::FullBlock(block) => block,
166+
};
167+
process_block!(block);
168+
{
169+
let mut recent_blocks = block_cache.lock().unwrap();
170+
let mut insert = true;
171+
for (height, _) in recent_blocks.iter() {
172+
if *height == block_height {
173+
insert = false;
174+
}
175+
}
176+
if insert {
177+
if recent_blocks.len() >= BLOCK_CACHE_SIZE {
178+
recent_blocks.pop_front();
179+
}
180+
recent_blocks.push_back((block_height, block));
181+
}
182+
}
183+
break 'tx_found;
132184
};
133-
if transaction_index as usize >= block.txdata.len() {
134-
return Err(UtxoLookupError::UnknownTx);
135-
}
136-
let mut transaction = block.txdata.swap_remove(transaction_index as usize);
137-
if output_index as usize >= transaction.output.len() {
138-
return Err(UtxoLookupError::UnknownTx);
139-
}
140185
let outpoint_unspent =
141-
source.is_output_unspent(OutPoint::new(transaction.txid(), output_index.into())).await
142-
.map_err(|_| UtxoLookupError::UnknownTx)?;
186+
source.is_output_unspent(outpoint).await.map_err(|_| UtxoLookupError::UnknownTx)?;
143187
if outpoint_unspent {
144-
Ok(transaction.output.swap_remove(output_index as usize))
188+
Ok(output)
145189
} else {
146190
Err(UtxoLookupError::UnknownTx)
147191
}
@@ -190,9 +234,10 @@ impl<S: FutureSpawner,
190234
let fut = res.clone();
191235
let source = self.source.clone();
192236
let gossiper = Arc::clone(&self.gossiper);
237+
let block_cache = Arc::clone(&self.block_cache);
193238
let pm = Arc::clone(&self.peer_manager);
194239
self.spawn.spawn(async move {
195-
let res = Self::retrieve_utxo(source, short_channel_id).await;
240+
let res = Self::retrieve_utxo(source, block_cache, short_channel_id).await;
196241
fut.resolve(gossiper.network_graph(), &*gossiper, res);
197242
pm.process_events();
198243
});

0 commit comments

Comments
 (0)