mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-19 22:34:46 -07:00
global: big snapshot
This commit is contained in:
@@ -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))?)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>,
|
||||
|
||||
Reference in New Issue
Block a user