Skip to content

Commit 4e5051b

Browse files
committed
optimize reorg handling code
1 parent cb81202 commit 4e5051b

1 file changed

Lines changed: 61 additions & 24 deletions

File tree

crates/bitcoind_rpc/src/bip158.rs

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,12 @@ impl<C: RpcApi> Iterator for FilterIter<'_, C> {
178178
let height = block.height;
179179
let hash = block.hash;
180180

181-
// Check continuity with previous block
182-
if height > 0 {
181+
// Check continuity with previous block for recent blocks
182+
// Only check blocks within the last 100 blocks as deep reorgs are extremely rare
183+
if height > 0 && height > self.height.saturating_sub(100) {
183184
let prev_height = height - 1;
184185

186+
// Use cached block if available, otherwise fetch
185187
let prev_hash = match self.blocks.get(&prev_height).copied() {
186188
Some(hash) => hash,
187189
None => self.client.get_block_hash(prev_height as u64)?,
@@ -204,16 +206,14 @@ impl<C: RpcApi> Iterator for FilterIter<'_, C> {
204206

205207
if self.spks.is_empty() {
206208
return Err(Error::NoScripts);
207-
}
208-
209-
let matches = filter
210-
.match_any(&hash, self.spks.iter().map(|s| s.as_bytes()))
211-
.map_err(Error::Bip158)?;
212-
213-
if matches {
209+
} else if filter
210+
.match_any(&hash, self.spks.iter().map(|script| script.as_bytes()))
211+
.map_err(Error::Bip158)?
212+
{
214213
let block = self.client.get_block(&hash)?;
215214
self.blocks.insert(height, hash);
216-
return Ok(Some(Event::Block(EventInner { height, block })));
215+
let inner = EventInner { height, block };
216+
return Ok(Some(Event::Block(inner)));
217217
} else {
218218
return Ok(Some(Event::NoMatch(height)));
219219
}
@@ -247,24 +247,61 @@ impl<C: RpcApi> FilterIter<'_, C> {
247247
}
248248
}
249249

250-
/// Find the fork point by checking previous blocks
251-
fn find_fork_point(&self, mut height: u32) -> Result<u32, Error> {
252-
let lookback_depth = 10; // Adjust based on security needs
253-
let min_height = height.saturating_sub(lookback_depth);
250+
fn find_fork_point(&self, current_height: u32) -> Result<u32, Error> {
251+
// Try a quick check of the immediate previous block first (common case)
252+
if current_height > 0 {
253+
let prev_height = current_height - 1;
254+
let prev_hash = self.client.get_block_hash(prev_height as u64)?;
255+
let prev_header = self.client.get_block_header_info(&prev_hash)?;
256+
257+
// If previous block links properly to its parent, it's valid
258+
if prev_height == 0
259+
|| prev_header.previous_block_hash.is_some()
260+
&& prev_header.previous_block_hash
261+
== Some(self.client.get_block_hash((prev_height - 1) as u64)?)
262+
{
263+
return Ok(prev_height);
264+
}
265+
}
254266

255-
while height > min_height {
256-
height -= 1;
257-
let current_hash = self.client.get_block_hash(height as u64)?;
258-
let current_header = self.client.get_block_header_info(&current_hash)?;
267+
// Binary search for fork point
268+
let max_lookback = 10; // Adjust based on security needs
269+
let min_height = current_height.saturating_sub(max_lookback);
259270

260-
if height == 0
261-
|| current_header.previous_block_hash
262-
== Some(self.client.get_block_hash((height - 1) as u64)?)
263-
{
264-
return Ok(height);
271+
let mut low = min_height;
272+
let mut high = current_height.saturating_sub(1);
273+
274+
while low <= high {
275+
let mid = low + (high - low) / 2;
276+
277+
// Check if this block is valid in the chain
278+
let hash = self.client.get_block_hash(mid as u64)?;
279+
let header = self.client.get_block_header_info(&hash)?;
280+
281+
let is_valid = if mid == 0 {
282+
true // Genesis is always valid
283+
} else {
284+
let prev_hash = self.client.get_block_hash((mid - 1) as u64)?;
285+
header.previous_block_hash == Some(prev_hash)
286+
};
287+
288+
if is_valid {
289+
// Check if the next block is invalid, which would make this our fork point
290+
if mid == high || {
291+
let next_hash = self.client.get_block_hash((mid + 1) as u64)?;
292+
let next_header = self.client.get_block_header_info(&next_hash)?;
293+
next_header.previous_block_hash != Some(hash)
294+
} {
295+
return Ok(mid);
296+
}
297+
low = mid + 1;
298+
} else {
299+
high = mid - 1;
265300
}
266301
}
267-
Ok(0) // Fallback to genesis
302+
303+
// Fall back to lowest valid block or genesis
304+
Ok(low.saturating_sub(1))
268305
}
269306

270307
/// Returns a chain update from the newly scanned blocks.

0 commit comments

Comments
 (0)