global: big snapshot

This commit is contained in:
nym21
2026-04-13 22:46:56 +02:00
parent c3cef71aa3
commit 765261648d
89 changed files with 4138 additions and 149 deletions

View File

@@ -1,13 +1,19 @@
use std::{thread::sleep, time::Duration};
use bitcoincore_rpc::{Client as CoreClient, Error as RpcError, RpcApi, jsonrpc};
use brk_error::Result;
use brk_error::{Error, Result};
use brk_types::Sats;
use parking_lot::RwLock;
use serde_json::value::RawValue;
use tracing::info;
use super::{Auth, BlockHeaderInfo, BlockInfo, BlockchainInfo, RawMempoolEntry, TxOutInfo};
/// Per-batch request count for `get_block_hashes_range`. Sized so the
/// JSON request body stays well under a megabyte and bitcoind doesn't
/// spend too long on a single batch before yielding results.
const BATCH_CHUNK: usize = 2000;
fn to_rpc_auth(auth: &Auth) -> bitcoincore_rpc::Auth {
match auth {
Auth::None => bitcoincore_rpc::Auth::None,
@@ -171,6 +177,66 @@ impl ClientInner {
Ok(self.call_with_retry(|c| c.get_block_hash(height))?)
}
/// Batched canonical height → block hash lookup over the inclusive
/// range `start..=end`. See the corepc backend for the rationale and
/// chunking strategy; this mirror uses bitcoincore-rpc's
/// `get_jsonrpc_client` accessor.
pub fn get_block_hashes_range(
&self,
start: u64,
end: u64,
) -> Result<Vec<bitcoin::BlockHash>> {
if end < start {
return Ok(Vec::new());
}
let total = (end - start + 1) as usize;
let mut hashes = Vec::with_capacity(total);
let mut chunk_start = start;
while chunk_start <= end {
let chunk_end = (chunk_start + BATCH_CHUNK as u64 - 1).min(end);
self.batch_get_block_hashes(chunk_start, chunk_end, &mut hashes)?;
chunk_start = chunk_end + 1;
}
Ok(hashes)
}
fn batch_get_block_hashes(
&self,
start: u64,
end: u64,
out: &mut Vec<bitcoin::BlockHash>,
) -> Result<()> {
let params: Vec<Box<RawValue>> = (start..=end)
.map(|h| {
RawValue::from_string(format!("[{h}]")).map_err(|e| Error::Parse(e.to_string()))
})
.collect::<Result<Vec<_>>>()?;
let client = self.client.read();
let jsonrpc_client = client.get_jsonrpc_client();
let requests: Vec<jsonrpc::Request> = params
.iter()
.map(|p| jsonrpc_client.build_request("getblockhash", Some(p)))
.collect();
let responses = jsonrpc_client
.send_batch(&requests)
.map_err(|e| Error::Parse(format!("getblockhash batch failed: {e}")))?;
for response in responses {
let response = response.ok_or(Error::Internal("Missing response in JSON-RPC batch"))?;
let hex: String = response
.result()
.map_err(|e| Error::Parse(format!("getblockhash batch result: {e}")))?;
out.push(
hex.parse::<bitcoin::BlockHash>()
.map_err(|e| Error::Parse(format!("invalid block hash hex: {e}")))?,
);
}
Ok(())
}
pub fn get_block_header(&self, hash: &bitcoin::BlockHash) -> Result<bitcoin::block::Header> {
Ok(self.call_with_retry(|c| c.get_block_header(hash))?)
}

View File

@@ -1,9 +1,10 @@
use std::{thread::sleep, time::Duration};
use brk_error::Result;
use brk_error::{Error, Result};
use brk_types::Sats;
use corepc_client::client_sync::Auth as CorepcAuth;
use parking_lot::RwLock;
use serde_json::value::RawValue;
use tracing::info;
use super::{Auth, BlockHeaderInfo, BlockInfo, BlockchainInfo, RawMempoolEntry, TxOutInfo};
@@ -11,6 +12,11 @@ use super::{Auth, BlockHeaderInfo, BlockInfo, BlockchainInfo, RawMempoolEntry, T
type CoreClient = corepc_client::client_sync::v30::Client;
type CoreError = corepc_client::client_sync::Error;
/// Per-batch request count for `get_block_hashes_range`. Sized so the
/// JSON request body stays well under a megabyte and bitcoind doesn't
/// spend too long on a single batch before yielding results.
const BATCH_CHUNK: usize = 2000;
#[derive(Debug)]
pub struct ClientInner {
url: String,
@@ -174,6 +180,73 @@ impl ClientInner {
Ok(r.block_hash()?)
}
/// Batched canonical height → block hash lookup over the inclusive
/// range `start..=end`. Internally splits into JSON-RPC batches of
/// `BATCH_CHUNK` requests so a 1M-block reindex doesn't try to push
/// a 50 MB request body or hold every response in memory at once.
/// Each chunk is one HTTP round-trip — still drops the per-call
/// overhead that dominates a sequential `get_block_hash` loop.
///
/// Returns hashes in canonical order (`start`, `start+1`, …, `end`).
pub fn get_block_hashes_range(
&self,
start: u64,
end: u64,
) -> Result<Vec<bitcoin::BlockHash>> {
if end < start {
return Ok(Vec::new());
}
let total = (end - start + 1) as usize;
let mut hashes = Vec::with_capacity(total);
let mut chunk_start = start;
while chunk_start <= end {
let chunk_end = (chunk_start + BATCH_CHUNK as u64 - 1).min(end);
self.batch_get_block_hashes(chunk_start, chunk_end, &mut hashes)?;
chunk_start = chunk_end + 1;
}
Ok(hashes)
}
fn batch_get_block_hashes(
&self,
start: u64,
end: u64,
out: &mut Vec<bitcoin::BlockHash>,
) -> Result<()> {
// Build raw param strings up front so each `Request` can borrow
// them; `corepc_jsonrpc::Client::build_request` takes a borrowed
// `&RawValue`.
let params: Vec<Box<RawValue>> = (start..=end)
.map(|h| {
RawValue::from_string(format!("[{h}]")).map_err(|e| Error::Parse(e.to_string()))
})
.collect::<Result<Vec<_>>>()?;
let client = self.client.read();
let requests: Vec<corepc_jsonrpc::Request> = params
.iter()
.map(|p| client.jsonrpc().build_request("getblockhash", Some(p)))
.collect();
let responses = client
.jsonrpc()
.send_batch(&requests)
.map_err(|e| Error::Parse(format!("getblockhash batch failed: {e}")))?;
for response in responses {
let response = response.ok_or(Error::Internal("Missing response in JSON-RPC batch"))?;
let hex: String = response
.result()
.map_err(|e| Error::Parse(format!("getblockhash batch result: {e}")))?;
out.push(
hex.parse::<bitcoin::BlockHash>()
.map_err(|e| Error::Parse(format!("invalid block hash hex: {e}")))?,
);
}
Ok(())
}
pub fn get_block_header(&self, hash: &bitcoin::BlockHash) -> Result<bitcoin::block::Header> {
let r = self.call_with_retry(|c| c.get_block_header(hash))?;
r.block_header()

View File

@@ -78,6 +78,22 @@ impl Client {
self.0.get_block_hash(height.into()).map(BlockHash::from)
}
/// Get every canonical block hash for the inclusive height range
/// `start..=end` in a single JSON-RPC batch request. Returns hashes
/// in canonical order (`start`, `start+1`, …, `end`). Use this
/// whenever resolving more than ~2 heights — one HTTP round-trip
/// beats N sequential `get_block_hash` calls once the per-call
/// overhead dominates.
pub fn get_block_hashes_range<H1, H2>(&self, start: H1, end: H2) -> Result<Vec<BlockHash>>
where
H1: Into<u64>,
H2: Into<u64>,
{
self.0
.get_block_hashes_range(start.into(), end.into())
.map(|v| v.into_iter().map(BlockHash::from).collect())
}
pub fn get_block_header<'a, H>(&self, hash: &'a H) -> Result<bitcoin::block::Header>
where
&'a H: Into<&'a bitcoin::BlockHash>,