Skip to content

Commit 7491cf4

Browse files
feat: update ReplicateStatusCause serialization and worker_api to support new WorkflowError format (#21)
1 parent 469aea6 commit 7491cf4

File tree

9 files changed

+269
-152
lines changed

9 files changed

+269
-152
lines changed

Cargo.lock

Lines changed: 21 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pre-compute/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ reqwest = { version = "0.12.15", features = ["blocking", "json"] }
1616
serde = "1.0.219"
1717
sha256 = "1.6.0"
1818
sha3 = "0.10.8"
19+
strum = "0.27.2"
20+
strum_macros = "0.27.2"
1921
thiserror = "2.0.12"
2022

2123
[dev-dependencies]

pre-compute/src/api/worker_api.rs

Lines changed: 71 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,6 @@ use crate::compute::{
44
};
55
use log::error;
66
use reqwest::{blocking::Client, header::AUTHORIZATION};
7-
use serde::Serialize;
8-
9-
/// Represents payload that can be sent to the worker API to report the outcome of the
10-
/// pre‑compute stage.
11-
///
12-
/// The JSON structure expected by the REST endpoint is:
13-
/// ```json
14-
/// {
15-
/// "cause": "<ReplicateStatusCause as string>"
16-
/// }
17-
/// ```
18-
///
19-
/// # Arguments
20-
///
21-
/// * `cause` - A reference to the ReplicateStatusCause indicating why the pre-compute operation exited
22-
///
23-
/// # Example
24-
///
25-
/// ```rust
26-
/// use tee_worker_pre_compute::api::worker_api::ExitMessage;
27-
/// use tee_worker_pre_compute::compute::errors::ReplicateStatusCause;
28-
///
29-
/// let exit_message = ExitMessage::from(&ReplicateStatusCause::PreComputeInvalidTeeSignature);
30-
/// ```
31-
#[derive(Serialize, Debug)]
32-
pub struct ExitMessage<'a> {
33-
pub cause: &'a ReplicateStatusCause,
34-
}
35-
36-
impl<'a> From<&'a ReplicateStatusCause> for ExitMessage<'a> {
37-
fn from(cause: &'a ReplicateStatusCause) -> Self {
38-
Self { cause }
39-
}
40-
}
417

428
/// Thin wrapper around a [`Client`] that knows how to reach the iExec worker API.
439
///
@@ -93,21 +59,21 @@ impl WorkerApiClient {
9359
Self::new(&base_url)
9460
}
9561

96-
/// Sends an exit cause for a pre-compute operation to the Worker API.
62+
/// Sends exit causes for a pre-compute operation to the Worker API.
9763
///
98-
/// This method reports the exit cause of a pre-compute operation to the Worker API,
64+
/// This method reports the exit causes of a pre-compute operation to the Worker API,
9965
/// which can be used for tracking and debugging purposes.
10066
///
10167
/// # Arguments
10268
///
10369
/// * `authorization` - The authorization token to use for the API request
104-
/// * `chain_task_id` - The chain task ID for which to report the exit cause
105-
/// * `exit_cause` - The exit cause to report
70+
/// * `chain_task_id` - The chain task ID for which to report the exit causes
71+
/// * `exit_causes` - The list of exit causes to report
10672
///
10773
/// # Returns
10874
///
109-
/// * `Ok(())` - If the exit cause was successfully reported
110-
/// * `Err(Error)` - If the exit cause could not be reported due to an HTTP error
75+
/// * `Ok(())` - If the exit causes were successfully reported
76+
/// * `Err(Error)` - If the exit causes could not be reported due to an HTTP error
11177
///
11278
/// # Errors
11379
///
@@ -117,33 +83,33 @@ impl WorkerApiClient {
11783
/// # Example
11884
///
11985
/// ```rust
120-
/// use tee_worker_pre_compute::api::worker_api::{ExitMessage, WorkerApiClient};
86+
/// use tee_worker_pre_compute::api::worker_api::WorkerApiClient;
12187
/// use tee_worker_pre_compute::compute::errors::ReplicateStatusCause;
12288
///
12389
/// let client = WorkerApiClient::new("http://worker:13100");
124-
/// let exit_message = ExitMessage::from(&ReplicateStatusCause::PreComputeInvalidTeeSignature);
90+
/// let exit_causes = vec![ReplicateStatusCause::PreComputeInvalidTeeSignature];
12591
///
126-
/// match client.send_exit_cause_for_pre_compute_stage(
92+
/// match client.send_exit_causes_for_pre_compute_stage(
12793
/// "authorization_token",
12894
/// "0x123456789abcdef",
129-
/// &exit_message,
95+
/// &exit_causes,
13096
/// ) {
131-
/// Ok(()) => println!("Exit cause reported successfully"),
132-
/// Err(error) => eprintln!("Failed to report exit cause: {error}"),
97+
/// Ok(()) => println!("Exit causes reported successfully"),
98+
/// Err(error) => eprintln!("Failed to report exit causes: {error}"),
13399
/// }
134100
/// ```
135-
pub fn send_exit_cause_for_pre_compute_stage(
101+
pub fn send_exit_causes_for_pre_compute_stage(
136102
&self,
137103
authorization: &str,
138104
chain_task_id: &str,
139-
exit_cause: &ExitMessage,
105+
exit_causes: &[ReplicateStatusCause],
140106
) -> Result<(), ReplicateStatusCause> {
141-
let url = format!("{}/compute/pre/{chain_task_id}/exit", self.base_url);
107+
let url = format!("{}/compute/pre/{chain_task_id}/exit-causes", self.base_url);
142108
match self
143109
.client
144110
.post(&url)
145111
.header(AUTHORIZATION, authorization)
146-
.json(exit_cause)
112+
.json(exit_causes)
147113
.send()
148114
{
149115
Ok(resp) => {
@@ -152,12 +118,12 @@ impl WorkerApiClient {
152118
Ok(())
153119
} else {
154120
let body = resp.text().unwrap_or_default();
155-
error!("Failed to send exit cause: [status:{status}, body:{body}]");
121+
error!("Failed to send exit causes: [status:{status}, body:{body}]");
156122
Err(ReplicateStatusCause::PreComputeFailedUnknownIssue)
157123
}
158124
}
159125
Err(err) => {
160-
error!("HTTP request failed when sending exit cause to {url}: {err:?}");
126+
error!("HTTP request failed when sending exit causes to {url}: {err:?}");
161127
Err(ReplicateStatusCause::PreComputeFailedUnknownIssue)
162128
}
163129
}
@@ -175,36 +141,52 @@ mod tests {
175141
matchers::{body_json, header, method, path},
176142
};
177143

178-
// region ExitMessage()
144+
// region Serialization tests
179145
#[test]
180-
fn should_serialize_exit_message() {
181-
let causes = [
146+
fn serialize_replicate_status_cause_succeeds_when_single_cause() {
147+
let causes = vec![
182148
(
183149
ReplicateStatusCause::PreComputeInvalidTeeSignature,
184-
"PRE_COMPUTE_INVALID_TEE_SIGNATURE",
150+
r#"{"cause":"PRE_COMPUTE_INVALID_TEE_SIGNATURE","message":"Invalid TEE signature"}"#,
185151
),
186152
(
187153
ReplicateStatusCause::PreComputeWorkerAddressMissing,
188-
"PRE_COMPUTE_WORKER_ADDRESS_MISSING",
154+
r#"{"cause":"PRE_COMPUTE_WORKER_ADDRESS_MISSING","message":"Worker address related environment variable is missing"}"#,
189155
),
190156
(
191-
ReplicateStatusCause::PreComputeFailedUnknownIssue,
192-
"PRE_COMPUTE_FAILED_UNKNOWN_ISSUE",
157+
ReplicateStatusCause::PreComputeDatasetUrlMissing("0xDatasetAdress1".to_string()),
158+
r#"{"cause":"PRE_COMPUTE_DATASET_URL_MISSING","message":"Dataset URL related environment variable is missing for dataset 0xDatasetAdress1"}"#,
159+
),
160+
(
161+
ReplicateStatusCause::PreComputeInvalidDatasetChecksum(
162+
"0xDatasetAdress2".to_string(),
163+
),
164+
r#"{"cause":"PRE_COMPUTE_INVALID_DATASET_CHECKSUM","message":"Invalid dataset checksum for dataset 0xDatasetAdress2"}"#,
193165
),
194166
];
195167

196-
for (cause, message) in causes {
197-
let exit_message = ExitMessage::from(&cause);
198-
let serialized = to_string(&exit_message).expect("Failed to serialize");
199-
let expected = format!("{{\"cause\":\"{message}\"}}");
200-
assert_eq!(serialized, expected);
168+
for (cause, expected_json) in causes {
169+
let serialized = to_string(&cause).expect("Failed to serialize");
170+
assert_eq!(serialized, expected_json);
201171
}
202172
}
173+
174+
#[test]
175+
fn serialize_vec_of_causes_succeeds_when_multiple_causes() {
176+
let causes = vec![
177+
ReplicateStatusCause::PreComputeDatasetUrlMissing("0xDatasetAdress".to_string()),
178+
ReplicateStatusCause::PreComputeInvalidDatasetChecksum("0xDatasetAdress".to_string()),
179+
];
180+
181+
let serialized = to_string(&causes).expect("Failed to serialize");
182+
let expected = r#"[{"cause":"PRE_COMPUTE_DATASET_URL_MISSING","message":"Dataset URL related environment variable is missing for dataset 0xDatasetAdress"},{"cause":"PRE_COMPUTE_INVALID_DATASET_CHECKSUM","message":"Invalid dataset checksum for dataset 0xDatasetAdress"}]"#;
183+
assert_eq!(serialized, expected);
184+
}
203185
// endregion
204186

205187
// region get_worker_api_client
206188
#[test]
207-
fn should_get_worker_api_client_with_env_var() {
189+
fn from_env_creates_client_with_custom_host_when_env_var_set() {
208190
with_vars(
209191
vec![(WorkerHostEnvVar.name(), Some("custom-worker-host:9999"))],
210192
|| {
@@ -215,29 +197,32 @@ mod tests {
215197
}
216198

217199
#[test]
218-
fn should_get_worker_api_client_without_env_var() {
200+
fn from_env_creates_client_with_default_host_when_env_var_unset() {
219201
temp_env::with_vars_unset(vec![WorkerHostEnvVar.name()], || {
220202
let client = WorkerApiClient::from_env();
221203
assert_eq!(client.base_url, format!("http://{DEFAULT_WORKER_HOST}"));
222204
});
223205
}
224206
// endregion
225207

226-
// region send_exit_cause_for_pre_compute_stage()
208+
// region send_exit_causes_for_pre_compute_stage()
227209
const CHALLENGE: &str = "challenge";
228210
const CHAIN_TASK_ID: &str = "0x123456789abcdef";
229211

230212
#[tokio::test]
231-
async fn should_send_exit_cause() {
213+
async fn send_exit_causes_succeeds_when_api_returns_success() {
232214
let mock_server = MockServer::start().await;
233215
let server_url = mock_server.uri();
234216

235-
let expected_body = json!({
236-
"cause": ReplicateStatusCause::PreComputeInvalidTeeSignature,
237-
});
217+
let expected_body = json!([
218+
{
219+
"cause": "PRE_COMPUTE_INVALID_TEE_SIGNATURE",
220+
"message": "Invalid TEE signature"
221+
}
222+
]);
238223

239224
Mock::given(method("POST"))
240-
.and(path(format!("/compute/pre/{CHAIN_TASK_ID}/exit")))
225+
.and(path(format!("/compute/pre/{CHAIN_TASK_ID}/exit-causes")))
241226
.and(header("Authorization", CHALLENGE))
242227
.and(body_json(&expected_body))
243228
.respond_with(ResponseTemplate::new(200))
@@ -246,13 +231,12 @@ mod tests {
246231
.await;
247232

248233
let result = tokio::task::spawn_blocking(move || {
249-
let exit_message =
250-
ExitMessage::from(&ReplicateStatusCause::PreComputeInvalidTeeSignature);
234+
let exit_causes = vec![ReplicateStatusCause::PreComputeInvalidTeeSignature];
251235
let worker_api_client = WorkerApiClient::new(&server_url);
252-
worker_api_client.send_exit_cause_for_pre_compute_stage(
236+
worker_api_client.send_exit_causes_for_pre_compute_stage(
253237
CHALLENGE,
254238
CHAIN_TASK_ID,
255-
&exit_message,
239+
&exit_causes,
256240
)
257241
})
258242
.await
@@ -262,26 +246,25 @@ mod tests {
262246
}
263247

264248
#[tokio::test]
265-
async fn should_not_send_exit_cause() {
249+
async fn send_exit_causes_fails_when_api_returns_error() {
266250
testing_logger::setup();
267251
let mock_server = MockServer::start().await;
268252
let server_url = mock_server.uri();
269253

270254
Mock::given(method("POST"))
271-
.and(path(format!("/compute/pre/{CHAIN_TASK_ID}/exit")))
255+
.and(path(format!("/compute/pre/{CHAIN_TASK_ID}/exit-causes")))
272256
.respond_with(ResponseTemplate::new(503).set_body_string("Service Unavailable"))
273257
.expect(1)
274258
.mount(&mock_server)
275259
.await;
276260

277261
let result = tokio::task::spawn_blocking(move || {
278-
let exit_message =
279-
ExitMessage::from(&ReplicateStatusCause::PreComputeFailedUnknownIssue);
262+
let exit_causes = vec![ReplicateStatusCause::PreComputeFailedUnknownIssue];
280263
let worker_api_client = WorkerApiClient::new(&server_url);
281-
let response = worker_api_client.send_exit_cause_for_pre_compute_stage(
264+
let response = worker_api_client.send_exit_causes_for_pre_compute_stage(
282265
CHALLENGE,
283266
CHAIN_TASK_ID,
284-
&exit_message,
267+
&exit_causes,
285268
);
286269
testing_logger::validate(|captured_logs| {
287270
let logs = captured_logs
@@ -292,7 +275,7 @@ mod tests {
292275
assert_eq!(logs.len(), 1);
293276
assert_eq!(
294277
logs[0].body,
295-
"Failed to send exit cause: [status:503 Service Unavailable, body:Service Unavailable]"
278+
"Failed to send exit causes: [status:503 Service Unavailable, body:Service Unavailable]"
296279
);
297280
});
298281
response
@@ -308,14 +291,14 @@ mod tests {
308291
}
309292

310293
#[test]
311-
fn test_send_exit_cause_http_request_failure() {
294+
fn send_exit_causes_fails_when_http_request_invalid() {
312295
testing_logger::setup();
313-
let exit_message = ExitMessage::from(&ReplicateStatusCause::PreComputeFailedUnknownIssue);
296+
let exit_causes = vec![ReplicateStatusCause::PreComputeFailedUnknownIssue];
314297
let worker_api_client = WorkerApiClient::new("wrong_url");
315-
let result = worker_api_client.send_exit_cause_for_pre_compute_stage(
298+
let result = worker_api_client.send_exit_causes_for_pre_compute_stage(
316299
CHALLENGE,
317300
CHAIN_TASK_ID,
318-
&exit_message,
301+
&exit_causes,
319302
);
320303
testing_logger::validate(|captured_logs| {
321304
let logs = captured_logs
@@ -326,7 +309,7 @@ mod tests {
326309
assert_eq!(logs.len(), 1);
327310
assert_eq!(
328311
logs[0].body,
329-
"HTTP request failed when sending exit cause to wrong_url/compute/pre/0x123456789abcdef/exit: reqwest::Error { kind: Builder, source: RelativeUrlWithoutBase }"
312+
"HTTP request failed when sending exit causes to wrong_url/compute/pre/0x123456789abcdef/exit-causes: reqwest::Error { kind: Builder, source: RelativeUrlWithoutBase }"
330313
);
331314
});
332315
assert!(result.is_err());

0 commit comments

Comments
 (0)