node/external_snark_worker/
external_snark_worker_actions.rs1use std::time::Duration;
2
3use openmina_core::{snark::SnarkJobId, ActionEvent};
4use redux::{EnablingCondition, Timestamp};
5use serde::{Deserialize, Serialize};
6
7use crate::{snark_pool::JobSummary, State};
8
9use super::{
10 ExternalSnarkWorkerError, ExternalSnarkWorkerState, ExternalSnarkWorkerWorkError,
11 SnarkWorkResult,
12};
13
14#[derive(Debug, Clone, Serialize, Deserialize, ActionEvent)]
15#[action_event(fields(display(job_id), display(error)))]
16pub enum ExternalSnarkWorkerAction {
17 Start,
18 Started,
19 StartTimeout {
20 now: Timestamp,
21 },
22 Kill,
23 Killed,
24
25 SubmitWork {
26 job_id: SnarkJobId,
27 summary: JobSummary,
28 },
29 WorkResult {
30 result: SnarkWorkResult,
31 },
32 WorkError {
33 error: ExternalSnarkWorkerWorkError,
34 },
35 WorkTimeout {
36 now: Timestamp,
37 },
38
39 CancelWork,
40 WorkCancelled,
41
42 PruneWork,
43
44 Error {
45 error: ExternalSnarkWorkerError,
46 permanent: bool,
47 },
48}
49
50pub type ExternalSnarkWorkerActionWithMetaRef<'a> =
51 redux::ActionWithMeta<&'a ExternalSnarkWorkerAction>;
52
53impl EnablingCondition<State> for ExternalSnarkWorkerAction {
54 fn is_enabled(&self, state: &State, _time: redux::Timestamp) -> bool {
55 match self {
56 ExternalSnarkWorkerAction::Start => {
57 state.config.snarker.is_some()
58 && matches!(
59 state.external_snark_worker.0.state,
60 ExternalSnarkWorkerState::None
61 )
62 }
63 ExternalSnarkWorkerAction::Started => {
64 matches!(
65 state.external_snark_worker.0.state,
66 ExternalSnarkWorkerState::Starting
67 )
68 }
69 ExternalSnarkWorkerAction::StartTimeout { now } => {
70 const TIMEOUT: Duration = Duration::from_secs(120);
71 matches!(
72 state.external_snark_worker.0.state,
73 ExternalSnarkWorkerState::Starting
74 ) && now
75 .checked_sub(state.external_snark_worker.0.timestamp)
76 .is_some_and(|d| d > TIMEOUT)
77 }
78 ExternalSnarkWorkerAction::Kill => !matches!(
79 state.external_snark_worker.0.state,
80 ExternalSnarkWorkerState::Error(_, false)
81 | ExternalSnarkWorkerState::None
82 | ExternalSnarkWorkerState::Killing
83 ),
84 ExternalSnarkWorkerAction::Killed => {
85 matches!(
86 state.external_snark_worker.0.state,
87 ExternalSnarkWorkerState::Killing
88 )
89 }
90 ExternalSnarkWorkerAction::SubmitWork { .. } => state.external_snark_worker.is_idle(),
91 ExternalSnarkWorkerAction::WorkResult { .. } => {
92 matches!(
93 state.external_snark_worker.0.state,
94 ExternalSnarkWorkerState::Working(..)
95 )
96 }
97 ExternalSnarkWorkerAction::WorkError { .. } => {
98 matches!(
99 state.external_snark_worker.0.state,
100 ExternalSnarkWorkerState::Working(..)
101 )
102 }
103 ExternalSnarkWorkerAction::WorkTimeout { now } => {
104 if let ExternalSnarkWorkerState::Working(_, summary) =
105 &state.external_snark_worker.0.state
106 {
107 now.checked_sub(state.external_snark_worker.0.timestamp)
108 .is_some_and(|d| d > summary.estimated_duration())
109 } else {
110 false
111 }
112 }
113 ExternalSnarkWorkerAction::CancelWork => {
114 matches!(
115 state.external_snark_worker.0.state,
116 ExternalSnarkWorkerState::Working(..)
117 )
118 }
119 ExternalSnarkWorkerAction::WorkCancelled => {
120 matches!(
121 state.external_snark_worker.0.state,
122 ExternalSnarkWorkerState::Cancelling(_)
123 )
124 }
125 ExternalSnarkWorkerAction::PruneWork => {
126 matches!(
127 state.external_snark_worker.0.state,
128 ExternalSnarkWorkerState::WorkReady(..)
129 | ExternalSnarkWorkerState::WorkError(..)
130 | ExternalSnarkWorkerState::Cancelled(..)
131 )
132 }
133 ExternalSnarkWorkerAction::Error { .. } => true,
134 }
135 }
136}