Skip to content

Commit daf5d75

Browse files
committed
wollet: fetch address histories concurrently
The address walk of the esplora full scan awaited one history request per address sequentially, so wall-clock grew linearly with the number of scanned addresses even though EsploraClientBuilder::concurrency already parallelizes transaction and header downloads. Drive the same batch through an ordered buffered stream honoring the configured concurrency: buffered (not buffer_unordered) because callers map results back to derivation indices positionally, and try_collect to stop at the first error like the sequential loop did. Default concurrency stays 1, so behavior is unchanged unless opted in.
1 parent b72bf48 commit daf5d75

3 files changed

Lines changed: 18 additions & 8 deletions

File tree

lwk_wollet/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
* Add Waterfalls descriptor subscriptions, returning `tip`, `mempool`, `block`, and `reorg` events that callers can use as wallet rescan hints.
66
* Esplora client: address transaction history now follows esplora's confirmed-transactions paging — previously an address with more confirmed transactions than one esplora page (25 on Blockstream esplora) got a silently truncated history, which could also evict the missing transactions from the scan cache.
7+
* Esplora client: address history requests within a scan batch run concurrently, honoring `EsploraClientBuilder::concurrency` (default 1, so behavior is unchanged unless opted in).
78

89
## 0.18.0
910

lwk_wollet/src/clients/asyncr/esplora.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use futures::lock::Mutex;
2727
#[cfg(not(target_arch = "wasm32"))]
2828
use tokio::sync::Mutex;
2929

30-
use futures::stream::{iter, StreamExt};
30+
use futures::stream::{iter, StreamExt, TryStreamExt};
3131
use reqwest::{Response, StatusCode};
3232
use serde::Deserialize;
3333
use std::sync::atomic::AtomicUsize;
@@ -225,11 +225,16 @@ impl EsploraClient {
225225
&self,
226226
addresses: &[Address],
227227
) -> Result<Vec<Vec<History>>, Error> {
228-
let mut result = vec![];
229-
for address in addresses.iter() {
230-
result.push(self.get_address_history(address).await?);
231-
}
232-
Ok(result)
228+
// `buffered` (not `buffer_unordered`) so results keep the input
229+
// order: callers map histories back to derivation indices
230+
// positionally (see `get_history`). `try_collect` stops at the
231+
// first error, dropping in-flight requests, matching the failure
232+
// semantics of the previous sequential loop.
233+
iter(addresses.iter())
234+
.map(|address| self.get_address_history(address))
235+
.buffered(self.concurrency)
236+
.try_collect()
237+
.await
233238
}
234239

235240
/// Fetch the confirmed transaction history of a single address plus its

lwk_wollet/tests/e2e.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,11 +1044,15 @@ async fn test_esplora_address_history_paging() {
10441044
// address (25 confirmed on Blockstream esplora); older confirmed
10451045
// transactions must be fetched via `/txs/chain/{last_seen_txid}` pages.
10461046
// Regression test: an address with more transactions than one page must
1047-
// sync its full history, not a silently truncated one.
1047+
// sync its full history, not a silently truncated one. concurrency(4)
1048+
// also exercises the ordered `buffered` address walk.
10481049
let env = TestEnvBuilder::from_env().with_esplora().build();
10491050
let url = env.esplora_url();
10501051
let network = Network::default_regtest();
1051-
let mut client = clients::asyncr::EsploraClient::new(network, &url);
1052+
let mut client = clients::asyncr::EsploraClientBuilder::new(&url, network)
1053+
.concurrency(4)
1054+
.build()
1055+
.unwrap();
10521056
let signer = generate_signer();
10531057
let view_key = generate_view_key();
10541058
let descriptor = format!("ct({},elwpkh({}/*))", view_key, signer.xpub());

0 commit comments

Comments
 (0)