Skip to content

Commit 9ea0dd0

Browse files
committed
refactor(esplora): define blocking Error enum
1 parent 0cbf254 commit 9ea0dd0

File tree

1 file changed

+58
-19
lines changed

1 file changed

+58
-19
lines changed

crates/esplora/src/blocking_ext.rs

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,54 @@ use bdk_core::{
77
BlockId, CheckPoint, ConfirmationBlockTime, Indexed, TxUpdate,
88
};
99
use esplora_client::{OutputStatus, Tx};
10+
use std::any::Any;
11+
use std::fmt;
1012
use std::thread::JoinHandle;
1113

1214
use crate::{insert_anchor_or_seen_at_from_status, insert_prevouts};
1315

14-
/// [`esplora_client::Error`]
15-
pub type Error = Box<esplora_client::Error>;
16+
#[derive(Debug)]
17+
pub enum Error {
18+
Client(esplora_client::Error),
19+
ThreadPanic(Option<String>),
20+
}
21+
22+
impl Error {
23+
fn from_thread_panic(err: Box<dyn Any + Send>) -> Self {
24+
if let Ok(msg) = err.downcast::<String>() {
25+
Self::ThreadPanic(Some(*msg))
26+
} else if let Ok(msg) = err.downcast::<&'static str>() {
27+
Self::ThreadPanic(Some(msg.to_string()))
28+
} else {
29+
Self::ThreadPanic(None)
30+
}
31+
}
32+
}
33+
34+
impl From<esplora_client::Error> for Error {
35+
fn from(err: esplora_client::Error) -> Self {
36+
Self::Client(err)
37+
}
38+
}
39+
40+
impl fmt::Display for Error {
41+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42+
match self {
43+
Self::Client(err) => write!(f, "{err}"),
44+
Self::ThreadPanic(Some(msg)) => write!(f, "worker thread panicked: {msg}"),
45+
Self::ThreadPanic(None) => write!(f, "worker thread panicked"),
46+
}
47+
}
48+
}
49+
50+
impl std::error::Error for Error {
51+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
52+
match self {
53+
Self::Client(err) => Some(err),
54+
_ => None,
55+
}
56+
}
57+
}
1658

1759
/// Trait to extend the functionality of [`esplora_client::BlockingClient`].
1860
///
@@ -241,15 +283,13 @@ fn chain_update(
241283
let mut tip = match point_of_agreement {
242284
Some(tip) => tip,
243285
None => {
244-
return Err(Box::new(esplora_client::Error::HeaderHashNotFound(
245-
local_cp_hash,
246-
)));
286+
return Err(esplora_client::Error::HeaderHashNotFound(local_cp_hash).into());
247287
}
248288
};
249289

250290
tip = tip
251291
.extend(conflicts.into_iter().rev().map(|b| (b.height, b.hash)))
252-
.map_err(|_| Box::new(esplora_client::Error::InvalidResponse))?;
292+
.map_err(|_| Error::from(esplora_client::Error::InvalidResponse))?;
253293

254294
for (anchor, _) in anchors {
255295
let height = anchor.block_id.height;
@@ -319,15 +359,15 @@ fn fetch_txs_with_keychain_spks<I: Iterator<Item = Indexed<SpkWithExpectedTxids>
319359

320360
if handles.is_empty() {
321361
if !processed_any {
322-
return Err(Box::new(esplora_client::Error::InvalidResponse));
362+
return Err(esplora_client::Error::InvalidResponse.into());
323363
}
324364
break;
325365
}
326366

327367
for handle in handles {
328368
let handle_result = handle
329369
.join()
330-
.map_err(|_| Box::new(esplora_client::Error::InvalidResponse))?;
370+
.map_err(Error::from_thread_panic)?;
331371
let (index, txs, evicted) = handle_result?;
332372
processed_any = true;
333373
if txs.is_empty() {
@@ -411,7 +451,7 @@ fn fetch_txs_with_txids<I: IntoIterator<Item = Txid>>(
411451
std::thread::spawn(move || {
412452
client
413453
.get_tx_info(&txid)
414-
.map_err(Box::new)
454+
.map_err(Error::from)
415455
.map(|t| (txid, t))
416456
})
417457
})
@@ -424,7 +464,7 @@ fn fetch_txs_with_txids<I: IntoIterator<Item = Txid>>(
424464
for handle in handles {
425465
let handle_result = handle
426466
.join()
427-
.map_err(|_| Box::new(esplora_client::Error::InvalidResponse))?;
467+
.map_err(Error::from_thread_panic)?;
428468
let (txid, tx_info) = handle_result?;
429469
if let Some(tx_info) = tx_info {
430470
if inserted_txs.insert(txid) {
@@ -476,7 +516,7 @@ fn fetch_txs_with_outpoints<I: IntoIterator<Item = OutPoint>>(
476516
std::thread::spawn(move || {
477517
client
478518
.get_output_status(&op.txid, op.vout as _)
479-
.map_err(Box::new)
519+
.map_err(Error::from)
480520
})
481521
})
482522
.collect::<Vec<JoinHandle<Result<Option<OutputStatus>, Error>>>>();
@@ -488,7 +528,7 @@ fn fetch_txs_with_outpoints<I: IntoIterator<Item = OutPoint>>(
488528
for handle in handles {
489529
let handle_result = handle
490530
.join()
491-
.map_err(|_| Box::new(esplora_client::Error::InvalidResponse))?;
531+
.map_err(Error::from_thread_panic)?;
492532
if let Some(op_status) = handle_result? {
493533
let spend_txid = match op_status.txid {
494534
Some(txid) => txid,
@@ -549,24 +589,23 @@ mod test {
549589
let res = (|| -> Result<(), Error> {
550590
let handle_result = handle
551591
.join()
552-
.map_err(|_| Box::new(esplora_client::Error::InvalidResponse))?;
553-
handle_result?;
554-
Ok(())
592+
.map_err(Error::from_thread_panic)?;
593+
handle_result
555594
})();
556595

557596
assert!(matches!(
558-
*res.unwrap_err(),
559-
esplora_client::Error::InvalidResponse
597+
res.unwrap_err(),
598+
Error::ThreadPanic(_)
560599
));
561600
}
562601

563602
#[test]
564603
fn ensure_last_index_none_returns_error() {
565604
let last_index: Option<u32> = None;
566605
let err = last_index
567-
.ok_or_else(|| Box::new(esplora_client::Error::InvalidResponse))
606+
.ok_or_else(|| Error::from(esplora_client::Error::InvalidResponse))
568607
.unwrap_err();
569-
assert!(matches!(*err, esplora_client::Error::InvalidResponse));
608+
assert!(matches!(err, Error::Client(esplora_client::Error::InvalidResponse)));
570609
}
571610

572611
macro_rules! local_chain {

0 commit comments

Comments
 (0)