From 92331fec305665242958a2cac98414346f9e133d Mon Sep 17 00:00:00 2001 From: Jean-Philippe Raynaud Date: Thu, 30 Jul 2026 18:53:59 +0200 Subject: [PATCH 1/5] fix(e2e): poll epoch transitions at a tenth of the epoch duration The exponential poll backoff exceeded short devnet epochs, so target epochs could be detected up to a full epoch after their boundary. --- .../mithril-end-to-end/src/toolkit/context.rs | 16 ++++++++++++++++ .../mithril-end-to-end/src/toolkit/wait.rs | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mithril-test-lab/mithril-end-to-end/src/toolkit/context.rs b/mithril-test-lab/mithril-end-to-end/src/toolkit/context.rs index 2ee68f3fec3..bab5fe0393a 100644 --- a/mithril-test-lab/mithril-end-to-end/src/toolkit/context.rs +++ b/mithril-test-lab/mithril-end-to-end/src/toolkit/context.rs @@ -31,6 +31,13 @@ impl ScenarioToolkitContext { Backoff::default() } + /// Backoff polling at a constant tenth of the epoch duration, so that epoch transitions + /// are detected shortly after the epoch boundary. + pub fn tenth_of_epoch_poll_backoff(&self) -> Backoff { + let delay = self.timeout_for_epochs(1) / 10; + Backoff::new(delay, delay, 1) + } + /// Timeout covering the given number of Cardano epochs. pub fn timeout_for_epochs(&self, epochs: u32) -> Duration { self.attempt_policy.timeout_for_epochs(epochs) @@ -96,6 +103,15 @@ mod tests { assert_eq!(policy.timeout_for_epochs(5), Duration::from_secs(50)); } + #[test] + fn tenth_of_epoch_poll_backoff_polls_at_a_constant_delay() { + let context = ScenarioToolkitContext::new(AttemptPolicy::new(Duration::from_secs(10))); + let mut backoff = context.tenth_of_epoch_poll_backoff(); + + assert_eq!(backoff.next_delay(), Duration::from_secs(1)); + assert_eq!(backoff.next_delay(), Duration::from_secs(1)); + } + #[test] fn named_timeouts_cover_expected_epochs() { let context = ScenarioToolkitContext::new(AttemptPolicy::new(Duration::from_secs(10))); diff --git a/mithril-test-lab/mithril-end-to-end/src/toolkit/wait.rs b/mithril-test-lab/mithril-end-to-end/src/toolkit/wait.rs index 02d56b2cc38..bac21408c5d 100644 --- a/mithril-test-lab/mithril-end-to-end/src/toolkit/wait.rs +++ b/mithril-test-lab/mithril-end-to-end/src/toolkit/wait.rs @@ -114,7 +114,7 @@ impl WaitToolkit { let epochs_to_wait = Self::compute_number_of_epochs_to_wait(target_epoch, current_epoch); let timeout = self.context.timeout_for_epochs(epochs_to_wait); - match poll_until!(timeout, self.context.poll_backoff(), { + match poll_until!(timeout, self.context.tenth_of_epoch_poll_backoff(), { match aggregator .chain_observer() .get_current_epoch() From 92d56411134bc75a839a1d29cfd1f1df64d647be Mon Sep 17 00:00:00 2001 From: Jean-Philippe Raynaud Date: Thu, 30 Jul 2026 18:53:59 +0200 Subject: [PATCH 2/5] fix(e2e): certify current epoch before protocol parameters restart A restart crossing an epoch boundary before the current epoch is certified creates an unrecoverable epoch gap that blocks the aggregator. --- .../mithril-end-to-end/src/scenario/full.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mithril-test-lab/mithril-end-to-end/src/scenario/full.rs b/mithril-test-lab/mithril-end-to-end/src/scenario/full.rs index af98b36a995..475b8d30c8a 100644 --- a/mithril-test-lab/mithril-end-to-end/src/scenario/full.rs +++ b/mithril-test-lab/mithril-end-to-end/src/scenario/full.rs @@ -175,6 +175,16 @@ impl FullScenario { .await?; if aggregator.is_first() || aggregator.version().is_below("0.7.94") { + // A certificate chain lagging one epoch behind the chain tip is normal, but a restart + // crossing an epoch boundary before the current epoch is certified would create an + // unrecoverable epoch gap and block the aggregator + let current_epoch = chain_observer.get_current_epoch().await?.unwrap_or_default(); + self.toolkit + .check + .certificate + .is_creating_certificate_with_min_epoch(aggregator, current_epoch) + .await?; + self.toolkit .exec .update_protocol_parameters(aggregator, infrastructure.aggregate_signature_type()) From 9bb7e7dc46d9b970807e96441656dd3ef7934b0f Mon Sep 17 00:00:00 2001 From: Jean-Philippe Raynaud Date: Thu, 30 Jul 2026 19:09:00 +0200 Subject: [PATCH 3/5] fix(e2e): extend artifact production timeout to three epochs A single epoch left no slack for certificate sealing latency, causing spurious 'Timeout exhausted waiting for...' failures. --- mithril-test-lab/mithril-end-to-end/src/toolkit/context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mithril-test-lab/mithril-end-to-end/src/toolkit/context.rs b/mithril-test-lab/mithril-end-to-end/src/toolkit/context.rs index bab5fe0393a..c8d8d47c3ff 100644 --- a/mithril-test-lab/mithril-end-to-end/src/toolkit/context.rs +++ b/mithril-test-lab/mithril-end-to-end/src/toolkit/context.rs @@ -45,7 +45,7 @@ impl ScenarioToolkitContext { /// Timeout to wait for the aggregator to produce a signed artifact once it is running. pub fn artifact_production_timeout(&self) -> Duration { - self.timeout_for_epochs(1) + self.timeout_for_epochs(3) } /// Timeout to wait for the devnet and aggregator to become ready during startup. @@ -118,7 +118,7 @@ mod tests { assert_eq!( context.artifact_production_timeout(), - Duration::from_secs(10) + Duration::from_secs(30) ); assert_eq!( context.startup_readiness_timeout(), From 31508bee001cbd22005d8beed210e92ef2051222 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Raynaud Date: Thu, 30 Jul 2026 21:13:39 +0200 Subject: [PATCH 4/5] fix(e2e): lighten updated protocol parameters Reduce the number of lotteries played to get closer to the initial parameter set, as the test only needs to exercise the parameters update. --- mithril-test-lab/mithril-end-to-end/src/toolkit/exec.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mithril-test-lab/mithril-end-to-end/src/toolkit/exec.rs b/mithril-test-lab/mithril-end-to-end/src/toolkit/exec.rs index 4b093fa6f9d..8acc4382e9b 100644 --- a/mithril-test-lab/mithril-end-to-end/src/toolkit/exec.rs +++ b/mithril-test-lab/mithril-end-to-end/src/toolkit/exec.rs @@ -102,9 +102,9 @@ impl ExecToolkit { aggregator.stop().await?; let protocol_parameters_new = match aggregate_signature_type { AggregateSignatureType::Concatenation => ProtocolParameters { - k: 145, - m: 210, - phi_f: 0.80, + k: 83, + m: 130, + phi_f: 0.75, }, AggregateSignatureType::Snark => ProtocolParameters { k: 7, From 520f12bd88addd1ffe30c596bf69bf41c774c88d Mon Sep 17 00:00:00 2001 From: Jean-Philippe Raynaud Date: Mon, 3 Aug 2026 17:38:37 +0200 Subject: [PATCH 5/5] chore: upgrade crate versions * mithril-end-to-end from `0.5.9` to `0.5.10` --- Cargo.lock | 2 +- mithril-test-lab/mithril-end-to-end/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1e7c45d8d2..9af686d6430 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4603,7 +4603,7 @@ dependencies = [ [[package]] name = "mithril-end-to-end" -version = "0.5.9" +version = "0.5.10" dependencies = [ "anyhow", "async-recursion", diff --git a/mithril-test-lab/mithril-end-to-end/Cargo.toml b/mithril-test-lab/mithril-end-to-end/Cargo.toml index 9223981b088..b0f18df0b04 100644 --- a/mithril-test-lab/mithril-end-to-end/Cargo.toml +++ b/mithril-test-lab/mithril-end-to-end/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-end-to-end" -version = "0.5.9" +version = "0.5.10" authors = { workspace = true } edition = { workspace = true } documentation = { workspace = true }