From cceb60e9c0588237948e4ebbdfebc5249c2d268d Mon Sep 17 00:00:00 2001 From: Toralf Wittner Date: Tue, 30 Sep 2025 14:40:09 +0200 Subject: [PATCH] Remove `run_dkg_handover_decryption_phase_e2e`. This test is redundant since [PR #502][1] was merged and is often failing on CI. The reason for its failure is a race condition: sometimes not all nodes finish the resharing because of all the DKG requests they send, less than t + 1 are answered because peers are themselves in an incomplete DKG state or they are not in a running state. In any case they are not replying. The test is then ultimately hanging and timing out because it attempts to subsequently collect the inclusion list from all decrypters and some can not provideit. The test would need to be fixed by allowing enough time for the decrypters of committee 1 to have their local DKG state ready before the members of committee 2 send their DKG requests. For production it is assumed that between scheduling the next committee and activating it, sufficient time is given. Rather than fixing the test this PR removes it because -- as mentioned above -- it is redundant with another test. [1]: https://github.com/EspressoSystems/timeboost/pull/502 --- timeboost-sequencer/src/decrypt.rs | 196 ++--------------------------- 1 file changed, 9 insertions(+), 187 deletions(-) diff --git a/timeboost-sequencer/src/decrypt.rs b/timeboost-sequencer/src/decrypt.rs index 923ec155..768a236f 100644 --- a/timeboost-sequencer/src/decrypt.rs +++ b/timeboost-sequencer/src/decrypt.rs @@ -427,15 +427,15 @@ impl Drop for Decrypter { enum WorkerState { /// Obtains the threshold decryption key from DKG bundles. /// - /// A node can recover its threshold decryption key in two ways: + /// A node can recover its threshold decryption key in two ways: /// 1. **Consensus**: by combining DKG bundles extracted directly from candidate lists produced /// by peers. /// 2. **Network**: by receiving and combining an agreed-upon subset of DKG bundles from a /// designated set of peers. /// - /// For the initial DKG, method (1) is used. - /// For resharing, method (2) is used, with the source peers being the previous committee. - /// When catching up—whether during the initial DKG or after resharing—the node also uses + /// For the initial DKG, method (1) is used. + /// For resharing, method (2) is used, with the source peers being the previous committee. + /// When catching up—whether during the initial DKG or after resharing—the node also uses /// method (2), but obtains the bundles from the current committee. DkgPending(HashMap), /// Active mode with decryption key ready. @@ -448,7 +448,7 @@ impl Default for WorkerState { } } -/// A `Worker` handles the production, exchange, and combination of decryption shares +/// A `Worker` handles the production, exchange, and combination of decryption shares /// in coordination with other peers. See [`Decrypter`] for details. #[derive(Builder)] struct Worker { @@ -1546,7 +1546,6 @@ mod tests { time::Instant, }; use test_utils::ports::alloc_port; - use timeboost_config::DECRYPTER_PORT_OFFSET; use timeboost_utils::types::logging; use cliquenet::AddressableCommittee; @@ -1573,7 +1572,6 @@ mod tests { const TEST_SEQNO: u64 = 10; const RETAIN_ROUNDS: usize = 100; const COM1: u64 = 1; - const COM2: u64 = 2; // Pre-generated deterministic keys for consistent testing // Generated via: `just mkconfig_local 5 --seed 42` @@ -1585,14 +1583,6 @@ mod tests { "6LMMEuoPRCkpDsnxnANCRCBC6JagdCHs2pjNicdmQpQE", ]; - const COM2_SIGNATURE_PRIVATE_KEY_STRINGS: [&str; COMMITTEE_SIZE] = [ - "6YJc9asDJQsFFHHg8iX23oL1sySx2EgTGM8CqDd71WMa", - "6NZdk8EGRSVS7sSjcjMiC8vL4KcSY41ELU6HaKyk1Drk", - "AMJEYXVhS4FfoqfD6VL5nZTNbzJQdtgdxBcVEAwZnLJ3", - "5mLCMSVT4f8HPqCqdKChxugpMcQ3x1C7hwfEnsTApFfp", - "4yhZYmNvAouKFpiYomrV9aH8kUZTLRH2hiCZtJzcAASa", - ]; - const COM1_DH_PRIVATE_KEY_STRINGS: [&str; COMMITTEE_SIZE] = [ "BB3zUfFQGfw3sL6bpp1JH1HozK6ehEDmRGoiCpQH62rZ", "4hjtciEvuoFVT55nAzvdP9E76r18QwntWwFoeginCGnP", @@ -1601,14 +1591,6 @@ mod tests { "39wAn3bQzpn19oa8CiaNUFd8GekQAJMMuzrbp8Jt3FKz", ]; - const COM2_DH_PRIVATE_KEY_STRINGS: [&str; COMMITTEE_SIZE] = [ - "Ho9ZZCkP1i6f12fkWGUZucJxX2qbv9L3UbB56sqwjvkw", - "F5E9cp5qUe4z2wdaRYvEhFMYXaLy4sEX1rwCWRcTH69i", - "5zhwDWJ1ut6q871aHCHmH7QRCHDykXjUSEriGTLkwezL", - "2PojAT3g5iqGTrgLhTRKKHqwbeoFhRkzkYLM9AttFoKK", - "Bk2wY7o2YE9gpu6jxiRSbWiMU88H8s9D9MQGMaF2LJ7h", - ]; - const COM1_DKG_PRIVATE_KEY_STRINGS: [&str; COMMITTEE_SIZE] = [ "BW8gq8MARtDkSJL6daobPtGQm22TKkXdbLNrNGngNGTB", "ARtqWGmRWrBqZUr4MmiLaPgzjsiKp5USsC9iQNRMZYy4", @@ -1617,18 +1599,10 @@ mod tests { "GFvv2wcQmiGpk5rFp1FGpeUjnVUyZmGM9k8VHb1Jn7EG", ]; - const COM2_DKG_PRIVATE_KEY_STRINGS: [&str; COMMITTEE_SIZE] = [ - "GLNPt6EYeFQbq8cCX5nKMA4LbCKdrUN5o5hmWTPJbkJZ", - "2n3Pz5NeUAVu5YaWgXN9CCrS6rC7NZwVZ48AG8m7JRqF", - "AGxRDhGvgovb1DZMjL3Y6Q4hF6UUpFCtpPD6sTJd827U", - "A3GUHxG3cPKvCch6XowztrHrrvaGS5JH3CuXQKCxQEnv", - "216zC1MgfV54cJJtt3AKdjJHhqfgZLZCmEyCkFN1bPoF", - ]; - - #[test] /// Tests the local DKG (Distributed Key Generation) end-to-end flow without networking. /// Verifies that committee members can generate threshold decryption keys and perform /// threshold encryption/decryption operations. + #[test] fn test_local_dkg_e2e() { logging::init_logging(); let mut rng = thread_rng(); @@ -1949,9 +1923,9 @@ mod tests { ); } - #[tokio::test] /// Tests integrated DKG to ensure it terminates with consistent public encryption keys /// across all committee members in a networked environment. + #[tokio::test] async fn test_dkg_termination() { logging::init_logging(); let (mut decrypters, setup) = setup( @@ -1985,174 +1959,22 @@ mod tests { } } - #[tokio::test] /// Tests the complete DKG and decryption phase end-to-end flow in a networked environment. /// Verifies that encrypted transactions can be properly decrypted after DKG completion. + #[tokio::test] async fn test_dkg_and_decryption_phase_e2e() { run_dkg_and_decryption_phase_e2e(false).await; } - #[tokio::test] /// Tests the complete DKG and decryption phase end-to-end flow in a networked environment. /// Verifies that encrypted transactions can be properly decrypted after DKG completion. /// The node that is catching up will not have the dealings locally enqueued but will instead /// fetch the dealings from other nodes to obtain the DKG key material. + #[tokio::test] async fn test_dkg_and_decryption_phase_e2e_with_catchup() { run_dkg_and_decryption_phase_e2e(true).await; } - #[tokio::test] - /// Tests the full spectrum of Decrypter states: - /// 1. Initial committee completes dkg and decrypts transactions. - /// 2. NextCommittee and UseCommittee events are triggered adding nodes to the network. - /// 3. Resharing is done in the background among new/old committee members. - /// 4. Old committee decrypts its last inclusion list triggering committee switch. - /// 5. New committee decrypts its first inclusion list using key from resharing. - async fn run_dkg_handover_decryption_phase_e2e() { - logging::init_logging(); - - let (mut com1_decrypters, com1_setup) = setup( - COM1, - &COM1_SIGNATURE_PRIVATE_KEY_STRINGS, - &COM1_DH_PRIVATE_KEY_STRINGS, - &COM1_DKG_PRIVATE_KEY_STRINGS, - None, - ) - .await; - tracing::info!("COM1 decrypters and setup initialized"); - - let com1_round = RoundNumber::new(DECRYPTION_ROUND); - - enqueue_all_dkg_bundles(&mut com1_decrypters, None).await; - tracing::info!("All DKG bundles enqueued for COM1"); - - for cell in com1_setup.dec_keys() { - cell.read().await; - } - tracing::info!("COM1 DKG keys generated and available"); - - let encryption_key = com1_setup.dec_keys()[0] - .get() - .expect("encryption key should be generated after DKG"); - - let com2_round = RoundNumber::new(DECRYPTION_ROUND + 1); - let (mut com2_decrypters, com2_setup) = setup( - COM2, - &COM2_SIGNATURE_PRIVATE_KEY_STRINGS, - &COM2_DH_PRIVATE_KEY_STRINGS, - &COM2_DKG_PRIVATE_KEY_STRINGS, - Some(com1_setup.clone()), - ) - .await; - tracing::info!("COM2 decrypters and setup initialized with previous committee"); - - // trigger NextCommittee event at each decrypter in COM1 - for decrypter in com1_decrypters.iter_mut() { - let mut sf_addr = com2_setup.addr_comm().clone(); - sf_addr.update_addresses(|a| a.clone().with_port(a.port() - DECRYPTER_PORT_OFFSET)); - decrypter - .next_committee(sf_addr, com2_setup.key_store().clone()) - .await - .expect("next committee event succeeds"); - } - tracing::info!("NextCommittee event triggered for all COM1 decrypters"); - - let priority_tx_message = b"Priority message for old committee"; - let regular_tx_message = b"Non-priority message for old committee"; - - let encrypted_inclusion_list = create_encrypted_inclusion_list( - com1_round, - com1_setup.addr_comm().committee().clone(), - com1_setup.sig_keys(), - encryption_key.pubkey(), - priority_tx_message, - regular_tx_message, - ); - tracing::info!("Encrypted inclusion list created for COM1 round"); - - // enqueues the same inclusion list to all nodes in COM1 - for decrypter in com1_decrypters.iter_mut() { - decrypter - .enqueue(encrypted_inclusion_list.clone()) - .await - .expect("Inclusion list should be enqueued successfully"); - } - tracing::info!("Encrypted inclusion list enqueued to all COM1 decrypters"); - - let _ = collect_inclusion_lists(&mut com1_decrypters).await; - tracing::info!("Decrypted inclusion lists collected from COM1 decrypters"); - - // enqueue resharing bundles (for COM2) at each decrypter in COM1 - enqueue_all_dkg_bundles(&mut com1_decrypters, Some(com2_setup.key_store().clone())).await; - tracing::info!("Resharing bundles enqueued for COM2 at all COM1 decrypters"); - - // trigger UseCommittee event for COM2 - for decrypter in com2_decrypters.iter_mut() { - decrypter - .use_committee(Round::new(com2_round, COM2)) - .await - .expect("use committee event succeeds"); - } - tracing::info!("UseCommittee event triggered for COM2 decrypters"); - - let priority_tx_message = b"Priority message for new committee"; - let regular_tx_message = b"Non-priority message for new committee"; - - let encrypted_inclusion_list = create_encrypted_inclusion_list( - com2_round, - com2_setup.addr_comm().committee().clone(), - com2_setup.sig_keys(), - encryption_key.pubkey(), // same encryption key - priority_tx_message, - regular_tx_message, - ); - tracing::info!("Encrypted inclusion list created for COM2 round"); - - for new_decrypter in com2_decrypters.iter_mut() { - new_decrypter - .enqueue(encrypted_inclusion_list.clone()) - .await - .expect("Inclusion list should be enqueued successfully"); - } - tracing::info!("Encrypted inclusion list enqueued to all COM2 decrypters"); - - let decrypted_inclusion_lists = collect_inclusion_lists(&mut com2_decrypters).await; - tracing::info!("Decrypted inclusion lists collected from COM2 decrypters"); - - // Verify that all decrypted inclusion lists are correct - for decrypted_list in decrypted_inclusion_lists { - assert_eq!( - decrypted_list.round(), - com2_round, - "Decrypted list should have the expected round number" - ); - assert_eq!( - decrypted_list.priority_bundles().len(), - 1, - "Should have exactly one priority bundle" - ); - assert_eq!( - decrypted_list.regular_bundles().len(), - 1, - "Should have exactly one regular bundle" - ); - - let decrypted_priority_data = decrypted_list.priority_bundles()[0].bundle().data(); - let decrypted_regular_data = decrypted_list.regular_bundles()[0].data(); - - assert_eq!( - decrypted_priority_data.to_vec(), - priority_tx_message.to_vec(), - "Decrypted priority transaction should match original" - ); - assert_eq!( - decrypted_regular_data.to_vec(), - regular_tx_message.to_vec(), - "Decrypted regular transaction should match original" - ); - } - } - /// Helper to run DKG and decryption phase E2E test. async fn run_dkg_and_decryption_phase_e2e(catchup: bool) { logging::init_logging();