]> Untitled Git - bdk/commitdiff
test(electrum): Improve benchmark
author志宇 <hello@evanlinjin.me>
Wed, 25 Jun 2025 23:34:50 +0000 (23:34 +0000)
committerWei Chen <wzc110@gmail.com>
Fri, 27 Jun 2025 19:40:00 +0000 (19:40 +0000)
* Actually use different spks
* Do not benchmark applying updates (only fetching/contructing)
* Have two benches: One with cache, one without.
* Remove `black_box`.

crates/electrum/benches/test_sync.rs

index 8a0191f2b4093cf8cf17f341c2e0c27ba406b081..063fdd629669dd0d66297acce3fbe1c29de2a42d 100644 (file)
@@ -1,46 +1,44 @@
-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_chain::bitcoin::{Address, Amount, ScriptBuf};
+use bdk_core::{
+    bitcoin::{
+        consensus::WriteExt,
+        hashes::Hash,
+        key::{Secp256k1, UntweakedPublicKey},
+        Network, TapNodeHash,
+    },
+    spk_client::SyncRequest,
+    CheckPoint,
 };
 use bdk_electrum::BdkElectrumClient;
 use bdk_testenv::{anyhow, bitcoincore_rpc::RpcApi, TestEnv};
 use criterion::{criterion_group, criterion_main, Criterion};
-use std::time::Duration;
+use electrum_client::ElectrumApi;
+use std::{collections::BTreeSet, time::Duration};
 
 // Batch size for `sync_with_electrum`.
-const BATCH_SIZE: usize = 5;
+const BATCH_SIZE: usize = 100;
 
-pub fn get_test_spk() -> ScriptBuf {
+pub fn get_test_spk(i: usize) -> 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)
+    let mut engine = TapNodeHash::engine();
+    engine.emit_u64(i as u64).expect("must emit");
+    ScriptBuf::new_p2tr(&secp, pk, Some(TapNodeHash::from_engine(engine)))
 }
 
-fn sync_with_electrum<I, Spks>(
-    client: &BdkElectrumClient<electrum_client::Client>,
-    spks: Spks,
-    chain: &mut LocalChain,
-    graph: &mut IndexedTxGraph<ConfirmationBlockTime, I>,
-) -> anyhow::Result<SyncResponse>
-where
-    I: Indexer,
-    I::ChangeSet: Default + Merge,
-    Spks: IntoIterator<Item = ScriptBuf>,
-    Spks::IntoIter: ExactSizeIterator + Send + 'static,
-{
+fn sync_with_electrum<E: ElectrumApi>(
+    client: &BdkElectrumClient<E>,
+    spks: &[ScriptBuf],
+    chain_tip: &CheckPoint,
+) -> anyhow::Result<()> {
     let update = client.sync(
-        SyncRequest::builder().chain_tip(chain.tip()).spks(spks),
+        SyncRequest::builder()
+            .chain_tip(chain_tip.clone())
+            .spks(spks.iter().cloned()),
         BATCH_SIZE,
         true,
     )?;
@@ -50,20 +48,11 @@ where
         "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)
+    Ok(())
 }
 
 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);
@@ -72,8 +61,8 @@ pub fn test_sync_performance(c: &mut Criterion) {
     env.mine_blocks(101, None).unwrap();
 
     // Scatter UTXOs across many blocks.
-    for _ in 0..NUM_BLOCKS {
-        let spk = get_test_spk();
+    for i in 0..NUM_BLOCKS {
+        let spk = get_test_spk(i);
         let addr = Address::from_script(&spk, Network::Regtest).unwrap();
         env.send(&addr, Amount::from_sat(10_000)).unwrap();
         env.mine_blocks(1, None).unwrap();
@@ -81,25 +70,43 @@ pub fn test_sync_performance(c: &mut Criterion) {
         spks.push(spk);
     }
     let _ = env.wait_until_electrum_sees_block(Duration::from_secs(6));
+    assert_eq!(
+        spks.iter().cloned().collect::<BTreeSet<_>>().len(),
+        spks.len(),
+        "all spks must be unique",
+    );
 
     // Setup receiver.
-    let genesis = env.bitcoind.client.get_block_hash(0).unwrap();
-    let (chain, _) = LocalChain::from_genesis_hash(genesis);
-    let graph = IndexedTxGraph::<ConfirmationBlockTime, _>::new({
-        let mut idx = SpkTxOutIndex::default();
-        idx.insert_spk((), spks[0].clone());
-        idx
+    let genesis_cp = CheckPoint::new(bdk_core::BlockId {
+        height: 0,
+        hash: env.bitcoind.client.get_block_hash(0).unwrap(),
     });
 
-    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 electrum_client =
+            electrum_client::Client::new(env.electrsd.electrum_url.as_str()).unwrap();
+        let spks = spks.clone();
+        let genesis_cp = genesis_cp.clone();
+        c.bench_function("sync_with_electrum", move |b| {
+            b.iter(|| {
+                sync_with_electrum(
+                    &BdkElectrumClient::new(&electrum_client),
+                    &spks,
+                    &genesis_cp,
+                )
+                .expect("must not error")
+            })
+        });
+    }
 
-            let _ = sync_with_electrum(&client, spks, &mut recv_chain, &mut recv_graph);
-        })
-    });
+    {
+        let client = BdkElectrumClient::new(
+            electrum_client::Client::new(env.electrsd.electrum_url.as_str()).unwrap(),
+        );
+        c.bench_function("sync_with_electrum_cached", move |b| {
+            b.iter(|| sync_with_electrum(&client, &spks, &genesis_cp).expect("must not error"))
+        });
+    }
 }
 
 criterion_group! {