diff options
Diffstat (limited to 'src/remake')
| -rw-r--r-- | src/remake/api.rs | 43 |
1 files changed, 29 insertions, 14 deletions
diff --git a/src/remake/api.rs b/src/remake/api.rs index 2cd55e5..7750242 100644 --- a/src/remake/api.rs +++ b/src/remake/api.rs @@ -204,7 +204,27 @@ impl From<RetentionRequest> for Retention { /// What `?force=` may override on a proposal (`force`). const FORCE: &[&str] = &["switchDelay", "supersede", "coordinatorActive", "clock"]; /// What `?force=` may override on an abort. -const ABORT_FORCE: &[&str] = &["coordinatorActive", "coordinatorInactive"]; +const ABORT_FORCE: &[&str] = &["coordinatorActive", "coordinatorInactive", "clock"]; + +/// `409` while this node's clock is off from most of its peers' (and `clock` isn't +/// forced): it would judge a remake's deadlines wrongly, proposing one or aborting one +/// as expired (`trust.md`, "What depends on clocks"). `what` is what it would do. +async fn clock_obstacle<H: Host>(host: &H, force: &Force, what: &str) -> Option<Response> { + let skew = host.clock_skew().await; + if !skew.off || force.allows("clock") { + return None; + } + let offset = skew.offset_ms.unwrap_or(0); + Some(error_response( + StatusCode::CONFLICT, + format!( + "this node's clock is {} {} most of its peers' (more than --clock-skew-warn), so it would judge {what} wrongly; fix its time synchronisation, or add ?force=clock", + crate::units::format_duration(Duration::from_millis(offset.unsigned_abs())), + if offset < 0 { "behind" } else { "ahead of" } + ), + Some(json!({ "force": "clock" })), + )) +} #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -298,19 +318,9 @@ async fn start_kind<H: Host>( Err(reason) => return error_response(StatusCode::BAD_REQUEST, reason, None), }; // The coordinator stamps and judges the voting period and the deadlines by its - // clock (`trust.md`, "What depends on clocks"). - let skew = host.clock_skew().await; - if skew.off && !force.allows("clock") { - let offset = skew.offset_ms.unwrap_or(0); - return error_response( - StatusCode::CONFLICT, - format!( - "this node's clock is {} {} most of its peers' (more than --clock-skew-warn), so it would judge the remake's deadlines wrongly; fix its time synchronisation, or add ?force=clock", - crate::units::format_duration(Duration::from_millis(offset.unsigned_abs())), - if offset < 0 { "behind" } else { "ahead of" } - ), - Some(json!({ "force": "clock" })), - ); + // clock. + if let Some(refused) = clock_obstacle(&host, &force, "the remake's deadlines").await { + return refused; } let request: StartRequest = if is_octet_stream(headers) { @@ -706,6 +716,11 @@ async fn abort_by_id<H: Host>(host: H, id: String, query: AbortQuery) -> Respons /// commit it any more (`coordinator`), unless `abort_obstacle` says otherwise. Answers /// with the final report. async fn abort_expired<H: Host>(host: &H, current: Report, force: &Force) -> Response { + // Whether it expired is judged by this node's clock: one far ahead would abort a + // remake its coordinator may still commit (a contested remake). + if let Some(refused) = clock_obstacle(host, force, "whether the remake expired").await { + return refused; + } let grace = grace(host); if let Some((why, force)) = abort_obstacle( ¤t.record, |
