mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-23 22:29:59 -07:00
270 lines
9.7 KiB
Rust
270 lines
9.7 KiB
Rust
//! Compares results from the bitcoincore-rpc and corepc backends.
|
|
//!
|
|
//! Run with:
|
|
//! cargo run -p brk_rpc --example compare_backends --features corepc
|
|
|
|
#[cfg(all(feature = "bitcoincore-rpc", feature = "corepc"))]
|
|
use std::time::{Duration, Instant};
|
|
|
|
#[cfg(not(all(feature = "bitcoincore-rpc", feature = "corepc")))]
|
|
fn main() {
|
|
eprintln!("This example requires both features: --features bitcoincore-rpc,corepc");
|
|
std::process::exit(1);
|
|
}
|
|
|
|
#[cfg(all(feature = "bitcoincore-rpc", feature = "corepc"))]
|
|
fn main() {
|
|
use brk_rpc::backend::{self, Auth};
|
|
|
|
brk_logger::init(None).unwrap();
|
|
|
|
let bitcoin_dir = brk_rpc::Client::default_bitcoin_path();
|
|
let auth = Auth::CookieFile(bitcoin_dir.join(".cookie"));
|
|
let url = brk_rpc::Client::default_url();
|
|
|
|
let bc = backend::bitcoincore::ClientInner::new(url, auth.clone(), 10, Duration::from_secs(1))
|
|
.expect("bitcoincore client");
|
|
let cp = backend::corepc::ClientInner::new(url, auth, 10, Duration::from_secs(1))
|
|
.expect("corepc client");
|
|
|
|
println!("=== Comparing backends ===\n");
|
|
|
|
// --- get_blockchain_info ---
|
|
{
|
|
let (t1, r1) = timed(|| bc.get_blockchain_info());
|
|
let (t2, r2) = timed(|| cp.get_blockchain_info());
|
|
let r1 = r1.unwrap();
|
|
let r2 = r2.unwrap();
|
|
println!("get_blockchain_info:");
|
|
println!(
|
|
" bitcoincore: headers={} blocks={} ({t1:?})",
|
|
r1.headers, r1.blocks
|
|
);
|
|
println!(
|
|
" corepc: headers={} blocks={} ({t2:?})",
|
|
r2.headers, r2.blocks
|
|
);
|
|
assert_eq!(r1.headers, r2.headers, "headers mismatch");
|
|
assert_eq!(r1.blocks, r2.blocks, "blocks mismatch");
|
|
println!(" MATCH\n");
|
|
}
|
|
|
|
// --- get_block_count ---
|
|
{
|
|
let (t1, r1) = timed(|| bc.get_block_count());
|
|
let (t2, r2) = timed(|| cp.get_block_count());
|
|
let r1 = r1.unwrap();
|
|
let r2 = r2.unwrap();
|
|
println!("get_block_count:");
|
|
println!(" bitcoincore: {r1} ({t1:?})");
|
|
println!(" corepc: {r2} ({t2:?})");
|
|
assert_eq!(r1, r2, "block count mismatch");
|
|
println!(" MATCH\n");
|
|
}
|
|
|
|
// --- get_block_hash (height 0) ---
|
|
let genesis_hash;
|
|
{
|
|
let (t1, r1) = timed(|| bc.get_block_hash(0));
|
|
let (t2, r2) = timed(|| cp.get_block_hash(0));
|
|
let r1 = r1.unwrap();
|
|
let r2 = r2.unwrap();
|
|
genesis_hash = r1;
|
|
println!("get_block_hash(0):");
|
|
println!(" bitcoincore: {r1} ({t1:?})");
|
|
println!(" corepc: {r2} ({t2:?})");
|
|
assert_eq!(r1, r2, "genesis hash mismatch");
|
|
println!(" MATCH\n");
|
|
}
|
|
|
|
// --- get_block_header ---
|
|
{
|
|
let (t1, r1) = timed(|| bc.get_block_header(&genesis_hash));
|
|
let (t2, r2) = timed(|| cp.get_block_header(&genesis_hash));
|
|
let r1 = r1.unwrap();
|
|
let r2 = r2.unwrap();
|
|
println!("get_block_header(genesis):");
|
|
println!(" bitcoincore: prev={} ({t1:?})", r1.prev_blockhash);
|
|
println!(" corepc: prev={} ({t2:?})", r2.prev_blockhash);
|
|
assert_eq!(r1, r2, "header mismatch");
|
|
println!(" MATCH\n");
|
|
}
|
|
|
|
// --- get_block_info ---
|
|
{
|
|
let (t1, r1) = timed(|| bc.get_block_info(&genesis_hash));
|
|
let (t2, r2) = timed(|| cp.get_block_info(&genesis_hash));
|
|
let r1 = r1.unwrap();
|
|
let r2 = r2.unwrap();
|
|
println!("get_block_info(genesis):");
|
|
println!(
|
|
" bitcoincore: height={} confirmations={} ({t1:?})",
|
|
r1.height, r1.confirmations
|
|
);
|
|
println!(
|
|
" corepc: height={} confirmations={} ({t2:?})",
|
|
r2.height, r2.confirmations
|
|
);
|
|
assert_eq!(r1.height, r2.height, "height mismatch");
|
|
// confirmations can drift by 1 between calls
|
|
assert!(
|
|
(r1.confirmations - r2.confirmations).abs() <= 1,
|
|
"confirmations mismatch: {} vs {}",
|
|
r1.confirmations,
|
|
r2.confirmations
|
|
);
|
|
println!(" MATCH\n");
|
|
}
|
|
|
|
// --- get_block_header_info ---
|
|
{
|
|
let (t1, r1) = timed(|| bc.get_block_header_info(&genesis_hash));
|
|
let (t2, r2) = timed(|| cp.get_block_header_info(&genesis_hash));
|
|
let r1 = r1.unwrap();
|
|
let r2 = r2.unwrap();
|
|
println!("get_block_header_info(genesis):");
|
|
println!(
|
|
" bitcoincore: height={} prev={:?} ({t1:?})",
|
|
r1.height, r1.previous_block_hash
|
|
);
|
|
println!(
|
|
" corepc: height={} prev={:?} ({t2:?})",
|
|
r2.height, r2.previous_block_hash
|
|
);
|
|
assert_eq!(r1.height, r2.height, "height mismatch");
|
|
assert_eq!(
|
|
r1.previous_block_hash, r2.previous_block_hash,
|
|
"prev hash mismatch"
|
|
);
|
|
println!(" MATCH\n");
|
|
}
|
|
|
|
// --- get_block (genesis) ---
|
|
{
|
|
let (t1, r1) = timed(|| bc.get_block(&genesis_hash));
|
|
let (t2, r2) = timed(|| cp.get_block(&genesis_hash));
|
|
let r1 = r1.unwrap();
|
|
let r2 = r2.unwrap();
|
|
println!("get_block(genesis):");
|
|
println!(" bitcoincore: txs={} ({t1:?})", r1.txdata.len());
|
|
println!(" corepc: txs={} ({t2:?})", r2.txdata.len());
|
|
assert_eq!(r1, r2, "block mismatch");
|
|
println!(" MATCH\n");
|
|
}
|
|
|
|
// --- get_raw_mempool ---
|
|
{
|
|
let (t1, r1) = timed(|| bc.get_raw_mempool());
|
|
let (t2, r2) = timed(|| cp.get_raw_mempool());
|
|
let r1 = r1.unwrap();
|
|
let r2 = r2.unwrap();
|
|
println!("get_raw_mempool:");
|
|
println!(" bitcoincore: {} txs ({t1:?})", r1.len());
|
|
println!(" corepc: {} txs ({t2:?})", r2.len());
|
|
// Mempool can change between calls, just check they're reasonable
|
|
println!(
|
|
" {} (mempool is live, counts may differ slightly)\n",
|
|
if r1.len() == r2.len() {
|
|
"MATCH"
|
|
} else {
|
|
"CLOSE"
|
|
}
|
|
);
|
|
}
|
|
|
|
// --- get_raw_mempool_verbose ---
|
|
{
|
|
let (t1, r1) = timed(|| bc.get_raw_mempool_verbose());
|
|
let (t2, r2) = timed(|| cp.get_raw_mempool_verbose());
|
|
let r1 = r1.unwrap();
|
|
let r2 = r2.unwrap();
|
|
println!("get_raw_mempool_verbose:");
|
|
println!(" bitcoincore: {} entries ({t1:?})", r1.len());
|
|
println!(" corepc: {} entries ({t2:?})", r2.len());
|
|
|
|
// Compare a sample entry if both have data
|
|
if let (Some((txid1, e1)), Some(_)) = (r1.first(), r2.first())
|
|
&& let Some((_, e2)) = r2.iter().find(|(t, _)| t == txid1)
|
|
{
|
|
println!(" sample txid {txid1}:");
|
|
println!(
|
|
" bitcoincore: vsize={} fee={} ancestor_count={}",
|
|
e1.vsize, e1.base_fee_sats, e1.ancestor_count
|
|
);
|
|
println!(
|
|
" corepc: vsize={} fee={} ancestor_count={}",
|
|
e2.vsize, e2.base_fee_sats, e2.ancestor_count
|
|
);
|
|
assert_eq!(e1.base_fee_sats, e2.base_fee_sats, "fee mismatch");
|
|
assert_eq!(
|
|
e1.ancestor_count, e2.ancestor_count,
|
|
"ancestor_count mismatch"
|
|
);
|
|
println!(" MATCH");
|
|
}
|
|
println!();
|
|
}
|
|
|
|
// --- get_raw_transaction_hex (tx from block 1, genesis coinbase can't be retrieved) ---
|
|
let block1_hash;
|
|
{
|
|
block1_hash = bc.get_block_hash(1).unwrap();
|
|
let block = bc.get_block(&block1_hash).unwrap();
|
|
let coinbase_txid = block.txdata[0].compute_txid();
|
|
let (t1, r1) = timed(|| bc.get_raw_transaction_hex(&coinbase_txid, Some(&block1_hash)));
|
|
let (t2, r2) = timed(|| cp.get_raw_transaction_hex(&coinbase_txid, Some(&block1_hash)));
|
|
let r1 = r1.unwrap();
|
|
let r2 = r2.unwrap();
|
|
println!("get_raw_transaction_hex(block 1 coinbase):");
|
|
println!(" bitcoincore: {}... ({t1:?})", &r1[..40.min(r1.len())]);
|
|
println!(" corepc: {}... ({t2:?})", &r2[..40.min(r2.len())]);
|
|
assert_eq!(r1, r2, "raw tx hex mismatch");
|
|
println!(" MATCH\n");
|
|
}
|
|
|
|
// --- get_tx_out (genesis coinbase, likely unspendable but test the call) ---
|
|
{
|
|
let block = bc.get_block(&genesis_hash).unwrap();
|
|
let coinbase_txid = block.txdata[0].compute_txid();
|
|
let (t1, r1) = timed(|| bc.get_tx_out(&coinbase_txid, 0, Some(false)));
|
|
let (t2, r2) = timed(|| cp.get_tx_out(&coinbase_txid, 0, Some(false)));
|
|
let r1 = r1.unwrap();
|
|
let r2 = r2.unwrap();
|
|
println!("get_tx_out(genesis coinbase, vout=0):");
|
|
match (&r1, &r2) {
|
|
(Some(a), Some(b)) => {
|
|
println!(
|
|
" bitcoincore: coinbase={} value={:?} ({t1:?})",
|
|
a.coinbase, a.value
|
|
);
|
|
println!(
|
|
" corepc: coinbase={} value={:?} ({t2:?})",
|
|
b.coinbase, b.value
|
|
);
|
|
assert_eq!(a.coinbase, b.coinbase, "coinbase mismatch");
|
|
assert_eq!(a.value, b.value, "value mismatch");
|
|
assert_eq!(a.script_pub_key, b.script_pub_key, "script mismatch");
|
|
println!(" MATCH");
|
|
}
|
|
(None, None) => {
|
|
println!(" both: None (spent) ({t1:?} / {t2:?})");
|
|
println!(" MATCH");
|
|
}
|
|
_ => {
|
|
println!(" MISMATCH: bitcoincore={r1:?}, corepc={r2:?}");
|
|
panic!("get_tx_out mismatch");
|
|
}
|
|
}
|
|
println!();
|
|
}
|
|
|
|
println!("=== All checks passed ===");
|
|
}
|
|
|
|
#[cfg(all(feature = "bitcoincore-rpc", feature = "corepc"))]
|
|
fn timed<T>(f: impl FnOnce() -> T) -> (Duration, T) {
|
|
let start = Instant::now();
|
|
let result = f();
|
|
(start.elapsed(), result)
|
|
}
|