From 7ba14fe70869869aa0c53664061e8a933a3ee679 Mon Sep 17 00:00:00 2001 From: Alok <227512169+alok844937-design@users.noreply.github.com> Date: Sun, 28 Jun 2026 09:33:59 +0530 Subject: [PATCH 1/2] core: split SyncRequest consuming and non-consuming iterators - Rename consuming iterators to drain_txids/drain_outpoints - Add read-only accessors: spks(), txids(), outpoints() - Deprecate iter_txids/iter_outpoints as compatibility wrappers delegating to drain_txids/drain_outpoints - Update call-sites in esplora and electrum backends - Update/rename tests accordingly Fixes #2220 --- crates/core/src/spk_client.rs | 36 +++++++++++++- crates/core/tests/test_spk_client.rs | 56 +++++++++++++++++++++- crates/electrum/src/bdk_electrum_client.rs | 4 +- crates/esplora/src/async_ext.rs | 4 +- crates/esplora/src/blocking_ext.rs | 4 +- 5 files changed, 95 insertions(+), 9 deletions(-) diff --git a/crates/core/src/spk_client.rs b/crates/core/src/spk_client.rs index 64fe3dd8cb..366ec8f8cf 100644 --- a/crates/core/src/spk_client.rs +++ b/crates/core/src/spk_client.rs @@ -383,14 +383,46 @@ impl SyncRequest { SyncIter::::new(self) } + /// Drain and iterate over [`Txid`]s contained in this request, consuming them. + pub fn drain_txids(&mut self) -> impl ExactSizeIterator + '_ { + SyncIter::::new(self) + } + + /// Drain and iterate over [`OutPoint`]s contained in this request, consuming them. + pub fn drain_outpoints(&mut self) -> impl ExactSizeIterator + '_ { + SyncIter::::new(self) + } + /// Iterate over [`Txid`]s contained in this request. + #[deprecated( + note = "use `drain_txids` for consuming iteration or `txids` for read-only iteration" + )] pub fn iter_txids(&mut self) -> impl ExactSizeIterator + '_ { - SyncIter::::new(self) + self.drain_txids() } /// Iterate over [`OutPoint`]s contained in this request. + #[deprecated( + note = "use `drain_outpoints` for consuming iteration or `outpoints` for read-only iteration" + )] pub fn iter_outpoints(&mut self) -> impl ExactSizeIterator + '_ { - SyncIter::::new(self) + self.drain_outpoints() + } + + /// Iterate over script pubkeys (with indexes) contained in this request, without + /// consuming them. + pub fn spks(&self) -> impl ExactSizeIterator + '_ { + self.spks.iter() + } + + /// Iterate over [`Txid`]s contained in this request, without consuming them. + pub fn txids(&self) -> impl ExactSizeIterator + '_ { + self.txids.iter() + } + + /// Iterate over [`OutPoint`]s contained in this request, without consuming them. + pub fn outpoints(&self) -> impl ExactSizeIterator + '_ { + self.outpoints.iter() } fn _call_inspect(&mut self, item: SyncItem) { diff --git a/crates/core/tests/test_spk_client.rs b/crates/core/tests/test_spk_client.rs index 38766ac31d..8441458ec0 100644 --- a/crates/core/tests/test_spk_client.rs +++ b/crates/core/tests/test_spk_client.rs @@ -1,4 +1,6 @@ -use bdk_core::spk_client::{FullScanResponse, SyncResponse}; +use bdk_core::bitcoin::hashes::Hash; +use bdk_core::bitcoin::{BlockHash, OutPoint, Txid}; +use bdk_core::spk_client::{FullScanResponse, SyncRequest, SyncResponse}; #[test] fn test_empty() { @@ -11,3 +13,55 @@ fn test_empty() { "Default `SyncResponse` must be empty" ); } + +#[test] +fn test_spks_does_not_consume() { + let spk1 = bitcoin::ScriptBuf::new(); + let spk2 = bitcoin::ScriptBuf::new(); + + let request: SyncRequest = SyncRequest::builder() + .spks_with_indexes(vec![(0u32, spk1), (1u32, spk2)]) + .build(); + + assert_eq!(request.spks().count(), 2); + assert_eq!(request.spks().count(), 2, "must not consume"); +} + +#[test] +fn test_txids_does_not_consume() { + let txid1 = Txid::from_byte_array([0x01; 32]); + + let request: SyncRequest<(), BlockHash> = SyncRequest::builder().txids(vec![txid1]).build(); + + assert_eq!(request.txids().count(), 1); + assert_eq!(request.txids().count(), 1, "must not consume"); +} + +#[test] +fn test_outpoints_does_not_consume() { + let outpoint1 = OutPoint::null(); + + let request: SyncRequest<(), BlockHash> = + SyncRequest::builder().outpoints(vec![outpoint1]).build(); + + assert_eq!(request.outpoints().count(), 1); + assert_eq!(request.outpoints().count(), 1, "must not consume"); +} + +#[test] +fn test_drain_txids_still_consumes_all_items() { + let txid1 = Txid::from_byte_array([0x01; 32]); + let txid2 = Txid::from_byte_array([0x02; 32]); + + let mut request: SyncRequest<(), BlockHash> = + SyncRequest::builder().txids(vec![txid1, txid2]).build(); + + assert_eq!(request.txids().count(), 2); + + let consumed: Vec<_> = request.drain_txids().collect(); + assert_eq!( + consumed.len(), + 2, + "drain_txids must still consume all items" + ); +} diff --git a/crates/electrum/src/bdk_electrum_client.rs b/crates/electrum/src/bdk_electrum_client.rs index a7c943150a..3e8aba193f 100644 --- a/crates/electrum/src/bdk_electrum_client.rs +++ b/crates/electrum/src/bdk_electrum_client.rs @@ -238,13 +238,13 @@ impl BdkElectrumClient { self.populate_with_txids( start_time, &mut tx_update, - request.iter_txids(), + request.drain_txids(), &mut pending_anchors, )?; self.populate_with_outpoints( start_time, &mut tx_update, - request.iter_outpoints(), + request.drain_outpoints(), &mut pending_anchors, )?; diff --git a/crates/esplora/src/async_ext.rs b/crates/esplora/src/async_ext.rs index 2b38289526..311c8d6a89 100644 --- a/crates/esplora/src/async_ext.rs +++ b/crates/esplora/src/async_ext.rs @@ -145,7 +145,7 @@ where self, start_time, &mut inserted_txs, - request.iter_txids(), + request.drain_txids(), parallel_requests, ) .await?, @@ -155,7 +155,7 @@ where self, start_time, &mut inserted_txs, - request.iter_outpoints(), + request.drain_outpoints(), parallel_requests, ) .await?, diff --git a/crates/esplora/src/blocking_ext.rs b/crates/esplora/src/blocking_ext.rs index 225b574ea4..4e4a85ae0d 100644 --- a/crates/esplora/src/blocking_ext.rs +++ b/crates/esplora/src/blocking_ext.rs @@ -133,14 +133,14 @@ impl EsploraExt for esplora_client::BlockingClient { self, start_time, &mut inserted_txs, - request.iter_txids(), + request.drain_txids(), parallel_requests, )?); tx_update.extend(fetch_txs_with_outpoints( self, start_time, &mut inserted_txs, - request.iter_outpoints(), + request.drain_outpoints(), parallel_requests, )?); From e1d4c6f33d079cb9cd2a4522989247f3c84499b4 Mon Sep 17 00:00:00 2001 From: Alok <227512169+alok844937-design@users.noreply.github.com> Date: Mon, 29 Jun 2026 03:08:08 +0530 Subject: [PATCH 2/2] test(core): remove redundant SyncRequest iterator tests --- crates/core/tests/test_spk_client.rs | 56 +--------------------------- 1 file changed, 1 insertion(+), 55 deletions(-) diff --git a/crates/core/tests/test_spk_client.rs b/crates/core/tests/test_spk_client.rs index 8441458ec0..38766ac31d 100644 --- a/crates/core/tests/test_spk_client.rs +++ b/crates/core/tests/test_spk_client.rs @@ -1,6 +1,4 @@ -use bdk_core::bitcoin::hashes::Hash; -use bdk_core::bitcoin::{BlockHash, OutPoint, Txid}; -use bdk_core::spk_client::{FullScanResponse, SyncRequest, SyncResponse}; +use bdk_core::spk_client::{FullScanResponse, SyncResponse}; #[test] fn test_empty() { @@ -13,55 +11,3 @@ fn test_empty() { "Default `SyncResponse` must be empty" ); } - -#[test] -fn test_spks_does_not_consume() { - let spk1 = bitcoin::ScriptBuf::new(); - let spk2 = bitcoin::ScriptBuf::new(); - - let request: SyncRequest = SyncRequest::builder() - .spks_with_indexes(vec![(0u32, spk1), (1u32, spk2)]) - .build(); - - assert_eq!(request.spks().count(), 2); - assert_eq!(request.spks().count(), 2, "must not consume"); -} - -#[test] -fn test_txids_does_not_consume() { - let txid1 = Txid::from_byte_array([0x01; 32]); - - let request: SyncRequest<(), BlockHash> = SyncRequest::builder().txids(vec![txid1]).build(); - - assert_eq!(request.txids().count(), 1); - assert_eq!(request.txids().count(), 1, "must not consume"); -} - -#[test] -fn test_outpoints_does_not_consume() { - let outpoint1 = OutPoint::null(); - - let request: SyncRequest<(), BlockHash> = - SyncRequest::builder().outpoints(vec![outpoint1]).build(); - - assert_eq!(request.outpoints().count(), 1); - assert_eq!(request.outpoints().count(), 1, "must not consume"); -} - -#[test] -fn test_drain_txids_still_consumes_all_items() { - let txid1 = Txid::from_byte_array([0x01; 32]); - let txid2 = Txid::from_byte_array([0x02; 32]); - - let mut request: SyncRequest<(), BlockHash> = - SyncRequest::builder().txids(vec![txid1, txid2]).build(); - - assert_eq!(request.txids().count(), 2); - - let consumed: Vec<_> = request.drain_txids().collect(); - assert_eq!( - consumed.len(), - 2, - "drain_txids must still consume all items" - ); -}