Skip to content

Commit 89b1584

Browse files
committed
feat(bip158): FilterIter stores the headers
1 parent 4e6b5da commit 89b1584

File tree

1 file changed

+32
-19
lines changed

1 file changed

+32
-19
lines changed

crates/bitcoind_rpc/src/bip158.rs

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use bdk_core::bitcoin;
1313
use bdk_core::{BlockId, CheckPoint};
1414
use bitcoin::{
1515
bip158::{self, BlockFilter},
16+
block::Header,
1617
Block, BlockHash, ScriptBuf,
1718
};
1819
use bitcoincore_rpc;
@@ -21,6 +22,9 @@ use bitcoincore_rpc::RpcApi;
2122
/// Block height
2223
type Height = u32;
2324

25+
/// Block header with associated block hash.
26+
type HashedHeader = (BlockHash, Header);
27+
2428
/// Type that generates block [`Event`]s by matching a list of script pubkeys against a
2529
/// [`BlockFilter`].
2630
#[derive(Debug)]
@@ -31,8 +35,8 @@ pub struct FilterIter<'c, C> {
3135
spks: Vec<ScriptBuf>,
3236
// local cp
3337
cp: Option<CheckPoint>,
34-
// blocks map
35-
blocks: BTreeMap<Height, BlockHash>,
38+
// block headers
39+
headers: BTreeMap<Height, HashedHeader>,
3640
// heights of matching blocks
3741
matched: BTreeSet<Height>,
3842
// best height counter
@@ -53,7 +57,7 @@ impl<'c, C: RpcApi> FilterIter<'c, C> {
5357
client,
5458
spks: vec![],
5559
cp: None,
56-
blocks: BTreeMap::new(),
60+
headers: BTreeMap::new(),
5761
matched: BTreeSet::new(),
5862
height,
5963
start: height,
@@ -102,6 +106,11 @@ impl<'c, C: RpcApi> FilterIter<'c, C> {
102106
hash: tip_hash,
103107
}))
104108
}
109+
110+
/// Return all of the block headers that were collected during the scan.
111+
pub fn block_headers(&self) -> &BTreeMap<Height, (BlockHash, Header)> {
112+
&self.headers
113+
}
105114
}
106115

107116
/// Event inner type
@@ -151,27 +160,27 @@ impl<C: RpcApi> Iterator for FilterIter<'_, C> {
151160

152161
let mut reorg_depth = 0;
153162

154-
loop {
163+
let header = loop {
155164
if reorg_depth >= Self::MAX_REORG_DEPTH {
156165
return Err(Error::ReorgDepthExceeded);
157166
}
158167

159168
let header = self.client.get_block_header(&hash)?;
160169

161170
let prev_height = height.saturating_sub(1);
162-
match self.blocks.get(&prev_height).copied() {
171+
match self.headers.get(&prev_height).copied() {
163172
// Not enough data.
164-
None => break,
173+
None => break header,
165174
// Ok, the chain is consistent.
166-
Some(prev_hash) if prev_hash == header.prev_blockhash => break,
175+
Some((prev_hash, _)) if prev_hash == header.prev_blockhash => break header,
167176
_ => {
168177
// Reorg detected, keep backtracking.
169178
height = height.saturating_sub(1);
170179
hash = self.client.get_block_hash(height as u64)?;
171180
reorg_depth += 1;
172181
}
173182
}
174-
}
183+
};
175184

176185
let filter_bytes = self.client.get_block_filter(&hash)?.filter;
177186
let filter = BlockFilter::new(&filter_bytes);
@@ -193,11 +202,11 @@ impl<C: RpcApi> Iterator for FilterIter<'_, C> {
193202

194203
// In case of a reorg, throw out any stale entries.
195204
if reorg_depth > 0 {
196-
self.blocks.split_off(&height);
205+
self.headers.split_off(&height);
197206
self.matched.split_off(&height);
198207
}
199208
// Record the scanned block
200-
self.blocks.insert(height, hash);
209+
self.headers.insert(height, (hash, header));
201210
// Record the matching block
202211
if let Ok(Some(Event::Block(..))) = next_event {
203212
self.matched.insert(height);
@@ -216,17 +225,21 @@ impl<C: RpcApi> FilterIter<'_, C> {
216225
fn find_base_with(&mut self, mut cp: CheckPoint) -> Result<BlockId, Error> {
217226
loop {
218227
let height = cp.height();
219-
let fetched_hash = match self.blocks.get(&height) {
220-
Some(hash) => *hash,
221-
None => self.client.get_block_hash(height as u64)?,
228+
let (fetched_hash, header) = match self.headers.get(&height).copied() {
229+
Some(value) => value,
230+
None => {
231+
let hash = self.client.get_block_hash(height as u64)?;
232+
let header = self.client.get_block_header(&hash)?;
233+
(hash, header)
234+
}
222235
};
223236
if cp.hash() == fetched_hash {
224-
// ensure this block also exists in self
225-
self.blocks.insert(height, cp.hash());
237+
// Ensure this block also exists in `self`.
238+
self.headers.insert(height, (fetched_hash, header));
226239
return Ok(cp.block_id());
227240
}
228-
// remember conflicts
229-
self.blocks.insert(height, fetched_hash);
241+
// Remember conflicts.
242+
self.headers.insert(height, (fetched_hash, header));
230243
cp = cp.prev().ok_or(Error::ReorgDepthExceeded)?;
231244
}
232245
}
@@ -236,15 +249,15 @@ impl<C: RpcApi> FilterIter<'_, C> {
236249
/// Returns `None` if this [`FilterIter`] was not constructed using a [`CheckPoint`], or
237250
/// if not all events have been emitted (by calling `next`).
238251
pub fn chain_update(&self) -> Option<CheckPoint> {
239-
if self.cp.is_none() || self.blocks.is_empty() || self.height <= self.stop {
252+
if self.cp.is_none() || self.headers.is_empty() || self.height <= self.stop {
240253
return None;
241254
}
242255

243256
// We return blocks up to and including the initial height, all of the matching blocks,
244257
// and blocks in the terminal range.
245258
let tail_range = self.stop.saturating_sub(9)..=self.stop;
246259
Some(
247-
CheckPoint::from_block_ids(self.blocks.iter().filter_map(|(&height, &hash)| {
260+
CheckPoint::from_block_ids(self.headers.iter().filter_map(|(&height, &(hash, _))| {
248261
if height <= self.start
249262
|| self.matched.contains(&height)
250263
|| tail_range.contains(&height)

0 commit comments

Comments
 (0)