Skip to content

Commit f21a21d

Browse files
committed
test(electrum): add criterion benchmark for sync
1 parent b57768d commit f21a21d

File tree

3 files changed

+116
-49
lines changed

3 files changed

+116
-49
lines changed

crates/electrum/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ electrum-client = { version = "0.23.1", features = [ "proxy" ], default-features
1919
[dev-dependencies]
2020
bdk_testenv = { path = "../testenv" }
2121
bdk_chain = { path = "../chain" }
22+
criterion = { version = "0.2" }
2223

2324
[features]
2425
default = ["use-rustls"]
@@ -29,3 +30,7 @@ use-openssl = ["electrum-client/use-openssl"]
2930
[[test]]
3031
name = "test_electrum"
3132
required-features = ["use-rustls"]
33+
34+
[[bench]]
35+
name = "test_sync"
36+
harness = false

crates/electrum/benches/test_sync.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use bdk_chain::{
2+
bitcoin::{Address, Amount, ScriptBuf},
3+
local_chain::LocalChain,
4+
spk_client::{SyncRequest, SyncResponse},
5+
spk_txout::SpkTxOutIndex,
6+
ConfirmationBlockTime, IndexedTxGraph, Indexer, Merge,
7+
};
8+
use bdk_core::bitcoin::{
9+
key::{Secp256k1, UntweakedPublicKey},
10+
Network,
11+
};
12+
use bdk_electrum::BdkElectrumClient;
13+
use bdk_testenv::{anyhow, bitcoincore_rpc::RpcApi, TestEnv};
14+
use criterion::{criterion_group, criterion_main, Criterion};
15+
use std::time::Duration;
16+
17+
// Batch size for `sync_with_electrum`.
18+
const BATCH_SIZE: usize = 5;
19+
20+
pub fn get_test_spk() -> ScriptBuf {
21+
const PK_BYTES: &[u8] = &[
22+
12, 244, 72, 4, 163, 4, 211, 81, 159, 82, 153, 123, 125, 74, 142, 40, 55, 237, 191, 231,
23+
31, 114, 89, 165, 83, 141, 8, 203, 93, 240, 53, 101,
24+
];
25+
let secp = Secp256k1::new();
26+
let pk = UntweakedPublicKey::from_slice(PK_BYTES).expect("Must be valid PK");
27+
ScriptBuf::new_p2tr(&secp, pk, None)
28+
}
29+
30+
fn sync_with_electrum<I, Spks>(
31+
client: &BdkElectrumClient<electrum_client::Client>,
32+
spks: Spks,
33+
chain: &mut LocalChain,
34+
graph: &mut IndexedTxGraph<ConfirmationBlockTime, I>,
35+
) -> anyhow::Result<SyncResponse>
36+
where
37+
I: Indexer,
38+
I::ChangeSet: Default + Merge,
39+
Spks: IntoIterator<Item = ScriptBuf>,
40+
Spks::IntoIter: ExactSizeIterator + Send + 'static,
41+
{
42+
let update = client.sync(
43+
SyncRequest::builder().chain_tip(chain.tip()).spks(spks),
44+
BATCH_SIZE,
45+
true,
46+
)?;
47+
48+
assert!(
49+
!update.tx_update.txs.is_empty(),
50+
"expected some transactions from sync, but got none"
51+
);
52+
53+
if let Some(chain_update) = update.chain_update.clone() {
54+
let _ = chain
55+
.apply_update(chain_update)
56+
.map_err(|err| anyhow::anyhow!("LocalChain update error: {:?}", err))?;
57+
}
58+
let _ = graph.apply_update(update.tx_update.clone());
59+
60+
Ok(update)
61+
}
62+
63+
pub fn test_sync_performance(c: &mut Criterion) {
64+
let env = TestEnv::new().unwrap();
65+
let electrum_client = electrum_client::Client::new(env.electrsd.electrum_url.as_str()).unwrap();
66+
let client = BdkElectrumClient::new(electrum_client);
67+
68+
const NUM_BLOCKS: usize = 100;
69+
let mut spks = Vec::with_capacity(NUM_BLOCKS);
70+
71+
// Mine some blocks and send transactions.
72+
env.mine_blocks(101, None).unwrap();
73+
74+
// Scatter UTXOs across many blocks.
75+
for _ in 0..NUM_BLOCKS {
76+
let spk = get_test_spk();
77+
let addr = Address::from_script(&spk, Network::Regtest).unwrap();
78+
env.send(&addr, Amount::from_sat(10_000)).unwrap();
79+
env.mine_blocks(1, None).unwrap();
80+
81+
spks.push(spk);
82+
}
83+
let _ = env.wait_until_electrum_sees_block(Duration::from_secs(6));
84+
85+
// Setup receiver.
86+
let genesis = env.bitcoind.client.get_block_hash(0).unwrap();
87+
let (chain, _) = LocalChain::from_genesis_hash(genesis);
88+
let graph = IndexedTxGraph::<ConfirmationBlockTime, _>::new({
89+
let mut idx = SpkTxOutIndex::default();
90+
idx.insert_spk((), spks[0].clone());
91+
idx
92+
});
93+
94+
c.bench_function("sync_with_electrum", move |b| {
95+
b.iter(|| {
96+
let spks = spks.clone();
97+
let mut recv_chain = chain.clone();
98+
let mut recv_graph = graph.clone();
99+
100+
let _ = sync_with_electrum(&client, spks, &mut recv_chain, &mut recv_graph);
101+
})
102+
});
103+
}
104+
105+
criterion_group! {
106+
name = benches;
107+
config = Criterion::default()
108+
.sample_size(10);
109+
targets = test_sync_performance
110+
}
111+
criterion_main!(benches);

crates/electrum/tests/test_electrum.rs

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ use core::time::Duration;
2020
use electrum_client::ElectrumApi;
2121
use std::collections::{BTreeSet, HashMap, HashSet};
2222
use std::str::FromStr;
23-
use std::time::Instant;
2423

2524
// Batch size for `sync_with_electrum`.
2625
const BATCH_SIZE: usize = 5;
@@ -882,51 +881,3 @@ fn test_check_fee_calculation() -> anyhow::Result<()> {
882881
}
883882
Ok(())
884883
}
885-
886-
#[test]
887-
pub fn test_sync_performance() -> anyhow::Result<()> {
888-
const EXPECTED_MAX_SYNC_TIME: Duration = Duration::from_secs(15);
889-
const NUM_ADDRESSES: usize = 1000;
890-
891-
let env = TestEnv::new()?;
892-
let electrum_client = electrum_client::Client::new(env.electrsd.electrum_url.as_str())?;
893-
let client = BdkElectrumClient::new(electrum_client);
894-
895-
// Generate test addresses.
896-
let mut spks = Vec::with_capacity(NUM_ADDRESSES);
897-
for _ in 0..NUM_ADDRESSES {
898-
spks.push(get_test_spk());
899-
}
900-
901-
// Mine some blocks and send transactions.
902-
env.mine_blocks(101, None)?;
903-
for spk in spks.iter().take(10) {
904-
let addr = Address::from_script(spk, Network::Regtest)?;
905-
env.send(&addr, Amount::from_sat(10_000))?;
906-
}
907-
env.mine_blocks(1, None)?;
908-
909-
// Setup receiver.
910-
let (mut recv_chain, _) = LocalChain::from_genesis_hash(env.bitcoind.client.get_block_hash(0)?);
911-
let mut recv_graph = IndexedTxGraph::<ConfirmationBlockTime, _>::new({
912-
let mut recv_index = SpkTxOutIndex::default();
913-
for spk in spks.iter() {
914-
recv_index.insert_spk((), spk.clone());
915-
}
916-
recv_index
917-
});
918-
919-
// Measure sync time.
920-
let start = Instant::now();
921-
let _ = sync_with_electrum(&client, spks.clone(), &mut recv_chain, &mut recv_graph)?;
922-
let sync_duration = start.elapsed();
923-
924-
assert!(
925-
sync_duration <= EXPECTED_MAX_SYNC_TIME,
926-
"Sync took {:?}, which is longer than expected {:?}",
927-
sync_duration,
928-
EXPECTED_MAX_SYNC_TIME
929-
);
930-
931-
Ok(())
932-
}

0 commit comments

Comments
 (0)