From fcd2a1b8cee7105de02a3581b2aac9a6c19c3195 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Feb 2026 08:23:22 +0000 Subject: [PATCH 01/16] feat: integrate share proofs in ciphernode --- crates/fhe/src/runtime.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/fhe/src/runtime.rs b/crates/fhe/src/runtime.rs index 64d6869b25..9ca229a386 100644 --- a/crates/fhe/src/runtime.rs +++ b/crates/fhe/src/runtime.rs @@ -185,3 +185,4 @@ impl SecretKeySerializer { }) } } + From 5b4f3f356f793efec0702303cb7473c8d8c0db6d Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Feb 2026 16:49:18 +0000 Subject: [PATCH 02/16] feat: add t1 proof gen --- crates/events/src/enclave_event/threshold_share_created.rs | 5 ++++- crates/fhe/src/runtime.rs | 1 - crates/keyshare/src/threshold_keyshare.rs | 2 ++ crates/test-helpers/src/usecase_helpers.rs | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/events/src/enclave_event/threshold_share_created.rs b/crates/events/src/enclave_event/threshold_share_created.rs index e9794a0397..69e84f76d7 100644 --- a/crates/events/src/enclave_event/threshold_share_created.rs +++ b/crates/events/src/enclave_event/threshold_share_created.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::E3id; +use crate::{E3id, SignedProofPayload}; use actix::Message; use derivative::Derivative; use e3_trbfv::shares::BfvEncryptedShares; @@ -32,6 +32,8 @@ pub struct ThresholdShare { pub sk_sss: BfvEncryptedShares, /// BFV-encrypted esi_sss - one per secret key (sk), each recipient can decrypt their share pub esi_sss: Vec, + /// The signed proof payload for fault attribution + pub signed_pk_generation_proof: Option, } impl ThresholdShare { @@ -49,6 +51,7 @@ impl ThresholdShare { pk_share: self.pk_share.clone(), sk_sss, esi_sss, + signed_pk_generation_proof: self.signed_pk_generation_proof.clone(), }) } diff --git a/crates/fhe/src/runtime.rs b/crates/fhe/src/runtime.rs index 9ca229a386..64d6869b25 100644 --- a/crates/fhe/src/runtime.rs +++ b/crates/fhe/src/runtime.rs @@ -185,4 +185,3 @@ impl SecretKeySerializer { }) } } - diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index b2a2e3a0f9..ea2532128a 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -726,6 +726,7 @@ impl ThresholdKeyshare { )) })?; } + Ok(()) } @@ -786,6 +787,7 @@ impl ThresholdKeyshare { pk_share, sk_sss: encrypted_sk_sss, esi_sss: encrypted_esi_sss, + signed_pk_generation_proof: None, }; // Build the proof request diff --git a/crates/test-helpers/src/usecase_helpers.rs b/crates/test-helpers/src/usecase_helpers.rs index 54b3039311..62aef11559 100644 --- a/crates/test-helpers/src/usecase_helpers.rs +++ b/crates/test-helpers/src/usecase_helpers.rs @@ -114,6 +114,7 @@ pub fn generate_shares_hash_map( esi_sss: encrypted_esi_sss, sk_sss: encrypted_sk_sss, pk_share, + signed_pk_generation_proof: None, }, ); } From 5b5ab6c1a795e88c008a42d045debcc7b4294bb9 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:41:48 +0000 Subject: [PATCH 03/16] chore: refactoring --- .../enclave_event/threshold_share_created.rs | 5 +- crates/keyshare/src/threshold_keyshare.rs | 9 +- crates/test-helpers/src/usecase_helpers.rs | 1 - crates/tests/tests/integration.rs | 116 +++++++++++------- 4 files changed, 82 insertions(+), 49 deletions(-) diff --git a/crates/events/src/enclave_event/threshold_share_created.rs b/crates/events/src/enclave_event/threshold_share_created.rs index 69e84f76d7..e9794a0397 100644 --- a/crates/events/src/enclave_event/threshold_share_created.rs +++ b/crates/events/src/enclave_event/threshold_share_created.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::{E3id, SignedProofPayload}; +use crate::E3id; use actix::Message; use derivative::Derivative; use e3_trbfv::shares::BfvEncryptedShares; @@ -32,8 +32,6 @@ pub struct ThresholdShare { pub sk_sss: BfvEncryptedShares, /// BFV-encrypted esi_sss - one per secret key (sk), each recipient can decrypt their share pub esi_sss: Vec, - /// The signed proof payload for fault attribution - pub signed_pk_generation_proof: Option, } impl ThresholdShare { @@ -51,7 +49,6 @@ impl ThresholdShare { pk_share: self.pk_share.clone(), sk_sss, esi_sss, - signed_pk_generation_proof: self.signed_pk_generation_proof.clone(), }) } diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index ea2532128a..82cb6b5433 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -35,8 +35,16 @@ use e3_utils::{NotifySync, MAILBOX_LIMIT}; use e3_zk_helpers::CiphernodesCommitteeSize; use fhe::bfv::{PublicKey, SecretKey}; use fhe_traits::{DeserializeParametrized, Serialize}; +<<<<<<< HEAD use rand::rngs::OsRng; use std::{collections::HashMap, mem, sync::Arc}; +======= +use rand::{rngs::OsRng, SeedableRng}; +use rand_chacha::ChaCha20Rng; +use std::{ + collections::HashMap, mem, sync::{Arc, Mutex} +}; +>>>>>>> be8c56d3 (chore: refactoring) use tracing::{info, trace, warn}; use crate::encryption_key_collector::{AllEncryptionKeysCollected, EncryptionKeyCollector}; @@ -787,7 +795,6 @@ impl ThresholdKeyshare { pk_share, sk_sss: encrypted_sk_sss, esi_sss: encrypted_esi_sss, - signed_pk_generation_proof: None, }; // Build the proof request diff --git a/crates/test-helpers/src/usecase_helpers.rs b/crates/test-helpers/src/usecase_helpers.rs index 62aef11559..54b3039311 100644 --- a/crates/test-helpers/src/usecase_helpers.rs +++ b/crates/test-helpers/src/usecase_helpers.rs @@ -114,7 +114,6 @@ pub fn generate_shares_hash_map( esi_sss: encrypted_esi_sss, sk_sss: encrypted_sk_sss, pk_share, - signed_pk_generation_proof: None, }, ); } diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index e4bd5291ac..97bdd1eddc 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -499,44 +499,68 @@ async fn test_trbfv_actor() -> Result<()> { )); // First, wait for all EncryptionKeyCreated events (BFV key exchange) + // Each of the 5 parties: + // - EncryptionKeyPending = 5 + // - ComputeRequest (T0 ZK proof) = 5 + // - ComputeResponse (T0 ZK proof) = 5 + // - EncryptionKeyCreated = 5 + // Total: 20 events let encryption_keys_timer = Instant::now(); - let expected = vec![ - "EncryptionKeyCreated", - "EncryptionKeyCreated", - "EncryptionKeyCreated", - "EncryptionKeyCreated", - "EncryptionKeyCreated", - ]; - let _ = nodes - .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) + let expected_count = 5 + 5 + 5 + 5; // EncKeyPending + T0 ComputeReq + T0 ComputeResp + EncKeyCreated + println!( + "DEBUG: Waiting for {} encryption key events...", + expected_count + ); + let h = nodes + .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) .await?; + println!("DEBUG: EncryptionKey phase events: {:?}", h.event_types()); report.push(( "All EncryptionKeyCreated events", encryption_keys_timer.elapsed(), )); - // Then wait for all ThresholdShareCreated events - // With domain-level splitting, each of the 5 parties publishes 5 events (one per target party) - // Total: 5 parties × 5 targets = 25 events + // Then wait for share generation compute events + ThresholdShareCreated + PkGenerationProofSigned + // Each of the 5 parties: + // - ComputeRequest + ComputeResponse for GenPkShareAndSkSss = 10 + // - ComputeRequest + ComputeResponse for GenEsiSss = 10 + // - ThresholdSharePending = 5 + // - ComputeRequest + ComputeResponse for T1 ZK proof = 10 + // - 5 ThresholdShareCreated events (one per target party) = 25 total + // - 1 PkGenerationProofSigned event = 5 total + // Total: 10 + 10 + 5 + 10 + 25 + 5 = 65 events let shares_timer = Instant::now(); - let expected: Vec<&str> = (0..25).map(|_| "ThresholdShareCreated").collect(); - let _ = nodes - .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) + let expected_count = 10 + 10 + 5 + 10 + 25 + 5; // GenPk + GenEsi + TSPending + T1 ZK + TSCreated + PkGenProof + println!( + "DEBUG: Waiting for {} share generation events...", + expected_count + ); + let h = nodes + .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) .await?; - report.push(("All ThresholdShareCreated events", shares_timer.elapsed())); - + println!( + "DEBUG: Share generation phase events: {:?}", + h.event_types() + ); + report.push(("All share generation events", shares_timer.elapsed())); + + // Wait for CalculateDecryptionKey compute events + KeyshareCreated + PublicKeyAggregated + // Each of the 5 parties: + // - ComputeRequest for CalculateDecryptionKey + // - ComputeResponse for CalculateDecryptionKey + // - KeyshareCreated + // Plus 1 PublicKeyAggregated at the end + // Total: 5 + 5 + 5 + 1 = 16 events let shares_to_pubkey_agg_timer = Instant::now(); - let expected = vec![ - "KeyshareCreated", - "KeyshareCreated", - "KeyshareCreated", - "KeyshareCreated", - "KeyshareCreated", - "PublicKeyAggregated", - ]; + let expected_count = 5 + 5 + 5 + 1; // ComputeRequest + ComputeResponse + KeyshareCreated + PublicKeyAggregated + println!( + "DEBUG: Waiting for {} decryption key events...", + expected_count + ); let h = nodes - .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) + .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) .await?; + println!("DEBUG: Decryption key phase events: {:?}", h.event_types()); report.push(( "ThresholdShares -> PublicKeyAggregated", @@ -548,14 +572,16 @@ async fn test_trbfv_actor() -> Result<()> { e3_requested_timer.elapsed(), )); let app_gen_timer = Instant::now(); - assert_eq!(h.event_types(), expected); - // Aggregate decryption + // Verify we got the expected events (last should be PublicKeyAggregated) // First we get the public key println!("Getting public key"); let Some(EnclaveEventData::PublicKeyAggregated(pubkey_event)) = h.last().map(|e| e.get_data()) else { - panic!("Was expecting event to be PublicKeyAggregated"); + panic!( + "Was expecting last event to be PublicKeyAggregated, got: {:?}", + h.event_types() + ); }; let pubkey_bytes = pubkey_event.pubkey.clone(); @@ -603,23 +629,24 @@ async fn test_trbfv_actor() -> Result<()> { println!("CiphertextOutputPublished event has been dispatched!"); // Lets grab decryption share events - let expected = vec![ - "CiphertextOutputPublished", - "DecryptionshareCreated", - "DecryptionshareCreated", - "DecryptionshareCreated", - "ComputeRequest", - "DecryptionshareCreated", - "DecryptionshareCreated", - "ComputeResponse", - "PlaintextAggregated", - ]; + // Each of the 5 parties: + // - ComputeRequest for CalculateDecryptionShare + // - ComputeResponse for CalculateDecryptionShare + // - DecryptionshareCreated + // Plus aggregation: + // - 1 CiphertextOutputPublished + // - 1 ComputeRequest (PlaintextAggregation) + // - 1 ComputeResponse (PlaintextAggregation) + // - 1 PlaintextAggregated + // Total: 1 + 5*3 + 3 = 19 events + let expected_count = 1 + (5 * 3) + 3; + println!("DEBUG: Waiting for {} decryption events...", expected_count); let h = nodes - .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) + .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) .await?; + println!("DEBUG: Decryption phase events: {:?}", h.event_types()); - assert_eq!(h.event_types(), expected); report.push(( "Ciphertext published -> PlaintextAggregated", publishing_ct_timer.elapsed(), @@ -630,7 +657,10 @@ async fn test_trbfv_actor() -> Result<()> { .. })) = h.last().map(|e| e.get_data()) else { - bail!("bad event") + bail!( + "Expected last event to be PlaintextAggregated, got: {:?}", + h.event_types() + ) }; let results = plaintext From 471955974372c36b31f771105ae00b5a2b5546e1 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Feb 2026 21:30:29 +0000 Subject: [PATCH 04/16] chore: integration test --- crates/tests/tests/integration.rs | 116 +++++++++++------------------- 1 file changed, 43 insertions(+), 73 deletions(-) diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 97bdd1eddc..e4bd5291ac 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -499,68 +499,44 @@ async fn test_trbfv_actor() -> Result<()> { )); // First, wait for all EncryptionKeyCreated events (BFV key exchange) - // Each of the 5 parties: - // - EncryptionKeyPending = 5 - // - ComputeRequest (T0 ZK proof) = 5 - // - ComputeResponse (T0 ZK proof) = 5 - // - EncryptionKeyCreated = 5 - // Total: 20 events let encryption_keys_timer = Instant::now(); - let expected_count = 5 + 5 + 5 + 5; // EncKeyPending + T0 ComputeReq + T0 ComputeResp + EncKeyCreated - println!( - "DEBUG: Waiting for {} encryption key events...", - expected_count - ); - let h = nodes - .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) + let expected = vec![ + "EncryptionKeyCreated", + "EncryptionKeyCreated", + "EncryptionKeyCreated", + "EncryptionKeyCreated", + "EncryptionKeyCreated", + ]; + let _ = nodes + .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) .await?; - println!("DEBUG: EncryptionKey phase events: {:?}", h.event_types()); report.push(( "All EncryptionKeyCreated events", encryption_keys_timer.elapsed(), )); - // Then wait for share generation compute events + ThresholdShareCreated + PkGenerationProofSigned - // Each of the 5 parties: - // - ComputeRequest + ComputeResponse for GenPkShareAndSkSss = 10 - // - ComputeRequest + ComputeResponse for GenEsiSss = 10 - // - ThresholdSharePending = 5 - // - ComputeRequest + ComputeResponse for T1 ZK proof = 10 - // - 5 ThresholdShareCreated events (one per target party) = 25 total - // - 1 PkGenerationProofSigned event = 5 total - // Total: 10 + 10 + 5 + 10 + 25 + 5 = 65 events + // Then wait for all ThresholdShareCreated events + // With domain-level splitting, each of the 5 parties publishes 5 events (one per target party) + // Total: 5 parties × 5 targets = 25 events let shares_timer = Instant::now(); - let expected_count = 10 + 10 + 5 + 10 + 25 + 5; // GenPk + GenEsi + TSPending + T1 ZK + TSCreated + PkGenProof - println!( - "DEBUG: Waiting for {} share generation events...", - expected_count - ); - let h = nodes - .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) + let expected: Vec<&str> = (0..25).map(|_| "ThresholdShareCreated").collect(); + let _ = nodes + .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) .await?; - println!( - "DEBUG: Share generation phase events: {:?}", - h.event_types() - ); - report.push(("All share generation events", shares_timer.elapsed())); - - // Wait for CalculateDecryptionKey compute events + KeyshareCreated + PublicKeyAggregated - // Each of the 5 parties: - // - ComputeRequest for CalculateDecryptionKey - // - ComputeResponse for CalculateDecryptionKey - // - KeyshareCreated - // Plus 1 PublicKeyAggregated at the end - // Total: 5 + 5 + 5 + 1 = 16 events + report.push(("All ThresholdShareCreated events", shares_timer.elapsed())); + let shares_to_pubkey_agg_timer = Instant::now(); - let expected_count = 5 + 5 + 5 + 1; // ComputeRequest + ComputeResponse + KeyshareCreated + PublicKeyAggregated - println!( - "DEBUG: Waiting for {} decryption key events...", - expected_count - ); + let expected = vec![ + "KeyshareCreated", + "KeyshareCreated", + "KeyshareCreated", + "KeyshareCreated", + "KeyshareCreated", + "PublicKeyAggregated", + ]; let h = nodes - .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) + .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) .await?; - println!("DEBUG: Decryption key phase events: {:?}", h.event_types()); report.push(( "ThresholdShares -> PublicKeyAggregated", @@ -572,16 +548,14 @@ async fn test_trbfv_actor() -> Result<()> { e3_requested_timer.elapsed(), )); let app_gen_timer = Instant::now(); + assert_eq!(h.event_types(), expected); + // Aggregate decryption - // Verify we got the expected events (last should be PublicKeyAggregated) // First we get the public key println!("Getting public key"); let Some(EnclaveEventData::PublicKeyAggregated(pubkey_event)) = h.last().map(|e| e.get_data()) else { - panic!( - "Was expecting last event to be PublicKeyAggregated, got: {:?}", - h.event_types() - ); + panic!("Was expecting event to be PublicKeyAggregated"); }; let pubkey_bytes = pubkey_event.pubkey.clone(); @@ -629,24 +603,23 @@ async fn test_trbfv_actor() -> Result<()> { println!("CiphertextOutputPublished event has been dispatched!"); // Lets grab decryption share events - // Each of the 5 parties: - // - ComputeRequest for CalculateDecryptionShare - // - ComputeResponse for CalculateDecryptionShare - // - DecryptionshareCreated - // Plus aggregation: - // - 1 CiphertextOutputPublished - // - 1 ComputeRequest (PlaintextAggregation) - // - 1 ComputeResponse (PlaintextAggregation) - // - 1 PlaintextAggregated - // Total: 1 + 5*3 + 3 = 19 events - let expected_count = 1 + (5 * 3) + 3; - println!("DEBUG: Waiting for {} decryption events...", expected_count); + let expected = vec![ + "CiphertextOutputPublished", + "DecryptionshareCreated", + "DecryptionshareCreated", + "DecryptionshareCreated", + "ComputeRequest", + "DecryptionshareCreated", + "DecryptionshareCreated", + "ComputeResponse", + "PlaintextAggregated", + ]; let h = nodes - .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) + .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) .await?; - println!("DEBUG: Decryption phase events: {:?}", h.event_types()); + assert_eq!(h.event_types(), expected); report.push(( "Ciphertext published -> PlaintextAggregated", publishing_ct_timer.elapsed(), @@ -657,10 +630,7 @@ async fn test_trbfv_actor() -> Result<()> { .. })) = h.last().map(|e| e.get_data()) else { - bail!( - "Expected last event to be PlaintextAggregated, got: {:?}", - h.event_types() - ) + bail!("bad event") }; let results = plaintext From 743107699733460f16148877d9231b03b35ee318 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Sat, 14 Feb 2026 10:26:25 +0000 Subject: [PATCH 05/16] chore: simplify flow --- crates/keyshare/src/threshold_keyshare.rs | 84 +++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index 82cb6b5433..678d49b0f5 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -738,6 +738,90 @@ impl ThresholdKeyshare { Ok(()) } + /// 3. GenEsiSss + pub fn handle_gen_esi_sss_requested(&self, msg: TypedEvent) -> Result<()> { + let (msg, ec) = msg.into_components(); + info!("GenEsiSss on ThresholdKeyshare"); + + let evt = msg.ciphernode_selected; + let e_sm_raw = msg.e_sm_raw; + let CiphernodeSelected { + e3_id, + .. + } = evt.clone(); + + let state = self + .state + .get() + .ok_or(anyhow!("State not found on ThrehsoldKeyshare"))?; + + let trbfv_config = state.get_trbfv_config(); + + let event = ComputeRequest::trbfv( + TrBFVRequest::GenEsiSss( + GenEsiSssRequest { + trbfv_config, + e_sm_raw, + } + .into(), + ), + CorrelationId::new(), + e3_id, + ); + + self.bus.publish(event, ec)?; + Ok(()) + } + + /// 3a. GenEsiSss result + pub fn handle_gen_esi_sss_response(&mut self, res: TypedEvent) -> Result<()> { + let (res, ec) = res.into_components(); + let output: GenEsiSssResponse = res.try_into()?; + + let esi_sss = output.esi_sss; + + // First store esi_sss in GeneratingThresholdShareData + self.state.try_mutate(&ec, |s| { + info!("try_store_esi_sss"); + let current: GeneratingThresholdShareData = s.clone().try_into()?; + s.new_state(KeyshareState::GeneratingThresholdShare( + GeneratingThresholdShareData { + esi_sss: Some(esi_sss), + ..current + }, + )) + })?; + + info!("esi stored"); + + // Check if all data is ready, if so call handle_shares_generated BEFORE transitioning + let current: GeneratingThresholdShareData = self.state.try_get()?.try_into()?; + let ready = current.pk_share.is_some() + && current.sk_sss.is_some() + && current.esi_sss.is_some() + && current.e_sm_raw.is_some() + && current.proof_request_data.is_some(); + + if ready { + // Call handle_shares_generated while still in GeneratingThresholdShare state + self.handle_shares_generated(ec.clone())?; + + // Now transition to AggregatingDecryptionKey with minimal state + self.state.try_mutate(&ec, |s| { + let current: GeneratingThresholdShareData = s.clone().try_into()?; + s.new_state(KeyshareState::AggregatingDecryptionKey( + AggregatingDecryptionKey { + pk_share: current.pk_share.expect("pk_share checked above"), + sk_bfv: current.sk_bfv, + signed_pk_generation_proof: None, + }, + )) + })?; + } + Ok(()) + } + + /// 4. SharesGenerated - Encrypt shares with BFV and publish pub fn handle_shares_generated(&mut self, ec: EventContext) -> Result<()> { let Some(ThresholdKeyshareState { From 98b0a8fdec79086133d53a8395fbb32f411ed7b2 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Sun, 15 Feb 2026 17:50:39 +0000 Subject: [PATCH 06/16] chore: conflicts --- crates/keyshare/src/threshold_keyshare.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index 678d49b0f5..8d68a58b68 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -42,7 +42,9 @@ use std::{collections::HashMap, mem, sync::Arc}; use rand::{rngs::OsRng, SeedableRng}; use rand_chacha::ChaCha20Rng; use std::{ - collections::HashMap, mem, sync::{Arc, Mutex} + collections::HashMap, + mem, + sync::{Arc, Mutex}, }; >>>>>>> be8c56d3 (chore: refactoring) use tracing::{info, trace, warn}; @@ -738,17 +740,14 @@ impl ThresholdKeyshare { Ok(()) } - /// 3. GenEsiSss - pub fn handle_gen_esi_sss_requested(&self, msg: TypedEvent) -> Result<()> { + /// 3. GenEsiSss + pub fn handle_gen_esi_sss_requested(&self, msg: TypedEvent) -> Result<()> { let (msg, ec) = msg.into_components(); info!("GenEsiSss on ThresholdKeyshare"); let evt = msg.ciphernode_selected; let e_sm_raw = msg.e_sm_raw; - let CiphernodeSelected { - e3_id, - .. - } = evt.clone(); + let CiphernodeSelected { e3_id, .. } = evt.clone(); let state = self .state @@ -821,7 +820,6 @@ impl ThresholdKeyshare { Ok(()) } - /// 4. SharesGenerated - Encrypt shares with BFV and publish pub fn handle_shares_generated(&mut self, ec: EventContext) -> Result<()> { let Some(ThresholdKeyshareState { From bbd825475d317c773aedc085678fe6be3ff4e5bb Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Mon, 16 Feb 2026 20:29:22 +0000 Subject: [PATCH 07/16] chore: use sensitive bytes --- crates/keyshare/src/threshold_keyshare.rs | 83 +---------------------- 1 file changed, 1 insertion(+), 82 deletions(-) diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index 8d68a58b68..f82c86af49 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -659,87 +659,6 @@ impl ThresholdKeyshare { Ok(()) } - /// 3. GenEsiSss - pub fn handle_gen_esi_sss_requested(&self, msg: TypedEvent) -> Result<()> { - let (msg, ec) = msg.into_components(); - info!("GenEsiSss on ThresholdKeyshare"); - - let evt = msg.ciphernode_selected; - let e_sm_raw_decrypted = ArcBytes::from_bytes(&msg.e_sm_raw.access_raw(&self.cipher)?); - let CiphernodeSelected { e3_id, .. } = evt.clone(); - - let state = self - .state - .get() - .ok_or(anyhow!("State not found on ThrehsoldKeyshare"))?; - - let trbfv_config = state.get_trbfv_config(); - - let event = ComputeRequest::trbfv( - TrBFVRequest::GenEsiSss( - GenEsiSssRequest { - trbfv_config, - e_sm_raw: e_sm_raw_decrypted, - } - .into(), - ), - CorrelationId::new(), - e3_id, - ); - - self.bus.publish(event, ec)?; - Ok(()) - } - - /// 3a. GenEsiSss result - pub fn handle_gen_esi_sss_response(&mut self, res: TypedEvent) -> Result<()> { - let (res, ec) = res.into_components(); - let output: GenEsiSssResponse = res.try_into()?; - - let esi_sss = output.esi_sss; - - // First store esi_sss in GeneratingThresholdShareData - self.state.try_mutate(&ec, |s| { - info!("try_store_esi_sss"); - let current: GeneratingThresholdShareData = s.clone().try_into()?; - s.new_state(KeyshareState::GeneratingThresholdShare( - GeneratingThresholdShareData { - esi_sss: Some(esi_sss), - ..current - }, - )) - })?; - - info!("esi stored"); - - // Check if all data is ready, if so call handle_shares_generated BEFORE transitioning - let current: GeneratingThresholdShareData = self.state.try_get()?.try_into()?; - let ready = current.pk_share.is_some() - && current.sk_sss.is_some() - && current.esi_sss.is_some() - && current.e_sm_raw.is_some() - && current.proof_request_data.is_some(); - - if ready { - // Call handle_shares_generated while still in GeneratingThresholdShare state - self.handle_shares_generated(ec.clone())?; - - // Now transition to AggregatingDecryptionKey with minimal state - self.state.try_mutate(&ec, |s| { - let current: GeneratingThresholdShareData = s.clone().try_into()?; - s.new_state(KeyshareState::AggregatingDecryptionKey( - AggregatingDecryptionKey { - pk_share: current.pk_share.expect("pk_share checked above"), - sk_bfv: current.sk_bfv, - signed_pk_generation_proof: None, - }, - )) - })?; - } - - Ok(()) - } - /// 3. GenEsiSss pub fn handle_gen_esi_sss_requested(&self, msg: TypedEvent) -> Result<()> { let (msg, ec) = msg.into_components(); @@ -760,7 +679,7 @@ impl ThresholdKeyshare { TrBFVRequest::GenEsiSss( GenEsiSssRequest { trbfv_config, - e_sm_raw, + e_sm_raw: e_sm_raw_decrypted, } .into(), ), From a6f58440f6d8db42ce587aa71f9cbcafb7d3f6ef Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:15:48 +0000 Subject: [PATCH 08/16] feat: integrate sk_share/e_sm_share computation --- Cargo.lock | 3 + .../src/enclave_event/compute_request/mod.rs | 1 + .../src/enclave_event/compute_request/zk.rs | 40 ++- crates/events/src/enclave_event/mod.rs | 5 + .../share_computation_proof_signed.rs | 25 ++ .../enclave_event/threshold_share_pending.rs | 8 +- crates/keyshare/src/threshold_keyshare.rs | 132 +++++++-- crates/multithread/Cargo.toml | 3 + crates/multithread/src/multithread.rs | 94 +++++- crates/test-helpers/src/usecase_helpers.rs | 2 +- crates/tests/tests/integration.rs | 36 +++ crates/trbfv/src/gen_esi_sss.rs | 20 +- crates/trbfv/src/shares/shares.rs | 4 + crates/zk-helpers/src/circuits/computation.rs | 2 +- crates/zk-prover/src/actors/proof_request.rs | 270 ++++++++++++++---- examples/CRISP/test/crisp.spec.ts | 2 +- templates/default/tests/integration.spec.ts | 2 +- 17 files changed, 550 insertions(+), 99 deletions(-) create mode 100644 crates/events/src/enclave_event/share_computation_proof_signed.rs diff --git a/Cargo.lock b/Cargo.lock index 95088c2a01..cb38cb9fa5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3499,6 +3499,7 @@ version = "0.1.15" dependencies = [ "actix", "anyhow", + "bincode 1.3.3", "e3-crypto", "e3-data", "e3-events", @@ -3510,6 +3511,8 @@ dependencies = [ "e3-zk-prover", "fhe", "fhe-traits", + "ndarray", + "num-bigint", "rand 0.8.5", "rayon", "thiserror 1.0.69", diff --git a/crates/events/src/enclave_event/compute_request/mod.rs b/crates/events/src/enclave_event/compute_request/mod.rs index e339ed9a14..00d31b8c9e 100644 --- a/crates/events/src/enclave_event/compute_request/mod.rs +++ b/crates/events/src/enclave_event/compute_request/mod.rs @@ -83,6 +83,7 @@ impl ToString for ComputeRequest { ComputeRequestKind::Zk(req) => match req { ZkRequest::PkBfv(_) => "ZkPkBfv", ZkRequest::PkGeneration(_) => "ZkPkGeneration", + ZkRequest::ShareComputation(_) => "ZkShareComputation", }, } .to_string() diff --git a/crates/events/src/enclave_event/compute_request/zk.rs b/crates/events/src/enclave_event/compute_request/zk.rs index a89bb530fc..e6f772ed53 100644 --- a/crates/events/src/enclave_event/compute_request/zk.rs +++ b/crates/events/src/enclave_event/compute_request/zk.rs @@ -9,7 +9,7 @@ use derivative::Derivative; use e3_crypto::SensitiveBytes; use e3_fhe_params::BfvPreset; use e3_utils::utility_types::ArcBytes; -use e3_zk_helpers::CiphernodesCommitteeSize; +use e3_zk_helpers::{computation::DkgInputType, CiphernodesCommitteeSize}; use serde::{Deserialize, Serialize}; /// ZK proof generation request variants. @@ -17,8 +17,26 @@ use serde::{Deserialize, Serialize}; pub enum ZkRequest { /// Generate proof for BFV public key (T0). PkBfv(PkBfvProofRequest), - /// Generate proof for PK generation (T1a). + /// Generate proof for PK generation (T1). PkGeneration(PkGenerationProofRequest), + /// Generate proof for share and esm computation (T2a and T2b). + ShareComputation(ShareComputationProofRequest), +} + +/// Request to generate a proof for share computation (T2a or T2b). +#[derive(Derivative, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derivative(Debug)] +pub struct ShareComputationProofRequest { + /// Raw secret polynomial bytes (sk or e_sm — witness, encrypted at rest). + pub secret_raw: SensitiveBytes, + /// Bincode-serialized SharedSecret containing Shamir shares (witness, encrypted at rest). + pub secret_sss_raw: SensitiveBytes, + /// Which secret type (SecretKey or SmudgingNoise). + pub dkg_input_type: DkgInputType, + /// BFV preset for parameter resolution. + pub params_preset: BfvPreset, + /// The size of the committee. + pub committee_size: CiphernodesCommitteeSize, } /// Request to generate a proof for BFV public key generation (T0). @@ -86,6 +104,15 @@ pub enum ZkResponse { PkBfv(PkBfvProofResponse), /// Proof for PK generation (T1a). PkGeneration(PkGenerationProofResponse), + /// Proof for share and esm computation (T2a and T2b). + ShareComputation(ShareComputationProofResponse), +} + +/// Response containing a generated share computation proof. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct ShareComputationProofResponse { + pub proof: Proof, + pub dkg_input_type: DkgInputType, } /// Response containing a generated BFV public key proof. @@ -100,6 +127,15 @@ pub struct PkGenerationProofResponse { pub proof: Proof, } +impl ShareComputationProofResponse { + pub fn new(proof: Proof, dkg_input_type: DkgInputType) -> Self { + Self { + proof, + dkg_input_type, + } + } +} + impl PkBfvProofResponse { pub fn new(proof: Proof) -> Self { Self { proof } diff --git a/crates/events/src/enclave_event/mod.rs b/crates/events/src/enclave_event/mod.rs index fba6dce82c..004bfd1924 100644 --- a/crates/events/src/enclave_event/mod.rs +++ b/crates/events/src/enclave_event/mod.rs @@ -36,6 +36,7 @@ mod plaintext_output_published; mod proof; mod publickey_aggregated; mod publish_document; +mod share_computation_proof_signed; mod shutdown; mod signed_proof; mod sync_effect; @@ -83,6 +84,7 @@ pub use plaintext_output_published::*; pub use proof::*; pub use publickey_aggregated::*; pub use publish_document::*; +pub use share_computation_proof_signed::*; pub use shutdown::*; pub use signed_proof::*; use strum::IntoStaticStr; @@ -215,6 +217,7 @@ pub enum EnclaveEventData { TicketSubmitted(TicketSubmitted), PlaintextOutputPublished(PlaintextOutputPublished), PkGenerationProofSigned(PkGenerationProofSigned), + ShareComputationProofSigned(ShareComputationProofSigned), EnclaveError(EnclaveError), E3RequestComplete(E3RequestComplete), E3Failed(E3Failed), @@ -468,6 +471,7 @@ impl EnclaveEventData { EnclaveEventData::DecryptionshareCreated(ref data) => Some(data.e3_id.clone()), EnclaveEventData::PlaintextAggregated(ref data) => Some(data.e3_id.clone()), EnclaveEventData::PkGenerationProofSigned(ref data) => Some(data.e3_id.clone()), + EnclaveEventData::ShareComputationProofSigned(ref data) => Some(data.e3_id.clone()), EnclaveEventData::CiphernodeSelected(ref data) => Some(data.e3_id.clone()), EnclaveEventData::ThresholdShareCreated(ref data) => Some(data.e3_id.clone()), EnclaveEventData::ThresholdSharePending(ref data) => Some(data.e3_id.clone()), @@ -519,6 +523,7 @@ impl_event_types!( PlaintextAggregated, PublishDocumentRequested, PkGenerationProofSigned, + ShareComputationProofSigned, E3RequestComplete, E3Failed, E3StageChanged, diff --git a/crates/events/src/enclave_event/share_computation_proof_signed.rs b/crates/events/src/enclave_event/share_computation_proof_signed.rs new file mode 100644 index 0000000000..52ecc54608 --- /dev/null +++ b/crates/events/src/enclave_event/share_computation_proof_signed.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use crate::{E3id, ProofType, SignedProofPayload}; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct ShareComputationProofSigned { + pub e3_id: E3id, + pub party_id: u64, + pub proof_type: ProofType, + pub signed_proof: SignedProofPayload, +} + +impl Display for ShareComputationProofSigned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/crates/events/src/enclave_event/threshold_share_pending.rs b/crates/events/src/enclave_event/threshold_share_pending.rs index be37599881..0f39aee7e0 100644 --- a/crates/events/src/enclave_event/threshold_share_pending.rs +++ b/crates/events/src/enclave_event/threshold_share_pending.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::{E3id, PkGenerationProofRequest, ThresholdShare}; +use crate::{E3id, PkGenerationProofRequest, ShareComputationProofRequest, ThresholdShare}; use actix::Message; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; @@ -16,8 +16,12 @@ pub struct ThresholdSharePending { pub e3_id: E3id, /// Full threshold share containing all encrypted shares for all parties pub full_share: Arc, - /// The proof request data for the zk actor + /// The proof request data for C1 (PkGeneration) pub proof_request: PkGenerationProofRequest, + /// The proof request for C2a (SkShareComputation) + pub sk_share_computation_request: ShareComputationProofRequest, + /// The proof request for C2b (ESmShareComputation) + pub e_sm_share_computation_request: ShareComputationProofRequest, } impl Display for ThresholdSharePending { diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index f82c86af49..49106f4af7 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -13,9 +13,10 @@ use e3_events::{ ComputeResponse, ComputeResponseKind, CorrelationId, DecryptionshareCreated, Die, E3RequestComplete, E3id, EType, EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCollectionFailed, EncryptionKeyCreated, EncryptionKeyPending, EventContext, - KeyshareCreated, PartyId, PkGenerationProofRequest, PkGenerationProofSigned, Sequenced, - SignedProofPayload, ThresholdShare, ThresholdShareCollectionFailed, ThresholdShareCreated, - ThresholdSharePending, TypedEvent, + KeyshareCreated, PartyId, PkGenerationProofRequest, PkGenerationProofSigned, ProofType, + Sequenced, ShareComputationProofRequest, ShareComputationProofSigned, SignedProofPayload, + ThresholdShare, ThresholdShareCollectionFailed, ThresholdShareCreated, ThresholdSharePending, + TypedEvent, }; use e3_fhe_params::create_deterministic_crp_from_default_seed; use e3_fhe_params::{BfvParamSet, BfvPreset}; @@ -32,21 +33,12 @@ use e3_trbfv::{ }; use e3_utils::{to_ordered_vec, utility_types::ArcBytes}; use e3_utils::{NotifySync, MAILBOX_LIMIT}; +use e3_zk_helpers::computation::DkgInputType; use e3_zk_helpers::CiphernodesCommitteeSize; use fhe::bfv::{PublicKey, SecretKey}; use fhe_traits::{DeserializeParametrized, Serialize}; -<<<<<<< HEAD use rand::rngs::OsRng; use std::{collections::HashMap, mem, sync::Arc}; -======= -use rand::{rngs::OsRng, SeedableRng}; -use rand_chacha::ChaCha20Rng; -use std::{ - collections::HashMap, - mem, - sync::{Arc, Mutex}, -}; ->>>>>>> be8c56d3 (chore: refactoring) use tracing::{info, trace, warn}; use crate::encryption_key_collector::{AllEncryptionKeysCollected, EncryptionKeyCollector}; @@ -109,6 +101,8 @@ pub struct AggregatingDecryptionKey { pk_share: ArcBytes, sk_bfv: SensitiveBytes, signed_pk_generation_proof: Option, + signed_sk_share_computation_proof: Option, + signed_e_sm_share_computation_proof: Option, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -117,6 +111,8 @@ pub struct ReadyForDecryption { sk_poly_sum: SensitiveBytes, es_poly_sum: Vec, signed_pk_generation_proof: Option, + signed_sk_share_computation_proof: Option, + signed_e_sm_share_computation_proof: Option, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -125,6 +121,8 @@ pub struct Decrypting { sk_poly_sum: SensitiveBytes, es_poly_sum: Vec, signed_pk_generation_proof: Option, + signed_sk_share_computation_proof: Option, + signed_e_sm_share_computation_proof: Option, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -455,6 +453,48 @@ impl ThresholdKeyshare { Ok(()) } + /// Handle ShareComputationProofSigned - stores the signed proof in state based on proof type (T1, T2a or T2b) + pub fn handle_share_computation_proof_signed( + &mut self, + msg: TypedEvent, + ) -> Result<()> { + let (msg, ec) = msg.into_components(); + let state = self.state.try_get()?; + + if msg.party_id != state.party_id { + return Ok(()); + } + + info!( + "Received ShareComputationProofSigned ({:?}) for party {} E3 {}", + msg.proof_type, msg.party_id, msg.e3_id + ); + + self.state.try_mutate(&ec, |s| { + let current: AggregatingDecryptionKey = s.clone().try_into()?; + let updated = match msg.proof_type { + ProofType::T1SkShareComputation => AggregatingDecryptionKey { + signed_sk_share_computation_proof: Some(msg.signed_proof), + ..current + }, + ProofType::T1ESmShareComputation => AggregatingDecryptionKey { + signed_e_sm_share_computation_proof: Some(msg.signed_proof), + ..current + }, + other => { + warn!( + "Unexpected proof type {:?} in ShareComputationProofSigned", + other + ); + current + } + }; + s.new_state(KeyshareState::AggregatingDecryptionKey(updated)) + })?; + + Ok(()) + } + pub fn handle_compute_response(&mut self, msg: TypedEvent) -> Result<()> { match &msg.response { ComputeResponseKind::TrBFV(trbfv) => match trbfv { @@ -664,9 +704,8 @@ impl ThresholdKeyshare { let (msg, ec) = msg.into_components(); info!("GenEsiSss on ThresholdKeyshare"); - let evt = msg.ciphernode_selected; let e_sm_raw = msg.e_sm_raw; - let CiphernodeSelected { e3_id, .. } = evt.clone(); + let CiphernodeSelected { e3_id, .. } = msg.ciphernode_selected; let state = self .state @@ -676,13 +715,10 @@ impl ThresholdKeyshare { let trbfv_config = state.get_trbfv_config(); let event = ComputeRequest::trbfv( - TrBFVRequest::GenEsiSss( - GenEsiSssRequest { - trbfv_config, - e_sm_raw: e_sm_raw_decrypted, - } - .into(), - ), + TrBFVRequest::GenEsiSss(GenEsiSssRequest { + trbfv_config, + e_sm_raw, + }), CorrelationId::new(), e3_id, ); @@ -732,6 +768,8 @@ impl ThresholdKeyshare { pk_share: current.pk_share.expect("pk_share checked above"), sk_bfv: current.sk_bfv, signed_pk_generation_proof: None, + signed_sk_share_computation_proof: None, + signed_e_sm_share_computation_proof: None, }, )) })?; @@ -780,6 +818,21 @@ impl ThresholdKeyshare { .map(|s| s.decrypt(&self.cipher)) .collect::>()?; + // Serialize for T2a/T2b proof requests (encrypted at rest) + let sk_sss_raw = SensitiveBytes::new( + bincode::serialize(&decrypted_sk_sss) + .map_err(|e| anyhow!("Failed to serialize sk_sss: {}", e))?, + &self.cipher, + )?; + let esi_sss_raw: Vec = decrypted_esi_sss + .iter() + .map(|s| { + let bytes = bincode::serialize(s) + .map_err(|e| anyhow!("Failed to serialize esi_sss: {}", e))?; + SensitiveBytes::new(bytes, &self.cipher) + }) + .collect::>()?; + // Encrypt shares for all recipients using BFV let mut rng = OsRng; let encrypted_sk_sss = @@ -804,6 +857,7 @@ impl ThresholdKeyshare { .threshold_counterpart() .ok_or_else(|| anyhow!("No threshold counterpart for {:?}", self.share_enc_preset))?; + // Build T1 request (PkGenerationProof) let proof_request = PkGenerationProofRequest::new( proof_request_data.pk0_share_raw.clone(), proof_request_data.sk_raw.clone(), @@ -813,8 +867,29 @@ impl ThresholdKeyshare { CiphernodesCommitteeSize::Small, // TODO: derive from config ); + // Build T2a request (SkShareComputation) + let sk_share_computation_request = ShareComputationProofRequest { + secret_raw: proof_request_data.sk_raw.clone(), + secret_sss_raw: sk_sss_raw, + dkg_input_type: DkgInputType::SecretKey, + params_preset: threshold_preset, + committee_size: CiphernodesCommitteeSize::Small, + }; + + // Build T2b request (ESmShareComputation) + let e_sm_share_computation_request = ShareComputationProofRequest { + secret_raw: e_sm_raw.clone(), + secret_sss_raw: esi_sss_raw + .into_iter() + .next() + .ok_or_else(|| anyhow!("esi_sss_raw is empty — expected at least one entry"))?, + dkg_input_type: DkgInputType::SmudgingNoise, + params_preset: threshold_preset, + committee_size: CiphernodesCommitteeSize::Small, + }; + info!( - "Publishing ThresholdSharePending for E3 {} (proof will be generated and signed by ProofRequestActor)", + "Publishing ThresholdSharePending for E3 {} (3 proofs: T1, T2a, T2b)", e3_id ); @@ -824,6 +899,8 @@ impl ThresholdKeyshare { e3_id: e3_id.clone(), full_share: Arc::new(full_share), proof_request, + sk_share_computation_request, + e_sm_share_computation_request, }, ec.clone(), )?; @@ -831,8 +908,6 @@ impl ThresholdKeyshare { Ok(()) } - /// 5. AllThresholdSharesCollected. This is fired after the ThresholdShareCreated events are - /// aggregateed in the decryption_key_collector::ThresholdShareCollector /// 5. AllThresholdSharesCollected - Decrypt received shares using BFV and aggregate pub fn handle_all_threshold_shares_collected( &self, @@ -930,6 +1005,8 @@ impl ThresholdKeyshare { sk_poly_sum, es_poly_sum, signed_pk_generation_proof: current.signed_pk_generation_proof, + signed_sk_share_computation_proof: current.signed_sk_share_computation_proof, + signed_e_sm_share_computation_proof: current.signed_e_sm_share_computation_proof, }); s.new_state(next) @@ -970,6 +1047,8 @@ impl ThresholdKeyshare { sk_poly_sum: current.sk_poly_sum, es_poly_sum: current.es_poly_sum, signed_pk_generation_proof: current.signed_pk_generation_proof, + signed_sk_share_computation_proof: current.signed_sk_share_computation_proof, + signed_e_sm_share_computation_proof: current.signed_e_sm_share_computation_proof, }); s.new_state(next) @@ -1056,6 +1135,9 @@ impl Handler for ThresholdKeyshare { EnclaveEventData::PkGenerationProofSigned(data) => { let _ = self.handle_pk_generation_proof_signed(TypedEvent::new(data, ec)); } + EnclaveEventData::ShareComputationProofSigned(data) => { + let _ = self.handle_share_computation_proof_signed(TypedEvent::new(data, ec)); + } EnclaveEventData::E3RequestComplete(data) => self.notify_sync(ctx, data), EnclaveEventData::E3Failed(data) => { warn!( diff --git a/crates/multithread/Cargo.toml b/crates/multithread/Cargo.toml index a8a33b975e..9f414b2ab9 100644 --- a/crates/multithread/Cargo.toml +++ b/crates/multithread/Cargo.toml @@ -9,6 +9,7 @@ repository.workspace = true [dependencies] actix = { workspace = true } anyhow = { workspace = true } +bincode = { workspace = true } e3-data = { workspace = true } e3-fhe-params = { workspace = true } e3-trbfv = { workspace = true } @@ -20,6 +21,8 @@ e3-utils = { workspace = true } e3-zk-prover = { workspace = true } fhe = { workspace = true } fhe-traits = { workspace = true } +ndarray = { workspace = true } +num-bigint = { workspace = true } rand = { workspace = true } rayon = { workspace = true } tokio = { workspace = true } diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index b514980d55..41ed5a427b 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -25,8 +25,10 @@ use e3_events::{ BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeRequestKind, ComputeResponse, EnclaveEvent, EnclaveEventData, EventPublisher, EventSubscriber, EventType, PkBfvProofRequest, PkBfvProofResponse, PkGenerationProofRequest, PkGenerationProofResponse, - TypedEvent, ZkError as ZkEventError, ZkRequest, ZkResponse, + ShareComputationProofRequest, ShareComputationProofResponse, TypedEvent, + ZkError as ZkEventError, ZkRequest, ZkResponse, }; +use e3_fhe_params::build_pair_for_preset; use e3_fhe_params::{BfvParamSet, BfvPreset}; use e3_polynomial::CrtPolynomial; use e3_trbfv::calculate_decryption_key::calculate_decryption_key; @@ -35,16 +37,22 @@ use e3_trbfv::calculate_threshold_decryption::calculate_threshold_decryption; use e3_trbfv::gen_esi_sss::gen_esi_sss; use e3_trbfv::gen_pk_share_and_sk_sss::gen_pk_share_and_sk_sss; use e3_trbfv::helpers::try_poly_from_bytes; +use e3_trbfv::shares::SharedSecret; use e3_trbfv::{TrBFVError, TrBFVRequest, TrBFVResponse}; use e3_utils::SharedRng; use e3_utils::MAILBOX_LIMIT; use e3_zk_helpers::circuits::dkg::pk::circuit::{PkCircuit, PkCircuitData}; +use e3_zk_helpers::circuits::dkg::share_computation::utils::compute_parity_matrix; use e3_zk_helpers::circuits::threshold::pk_generation::circuit::{ PkGenerationCircuit, PkGenerationCircuitData, }; +use e3_zk_helpers::computation::DkgInputType; +use e3_zk_helpers::dkg::share_computation::{ShareComputationCircuit, ShareComputationCircuitData}; use e3_zk_prover::{Provable, ZkBackend, ZkProver}; use fhe::bfv::PublicKey; use fhe_traits::DeserializeParametrized; +use ndarray::Array2; +use num_bigint::BigInt; use rand::Rng; use tracing::{error, info}; @@ -398,6 +406,9 @@ fn handle_zk_request( ZkRequest::PkGeneration(req) => timefunc("zk_pk_generation", id, || { handle_pk_generation_proof(&prover, &cipher, req, request.clone()) }), + ZkRequest::ShareComputation(req) => timefunc("zk_share_computation", id, || { + handle_share_computation_proof(&prover, &cipher, req, request.clone()) + }), } } @@ -409,6 +420,87 @@ fn make_zk_error(request: &ComputeRequest, msg: String) -> ComputeRequestError { ) } +fn handle_share_computation_proof( + prover: &ZkProver, + cipher: &Cipher, + req: ShareComputationProofRequest, + request: ComputeRequest, +) -> Result { + // 1. Build BFV threshold parameters + let (threshold_params, _dkg_params) = build_pair_for_preset(req.params_preset.clone()) + .map_err(|e| make_zk_error(&request, format!("build_pair_for_preset: {}", e)))?; + + // 2. Decrypt sensitive witness fields + let secret_bytes = req + .secret_raw + .access_raw(cipher) + .map_err(|e| make_zk_error(&request, format!("secret_raw decrypt: {}", e)))?; + let secret_sss_bytes = req + .secret_sss_raw + .access_raw(cipher) + .map_err(|e| make_zk_error(&request, format!("secret_sss_raw decrypt: {}", e)))?; + + // 3. Deserialize secret polynomial + let secret_poly = try_poly_from_bytes(&secret_bytes, &threshold_params) + .map_err(|e| make_zk_error(&request, format!("secret_raw: {}", e)))?; + let mut secret = CrtPolynomial::from_fhe_polynomial(&secret_poly); + if req.dkg_input_type == DkgInputType::SecretKey { + secret + .center(threshold_params.moduli()) + .map_err(|e| make_zk_error(&request, format!("Failed to center polynomial: {}", e)))?; + } + + // 4. Deserialize Shamir shares (bincode-encoded SharedSecret) + let shared_secret: SharedSecret = bincode::deserialize(&secret_sss_bytes) + .map_err(|e| make_zk_error(&request, format!("secret_sss_raw deserialize: {}", e)))?; + + // Convert Vec> → Vec> + let secret_sss: Vec> = shared_secret + .moduli_data() + .iter() + .map(|arr| arr.mapv(|v| BigInt::from(v))) + .collect(); + + // 4. Compute parity matrix + let committee = req.committee_size.values(); + let parity_matrix = + compute_parity_matrix(threshold_params.moduli(), committee.n, committee.threshold) + .map_err(|e| make_zk_error(&request, format!("compute_parity_matrix: {}", e)))?; + + // 5. Build circuit data + let circuit_data = ShareComputationCircuitData { + dkg_input_type: req.dkg_input_type, + secret, + secret_sss, + parity_matrix, + n_parties: committee.n as u32, + threshold: committee.threshold as u32, + }; + + // 6. Generate proof + let circuit = ShareComputationCircuit; + let e3_id_str = request.e3_id.to_string(); + + let proof = circuit + .prove(prover, &req.params_preset, &circuit_data, &e3_id_str) + .map_err(|e| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), + ) + })?; + + // 7. Return response + Ok(ComputeResponse::zk( + ZkResponse::ShareComputation(ShareComputationProofResponse { + proof, + dkg_input_type: req.dkg_input_type, + }), + request.correlation_id, + request.e3_id, + )) +} + fn handle_pk_generation_proof( prover: &ZkProver, cipher: &Cipher, diff --git a/crates/test-helpers/src/usecase_helpers.rs b/crates/test-helpers/src/usecase_helpers.rs index 54b3039311..3372e9ca60 100644 --- a/crates/test-helpers/src/usecase_helpers.rs +++ b/crates/test-helpers/src/usecase_helpers.rs @@ -81,7 +81,7 @@ pub fn generate_shares_hash_map( &cipher, GenEsiSssRequest { trbfv_config: trbfv_config.clone(), - e_sm_raw: ArcBytes::from_bytes(&e_sm_raw.access_raw(&cipher)?), + e_sm_raw: e_sm_raw.clone(), }, )?; diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index e4bd5291ac..ad27ea2a9e 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -132,6 +132,42 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); + // Copy T2a (sk_share_computation) circuit + let sk_share_comp_circuit_dir = circuits_dir.join("dkg").join("sk_share_computation"); + tokio::fs::create_dir_all(&sk_share_comp_circuit_dir) + .await + .unwrap(); + tokio::fs::copy( + dkg_target.join("sk_share_computation.json"), + sk_share_comp_circuit_dir.join("sk_share_computation.json"), + ) + .await + .unwrap(); + tokio::fs::copy( + dkg_target.join("sk_share_computation.vk"), + sk_share_comp_circuit_dir.join("sk_share_computation.vk"), + ) + .await + .unwrap(); + + // Copy T2b (e_sm_share_computation) circuit + let e_sm_share_comp_circuit_dir = circuits_dir.join("dkg").join("e_sm_share_computation"); + tokio::fs::create_dir_all(&e_sm_share_comp_circuit_dir) + .await + .unwrap(); + tokio::fs::copy( + dkg_target.join("e_sm_share_computation.json"), + e_sm_share_comp_circuit_dir.join("e_sm_share_computation.json"), + ) + .await + .unwrap(); + tokio::fs::copy( + dkg_target.join("e_sm_share_computation.vk"), + e_sm_share_comp_circuit_dir.join("e_sm_share_computation.vk"), + ) + .await + .unwrap(); + let backend = ZkBackend::new( BBPath::Default(bb_binary), circuits_dir, diff --git a/crates/trbfv/src/gen_esi_sss.rs b/crates/trbfv/src/gen_esi_sss.rs index 7eb473f9f4..213d15e816 100644 --- a/crates/trbfv/src/gen_esi_sss.rs +++ b/crates/trbfv/src/gen_esi_sss.rs @@ -10,7 +10,7 @@ use crate::{ TrBFVConfig, }; use anyhow::{Context, Result}; -use e3_crypto::Cipher; +use e3_crypto::{Cipher, SensitiveBytes}; use e3_utils::{utility_types::ArcBytes, SharedRng}; use fhe::trbfv::ShareManager; use serde::{Deserialize, Serialize}; @@ -20,8 +20,8 @@ use tracing::info; pub struct GenEsiSssRequest { /// TrBFV configuration pub trbfv_config: TrBFVConfig, - /// This is pre-generated smudging noise. - pub e_sm_raw: ArcBytes, + /// Pre-generated smudging noise polynomial (private witness, encrypted at rest). + pub e_sm_raw: SensitiveBytes, } struct InnerRequest { @@ -29,12 +29,12 @@ struct InnerRequest { pub e_sm_raw: ArcBytes, } -impl TryFrom for InnerRequest { - type Error = anyhow::Error; - fn try_from(value: GenEsiSssRequest) -> std::result::Result { +impl GenEsiSssRequest { + fn into_inner(self, cipher: &Cipher) -> Result { + let e_sm_decrypted = self.e_sm_raw.access(cipher)?; Ok(InnerRequest { - trbfv_config: value.trbfv_config, - e_sm_raw: value.e_sm_raw, + trbfv_config: self.trbfv_config, + e_sm_raw: ArcBytes::from_bytes(&e_sm_decrypted), }) } } @@ -74,7 +74,7 @@ pub fn gen_esi_sss( req: GenEsiSssRequest, ) -> Result { info!("gen_esi_sss"); - let req: InnerRequest = req.try_into()?; + let req: InnerRequest = req.into_inner(cipher)?; let params = req.trbfv_config.params(); let threshold = req.trbfv_config.threshold() as usize; @@ -85,7 +85,7 @@ pub fn gen_esi_sss( let e_sm_poly = try_poly_from_bytes(&e_sm_raw, ¶ms)?; let mut share_manager = ShareManager::new(num_ciphernodes, threshold, params.clone()); - info!("gen_esi_sss:generate_smudging_error..."); + info!("gen_esi_sss:generate_secret_shares_from_poly..."); let esi_sss = vec![SharedSecret::from( share_manager diff --git a/crates/trbfv/src/shares/shares.rs b/crates/trbfv/src/shares/shares.rs index 03828616ce..0ddc1f400e 100644 --- a/crates/trbfv/src/shares/shares.rs +++ b/crates/trbfv/src/shares/shares.rs @@ -21,6 +21,10 @@ impl SharedSecret { Self { moduli_data } } + pub fn moduli_data(&self) -> &Vec> { + &self.moduli_data + } + /// Extract one party's complete share across all moduli. pub fn extract_party_share(&self, party_id: usize) -> Result { let Some(first) = self.moduli_data.get(0) else { diff --git a/crates/zk-helpers/src/circuits/computation.rs b/crates/zk-helpers/src/circuits/computation.rs index 0a2d933346..b56391036b 100644 --- a/crates/zk-helpers/src/circuits/computation.rs +++ b/crates/zk-helpers/src/circuits/computation.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; /// Variant for input types for DKG. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Serialize, Hash)] pub enum DkgInputType { /// The input type that generates shares of a secret key using secret sharing. SecretKey, diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index 170942d18b..479a677926 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -14,12 +14,19 @@ use e3_events::{ ComputeResponseKind, CorrelationId, E3id, EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCreated, EncryptionKeyPending, EventContext, EventPublisher, EventSubscriber, EventType, PkBfvProofRequest, PkGenerationProofSigned, Proof, ProofPayload, ProofType, - Sequenced, SignedProofPayload, ThresholdShare, ThresholdShareCreated, ThresholdSharePending, - TypedEvent, ZkRequest, ZkResponse, + Sequenced, ShareComputationProofSigned, SignedProofPayload, ThresholdShare, + ThresholdShareCreated, ThresholdSharePending, TypedEvent, ZkRequest, ZkResponse, }; use e3_utils::NotifySync; use tracing::{error, info}; +#[derive(Clone, Debug)] +enum ThresholdProofKind { + PkGeneration, + SkShareComputation, + ESmShareComputation, +} + #[derive(Clone, Debug)] struct PendingProofRequest { e3_id: E3id, @@ -27,9 +34,42 @@ struct PendingProofRequest { } #[derive(Clone, Debug)] -struct PendingThresholdShareProof { +struct PendingThresholdProofs { e3_id: E3id, full_share: Arc, + ec: EventContext, + pk_generation_proof: Option, + sk_share_computation_proof: Option, + e_sm_share_computation_proof: Option, +} + +impl PendingThresholdProofs { + fn new(e3_id: E3id, full_share: Arc, ec: EventContext) -> Self { + Self { + e3_id, + full_share, + ec, + pk_generation_proof: None, + sk_share_computation_proof: None, + e_sm_share_computation_proof: None, + } + } + + fn is_complete(&self) -> bool { + self.pk_generation_proof.is_some() + && self.sk_share_computation_proof.is_some() + && self.e_sm_share_computation_proof.is_some() + } + + fn store_proof(&mut self, kind: &ThresholdProofKind, proof: Proof) { + match kind { + ThresholdProofKind::PkGeneration => self.pk_generation_proof = Some(proof), + ThresholdProofKind::SkShareComputation => self.sk_share_computation_proof = Some(proof), + ThresholdProofKind::ESmShareComputation => { + self.e_sm_share_computation_proof = Some(proof) + } + } + } } /// Core actor that handles encryption key proof requests. @@ -41,7 +81,8 @@ pub struct ProofRequestActor { bus: BusHandle, signer: PrivateKeySigner, pending: HashMap, - pending_threshold: HashMap, + threshold_correlation: HashMap, + pending_threshold: HashMap, } impl ProofRequestActor { @@ -51,6 +92,7 @@ impl ProofRequestActor { signer, pending: HashMap::new(), pending_threshold: HashMap::new(), + threshold_correlation: HashMap::new(), } } @@ -92,25 +134,73 @@ impl ProofRequestActor { fn handle_threshold_share_pending(&mut self, msg: TypedEvent) { let (msg, ec) = msg.into_components(); - let correlation_id = CorrelationId::new(); + let e3_id = msg.e3_id.clone(); + self.pending_threshold.insert( - correlation_id, - PendingThresholdShareProof { - e3_id: msg.e3_id.clone(), - full_share: msg.full_share.clone(), - }, + e3_id.clone(), + PendingThresholdProofs::new(e3_id.clone(), msg.full_share.clone(), ec.clone()), ); - let request = ComputeRequest::zk( - ZkRequest::PkGeneration(msg.proof_request), - correlation_id, - msg.e3_id, + // C1: PkGeneration + let t1_corr = CorrelationId::new(); + self.threshold_correlation + .insert(t1_corr, (e3_id.clone(), ThresholdProofKind::PkGeneration)); + info!("Requesting T1 PkGeneration proof"); + if let Err(err) = self.bus.publish( + ComputeRequest::zk( + ZkRequest::PkGeneration(msg.proof_request), + t1_corr, + e3_id.clone(), + ), + ec.clone(), + ) { + error!("Failed to publish T1 proof request: {err}"); + self.threshold_correlation.remove(&t1_corr); + self.pending_threshold.remove(&e3_id); + return; + } + + // C2a: SkShareComputation + let t2a_corr = CorrelationId::new(); + self.threshold_correlation.insert( + t2a_corr, + (e3_id.clone(), ThresholdProofKind::SkShareComputation), ); + info!("Requesting T2a SkShareComputation proof"); + if let Err(err) = self.bus.publish( + ComputeRequest::zk( + ZkRequest::ShareComputation(msg.sk_share_computation_request), + t2a_corr, + e3_id.clone(), + ), + ec.clone(), + ) { + error!("Failed to publish T2a proof request: {err}"); + self.threshold_correlation + .retain(|_, (eid, _)| *eid != e3_id); + self.pending_threshold.remove(&e3_id); + return; + } - info!("Requesting T1 PkGeneration proof generation"); - if let Err(err) = self.bus.publish(request, ec) { - error!("Failed to publish ZK proof request: {err}"); - self.pending_threshold.remove(&correlation_id); + // C2b: ESmShareComputation + let t2b_corr = CorrelationId::new(); + self.threshold_correlation.insert( + t2b_corr, + (e3_id.clone(), ThresholdProofKind::ESmShareComputation), + ); + info!("Requesting T2b ESmShareComputation proof"); + if let Err(err) = self.bus.publish( + ComputeRequest::zk( + ZkRequest::ShareComputation(msg.e_sm_share_computation_request), + t2b_corr, + e3_id.clone(), + ), + ec.clone(), + ) { + error!("Failed to publish T2b proof request: {err}"); + self.threshold_correlation + .retain(|_, (eid, _)| *eid != e3_id); + self.pending_threshold.remove(&e3_id); } } @@ -121,69 +211,135 @@ impl ProofRequestActor { self.handle_pk_bfv_response(&msg.correlation_id, resp.proof.clone(), &ec); } ComputeResponseKind::Zk(ZkResponse::PkGeneration(resp)) => { - self.handle_pk_generation_response(&msg.correlation_id, resp.proof.clone(), &ec); + self.handle_threshold_proof_response(&msg.correlation_id, resp.proof.clone()); + } + ComputeResponseKind::Zk(ZkResponse::ShareComputation(resp)) => { + self.handle_threshold_proof_response(&msg.correlation_id, resp.proof.clone()); } _ => {} } } - fn handle_pk_generation_response( - &mut self, - correlation_id: &CorrelationId, - proof: Proof, - ec: &EventContext, - ) { - let Some(pending) = self.pending_threshold.remove(correlation_id) else { + fn handle_threshold_proof_response(&mut self, correlation_id: &CorrelationId, proof: Proof) { + let Some((e3_id, kind)) = self.threshold_correlation.remove(correlation_id) else { + return; + }; + + let Some(pending) = self.pending_threshold.get_mut(&e3_id) else { error!( - "Received PkGeneration ComputeResponse with correlation_id {:?} but no matching pending request found.", - correlation_id + "No pending threshold proofs for E3 {} — orphan correlation", + e3_id ); return; }; + info!("Received {:?} proof for E3 {}", kind, e3_id); + pending.store_proof(&kind, proof); + + if pending.is_complete() { + info!("All 3 threshold proofs complete for E3 {}", e3_id); + let pending = self.pending_threshold.remove(&e3_id).unwrap(); + self.publish_threshold_share_with_proofs(pending); + } + } + + fn sign_proof( + &self, + e3_id: &E3id, + proof_type: ProofType, + proof: Proof, + ) -> Option { let payload = ProofPayload { - e3_id: pending.e3_id.clone(), - proof_type: ProofType::T1PkGeneration, - proof: proof.clone(), + e3_id: e3_id.clone(), + proof_type, + proof, }; - - let signed = match SignedProofPayload::sign(payload, &self.signer) { - Ok(s) => { - info!( - "Signed T1 PkGeneration proof for party {} (signer: {})", - pending.full_share.party_id, - self.signer.address() - ); - s - } + match SignedProofPayload::sign(payload, &self.signer) { + Ok(signed) => Some(signed), Err(err) => { - error!("Failed to sign T1 PkGeneration proof payload: {err} — shares will not be published"); - return; + error!("Failed to sign {:?} proof: {err}", proof_type); + None } - }; + } + } + fn publish_threshold_share_with_proofs(&mut self, pending: PendingThresholdProofs) { + let e3_id = &pending.e3_id; let party_id = pending.full_share.party_id; - let e3_id = pending.e3_id.clone(); + let ec = &pending.ec; + + let Some(signed_pk_gen) = self.sign_proof( + e3_id, + ProofType::T1PkGeneration, + pending.pk_generation_proof.expect("checked"), + ) else { + error!("Failed to sign T1 proof — shares will not be published"); + return; + }; + + let Some(signed_sk_share) = self.sign_proof( + e3_id, + ProofType::T1SkShareComputation, + pending.sk_share_computation_proof.expect("checked"), + ) else { + error!("Failed to sign T2a proof — shares will not be published"); + return; + }; + + let Some(signed_e_sm_share) = self.sign_proof( + e3_id, + ProofType::T1ESmShareComputation, + pending.e_sm_share_computation_proof.expect("checked"), + ) else { + error!("Failed to sign T2b proof — shares will not be published"); + return; + }; info!( - "Publishing PkGenerationProofSigned for E3 {} party {}", - pending.e3_id, party_id + "All proofs signed for E3 {} party {} (signer: {})", + e3_id, + party_id, + self.signer.address() ); + + let share = &pending.full_share; + let num_parties = share.num_parties(); + if let Err(err) = self.bus.publish( PkGenerationProofSigned { - e3_id: pending.e3_id, + e3_id: e3_id.clone(), party_id, - signed_proof: signed, + signed_proof: signed_pk_gen, }, ec.clone(), ) { error!("Failed to publish PkGenerationProofSigned: {err}"); } - let share = &pending.full_share; + if let Err(err) = self.bus.publish( + ShareComputationProofSigned { + e3_id: e3_id.clone(), + party_id, + proof_type: ProofType::T1SkShareComputation, + signed_proof: signed_sk_share, + }, + ec.clone(), + ) { + error!("Failed to publish SkShareComputationProofSigned: {err}"); + } + + if let Err(err) = self.bus.publish( + ShareComputationProofSigned { + e3_id: e3_id.clone(), + party_id, + proof_type: ProofType::T1ESmShareComputation, + signed_proof: signed_e_sm_share, + }, + ec.clone(), + ) { + error!("Failed to publish ESmShareComputationProofSigned: {err}"); + } - // Publish per-party shares - let num_parties = share.num_parties(); info!( "Publishing ThresholdShareCreated for E3 {} to {} parties", e3_id, num_parties @@ -274,10 +430,14 @@ impl ProofRequestActor { ); } - if let Some(pending) = self.pending_threshold.remove(msg.correlation_id()) { + if let Some((e3_id, kind)) = self.threshold_correlation.remove(msg.correlation_id()) { error!( - "T1 PkShareGeneration proof request failed for E3 {}: {err} — threshold share will not be published without proof", pending.e3_id - ) + "T1 {:?} proof request failed for E3 {}: {err} — threshold share will not be published without proof", + kind, e3_id + ); + self.threshold_correlation + .retain(|_, (eid, _)| *eid != e3_id); + self.pending_threshold.remove(&e3_id); } } } diff --git a/examples/CRISP/test/crisp.spec.ts b/examples/CRISP/test/crisp.spec.ts index 27758a508e..0f26a010dc 100644 --- a/examples/CRISP/test/crisp.spec.ts +++ b/examples/CRISP/test/crisp.spec.ts @@ -44,7 +44,7 @@ async function checkE3Ready(e3id: number): Promise { } } -async function waitForE3Ready(e3id: number, maxWaitMs: number = 30000): Promise { +async function waitForE3Ready(e3id: number, maxWaitMs: number = 80000): Promise { const startTime = Date.now() while (Date.now() - startTime < maxWaitMs) { const isActivated = await checkE3Ready(e3id) diff --git a/templates/default/tests/integration.spec.ts b/templates/default/tests/integration.spec.ts index 8459432531..01c27e423a 100644 --- a/templates/default/tests/integration.spec.ts +++ b/templates/default/tests/integration.spec.ts @@ -162,7 +162,7 @@ describe('Integration', () => { const { waitForEvent } = await setupEventListeners(sdk, store) const threshold: [number, number] = [DEFAULT_E3_CONFIG.threshold_min, DEFAULT_E3_CONFIG.threshold_max] - const duration = 30 + const duration = 70 const inputWindow = await calculateInputWindow(publicClient, duration) const thresholdBfvParams = await sdk.getThresholdBfvParamsSet() const e3ProgramParams = encodeBfvParams(thresholdBfvParams) From ecadb7cb357e19bcc149e10b6c884997aa2c2ad4 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:01:10 +0000 Subject: [PATCH 09/16] chore: run on self hosted --- .github/workflows/ci.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e25418ea2..d7e2ec7a36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ permissions: jobs: rust_tests: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - uses: actions/checkout@v6 @@ -80,7 +80,7 @@ jobs: run: 'cargo test --test integration -- --nocapture' zk_prover_integration: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - uses: actions/checkout@v6 @@ -109,7 +109,7 @@ jobs: run: 'cargo test -p e3-zk-prover --features integration-tests --test integration_tests -- --nocapture' build_e3_support_risc0: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - uses: actions/checkout@v6 - name: Generate tags @@ -146,7 +146,7 @@ jobs: type=gha,mode=max,scope=e3-support build_ciphernode_image: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - uses: actions/checkout@v6 @@ -353,7 +353,7 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY build_enclave_cli: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - uses: actions/checkout@v6 with: @@ -384,7 +384,7 @@ jobs: retention-days: 1 crisp_unit: - runs-on: ubuntu-latest + runs-on: self-hosted needs: [build_crisp_sdk] steps: - uses: actions/checkout@v6 @@ -457,7 +457,7 @@ jobs: run: 'pnpm test:contracts' crisp_e2e: - runs-on: ubuntu-latest + runs-on: self-hosted needs: [build_enclave_cli, build_crisp_sdk] steps: - uses: actions/checkout@v6 @@ -565,7 +565,7 @@ jobs: retention-days: 30 build_circuits: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - uses: actions/checkout@v6 with: @@ -618,7 +618,7 @@ jobs: if-no-files-found: error zk_prover_e2e: - runs-on: ubuntu-latest + runs-on: self-hosted needs: [build_circuits] steps: - uses: actions/checkout@v6 @@ -657,7 +657,7 @@ jobs: run: cargo test -p e3-zk-prover --test local_e2e_tests -- --nocapture build_e3_support_dev: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - uses: actions/checkout@v6 with: @@ -686,7 +686,7 @@ jobs: if-no-files-found: error build_sdk: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - uses: actions/checkout@v6 with: @@ -735,7 +735,7 @@ jobs: if-no-files-found: warn build_crisp_sdk: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - uses: actions/checkout@v6 @@ -779,7 +779,7 @@ jobs: if-no-files-found: warn template_integration: - runs-on: ubuntu-latest + runs-on: self-hosted needs: [build_enclave_cli, build_e3_support_dev, build_sdk] steps: - uses: actions/checkout@v6 @@ -839,7 +839,7 @@ jobs: pnpm test:integration test_enclave_init: - runs-on: ubuntu-latest + runs-on: self-hosted needs: [build_enclave_cli, build_e3_support_dev] steps: - name: Install pnpm @@ -876,7 +876,7 @@ jobs: enclave init mycitest --verbose --template=${{ github.server_url }}/${{ github.repository }}.git#${BRANCH}:templates/default contrib-readme-job: - runs-on: ubuntu-latest + runs-on: self-hosted name: Populate Contributors List # Only run on main branch to avoid branch conflicts if: github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, 'contrib-readme-action') From 121ddb7fee3a157170d9e0fe3c67a73c26109bea Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:28:03 +0000 Subject: [PATCH 10/16] chore: use large runner --- .github/workflows/ci.yml | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7e2ec7a36..a01cfe5e6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ permissions: jobs: rust_tests: - runs-on: self-hosted + runs-on: enclave-ci steps: - uses: actions/checkout@v6 @@ -80,7 +80,7 @@ jobs: run: 'cargo test --test integration -- --nocapture' zk_prover_integration: - runs-on: self-hosted + runs-on: enclave-ci steps: - uses: actions/checkout@v6 @@ -109,7 +109,7 @@ jobs: run: 'cargo test -p e3-zk-prover --features integration-tests --test integration_tests -- --nocapture' build_e3_support_risc0: - runs-on: self-hosted + runs-on: enclave-ci steps: - uses: actions/checkout@v6 - name: Generate tags @@ -146,7 +146,7 @@ jobs: type=gha,mode=max,scope=e3-support build_ciphernode_image: - runs-on: self-hosted + runs-on: enclave-ci steps: - uses: actions/checkout@v6 @@ -186,7 +186,7 @@ jobs: type=gha,mode=max,scope=ciphernode test_contracts: - runs-on: 'ubuntu-latest' + runs-on: enclave-ci steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -221,7 +221,7 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY test_net: - runs-on: 'ubuntu-latest' + runs-on: enclave-ci steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -234,7 +234,7 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY integration_prebuild: - runs-on: 'ubuntu-latest' + runs-on: enclave-ci steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -291,7 +291,7 @@ jobs: ciphernode_integration_test: needs: [integration_prebuild, build_enclave_cli, build_sdk] - runs-on: 'ubuntu-latest' + runs-on: enclave-ci strategy: matrix: test-suite: [base, persist] @@ -353,7 +353,7 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY build_enclave_cli: - runs-on: self-hosted + runs-on: enclave-ci steps: - uses: actions/checkout@v6 with: @@ -384,7 +384,7 @@ jobs: retention-days: 1 crisp_unit: - runs-on: self-hosted + runs-on: enclave-ci needs: [build_crisp_sdk] steps: - uses: actions/checkout@v6 @@ -457,7 +457,7 @@ jobs: run: 'pnpm test:contracts' crisp_e2e: - runs-on: self-hosted + runs-on: enclave-ci needs: [build_enclave_cli, build_crisp_sdk] steps: - uses: actions/checkout@v6 @@ -565,7 +565,7 @@ jobs: retention-days: 30 build_circuits: - runs-on: self-hosted + runs-on: enclave-ci steps: - uses: actions/checkout@v6 with: @@ -618,7 +618,7 @@ jobs: if-no-files-found: error zk_prover_e2e: - runs-on: self-hosted + runs-on: enclave-ci needs: [build_circuits] steps: - uses: actions/checkout@v6 @@ -657,7 +657,7 @@ jobs: run: cargo test -p e3-zk-prover --test local_e2e_tests -- --nocapture build_e3_support_dev: - runs-on: self-hosted + runs-on: enclave-ci steps: - uses: actions/checkout@v6 with: @@ -686,7 +686,7 @@ jobs: if-no-files-found: error build_sdk: - runs-on: self-hosted + runs-on: enclave-ci steps: - uses: actions/checkout@v6 with: @@ -735,7 +735,7 @@ jobs: if-no-files-found: warn build_crisp_sdk: - runs-on: self-hosted + runs-on: enclave-ci steps: - uses: actions/checkout@v6 @@ -779,7 +779,7 @@ jobs: if-no-files-found: warn template_integration: - runs-on: self-hosted + runs-on: enclave-ci needs: [build_enclave_cli, build_e3_support_dev, build_sdk] steps: - uses: actions/checkout@v6 @@ -839,7 +839,7 @@ jobs: pnpm test:integration test_enclave_init: - runs-on: self-hosted + runs-on: enclave-ci needs: [build_enclave_cli, build_e3_support_dev] steps: - name: Install pnpm @@ -876,7 +876,7 @@ jobs: enclave init mycitest --verbose --template=${{ github.server_url }}/${{ github.repository }}.git#${BRANCH}:templates/default contrib-readme-job: - runs-on: self-hosted + runs-on: enclave-ci name: Populate Contributors List # Only run on main branch to avoid branch conflicts if: github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, 'contrib-readme-action') From f98ce0ecdf3aecc0ef3126176c51d01160d43631 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:38:15 +0000 Subject: [PATCH 11/16] chore: change runner name --- .github/workflows/ci.yml | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a01cfe5e6f..b6939185f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ permissions: jobs: rust_tests: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - uses: actions/checkout@v6 @@ -80,7 +80,7 @@ jobs: run: 'cargo test --test integration -- --nocapture' zk_prover_integration: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - uses: actions/checkout@v6 @@ -109,7 +109,7 @@ jobs: run: 'cargo test -p e3-zk-prover --features integration-tests --test integration_tests -- --nocapture' build_e3_support_risc0: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - uses: actions/checkout@v6 - name: Generate tags @@ -146,7 +146,7 @@ jobs: type=gha,mode=max,scope=e3-support build_ciphernode_image: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - uses: actions/checkout@v6 @@ -186,7 +186,7 @@ jobs: type=gha,mode=max,scope=ciphernode test_contracts: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -221,7 +221,7 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY test_net: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -234,7 +234,7 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY integration_prebuild: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -291,7 +291,7 @@ jobs: ciphernode_integration_test: needs: [integration_prebuild, build_enclave_cli, build_sdk] - runs-on: enclave-ci + runs-on: enclave-ci-runner strategy: matrix: test-suite: [base, persist] @@ -353,7 +353,7 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY build_enclave_cli: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - uses: actions/checkout@v6 with: @@ -384,7 +384,7 @@ jobs: retention-days: 1 crisp_unit: - runs-on: enclave-ci + runs-on: enclave-ci-runner needs: [build_crisp_sdk] steps: - uses: actions/checkout@v6 @@ -457,7 +457,7 @@ jobs: run: 'pnpm test:contracts' crisp_e2e: - runs-on: enclave-ci + runs-on: enclave-ci-runner needs: [build_enclave_cli, build_crisp_sdk] steps: - uses: actions/checkout@v6 @@ -565,7 +565,7 @@ jobs: retention-days: 30 build_circuits: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - uses: actions/checkout@v6 with: @@ -618,7 +618,7 @@ jobs: if-no-files-found: error zk_prover_e2e: - runs-on: enclave-ci + runs-on: enclave-ci-runner needs: [build_circuits] steps: - uses: actions/checkout@v6 @@ -657,7 +657,7 @@ jobs: run: cargo test -p e3-zk-prover --test local_e2e_tests -- --nocapture build_e3_support_dev: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - uses: actions/checkout@v6 with: @@ -686,7 +686,7 @@ jobs: if-no-files-found: error build_sdk: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - uses: actions/checkout@v6 with: @@ -735,7 +735,7 @@ jobs: if-no-files-found: warn build_crisp_sdk: - runs-on: enclave-ci + runs-on: enclave-ci-runner steps: - uses: actions/checkout@v6 @@ -779,7 +779,7 @@ jobs: if-no-files-found: warn template_integration: - runs-on: enclave-ci + runs-on: enclave-ci-runner needs: [build_enclave_cli, build_e3_support_dev, build_sdk] steps: - uses: actions/checkout@v6 @@ -839,7 +839,7 @@ jobs: pnpm test:integration test_enclave_init: - runs-on: enclave-ci + runs-on: enclave-ci-runner needs: [build_enclave_cli, build_e3_support_dev] steps: - name: Install pnpm @@ -876,7 +876,7 @@ jobs: enclave init mycitest --verbose --template=${{ github.server_url }}/${{ github.repository }}.git#${BRANCH}:templates/default contrib-readme-job: - runs-on: enclave-ci + runs-on: enclave-ci-runner name: Populate Contributors List # Only run on main branch to avoid branch conflicts if: github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, 'contrib-readme-action') From 619dc0d040af0342f3e87a30254a7a5874ab7cfe Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:45:32 +0000 Subject: [PATCH 12/16] chore: add ci group --- .github/workflows/ci.yml | 57 ++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6939185f1..1101569316 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,8 @@ permissions: jobs: rust_tests: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - uses: actions/checkout@v6 @@ -80,7 +81,8 @@ jobs: run: 'cargo test --test integration -- --nocapture' zk_prover_integration: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - uses: actions/checkout@v6 @@ -109,7 +111,8 @@ jobs: run: 'cargo test -p e3-zk-prover --features integration-tests --test integration_tests -- --nocapture' build_e3_support_risc0: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - uses: actions/checkout@v6 - name: Generate tags @@ -146,7 +149,8 @@ jobs: type=gha,mode=max,scope=e3-support build_ciphernode_image: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - uses: actions/checkout@v6 @@ -186,7 +190,8 @@ jobs: type=gha,mode=max,scope=ciphernode test_contracts: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -221,7 +226,8 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY test_net: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -234,7 +240,8 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY integration_prebuild: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -291,7 +298,8 @@ jobs: ciphernode_integration_test: needs: [integration_prebuild, build_enclave_cli, build_sdk] - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci strategy: matrix: test-suite: [base, persist] @@ -353,7 +361,8 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY build_enclave_cli: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - uses: actions/checkout@v6 with: @@ -384,7 +393,8 @@ jobs: retention-days: 1 crisp_unit: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci needs: [build_crisp_sdk] steps: - uses: actions/checkout@v6 @@ -457,7 +467,8 @@ jobs: run: 'pnpm test:contracts' crisp_e2e: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci needs: [build_enclave_cli, build_crisp_sdk] steps: - uses: actions/checkout@v6 @@ -565,7 +576,8 @@ jobs: retention-days: 30 build_circuits: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - uses: actions/checkout@v6 with: @@ -618,7 +630,8 @@ jobs: if-no-files-found: error zk_prover_e2e: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci needs: [build_circuits] steps: - uses: actions/checkout@v6 @@ -657,7 +670,8 @@ jobs: run: cargo test -p e3-zk-prover --test local_e2e_tests -- --nocapture build_e3_support_dev: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - uses: actions/checkout@v6 with: @@ -686,7 +700,8 @@ jobs: if-no-files-found: error build_sdk: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - uses: actions/checkout@v6 with: @@ -735,7 +750,8 @@ jobs: if-no-files-found: warn build_crisp_sdk: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci steps: - uses: actions/checkout@v6 @@ -779,7 +795,8 @@ jobs: if-no-files-found: warn template_integration: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci needs: [build_enclave_cli, build_e3_support_dev, build_sdk] steps: - uses: actions/checkout@v6 @@ -839,7 +856,8 @@ jobs: pnpm test:integration test_enclave_init: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci needs: [build_enclave_cli, build_e3_support_dev] steps: - name: Install pnpm @@ -876,7 +894,8 @@ jobs: enclave init mycitest --verbose --template=${{ github.server_url }}/${{ github.repository }}.git#${BRANCH}:templates/default contrib-readme-job: - runs-on: enclave-ci-runner + runs-on: + group: enclave-ci name: Populate Contributors List # Only run on main branch to avoid branch conflicts if: github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, 'contrib-readme-action') From 5e8920aa2499d154db941a9185bed5a003668087 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 20 Feb 2026 15:01:25 +0000 Subject: [PATCH 13/16] chore: specify label --- .github/workflows/ci.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1101569316..8d846344d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,7 @@ jobs: rust_tests: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - uses: actions/checkout@v6 @@ -83,6 +84,7 @@ jobs: zk_prover_integration: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - uses: actions/checkout@v6 @@ -113,6 +115,7 @@ jobs: build_e3_support_risc0: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - uses: actions/checkout@v6 - name: Generate tags @@ -151,6 +154,7 @@ jobs: build_ciphernode_image: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - uses: actions/checkout@v6 @@ -192,6 +196,7 @@ jobs: test_contracts: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -228,6 +233,7 @@ jobs: test_net: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -242,6 +248,7 @@ jobs: integration_prebuild: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - name: 'Check out the repo' uses: 'actions/checkout@v6' @@ -300,6 +307,7 @@ jobs: needs: [integration_prebuild, build_enclave_cli, build_sdk] runs-on: group: enclave-ci + labels: [enclave-ci-runner] strategy: matrix: test-suite: [base, persist] @@ -363,6 +371,7 @@ jobs: build_enclave_cli: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - uses: actions/checkout@v6 with: @@ -395,6 +404,7 @@ jobs: crisp_unit: runs-on: group: enclave-ci + labels: [enclave-ci-runner] needs: [build_crisp_sdk] steps: - uses: actions/checkout@v6 @@ -469,6 +479,7 @@ jobs: crisp_e2e: runs-on: group: enclave-ci + labels: [enclave-ci-runner] needs: [build_enclave_cli, build_crisp_sdk] steps: - uses: actions/checkout@v6 @@ -578,6 +589,7 @@ jobs: build_circuits: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - uses: actions/checkout@v6 with: @@ -632,6 +644,7 @@ jobs: zk_prover_e2e: runs-on: group: enclave-ci + labels: [enclave-ci-runner] needs: [build_circuits] steps: - uses: actions/checkout@v6 @@ -672,6 +685,7 @@ jobs: build_e3_support_dev: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - uses: actions/checkout@v6 with: @@ -702,6 +716,7 @@ jobs: build_sdk: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - uses: actions/checkout@v6 with: @@ -752,6 +767,7 @@ jobs: build_crisp_sdk: runs-on: group: enclave-ci + labels: [enclave-ci-runner] steps: - uses: actions/checkout@v6 @@ -797,6 +813,7 @@ jobs: template_integration: runs-on: group: enclave-ci + labels: [enclave-ci-runner] needs: [build_enclave_cli, build_e3_support_dev, build_sdk] steps: - uses: actions/checkout@v6 @@ -858,6 +875,7 @@ jobs: test_enclave_init: runs-on: group: enclave-ci + labels: [enclave-ci-runner] needs: [build_enclave_cli, build_e3_support_dev] steps: - name: Install pnpm @@ -896,6 +914,7 @@ jobs: contrib-readme-job: runs-on: group: enclave-ci + labels: [enclave-ci-runner] name: Populate Contributors List # Only run on main branch to avoid branch conflicts if: github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, 'contrib-readme-action') From 74a640bed369955e8c2055faf525e4cd40dba172 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 20 Feb 2026 16:58:20 +0000 Subject: [PATCH 14/16] chore: let crisp e3 run for longer --- examples/CRISP/server/.env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index 0d81df449f..906f1bc646 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -23,7 +23,7 @@ FEE_TOKEN_ADDRESS="0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" # After this interval, the computation phase starts automatically # After activation + this interval, ciphernodes are then not responsing to # any more decryption requests -E3_DURATION=130 +E3_DURATION=180 E3_THRESHOLD_MIN=2 E3_THRESHOLD_MAX=5 From 52b3929b979a957e328b8ddf1e974cd59c8ad6b5 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:04:06 +0000 Subject: [PATCH 15/16] chore: pr comments --- crates/keyshare/src/threshold_keyshare.rs | 2 +- crates/multithread/src/multithread.rs | 8 ++++---- crates/zk-prover/src/actors/proof_request.rs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index 49106f4af7..188e05405b 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -453,7 +453,7 @@ impl ThresholdKeyshare { Ok(()) } - /// Handle ShareComputationProofSigned - stores the signed proof in state based on proof type (T1, T2a or T2b) + /// Handle ShareComputationProofSigned - stores the signed proof in state based on proof type (C1, C2a or C2b) pub fn handle_share_computation_proof_signed( &mut self, msg: TypedEvent, diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 41ed5a427b..319bbe2fb8 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -461,13 +461,13 @@ fn handle_share_computation_proof( .map(|arr| arr.mapv(|v| BigInt::from(v))) .collect(); - // 4. Compute parity matrix + // 5. Compute parity matrix let committee = req.committee_size.values(); let parity_matrix = compute_parity_matrix(threshold_params.moduli(), committee.n, committee.threshold) .map_err(|e| make_zk_error(&request, format!("compute_parity_matrix: {}", e)))?; - // 5. Build circuit data + // 6. Build circuit data let circuit_data = ShareComputationCircuitData { dkg_input_type: req.dkg_input_type, secret, @@ -477,7 +477,7 @@ fn handle_share_computation_proof( threshold: committee.threshold as u32, }; - // 6. Generate proof + // 7. Generate proof let circuit = ShareComputationCircuit; let e3_id_str = request.e3_id.to_string(); @@ -490,7 +490,7 @@ fn handle_share_computation_proof( ) })?; - // 7. Return response + // 8. Return response Ok(ComputeResponse::zk( ZkResponse::ShareComputation(ShareComputationProofResponse { proof, diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index 479a677926..76b8158712 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -273,7 +273,7 @@ impl ProofRequestActor { ProofType::T1PkGeneration, pending.pk_generation_proof.expect("checked"), ) else { - error!("Failed to sign T1 proof — shares will not be published"); + error!("Failed to sign C1 proof — shares will not be published"); return; }; @@ -282,7 +282,7 @@ impl ProofRequestActor { ProofType::T1SkShareComputation, pending.sk_share_computation_proof.expect("checked"), ) else { - error!("Failed to sign T2a proof — shares will not be published"); + error!("Failed to sign C2a proof — shares will not be published"); return; }; @@ -291,7 +291,7 @@ impl ProofRequestActor { ProofType::T1ESmShareComputation, pending.e_sm_share_computation_proof.expect("checked"), ) else { - error!("Failed to sign T2b proof — shares will not be published"); + error!("Failed to sign C2b proof — shares will not be published"); return; }; From 38c21d207460e1644e756fbbc02a42b2287d9362 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:06:04 +0000 Subject: [PATCH 16/16] chore: pr comments --- .../events/src/enclave_event/compute_request/zk.rs | 12 ++++++------ .../enclave_event/share_computation_proof_signed.rs | 3 +-- crates/keyshare/src/threshold_keyshare.rs | 5 +++-- crates/zk-prover/src/actors/proof_request.rs | 2 -- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/events/src/enclave_event/compute_request/zk.rs b/crates/events/src/enclave_event/compute_request/zk.rs index e6f772ed53..9d5f88d570 100644 --- a/crates/events/src/enclave_event/compute_request/zk.rs +++ b/crates/events/src/enclave_event/compute_request/zk.rs @@ -15,15 +15,15 @@ use serde::{Deserialize, Serialize}; /// ZK proof generation request variants. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum ZkRequest { - /// Generate proof for BFV public key (T0). + /// Generate proof for BFV public key (C0). PkBfv(PkBfvProofRequest), - /// Generate proof for PK generation (T1). + /// Generate proof for PK generation (C1). PkGeneration(PkGenerationProofRequest), - /// Generate proof for share and esm computation (T2a and T2b). + /// Generate proof for share and esm computation (C2a and C2b). ShareComputation(ShareComputationProofRequest), } -/// Request to generate a proof for share computation (T2a or T2b). +/// Request to generate a proof for share computation (C2a or C2b). #[derive(Derivative, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derivative(Debug)] pub struct ShareComputationProofRequest { @@ -39,7 +39,7 @@ pub struct ShareComputationProofRequest { pub committee_size: CiphernodesCommitteeSize, } -/// Request to generate a proof for BFV public key generation (T0). +/// Request to generate a proof for BFV public key generation (C0). #[derive(Derivative, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derivative(Debug)] pub struct PkBfvProofRequest { @@ -49,7 +49,7 @@ pub struct PkBfvProofRequest { pub params_preset: BfvPreset, } -/// Request to generate a proof for PK share generation (T1a). +/// Request to generate a proof for PK share generation (C1). #[derive(Derivative, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derivative(Debug)] pub struct PkGenerationProofRequest { diff --git a/crates/events/src/enclave_event/share_computation_proof_signed.rs b/crates/events/src/enclave_event/share_computation_proof_signed.rs index 52ecc54608..0f417ec797 100644 --- a/crates/events/src/enclave_event/share_computation_proof_signed.rs +++ b/crates/events/src/enclave_event/share_computation_proof_signed.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::{E3id, ProofType, SignedProofPayload}; +use crate::{E3id, SignedProofPayload}; use actix::Message; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; @@ -14,7 +14,6 @@ use std::fmt::{self, Display}; pub struct ShareComputationProofSigned { pub e3_id: E3id, pub party_id: u64, - pub proof_type: ProofType, pub signed_proof: SignedProofPayload, } diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index 188e05405b..590f4599e2 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -465,14 +465,15 @@ impl ThresholdKeyshare { return Ok(()); } + let proof_type = msg.signed_proof.payload.proof_type; info!( "Received ShareComputationProofSigned ({:?}) for party {} E3 {}", - msg.proof_type, msg.party_id, msg.e3_id + proof_type, msg.party_id, msg.e3_id ); self.state.try_mutate(&ec, |s| { let current: AggregatingDecryptionKey = s.clone().try_into()?; - let updated = match msg.proof_type { + let updated = match proof_type { ProofType::T1SkShareComputation => AggregatingDecryptionKey { signed_sk_share_computation_proof: Some(msg.signed_proof), ..current diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index 76b8158712..c3c91fa453 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -320,7 +320,6 @@ impl ProofRequestActor { ShareComputationProofSigned { e3_id: e3_id.clone(), party_id, - proof_type: ProofType::T1SkShareComputation, signed_proof: signed_sk_share, }, ec.clone(), @@ -332,7 +331,6 @@ impl ProofRequestActor { ShareComputationProofSigned { e3_id: e3_id.clone(), party_id, - proof_type: ProofType::T1ESmShareComputation, signed_proof: signed_e_sm_share, }, ec.clone(),