Skip to content

Commit 7fbbd5d

Browse files
committed
refactor(jd-client): propagate initialization errors instead of
panicking Replaces several `expect()` and `unwrap()` calls in `JobDeclaratorClient::start` with proper error propagation using `Result`. - Updated `start()` signature to return `JDCError<JobDeclarationClient>`. - Introduced `InitializationError(String)` variant to `JDCErrorKind` to preserve context from formerly panicking calls. - Updated `main.rs` and integration tests to handle the new `Result` type - Fix clippy and fmt checks
1 parent a514479 commit 7fbbd5d

4 files changed

Lines changed: 61 additions & 24 deletions

File tree

integration-tests/lib/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,11 @@ pub fn start_jdc(
274274
);
275275
let ret = jd_client_sv2::JobDeclaratorClient::new(jd_client_proxy);
276276
let ret_clone = ret.clone();
277-
tokio::spawn(async move { ret_clone.start().await });
277+
tokio::spawn(async move {
278+
if let Err(e) = ret_clone.start().await {
279+
panic!("Integration test JDC failed to start: {e}");
280+
}
281+
});
278282
(ret, jdc_address, monitoring_address)
279283
}
280284

miner-apps/jd-client/src/lib/error.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,17 @@ pub struct Upstream;
5757
#[derive(Debug)]
5858
pub struct Downstream;
5959

60+
#[derive(Debug)]
61+
pub struct JobDeclaratorClient;
62+
6063
#[derive(Debug)]
6164
pub struct JDCError<Owner> {
6265
pub kind: JDCErrorKind,
6366
pub action: Action,
6467
_owner: PhantomData<Owner>,
6568
}
6669

67-
#[derive(Debug, Clone, Copy)]
70+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6871
pub enum Action {
6972
Log,
7073
Disconnect(DownstreamId),
@@ -84,12 +87,14 @@ impl CanDisconnect for ChannelManager {}
8487
impl CanFallback for Upstream {}
8588
impl CanFallback for JobDeclarator {}
8689
impl CanFallback for ChannelManager {}
90+
impl CanFallback for JobDeclaratorClient {}
8791

8892
impl CanShutdown for ChannelManager {}
8993
impl CanShutdown for TemplateProvider {}
9094
impl CanShutdown for Downstream {}
9195
impl CanShutdown for Upstream {}
9296
impl CanShutdown for JobDeclarator {}
97+
impl CanShutdown for JobDeclaratorClient {}
9398

9499
impl<O> JDCError<O> {
95100
pub fn log<E: Into<JDCErrorKind>>(kind: E) -> Self {
@@ -256,6 +261,8 @@ pub enum JDCErrorKind {
256261
InvalidKey,
257262
/// Upstream not found
258263
UpstreamNotFound,
264+
/// JDC initialization error with details
265+
InitializationError(String),
259266
}
260267

261268
impl std::error::Error for JDCErrorKind {}
@@ -395,6 +402,9 @@ impl fmt::Display for JDCErrorKind {
395402
CouldNotInitiateSystem => write!(f, "Could not initiate subsystem"),
396403
InvalidKey => write!(f, "Invalid key used during noise handshake"),
397404
UpstreamNotFound => write!(f, "Upstream not found"),
405+
InitializationError(ref err) => {
406+
write!(f, "Cannot initialize JD client: {err:?}")
407+
}
398408
}
399409
}
400410
}

miner-apps/jd-client/src/lib/mod.rs

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use tracing::{debug, error, info, warn};
2222
use crate::{
2323
channel_manager::ChannelManager,
2424
config::JobDeclaratorClientConfig,
25-
error::JDCErrorKind,
25+
error::{Action, JDCError, JDCErrorKind, JDCResult},
2626
jd_mode::JDMode,
2727
job_declarator::JobDeclarator,
2828
template_receiver::{
@@ -68,7 +68,7 @@ impl JobDeclaratorClient {
6868
}
6969

7070
/// Starts the Job Declarator Client (JDC) main loop.
71-
pub async fn start(&self) {
71+
pub async fn start(&self) -> JDCResult<(), error::JobDeclaratorClient> {
7272
info!(
7373
"Job declarator client starting... setting up subsystems, User Identity: {}",
7474
self.config.user_identity()
@@ -78,12 +78,17 @@ impl JobDeclaratorClient {
7878
let mut encoded_outputs = vec![];
7979
let mode = JDMode::new(self.config.mode);
8080

81-
if let Err(e) = miner_coinbase_outputs.consensus_encode(&mut encoded_outputs) {
82-
error!(error = ?e, "Invalid coinbase output in config");
81+
if miner_coinbase_outputs
82+
.consensus_encode(&mut encoded_outputs)
83+
.is_err()
84+
{
8385
self.cancellation_token.cancel();
8486
self.shutdown_notify.notify_waiters();
8587
self.is_alive.store(false, Ordering::Relaxed);
86-
return;
88+
89+
return Err(JDCError::shutdown(JDCErrorKind::InitializationError(
90+
"Invalid coinbase output in config".to_string(),
91+
)));
8792
}
8893

8994
let mut fallback_coordinator = FallbackCoordinator::new();
@@ -123,11 +128,11 @@ impl JobDeclaratorClient {
123128
{
124129
Ok(channel_manager) => channel_manager,
125130
Err(e) => {
126-
error!(error = ?e, "Failed to initialize channel manager");
127131
self.cancellation_token.cancel();
128132
self.shutdown_notify.notify_waiters();
129133
self.is_alive.store(false, Ordering::Relaxed);
130-
return;
134+
135+
return Err(JDCError::shutdown(e.kind));
131136
}
132137
};
133138

@@ -207,11 +212,11 @@ impl JobDeclaratorClient {
207212
{
208213
Ok(template_receiver) => template_receiver,
209214
Err(e) => {
210-
error!(error = ?e, "Failed to initialize SV2 template receiver");
211215
self.cancellation_token.cancel();
212216
self.shutdown_notify.notify_waiters();
213217
self.is_alive.store(false, Ordering::Relaxed);
214-
return;
218+
219+
return Err(JDCError::shutdown(e.kind));
215220
}
216221
};
217222

@@ -235,13 +240,11 @@ impl JobDeclaratorClient {
235240
) {
236241
Some(unix_socket_path) => unix_socket_path,
237242
None => {
238-
error!(
239-
"Could not determine Bitcoin data directory. Please set data_dir in config."
240-
);
241243
self.cancellation_token.cancel();
242244
self.shutdown_notify.notify_waiters();
243245
self.is_alive.store(false, Ordering::Relaxed);
244-
return;
246+
247+
return Err(JDCError::shutdown(JDCErrorKind::InitializationError("Could not determine Bitcoin data directory. Please set data_dir in config.".to_string())));
245248
}
246249
};
247250

@@ -638,6 +641,8 @@ impl JobDeclaratorClient {
638641
self.shutdown_notify.notify_waiters();
639642
self.is_alive.store(false, Ordering::Relaxed);
640643
info!("JD Client shutdown complete.");
644+
645+
Ok(())
641646
}
642647

643648
pub async fn shutdown(&self) {
@@ -664,7 +669,7 @@ impl JobDeclaratorClient {
664669
fallback_coordinator: FallbackCoordinator,
665670
mode: JDMode,
666671
task_manager: Arc<TaskManager>,
667-
) -> Result<(Upstream, JobDeclarator), JDCErrorKind> {
672+
) -> JDCResult<(Upstream, JobDeclarator), error::JobDeclaratorClient> {
668673
const MAX_RETRIES: usize = 3;
669674
let upstream_len = upstreams.len();
670675
for (i, upstream_entry) in upstreams.iter_mut().enumerate() {
@@ -682,7 +687,9 @@ impl JobDeclaratorClient {
682687
biased;
683688
_ = cancellation_token.cancelled() => {
684689
info!("Shutdown requested while waiting to initialize upstream, aborting retries");
685-
return Err(JDCErrorKind::CouldNotInitiateSystem);
690+
return Err(
691+
JDCError::shutdown(JDCErrorKind::CouldNotInitiateSystem)
692+
);
686693
}
687694
_ = tokio::time::sleep(Duration::from_secs(1)) => {}
688695
}
@@ -699,7 +706,7 @@ impl JobDeclaratorClient {
699706
info!(
700707
"Shutdown requested before upstream connection attempt, aborting retries"
701708
);
702-
return Err(JDCErrorKind::CouldNotInitiateSystem);
709+
return Err(JDCError::shutdown(JDCErrorKind::CouldNotInitiateSystem));
703710
}
704711

705712
info!("Connection attempt {}/{}...", attempt, MAX_RETRIES);
@@ -729,11 +736,18 @@ impl JobDeclaratorClient {
729736
biased;
730737
_ = cancellation_token.cancelled() => {
731738
info!("Shutdown requested after upstream initialization failure, aborting retries");
732-
return Err(JDCErrorKind::CouldNotInitiateSystem);
739+
return Err(
740+
JDCError::shutdown(JDCErrorKind::CouldNotInitiateSystem)
741+
);
733742
}
734743
_ = tokio::time::sleep(Duration::from_secs(1)) => {}
735744
}
736745

746+
if e.action == Action::Shutdown {
747+
info!("Encountered a shutdown error during upstream initialization, aborting retries");
748+
return Err(e);
749+
}
750+
737751
warn!(
738752
"Attempt {}/{} failed for pool={}:{}, jds={}:{}: {:?}",
739753
attempt,
@@ -760,7 +774,7 @@ impl JobDeclaratorClient {
760774
}
761775

762776
tracing::error!("All upstreams failed after {} retries each", MAX_RETRIES);
763-
Err(JDCErrorKind::CouldNotInitiateSystem)
777+
Err(JDCError::shutdown(JDCErrorKind::CouldNotInitiateSystem))
764778
}
765779
}
766780

@@ -778,7 +792,7 @@ async fn try_initialize_single(
778792
mode: JDMode,
779793
task_manager: Arc<TaskManager>,
780794
config: &JobDeclaratorClientConfig,
781-
) -> Result<(Upstream, JobDeclarator), JDCErrorKind> {
795+
) -> JDCResult<(Upstream, JobDeclarator), error::JobDeclaratorClient> {
782796
info!("Upstream connection in-progress at initialize single");
783797
let upstream = Upstream::new(
784798
upstream_entry,
@@ -790,7 +804,10 @@ async fn try_initialize_single(
790804
config.required_extensions().to_vec(),
791805
)
792806
.await
793-
.map_err(|error| error.kind)?;
807+
.map_err(|error| match error.action {
808+
Action::Shutdown => JDCError::shutdown(error.kind),
809+
_ => JDCError::fallback(error.kind),
810+
})?;
794811

795812
info!("Upstream connection done at initialize single");
796813

@@ -804,7 +821,10 @@ async fn try_initialize_single(
804821
task_manager.clone(),
805822
)
806823
.await
807-
.map_err(|error| error.kind)?;
824+
.map_err(|error| match error.action {
825+
Action::Shutdown => JDCError::shutdown(error.kind),
826+
_ => JDCError::fallback(error.kind),
827+
})?;
808828

809829
Ok((upstream, job_declarator))
810830
}

miner-apps/jd-client/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,8 @@ async fn inner_main() {
2525
});
2626

2727
init_logging(jdc_config.log_file());
28-
JobDeclaratorClient::new(jdc_config).start().await;
28+
if let Err(e) = JobDeclaratorClient::new(jdc_config).start().await {
29+
tracing::error!("Job Declarator Client failed to start: {e}");
30+
std::process::exit(1);
31+
};
2932
}

0 commit comments

Comments
 (0)