Skip to content

Commit f12f2b2

Browse files
committed
feat(electrum): batch transaction.get_merkle calls via batch_call
1 parent f60ebe0 commit f12f2b2

2 files changed

Lines changed: 33 additions & 28 deletions

File tree

crates/electrum/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ workspace = true
1515
[dependencies]
1616
bdk_core = { path = "../core", version = "0.6.0" }
1717
electrum-client = { version = "0.23.1", features = [ "proxy" ], default-features = false }
18+
serde_json = "1.0"
1819

1920
[dev-dependencies]
2021
bdk_testenv = { path = "../testenv" }

crates/electrum/src/bdk_electrum_client.rs

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ use std::sync::{Arc, Mutex};
1212
/// We include a chain suffix of a certain length for the purpose of robustness.
1313
const CHAIN_SUFFIX_LENGTH: u32 = 8;
1414

15-
/// Maximum batch size for proof validation requests
16-
const MAX_BATCH_SIZE: usize = 100;
17-
1815
/// Wrapper around an [`electrum_client::ElectrumApi`] which includes an internal in-memory
1916
/// transaction cache to avoid re-fetching already downloaded transactions.
2017
#[derive(Debug)]
@@ -499,35 +496,42 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
499496
}
500497
}
501498

502-
// Fetch missing proofs in batches
503-
for chunk in to_fetch.chunks(MAX_BATCH_SIZE) {
504-
for &(txid, height, hash) in chunk {
505-
// Fetch the raw proof.
506-
let proof = self.inner.transaction_get_merkle(&txid, height)?;
507-
508-
// Validate against header, retrying once on stale header.
509-
let mut header = {
510-
let cache = self.block_header_cache.lock().unwrap();
511-
cache[&(height as u32)]
512-
};
513-
let mut valid = electrum_client::utils::validate_merkle_proof(
499+
// Batch all get_merkle calls.
500+
let mut batch = electrum_client::Batch::default();
501+
for &(txid, height, _) in &to_fetch {
502+
batch.raw(
503+
"blockchain.transaction.get_merkle".into(),
504+
vec![
505+
electrum_client::Param::String(format!("{:x}", txid)),
506+
electrum_client::Param::Usize(height),
507+
],
508+
);
509+
}
510+
let resps = self.inner.batch_call(&batch)?;
511+
512+
// Validate each proof, retrying once for each stale header.
513+
for ((txid, height, hash), resp) in to_fetch.into_iter().zip(resps.into_iter()) {
514+
let proof: electrum_client::GetMerkleRes = serde_json::from_value(resp)?;
515+
516+
let mut header = {
517+
let cache = self.block_header_cache.lock().unwrap();
518+
cache[&(height as u32)]
519+
};
520+
let mut valid =
521+
electrum_client::utils::validate_merkle_proof(&txid, &header.merkle_root, &proof);
522+
if !valid {
523+
let new_header = self.inner.block_header(height)?;
524+
self.block_header_cache
525+
.lock()
526+
.unwrap()
527+
.insert(height as u32, new_header);
528+
header = new_header;
529+
valid = electrum_client::utils::validate_merkle_proof(
514530
&txid,
515531
&header.merkle_root,
516532
&proof,
517533
);
518-
if !valid {
519-
let new_header = self.inner.block_header(height)?;
520-
self.block_header_cache
521-
.lock()
522-
.unwrap()
523-
.insert(height as u32, new_header);
524-
header = new_header;
525-
valid = electrum_client::utils::validate_merkle_proof(
526-
&txid,
527-
&header.merkle_root,
528-
&proof,
529-
);
530-
}
534+
}
531535

532536
// Build and cache the anchor if merkle proof is valid.
533537
if valid {

0 commit comments

Comments
 (0)