Skip to content

Commit cbb0ed1

Browse files
committed
allow breaking inside for_blocks closures using ControlFlow
1 parent 603830f commit cbb0ed1

5 files changed

Lines changed: 41 additions & 19 deletions

File tree

src/daemon.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use serde_json::{json, value::RawValue, Value};
1010

1111
use std::fs::File;
1212
use std::io::Read;
13+
use std::ops::ControlFlow;
1314
use std::path::Path;
1415

1516
use crate::{
@@ -275,10 +276,10 @@ impl Daemon {
275276
self.p2p.lock().get_new_headers(chain)
276277
}
277278

278-
pub(crate) fn for_blocks<B, F>(&self, blockhashes: B, func: F) -> Result<()>
279+
pub(crate) fn for_blocks<B, F, R>(&self, blockhashes: B, func: F) -> Result<ControlFlow<R>>
279280
where
280281
B: IntoIterator<Item = BlockHash>,
281-
F: FnMut(BlockHash, SerBlock),
282+
F: FnMut(BlockHash, SerBlock) -> ControlFlow<R>,
282283
{
283284
self.p2p.lock().for_blocks(blockhashes, func)
284285
}

src/index.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ impl Index {
216216
index_single_block(blockhash, block, height, &mut batch);
217217
});
218218
self.stats.height.set("tip", height as f64);
219+
ControlFlow::Continue::<()>(())
219220
})?;
220221
let heights: Vec<_> = heights.collect();
221222
assert!(

src/p2p.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crossbeam_channel::{bounded, select, Receiver, Sender};
2222

2323
use std::io::Write;
2424
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream};
25+
use std::ops::ControlFlow;
2526
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
2627

2728
use crate::types::SerBlock;
@@ -93,21 +94,26 @@ impl Connection {
9394
/// Request and process the specified blocks (in the specified order).
9495
/// See https://en.bitcoin.it/wiki/Protocol_documentation#getblocks for details.
9596
/// Defined as `&mut self` to prevent concurrent invocations (https://github.com/romanz/electrs/pull/526#issuecomment-934685515).
96-
pub(crate) fn for_blocks<B, F>(&mut self, blockhashes: B, mut func: F) -> Result<()>
97+
pub(crate) fn for_blocks<B, F, R>(
98+
&mut self,
99+
blockhashes: B,
100+
mut func: F,
101+
) -> Result<ControlFlow<R>>
97102
where
98103
B: IntoIterator<Item = BlockHash>,
99-
F: FnMut(BlockHash, SerBlock),
104+
F: FnMut(BlockHash, SerBlock) -> ControlFlow<R>,
100105
{
101106
self.blocks_duration.observe_duration("total", || {
102107
let blockhashes: Vec<BlockHash> = blockhashes.into_iter().collect();
103108
if blockhashes.is_empty() {
104-
return Ok(());
109+
return Ok(ControlFlow::Continue(()));
105110
}
106111
self.blocks_duration.observe_duration("request", || {
107112
debug!("loading {} blocks", blockhashes.len());
108113
self.req_send.send(Request::get_blocks(&blockhashes))
109114
})?;
110115

116+
let mut ret = ControlFlow::Continue(());
111117
for hash in blockhashes {
112118
let block = self.blocks_duration.observe_duration("response", || {
113119
let block = self
@@ -123,10 +129,13 @@ impl Connection {
123129
);
124130
Ok(block)
125131
})?;
126-
self.blocks_duration
127-
.observe_duration("process", || func(hash, block));
132+
if ret.is_continue() {
133+
ret = self
134+
.blocks_duration
135+
.observe_duration("process", || func(hash, block));
136+
}
128137
}
129-
Ok(())
138+
Ok(ret)
130139
})
131140
}
132141

src/status.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,15 @@ impl ScriptHashStatus {
308308
}
309309

310310
/// Apply func only on the new blocks (fetched from daemon).
311-
fn for_new_blocks<B, F>(&self, blockhashes: B, daemon: &Daemon, func: F) -> Result<()>
311+
fn for_new_blocks<B, F, R>(
312+
&self,
313+
blockhashes: B,
314+
daemon: &Daemon,
315+
func: F,
316+
) -> Result<ControlFlow<R>>
312317
where
313318
B: IntoIterator<Item = BlockHash>,
314-
F: FnMut(BlockHash, SerBlock),
319+
F: FnMut(BlockHash, SerBlock) -> ControlFlow<R>,
315320
{
316321
daemon.for_blocks(
317322
blockhashes
@@ -347,6 +352,7 @@ impl ScriptHashStatus {
347352
.or_insert_with(|| TxEntry::new(filtered_outputs.txid))
348353
.outputs = filtered_outputs.result;
349354
}
355+
ControlFlow::Continue::<()>(())
350356
})?;
351357
let spending_blockhashes: HashSet<BlockHash> = outpoints
352358
.par_iter()
@@ -361,6 +367,7 @@ impl ScriptHashStatus {
361367
.or_insert_with(|| TxEntry::new(filtered_inputs.txid))
362368
.spent = filtered_inputs.result;
363369
}
370+
ControlFlow::Continue::<()>(())
364371
})?;
365372

366373
Ok(result

src/tracker.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use bitcoin_slices::{
55
Error::VisitBreak,
66
Visit,
77
};
8+
use std::ops::ControlFlow;
89

910
use crate::{
1011
cache::Cache,
@@ -109,17 +110,20 @@ impl Tracker {
109110
) -> Result<Option<(BlockHash, Transaction)>> {
110111
// Note: there are two blocks with coinbase transactions having same txid (see BIP-30)
111112
let blockhashes = self.index.filter_by_txid(txid);
112-
let mut result = None;
113-
daemon.for_blocks(blockhashes, |blockhash, block| {
114-
if result.is_some() {
115-
return; // keep first matching transaction
116-
}
113+
let result = daemon.for_blocks(blockhashes, |blockhash, block| {
117114
let mut visitor = FindTransaction::new(txid);
118-
result = match bsl::Block::visit(&block, &mut visitor) {
119-
Ok(_) | Err(VisitBreak) => visitor.tx_found().map(|tx| (blockhash, tx)),
115+
match bsl::Block::visit(&block, &mut visitor) {
116+
Ok(_) | Err(VisitBreak) => (),
120117
Err(e) => panic!("core returned invalid block: {:?}", e),
121-
};
118+
}
119+
match visitor.tx_found() {
120+
Some(tx) => ControlFlow::Break((blockhash, tx)),
121+
None => ControlFlow::Continue(()),
122+
}
122123
})?;
123-
Ok(result)
124+
Ok(match result {
125+
ControlFlow::Continue(..) => None,
126+
ControlFlow::Break(x) => Some(x),
127+
})
124128
}
125129
}

0 commit comments

Comments
 (0)