Skip to content

Commit d618080

Browse files
authored
Merge pull request #2381 from input-output-hk/sfa/2363/error_messages_as_warning_in_signer
Error messages as warning in signer
2 parents 773e911 + 0697709 commit d618080

File tree

10 files changed

+214
-26
lines changed

10 files changed

+214
-26
lines changed

Cargo.lock

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

DEV-ADR.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22

33
---
44

5+
<!--
6+
Template of ADR
7+
8+
## ID. TITLE
9+
10+
date: 2025-XX-XX
11+
status: Accepted
12+
13+
### Context
14+
15+
To complete
16+
17+
### Decision
18+
19+
To complete
20+
21+
### Consequences
22+
23+
To complete
24+
-->
25+
526
## 2. Remove Artifacts serialization support when compiling to WebAssembly
627

728
date: 2025-02-26
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
slug: 10
3+
title: |
4+
10. Specific Mithril Http status code
5+
authors:
6+
- name: Mithril Team
7+
tags: [Accepted]
8+
date: 2025-03-21
9+
---
10+
11+
## Status
12+
13+
Accepted
14+
15+
## Context
16+
17+
In exchanges between the signer and the aggregator, we need to retrieve the reason why a request was unsuccessful.
18+
Error handling will depend on the specific functional case of Mithril.
19+
We could have reused existing HTTP codes, but they are too general and could be returned for cases other than the one we wish to isolate.
20+
21+
## Decision
22+
23+
We therefore decided to return specific error codes when we need to identify the functional case.
24+
We start at 450 for client error codes and at 550 for server error codes.
25+
26+
## Consequences
27+
28+
Specific Mithril HTTP status code on server error should be between 550 and 599.
29+
For client error the HTTP status code should be between 450 and 499.

mithril-aggregator/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mithril-aggregator"
3-
version = "0.7.19"
3+
version = "0.7.20"
44
description = "A Mithril Aggregator server"
55
authors = { workspace = true }
66
edition = { workspace = true }

mithril-aggregator/src/http_server/routes/reply.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ use mithril_persistence::sqlite::error::{SqliteError, SQLITE_BUSY};
1010
use crate::tools::downcast_check;
1111
use crate::SignerRegistrationError;
1212

13+
pub struct MithrilStatusCode();
14+
15+
impl MithrilStatusCode {
16+
pub fn registration_round_not_yet_opened() -> StatusCode {
17+
// The unwrap is safe here because `from_16` function return error only for values outside of the range 100-999,
18+
StatusCode::from_u16(550).unwrap()
19+
}
20+
}
21+
1322
pub fn json<T>(value: &T, status_code: StatusCode) -> Box<dyn warp::Reply>
1423
where
1524
T: Serialize,
@@ -46,7 +55,7 @@ pub fn server_error<E: Into<StdError>>(error: E) -> Box<dyn warp::Reply> {
4655
if downcast_check::<SignerRegistrationError>(&std_error, |e| {
4756
matches!(e, SignerRegistrationError::RegistrationRoundNotYetOpened)
4857
}) {
49-
code = StatusCode::SERVICE_UNAVAILABLE;
58+
code = MithrilStatusCode::registration_round_not_yet_opened();
5059
}
5160

5261
code
@@ -59,10 +68,6 @@ pub fn internal_server_error<T: Into<ServerError>>(message: T) -> Box<dyn warp::
5968
json(&message.into(), StatusCode::INTERNAL_SERVER_ERROR)
6069
}
6170

62-
pub fn service_unavailable<T: Into<ServerError>>(message: T) -> Box<dyn warp::Reply> {
63-
json(&message.into(), StatusCode::SERVICE_UNAVAILABLE)
64-
}
65-
6671
pub fn add_content_disposition_header(
6772
reply: warp::fs::File,
6873
filepath: &Path,
@@ -113,16 +118,22 @@ mod tests {
113118
}
114119

115120
#[test]
116-
fn test_server_error_convert_signer_registration_round_not_yet_opened_to_503() {
121+
fn test_server_error_convert_signer_registration_round_not_yet_opened_to_550() {
117122
let err = SignerRegistrationError::RegistrationRoundNotYetOpened;
118123
let response = server_error(err).into_response();
119124

120-
assert_eq!(StatusCode::SERVICE_UNAVAILABLE, response.status());
125+
assert_eq!(
126+
MithrilStatusCode::registration_round_not_yet_opened(),
127+
response.status()
128+
);
121129

122130
// Wrapping the error in a StdError should also work
123131
let err = anyhow!(SignerRegistrationError::RegistrationRoundNotYetOpened);
124132
let response = server_error(err).into_response();
125133

126-
assert_eq!(StatusCode::SERVICE_UNAVAILABLE, response.status());
134+
assert_eq!(
135+
MithrilStatusCode::registration_round_not_yet_opened(),
136+
response.status()
137+
);
127138
}
128139
}

mithril-aggregator/src/http_server/routes/signer_routes.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ mod handlers {
174174
}
175175
Err(SignerRegistrationError::RegistrationRoundNotYetOpened) => {
176176
warn!(logger, "register_signer::registration_round_not_yed_opened");
177-
Ok(reply::service_unavailable(
178-
SignerRegistrationError::RegistrationRoundNotYetOpened.to_string(),
177+
Ok(reply::server_error(
178+
SignerRegistrationError::RegistrationRoundNotYetOpened,
179179
))
180180
}
181181
Err(err) => {
@@ -275,6 +275,7 @@ mod tests {
275275

276276
use crate::{
277277
database::{record::SignerRecord, repository::MockSignerGetter},
278+
http_server::routes::reply::MithrilStatusCode,
278279
initialize_dependencies,
279280
services::{FakeEpochService, MockSignerRegisterer},
280281
store::MockVerificationKeyStorer,
@@ -478,7 +479,7 @@ mod tests {
478479
}
479480

480481
#[tokio::test]
481-
async fn test_register_signer_post_ko_503() {
482+
async fn test_register_signer_post_ko_550() {
482483
let mut mock_signer_registerer = MockSignerRegisterer::new();
483484
mock_signer_registerer
484485
.expect_register_signer()
@@ -506,7 +507,7 @@ mod tests {
506507
"application/json",
507508
&signer,
508509
&response,
509-
&StatusCode::SERVICE_UNAVAILABLE,
510+
&MithrilStatusCode::registration_round_not_yet_opened(),
510511
)
511512
.unwrap();
512513
}

mithril-signer/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mithril-signer"
3-
version = "0.2.235"
3+
version = "0.2.236"
44
description = "A Mithril Signer"
55
authors = { workspace = true }
66
edition = { workspace = true }

mithril-signer/src/runtime/state_machine.rs

Lines changed: 112 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use anyhow::Error;
12
use slog::{debug, info, Logger};
23
use std::{fmt::Display, ops::Deref, sync::Arc, time::Duration};
34
use tokio::{sync::Mutex, time::sleep};
@@ -8,8 +9,11 @@ use mithril_common::{
89
logging::LoggerExtensions,
910
};
1011

11-
use crate::entities::{BeaconToSign, SignerEpochSettings};
1212
use crate::MetricsService;
13+
use crate::{
14+
entities::{BeaconToSign, SignerEpochSettings},
15+
services::AggregatorClientError,
16+
};
1317

1418
use super::{Runner, RuntimeError};
1519

@@ -314,19 +318,36 @@ impl StateMachine {
314318
nested_error: Some(e),
315319
})?;
316320

317-
self.runner.register_signer_to_aggregator()
318-
.await.map_err(|e| {
319-
if e.downcast_ref::<ProtocolInitializerError>().is_some() {
320-
RuntimeError::Critical { message: format!("Could not register to aggregator in 'unregistered → registered' phase for epoch {:?}.", epoch), nested_error: Some(e) }
321+
fn handle_registration_result(
322+
register_result: Result<(), Error>,
323+
epoch: Epoch,
324+
) -> Result<Option<SignerState>, RuntimeError> {
325+
if let Err(e) = register_result {
326+
if let Some(AggregatorClientError::RegistrationRoundNotYetOpened(_)) =
327+
e.downcast_ref::<AggregatorClientError>()
328+
{
329+
Ok(Some(SignerState::Unregistered { epoch }))
330+
} else if e.downcast_ref::<ProtocolInitializerError>().is_some() {
331+
Err(RuntimeError::Critical { message: format!("Could not register to aggregator in 'unregistered → registered' phase for epoch {:?}.", epoch), nested_error: Some(e) })
332+
} else {
333+
Err(RuntimeError::KeepState { message: format!("Could not register to aggregator in 'unregistered → registered' phase for epoch {:?}.", epoch), nested_error: Some(e) })
334+
}
321335
} else {
322-
RuntimeError::KeepState { message: format!("Could not register to aggregator in 'unregistered → registered' phase for epoch {:?}.", epoch), nested_error: Some(e) }
336+
Ok(None)
323337
}
324-
})?;
338+
}
339+
340+
let register_result = self.runner.register_signer_to_aggregator().await;
341+
let next_state_found = handle_registration_result(register_result, epoch)?;
325342

326343
self.metrics_service
327344
.get_signer_registration_success_since_startup_counter()
328345
.increment();
329346

347+
if let Some(state) = next_state_found {
348+
return Ok(state);
349+
}
350+
330351
self.metrics_service
331352
.get_signer_registration_success_last_epoch_gauge()
332353
.record(epoch);
@@ -452,13 +473,15 @@ impl StateMachine {
452473

453474
#[cfg(test)]
454475
mod tests {
476+
use anyhow::anyhow;
455477
use chrono::DateTime;
456478
use mockall::predicate;
457479

458480
use mithril_common::entities::{ChainPoint, Epoch, ProtocolMessage, SignedEntityType};
459481
use mithril_common::test_utils::fake_data;
460482

461483
use crate::runtime::runner::MockSignerRunner;
484+
use crate::services::AggregatorClientError;
462485
use crate::test_tools::TestLogger;
463486

464487
use super::*;
@@ -645,6 +668,88 @@ mod tests {
645668
},
646669
state_machine.get_state().await
647670
);
671+
672+
let metrics_service = state_machine.metrics_service;
673+
let success_since_startup =
674+
metrics_service.get_runtime_cycle_success_since_startup_counter();
675+
assert_eq!(1, success_since_startup.get());
676+
}
677+
678+
#[tokio::test]
679+
async fn unregistered_to_ready_to_sign_counter() {
680+
let mut runner = MockSignerRunner::new();
681+
682+
runner
683+
.expect_get_epoch_settings()
684+
.once()
685+
.returning(|| Ok(Some(SignerEpochSettings::dummy())));
686+
687+
runner
688+
.expect_inform_epoch_settings()
689+
.with(predicate::eq(SignerEpochSettings::dummy()))
690+
.once()
691+
.returning(|_| Ok(()));
692+
693+
runner
694+
.expect_get_current_time_point()
695+
.times(2)
696+
.returning(|| Ok(TimePoint::dummy()));
697+
runner
698+
.expect_update_stake_distribution()
699+
.once()
700+
.returning(|_| Ok(()));
701+
runner
702+
.expect_register_signer_to_aggregator()
703+
.once()
704+
.returning(|| {
705+
Err(AggregatorClientError::RegistrationRoundNotYetOpened(
706+
anyhow!("Not yet opened"),
707+
))?
708+
});
709+
710+
runner.expect_upkeep().never();
711+
runner.expect_can_sign_current_epoch().never();
712+
713+
let state_machine = init_state_machine(
714+
SignerState::Unregistered {
715+
epoch: TimePoint::dummy().epoch,
716+
},
717+
runner,
718+
);
719+
720+
state_machine
721+
.cycle()
722+
.await
723+
.expect("Cycling the state machine should not fail");
724+
725+
assert_eq!(
726+
SignerState::Unregistered {
727+
epoch: TimePoint::dummy().epoch,
728+
},
729+
state_machine.get_state().await
730+
);
731+
732+
let metrics_service = state_machine.metrics_service;
733+
assert_eq!(
734+
1,
735+
metrics_service
736+
.get_signer_registration_success_since_startup_counter()
737+
.get()
738+
);
739+
740+
assert_eq!(
741+
0 as f64,
742+
metrics_service
743+
.get_signer_registration_success_last_epoch_gauge()
744+
.get()
745+
);
746+
747+
assert_eq!(
748+
1,
749+
metrics_service
750+
.get_runtime_cycle_total_since_startup_counter()
751+
.get()
752+
);
648753
}
649754

650755
#[tokio::test]

0 commit comments

Comments
 (0)