1use std::fmt;
7use std::str::FromStr;
8
9use serde::{Deserialize, Serialize};
10
11pub use stoffel_mpc_coordinator_shared::CoordinatorError;
12pub use stoffelnet::network_utils::{ConsensusError, NetworkError};
13
14pub type Result<T> = std::result::Result<T, Error>;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum ErrorCategory {
20 Compilation,
21 Configuration,
22 Network,
23 Consensus,
24 Coordinator,
25 Preprocessing,
26 Computation,
27 Runtime,
28 Input,
29 Unsupported,
30 Io,
31 Bytecode,
32 ConfigParse,
33 ConfigSerialize,
34}
35
36impl ErrorCategory {
37 pub fn as_str(self) -> &'static str {
38 match self {
39 ErrorCategory::Compilation => "compilation",
40 ErrorCategory::Configuration => "configuration",
41 ErrorCategory::Network => "network",
42 ErrorCategory::Consensus => "consensus",
43 ErrorCategory::Coordinator => "coordinator",
44 ErrorCategory::Preprocessing => "preprocessing",
45 ErrorCategory::Computation => "computation",
46 ErrorCategory::Runtime => "runtime",
47 ErrorCategory::Input => "input",
48 ErrorCategory::Unsupported => "unsupported",
49 ErrorCategory::Io => "io",
50 ErrorCategory::Bytecode => "bytecode",
51 ErrorCategory::ConfigParse => "config_parse",
52 ErrorCategory::ConfigSerialize => "config_serialize",
53 }
54 }
55
56 pub fn is_recoverable(self) -> bool {
57 matches!(
58 self,
59 ErrorCategory::Network
60 | ErrorCategory::Consensus
61 | ErrorCategory::Preprocessing
62 | ErrorCategory::Io
63 )
64 }
65
66 pub fn recovery_hint(self) -> Option<&'static str> {
67 match self {
68 ErrorCategory::Network => {
69 Some("check connectivity and retry with the same program and inputs")
70 }
71 ErrorCategory::Consensus => Some(
72 "verify all parties see the same node/client set and consider a longer timeout",
73 ),
74 ErrorCategory::Preprocessing => {
75 Some("ensure each server has enough triples and random shares before retrying")
76 }
77 ErrorCategory::Io => Some("check the file path or filesystem permissions and retry"),
78 ErrorCategory::Configuration => {
79 Some("fix the invalid SDK configuration before retrying")
80 }
81 ErrorCategory::Input => {
82 Some("fix the provided function or client inputs before retrying")
83 }
84 ErrorCategory::Unsupported => {
85 Some("use a supported SDK execution mode or wire the required lower-level backend")
86 }
87 _ => None,
88 }
89 }
90}
91
92impl fmt::Display for ErrorCategory {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 f.write_str(self.as_str())
95 }
96}
97
98impl FromStr for ErrorCategory {
99 type Err = Error;
100
101 fn from_str(value: &str) -> Result<Self> {
102 match value.trim() {
103 "compilation" => Ok(ErrorCategory::Compilation),
104 "configuration" => Ok(ErrorCategory::Configuration),
105 "network" => Ok(ErrorCategory::Network),
106 "consensus" => Ok(ErrorCategory::Consensus),
107 "coordinator" => Ok(ErrorCategory::Coordinator),
108 "preprocessing" => Ok(ErrorCategory::Preprocessing),
109 "computation" => Ok(ErrorCategory::Computation),
110 "runtime" => Ok(ErrorCategory::Runtime),
111 "input" => Ok(ErrorCategory::Input),
112 "unsupported" => Ok(ErrorCategory::Unsupported),
113 "io" => Ok(ErrorCategory::Io),
114 "bytecode" => Ok(ErrorCategory::Bytecode),
115 "config_parse" => Ok(ErrorCategory::ConfigParse),
116 "config_serialize" => Ok(ErrorCategory::ConfigSerialize),
117 category => Err(Error::Configuration(format!(
118 "unsupported error category '{category}'"
119 ))),
120 }
121 }
122}
123
124#[derive(Debug, thiserror::Error)]
125pub enum Error {
126 #[error("Compilation failed: {0}")]
127 Compilation(String),
128 #[error("Invalid configuration: {0}")]
129 Configuration(String),
130 #[error("Network error: {0}")]
131 Network(#[from] NetworkError),
132 #[error("Network connection error: {0}")]
133 NetworkConnection(String),
134 #[error("Consensus failed: {0}")]
135 Consensus(#[from] ConsensusError),
136 #[error("Coordinator error: {0}")]
137 Coordinator(#[from] CoordinatorError),
138 #[error("Preprocessing failed: {0}")]
139 Preprocessing(String),
140 #[error("Computation failed: {0}")]
141 Computation(String),
142 #[error("Function not found: {0}")]
143 FunctionNotFound(String),
144 #[error("Invalid input: {0}")]
145 InvalidInput(String),
146 #[error("Unsupported SDK operation: {0}")]
147 Unsupported(String),
148 #[error("IO error: {0}")]
149 Io(#[from] std::io::Error),
150 #[error("Bytecode error: {0}")]
151 Bytecode(String),
152 #[error("Config parse error: {0}")]
153 ConfigParse(#[from] toml::de::Error),
154 #[error("Config serialize error: {0}")]
155 ConfigSerialize(#[from] toml::ser::Error),
156}
157
158impl Error {
159 pub fn category(&self) -> ErrorCategory {
160 match self {
161 Error::Compilation(_) => ErrorCategory::Compilation,
162 Error::Configuration(_) => ErrorCategory::Configuration,
163 Error::Network(_) => ErrorCategory::Network,
164 Error::NetworkConnection(_) => ErrorCategory::Network,
165 Error::Consensus(_) => ErrorCategory::Consensus,
166 Error::Coordinator(_) => ErrorCategory::Coordinator,
167 Error::Preprocessing(_) => ErrorCategory::Preprocessing,
168 Error::Computation(_) => ErrorCategory::Computation,
169 Error::FunctionNotFound(_) => ErrorCategory::Runtime,
170 Error::InvalidInput(_) => ErrorCategory::Input,
171 Error::Unsupported(_) => ErrorCategory::Unsupported,
172 Error::Io(_) => ErrorCategory::Io,
173 Error::Bytecode(_) => ErrorCategory::Bytecode,
174 Error::ConfigParse(_) => ErrorCategory::ConfigParse,
175 Error::ConfigSerialize(_) => ErrorCategory::ConfigSerialize,
176 }
177 }
178
179 pub fn is_recoverable(&self) -> bool {
180 self.category().is_recoverable()
181 }
182
183 pub fn recovery_hint(&self) -> Option<&'static str> {
184 self.category().recovery_hint()
185 }
186}
187
188pub(crate) fn format_compiler_errors<E: fmt::Display>(errors: &[E]) -> String {
189 errors
190 .iter()
191 .map(ToString::to_string)
192 .collect::<Vec<_>>()
193 .join("\n")
194}