From: Wei Chen Date: Sun, 25 May 2025 18:28:19 +0000 (+0000) Subject: test(electrum): add `criterion` benchmark for `sync` X-Git-Tag: bitcoind_rpc-0.21.0~17^2~3 X-Git-Url: http://internal-gitweb-vhost/script/%22https:/database/scripts/static/struct.ElectrumBlockchainConfig.html?a=commitdiff_plain;h=f21a21d8c8dfc5b84957c854c1f1daee59bcc620;p=bdk test(electrum): add `criterion` benchmark for `sync` --- diff --git a/crates/electrum/Cargo.toml b/crates/electrum/Cargo.toml index a6b619eb..95982eef 100644 --- a/crates/electrum/Cargo.toml +++ b/crates/electrum/Cargo.toml @@ -19,6 +19,7 @@ electrum-client = { version = "0.23.1", features = [ "proxy" ], default-features [dev-dependencies] bdk_testenv = { path = "../testenv" } bdk_chain = { path = "../chain" } +criterion = { version = "0.2" } [features] default = ["use-rustls"] @@ -29,3 +30,7 @@ use-openssl = ["electrum-client/use-openssl"] [[test]] name = "test_electrum" required-features = ["use-rustls"] + +[[bench]] +name = "test_sync" +harness = false diff --git a/crates/electrum/benches/test_sync.rs b/crates/electrum/benches/test_sync.rs new file mode 100644 index 00000000..8a0191f2 --- /dev/null +++ b/crates/electrum/benches/test_sync.rs @@ -0,0 +1,111 @@ +use bdk_chain::{ + bitcoin::{Address, Amount, ScriptBuf}, + local_chain::LocalChain, + spk_client::{SyncRequest, SyncResponse}, + spk_txout::SpkTxOutIndex, + ConfirmationBlockTime, IndexedTxGraph, Indexer, Merge, +}; +use bdk_core::bitcoin::{ + key::{Secp256k1, UntweakedPublicKey}, + Network, +}; +use bdk_electrum::BdkElectrumClient; +use bdk_testenv::{anyhow, bitcoincore_rpc::RpcApi, TestEnv}; +use criterion::{criterion_group, criterion_main, Criterion}; +use std::time::Duration; + +// Batch size for `sync_with_electrum`. +const BATCH_SIZE: usize = 5; + +pub fn get_test_spk() -> ScriptBuf { + const PK_BYTES: &[u8] = &[ + 12, 244, 72, 4, 163, 4, 211, 81, 159, 82, 153, 123, 125, 74, 142, 40, 55, 237, 191, 231, + 31, 114, 89, 165, 83, 141, 8, 203, 93, 240, 53, 101, + ]; + let secp = Secp256k1::new(); + let pk = UntweakedPublicKey::from_slice(PK_BYTES).expect("Must be valid PK"); + ScriptBuf::new_p2tr(&secp, pk, None) +} + +fn sync_with_electrum( + client: &BdkElectrumClient, + spks: Spks, + chain: &mut LocalChain, + graph: &mut IndexedTxGraph, +) -> anyhow::Result +where + I: Indexer, + I::ChangeSet: Default + Merge, + Spks: IntoIterator, + Spks::IntoIter: ExactSizeIterator + Send + 'static, +{ + let update = client.sync( + SyncRequest::builder().chain_tip(chain.tip()).spks(spks), + BATCH_SIZE, + true, + )?; + + assert!( + !update.tx_update.txs.is_empty(), + "expected some transactions from sync, but got none" + ); + + if let Some(chain_update) = update.chain_update.clone() { + let _ = chain + .apply_update(chain_update) + .map_err(|err| anyhow::anyhow!("LocalChain update error: {:?}", err))?; + } + let _ = graph.apply_update(update.tx_update.clone()); + + Ok(update) +} + +pub fn test_sync_performance(c: &mut Criterion) { + let env = TestEnv::new().unwrap(); + let electrum_client = electrum_client::Client::new(env.electrsd.electrum_url.as_str()).unwrap(); + let client = BdkElectrumClient::new(electrum_client); + + const NUM_BLOCKS: usize = 100; + let mut spks = Vec::with_capacity(NUM_BLOCKS); + + // Mine some blocks and send transactions. + env.mine_blocks(101, None).unwrap(); + + // Scatter UTXOs across many blocks. + for _ in 0..NUM_BLOCKS { + let spk = get_test_spk(); + let addr = Address::from_script(&spk, Network::Regtest).unwrap(); + env.send(&addr, Amount::from_sat(10_000)).unwrap(); + env.mine_blocks(1, None).unwrap(); + + spks.push(spk); + } + let _ = env.wait_until_electrum_sees_block(Duration::from_secs(6)); + + // Setup receiver. + let genesis = env.bitcoind.client.get_block_hash(0).unwrap(); + let (chain, _) = LocalChain::from_genesis_hash(genesis); + let graph = IndexedTxGraph::::new({ + let mut idx = SpkTxOutIndex::default(); + idx.insert_spk((), spks[0].clone()); + idx + }); + + c.bench_function("sync_with_electrum", move |b| { + b.iter(|| { + let spks = spks.clone(); + let mut recv_chain = chain.clone(); + let mut recv_graph = graph.clone(); + + let _ = sync_with_electrum(&client, spks, &mut recv_chain, &mut recv_graph); + }) + }); +} + +criterion_group! { + name = benches; + config = Criterion::default() + .sample_size(10); + targets = test_sync_performance +} +criterion_main!(benches); diff --git a/crates/electrum/tests/test_electrum.rs b/crates/electrum/tests/test_electrum.rs index 1973b456..5302e62f 100644 --- a/crates/electrum/tests/test_electrum.rs +++ b/crates/electrum/tests/test_electrum.rs @@ -20,7 +20,6 @@ use core::time::Duration; use electrum_client::ElectrumApi; use std::collections::{BTreeSet, HashMap, HashSet}; use std::str::FromStr; -use std::time::Instant; // Batch size for `sync_with_electrum`. const BATCH_SIZE: usize = 5; @@ -882,51 +881,3 @@ fn test_check_fee_calculation() -> anyhow::Result<()> { } Ok(()) } - -#[test] -pub fn test_sync_performance() -> anyhow::Result<()> { - const EXPECTED_MAX_SYNC_TIME: Duration = Duration::from_secs(15); - const NUM_ADDRESSES: usize = 1000; - - let env = TestEnv::new()?; - let electrum_client = electrum_client::Client::new(env.electrsd.electrum_url.as_str())?; - let client = BdkElectrumClient::new(electrum_client); - - // Generate test addresses. - let mut spks = Vec::with_capacity(NUM_ADDRESSES); - for _ in 0..NUM_ADDRESSES { - spks.push(get_test_spk()); - } - - // Mine some blocks and send transactions. - env.mine_blocks(101, None)?; - for spk in spks.iter().take(10) { - let addr = Address::from_script(spk, Network::Regtest)?; - env.send(&addr, Amount::from_sat(10_000))?; - } - env.mine_blocks(1, None)?; - - // Setup receiver. - let (mut recv_chain, _) = LocalChain::from_genesis_hash(env.bitcoind.client.get_block_hash(0)?); - let mut recv_graph = IndexedTxGraph::::new({ - let mut recv_index = SpkTxOutIndex::default(); - for spk in spks.iter() { - recv_index.insert_spk((), spk.clone()); - } - recv_index - }); - - // Measure sync time. - let start = Instant::now(); - let _ = sync_with_electrum(&client, spks.clone(), &mut recv_chain, &mut recv_graph)?; - let sync_duration = start.elapsed(); - - assert!( - sync_duration <= EXPECTED_MAX_SYNC_TIME, - "Sync took {:?}, which is longer than expected {:?}", - sync_duration, - EXPECTED_MAX_SYNC_TIME - ); - - Ok(()) -}