From 424b3e0538717bac36bdea9f63915c49d81e700b Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Wed, 11 Mar 2026 20:25:58 +0000 Subject: [PATCH 1/6] feat: allow requester to specify committee size --- crates/evm-helpers/src/contracts.rs | 25 +++++++++++++------ crates/indexer/src/indexer.rs | 2 +- crates/indexer/src/models.rs | 3 ++- .../zk-helpers/src/ciphernodes_committee.rs | 7 ++++++ .../enclave-contracts/contracts/Enclave.sol | 3 +++ .../contracts/interfaces/IEnclave.sol | 12 +++++++-- 6 files changed, 40 insertions(+), 12 deletions(-) diff --git a/crates/evm-helpers/src/contracts.rs b/crates/evm-helpers/src/contracts.rs index a85b07ed0c..bdb27f6e88 100644 --- a/crates/evm-helpers/src/contracts.rs +++ b/crates/evm-helpers/src/contracts.rs @@ -19,6 +19,7 @@ use alloy::{ use async_trait::async_trait; use eyre::Result; use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; use std::marker::PhantomData; use std::sync::Arc; use tokio::sync::Mutex; @@ -38,10 +39,18 @@ where } sol! { + #[derive(Debug, Serialize, Deserialize)] + enum CommitteeSize { + Micro, + Small, + Medium, + Large, + } + #[derive(Debug)] struct E3 { uint256 seed; - uint32[2] threshold; + CommitteeSize committeeSize; uint256 requestBlock; uint256[2] inputWindow; bytes32 encryptionSchemeId; @@ -57,7 +66,7 @@ sol! { #[derive(Debug)] struct E3RequestParams { - uint32[2] threshold; + CommitteeSize committeeSize; uint256[2] inputWindow; address e3Program; bytes e3ProgramParams; @@ -157,7 +166,7 @@ pub trait EnclaveRead { /// Get the fee quote for an E3 request async fn get_e3_quote( &self, - threshold: [u32; 2], + commitee_size: CommitteeSize, input_window: [U256; 2], e3_program: Address, e3_params: Bytes, @@ -181,7 +190,7 @@ pub trait EnclaveWrite { /// Request a new E3 async fn request_e3( &self, - threshold: [u32; 2], + committee_size: CommitteeSize, input_window: [U256; 2], e3_program: Address, e3_params: Bytes, @@ -385,14 +394,14 @@ where async fn get_e3_quote( &self, - threshold: [u32; 2], + committee_size: CommitteeSize, input_window: [U256; 2], e3_program: Address, e3_params: Bytes, compute_provider_params: Bytes, ) -> Result { let e3_request = E3RequestParams { - threshold, + committeeSize: committee_size, inputWindow: input_window, e3Program: e3_program, e3ProgramParams: e3_params, @@ -441,7 +450,7 @@ where impl EnclaveWrite for EnclaveContract { async fn request_e3( &self, - threshold: [u32; 2], + committee_size: CommitteeSize, input_window: [U256; 2], e3_program: Address, e3_params: Bytes, @@ -458,7 +467,7 @@ impl EnclaveWrite for EnclaveContract { let e3_id = contract.nexte3Id().call().await?; let e3_request = E3RequestParams { - threshold, + committeeSize: committee_size, inputWindow: input_window, e3Program: e3_program, e3ProgramParams: e3_params.clone(), diff --git a/crates/indexer/src/indexer.rs b/crates/indexer/src/indexer.rs index 7bdf58ff4e..e6057d03e8 100644 --- a/crates/indexer/src/indexer.rs +++ b/crates/indexer/src/indexer.rs @@ -364,7 +364,7 @@ impl EnclaveIndexer { request_block, seed, input_window, - threshold: e3.threshold, + committee_size: e3.committeSize, requester: e3.requester.to_string(), }; diff --git a/crates/indexer/src/models.rs b/crates/indexer/src/models.rs index b3c8a0fec7..306fc3f63e 100644 --- a/crates/indexer/src/models.rs +++ b/crates/indexer/src/models.rs @@ -4,6 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use e3_evm_helpers::contracts::CommitteeSize; use serde::{Deserialize, Serialize}; // This correlates with the information from the contract @@ -23,6 +24,6 @@ pub struct E3 { pub request_block: u64, pub seed: [u8; 32], pub input_window: [u64; 2], - pub threshold: [u32; 2], + pub commitee_size: CommitteeSize, pub requester: String, } diff --git a/crates/zk-helpers/src/ciphernodes_committee.rs b/crates/zk-helpers/src/ciphernodes_committee.rs index f1a912296a..5044210333 100644 --- a/crates/zk-helpers/src/ciphernodes_committee.rs +++ b/crates/zk-helpers/src/ciphernodes_committee.rs @@ -11,6 +11,8 @@ use serde::{Deserialize, Serialize}; /// the default values that must be used and shared among the whole enclave repository. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum CiphernodesCommitteeSize { + /// Tiny committee size (for quick local testing with production parameters). + Micro, /// Small committee size (fast local/testing). Small, /// Medium committee size (default). @@ -33,6 +35,11 @@ impl CiphernodesCommitteeSize { /// Returns `(num_parties, num_honest_parties, threshold)` for this size. pub fn values(self) -> CiphernodesCommittee { match self { + CiphernodesCommitteeSize::Micro => CiphernodesCommittee { + n: 2, + h: 2, + threshold: 1, + }, CiphernodesCommitteeSize::Small => CiphernodesCommittee { n: 5, h: 5, diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index 017a15a722..33ceb528fb 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -98,6 +98,9 @@ contract Enclave is IEnclave, OwnableUpgradeable { /// @notice Maps E3 ID to the fee token used at request time mapping(uint256 e3Id => IERC20 token) internal _e3FeeTokens; + /// @notice + mapping(CommitteSize => uint8[2] threshold) public committeeThresholds; + /// @notice Global timeout configuration E3TimeoutConfig internal _timeoutConfig; diff --git a/packages/enclave-contracts/contracts/interfaces/IEnclave.sol b/packages/enclave-contracts/contracts/interfaces/IEnclave.sol index 2a87c8a3a5..00e22f718d 100644 --- a/packages/enclave-contracts/contracts/interfaces/IEnclave.sol +++ b/packages/enclave-contracts/contracts/interfaces/IEnclave.sol @@ -18,6 +18,14 @@ interface IEnclave { // // //////////////////////////////////////////////////////////// + /// @notice Sizes of committees for E3 computations + enum CommitteeSize { + Micro, + Small, + Medium, + Large + } + /// @notice Lifecycle stages of an E3 computation enum E3Stage { None, @@ -211,14 +219,14 @@ interface IEnclave { //////////////////////////////////////////////////////////// /// @notice This struct contains the parameters to submit a request to Enclave. - /// @param threshold The M/N threshold for the committee. + /// @param committeeSize The M/N threshold and honest parties for the committee. /// @param inputWindow When the program will start and stop accepting inputs. /// @param e3Program The address of the E3 Program. /// @param e3ProgramParams The ABI encoded computation parameters. /// @param computeProviderParams The ABI encoded compute provider parameters. /// @param customParams Arbitrary ABI-encoded application-defined parameters. struct E3RequestParams { - uint32[2] threshold; + CommitteeSize committeeSize; uint256[2] inputWindow; IE3Program e3Program; bytes e3ProgramParams; From 1bc5b1fa3932b3783f194d5e6f93a09b88267ca1 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:06:45 +0000 Subject: [PATCH 2/6] feat: add committee size to e3 request --- crates/evm/Cargo.toml | 1 + crates/evm/src/enclave_sol_reader.rs | 14 +++- crates/keyshare/src/threshold_keyshare.rs | 15 +++-- .../zk-helpers/src/ciphernodes_committee.rs | 12 ++++ examples/CRISP/server/.env.example | 4 +- examples/CRISP/server/src/cli/commands.rs | 16 +++-- examples/CRISP/server/src/config.rs | 3 +- .../CRISP/server/src/server/routes/rounds.rs | 12 +++- .../enclave-contracts/contracts/Enclave.sol | 35 ++++++---- .../contracts/interfaces/IE3.sol | 5 +- .../contracts/interfaces/IEnclave.sol | 16 +++++ .../scripts/deployEnclave.ts | 8 +++ packages/enclave-contracts/tasks/enclave.ts | 17 ++--- .../enclave-contracts/test/Enclave.spec.ts | 64 +++++++------------ .../src/contracts/contract-client.ts | 4 +- packages/enclave-sdk/src/contracts/types.ts | 11 +++- packages/enclave-sdk/src/enclave-sdk.ts | 2 +- packages/enclave-sdk/src/events/types.ts | 2 +- packages/enclave-sdk/src/index.ts | 2 +- packages/enclave-sdk/src/types.ts | 2 +- packages/enclave-sdk/src/utils.ts | 3 +- .../src/pages/steps/RequestComputation.tsx | 5 +- templates/default/tests/integration.spec.ts | 8 +-- tests/integration/base.sh | 3 +- tests/integration/persist.sh | 3 +- 25 files changed, 162 insertions(+), 105 deletions(-) diff --git a/crates/evm/Cargo.toml b/crates/evm/Cargo.toml index 062431cdfb..75299bfa28 100644 --- a/crates/evm/Cargo.toml +++ b/crates/evm/Cargo.toml @@ -23,6 +23,7 @@ e3-fhe-params = { workspace = true } e3-sortition = { workspace = true } e3-trbfv = { workspace = true } e3-utils = { workspace = true } +e3-zk-helpers = { workspace = true } futures-util = { workspace = true } hex = { workspace = true } num-bigint = { workspace = true } diff --git a/crates/evm/src/enclave_sol_reader.rs b/crates/evm/src/enclave_sol_reader.rs index aa5d6b0c29..8505ddb824 100644 --- a/crates/evm/src/enclave_sol_reader.rs +++ b/crates/evm/src/enclave_sol_reader.rs @@ -15,6 +15,7 @@ use e3_events::{E3Failed, E3Stage, E3StageChanged, FailureReason}; use e3_fhe_params::decode_bfv_params_arc; use e3_trbfv::helpers::calculate_error_size; use e3_utils::ArcBytes; +use e3_zk_helpers::CiphernodesCommitteeSize; use num_bigint::BigUint; use tracing::{error, info, trace, warn}; @@ -29,8 +30,17 @@ struct E3RequestedWithChainId(pub IEnclave::E3Requested, pub u64); impl From for e3_events::E3Requested { fn from(value: E3RequestedWithChainId) -> Self { let params_bytes = value.0.e3.e3ProgramParams.to_vec(); - let threshold_m = value.0.e3.threshold[0] as usize; - let threshold_n = value.0.e3.threshold[1] as usize; + + // Derive threshold values from committee size enum + let committee_size = match value.0.e3.committeeSize { + 0 => CiphernodesCommitteeSize::Micro, + 1 => CiphernodesCommitteeSize::Small, + _ => panic!("Unsupported committee size: {}", value.0.e3.committeeSize), + }; + let committee = committee_size.values(); + let threshold_m = committee.threshold; + let threshold_n = committee.n; + let params_arc = decode_bfv_params_arc(¶ms_bytes).expect("Failed to decode BFV params"); // TODO: These should be delivered from the e3_program contract diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index c1282dd1ce..83f33b493b 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -1012,12 +1012,17 @@ impl ThresholdKeyshare { }), party_id, e3_id, + threshold_m, + threshold_n, .. }) = self.state.get() else { bail!("Invalid state - expected GeneratingThresholdShare with all data"); }; + let derived_committee_size = + CiphernodesCommitteeSize::from_threshold(*threshold_m as usize, *threshold_n as usize); + // Get collected BFV public keys from all parties (from persisted state) let encryption_keys = &collected_encryption_keys; @@ -1090,7 +1095,7 @@ impl ThresholdKeyshare { proof_request_data.eek_raw.clone(), e_sm_raw.clone(), threshold_preset, - CiphernodesCommitteeSize::Small, // TODO: derive from config + derived_committee_size, ); // Build C2a request (SkShareComputation) @@ -1099,7 +1104,7 @@ impl ThresholdKeyshare { secret_sss_raw: sk_sss_raw, dkg_input_type: DkgInputType::SecretKey, params_preset: threshold_preset, - committee_size: CiphernodesCommitteeSize::Small, // TODO: derive from config + committee_size: derived_committee_size, }; // Build C2b request (ESmShareComputation) @@ -1111,7 +1116,7 @@ impl ThresholdKeyshare { .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, // TODO: derive from config + committee_size: derived_committee_size, }; // Build C3a proof requests (SK share encryption) from witnesses @@ -1133,7 +1138,7 @@ impl ThresholdKeyshare { e1_rns_raw: SensitiveBytes::new(witness.e1_rns.to_bytes(), &self.cipher)?, dkg_input_type: DkgInputType::SecretKey, params_preset: threshold_preset, - committee_size: CiphernodesCommitteeSize::Small, + committee_size: derived_committee_size, recipient_party_id: recipient_idx, row_index: row_idx, esi_index: 0, @@ -1161,7 +1166,7 @@ impl ThresholdKeyshare { e1_rns_raw: SensitiveBytes::new(witness.e1_rns.to_bytes(), &self.cipher)?, dkg_input_type: DkgInputType::SmudgingNoise, params_preset: threshold_preset, - committee_size: CiphernodesCommitteeSize::Small, + committee_size: derived_committee_size, recipient_party_id: recipient_idx, row_index: row_idx, esi_index: esi_idx, diff --git a/crates/zk-helpers/src/ciphernodes_committee.rs b/crates/zk-helpers/src/ciphernodes_committee.rs index 5044210333..39bf50c5be 100644 --- a/crates/zk-helpers/src/ciphernodes_committee.rs +++ b/crates/zk-helpers/src/ciphernodes_committee.rs @@ -32,6 +32,18 @@ pub struct CiphernodesCommittee { } impl CiphernodesCommitteeSize { + /// Derives the committee size from threshold values (M, N). + pub fn from_threshold(threshold_m: usize, threshold_n: usize) -> Self { + match (threshold_m, threshold_n) { + (1, 2) => Self::Micro, + (2, 5) => Self::Small, + _ => panic!( + "Unknown committee size for threshold ({}, {})", + threshold_m, threshold_n + ), + } + } + /// Returns `(num_parties, num_honest_parties, threshold)` for this size. pub fn values(self) -> CiphernodesCommittee { match self { diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index 1ffba7c578..f842f0c5a2 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -24,8 +24,8 @@ FEE_TOKEN_ADDRESS="0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" # After activation + this interval, ciphernodes are then not responsing to # any more decryption requests E3_DURATION=250 -E3_THRESHOLD_MIN=2 -E3_THRESHOLD_MAX=5 +# 0=Micro, 1=Small, 2=Medium, 3=Large +E3_COMMITTEE_SIZE=1 # E3 Compute Provider Config E3_COMPUTE_PROVIDER_NAME="RISC0" diff --git a/examples/CRISP/server/src/cli/commands.rs b/examples/CRISP/server/src/cli/commands.rs index ba19cc5131..1d61f95fc8 100644 --- a/examples/CRISP/server/src/cli/commands.rs +++ b/examples/CRISP/server/src/cli/commands.rs @@ -19,7 +19,7 @@ use alloy::providers::{Provider, ProviderBuilder}; use alloy::sol_types::SolValue; use crisp::config::CONFIG; use e3_fhe_params::{build_bfv_params_from_set_arc, encode_bfv_params}; -use e3_sdk::evm_helpers::contracts::{EnclaveContract, EnclaveRead, EnclaveWrite, E3}; +use e3_sdk::evm_helpers::contracts::{CommitteeSize, EnclaveContract, EnclaveRead, EnclaveWrite, E3}; use fhe::bfv::{BfvParameters, Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}; use fhe_traits::{ DeserializeParametrized, FheDecoder, FheDecrypter, FheEncoder, FheEncrypter, @@ -130,7 +130,13 @@ pub async fn initialize_crisp_round( .abi_encode(), ); - let threshold: [u32; 2] = [CONFIG.e3_threshold_min, CONFIG.e3_threshold_max]; + let committee_size = match CONFIG.e3_committee_size { + 0 => CommitteeSize::Micro, + 1 => CommitteeSize::Small, + 2 => CommitteeSize::Medium, + 3 => CommitteeSize::Large, + _ => panic!("Invalid committee size: {}", CONFIG.e3_committee_size), + }; let e3_params = Bytes::from(encode_bfv_params(&generate_bfv_parameters())); let compute_provider_params = ComputeProviderParams { name: CONFIG.e3_compute_provider_name.to_string(), @@ -155,7 +161,7 @@ pub async fn initialize_crisp_round( let fee_amount = contract .get_e3_quote( - threshold, + committee_size.clone(), input_window, e3_program, e3_params.clone(), @@ -178,7 +184,7 @@ pub async fn initialize_crisp_round( info!("Requesting E3 on contract: {}", CONFIG.enclave_address); - info!("Debug - threshold: {:?}", threshold); + info!("Debug - committee_size: {:?}", committee_size); info!("Debug - input_window: {:?}", input_window); info!("Debug - current timestamp: {:?}", current_timestamp); info!("Debug - e3_program: {}", e3_program); @@ -200,7 +206,7 @@ pub async fn initialize_crisp_round( let (res, e3_id) = contract .request_e3( - threshold, + committee_size, input_window, e3_program, e3_params, diff --git a/examples/CRISP/server/src/config.rs b/examples/CRISP/server/src/config.rs index 40d76f4767..8976a0ac73 100644 --- a/examples/CRISP/server/src/config.rs +++ b/examples/CRISP/server/src/config.rs @@ -23,8 +23,7 @@ pub struct Config { pub chain_id: u64, pub cron_api_key: String, // E3 parameters - pub e3_threshold_min: u32, - pub e3_threshold_max: u32, + pub e3_committee_size: u8, // 0=Micro, 1=Small, 2=Medium, 3=Large pub e3_duration: u64, pub e3_compute_provider_name: String, pub e3_compute_provider_parallel: bool, diff --git a/examples/CRISP/server/src/server/routes/rounds.rs b/examples/CRISP/server/src/server/routes/rounds.rs index 16edd129a7..cbfdd9794f 100644 --- a/examples/CRISP/server/src/server/routes/rounds.rs +++ b/examples/CRISP/server/src/server/routes/rounds.rs @@ -17,7 +17,7 @@ use alloy::primitives::{Address, Bytes, U256}; use alloy::sol_types::SolValue; use e3_fhe_params::default_param_set; use e3_fhe_params::{build_bfv_params_from_set_arc, encode_bfv_params}; -use e3_sdk::evm_helpers::contracts::{EnclaveContract, EnclaveRead, EnclaveWrite}; +use e3_sdk::evm_helpers::contracts::{CommitteeSize, EnclaveContract, EnclaveRead, EnclaveWrite}; use log::{error, info}; pub fn setup_routes(config: &mut web::ServiceConfig) { @@ -208,7 +208,13 @@ pub async fn initialize_crisp_round( let custom_params_bytes = Bytes::from((token_address, balance_threshold).abi_encode()); info!("Requesting E3..."); - let threshold: [u32; 2] = [CONFIG.e3_threshold_min, CONFIG.e3_threshold_max]; + let committee_size = match CONFIG.e3_committee_size { + 0 => CommitteeSize::Micro, + 1 => CommitteeSize::Small, + 2 => CommitteeSize::Medium, + 3 => CommitteeSize::Large, + _ => return Err(format!("Invalid committee size: {}", CONFIG.e3_committee_size).into()), + }; let current_timestamp = get_current_timestamp_rpc().await?; // Buffer so tx can mine before window opens; end = start + duration so voting window equals e3_duration @@ -226,7 +232,7 @@ pub async fn initialize_crisp_round( let compute_provider_params = Bytes::from(bincode::serialize(&compute_provider_params)?); let (receipt, e3_id) = contract .request_e3( - threshold, + committee_size, input_window, e3_program, e3_params, diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index 33ceb528fb..2b032181b1 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -98,8 +98,8 @@ contract Enclave is IEnclave, OwnableUpgradeable { /// @notice Maps E3 ID to the fee token used at request time mapping(uint256 e3Id => IERC20 token) internal _e3FeeTokens; - /// @notice - mapping(CommitteSize => uint8[2] threshold) public committeeThresholds; + /// @notice Maps committee size to threshold values [quorum, total] + mapping(CommitteeSize => uint32[2] threshold) public committeeThresholds; /// @notice Global timeout configuration E3TimeoutConfig internal _timeoutConfig; @@ -145,9 +145,9 @@ contract Enclave is IEnclave, OwnableUpgradeable { /// @param output The invalid output data. error InvalidOutput(bytes output); - /// @notice Thrown when the threshold parameters are invalid (e.g., M > N or M = 0). - /// @param threshold The invalid threshold array [M, N]. - error InvalidThreshold(uint32[2] threshold); + /// @notice Thrown when the committee size has not been configured with thresholds. + /// @param committeeSize The unconfigured committee size. + error CommitteeSizeNotConfigured(CommitteeSize committeeSize); /// @notice Thrown when attempting to publish ciphertext output that has already been published. /// @param e3Id The ID of the E3. @@ -287,11 +287,11 @@ contract Enclave is IEnclave, OwnableUpgradeable { function request( E3RequestParams calldata requestParams ) external returns (uint256 e3Id, E3 memory e3) { - // check whether the threshold config is valid + // Resolve committee size to threshold values + uint32[2] memory threshold = committeeThresholds[requestParams.committeeSize]; require( - requestParams.threshold[1] >= requestParams.threshold[0] && - requestParams.threshold[0] > 0, - InvalidThreshold(requestParams.threshold) + threshold[1] > 0, + CommitteeSizeNotConfigured(requestParams.committeeSize) ); // input start date should be in the future @@ -328,7 +328,7 @@ contract Enclave is IEnclave, OwnableUpgradeable { uint256 seed = uint256(keccak256(abi.encode(block.prevrandao, e3Id))); e3.seed = seed; - e3.threshold = requestParams.threshold; + e3.committeeSize = requestParams.committeeSize; e3.requestBlock = block.number; e3.inputWindow = requestParams.inputWindow; e3.e3Program = requestParams.e3Program; @@ -371,7 +371,7 @@ contract Enclave is IEnclave, OwnableUpgradeable { ciphernodeRegistry.requestCommittee( e3Id, seed, - requestParams.threshold + threshold ), CommitteeSelectionFailed() ); @@ -982,6 +982,19 @@ contract Enclave is IEnclave, OwnableUpgradeable { emit TimeoutConfigUpdated(config); } + /// @inheritdoc IEnclave + function setCommitteeThresholds( + CommitteeSize size, + uint32[2] calldata threshold + ) external onlyOwner { + require( + threshold[1] >= threshold[0] && threshold[0] > 0, + "Invalid threshold" + ); + committeeThresholds[size] = threshold; + emit CommitteeThresholdsUpdated(size, threshold); + } + //////////////////////////////////////////////////////////// // // // Get Functions // diff --git a/packages/enclave-contracts/contracts/interfaces/IE3.sol b/packages/enclave-contracts/contracts/interfaces/IE3.sol index ed83c285ef..6923c458e1 100644 --- a/packages/enclave-contracts/contracts/interfaces/IE3.sol +++ b/packages/enclave-contracts/contracts/interfaces/IE3.sol @@ -7,6 +7,7 @@ pragma solidity >=0.8.27; import { IE3Program } from "./IE3Program.sol"; import { IDecryptionVerifier } from "./IDecryptionVerifier.sol"; +import { IEnclave } from "./IEnclave.sol"; /** * @title E3 @@ -14,7 +15,7 @@ import { IDecryptionVerifier } from "./IDecryptionVerifier.sol"; * @dev This struct tracks all parameters, state, and results of an encrypted computation * from request through completion * @param seed Random seed for committee selection and computation initialization - * @param threshold M/N threshold for the committee (M required out of N total members) + * @param committeeSize The committee size enum value for this computation * @param requestBlock Block number when the E3 computation was requested * @param inputWindow When to start and stop accepting inputs from data providers * @param encryptionSchemeId Identifier for the encryption scheme used in this computation @@ -29,7 +30,7 @@ import { IDecryptionVerifier } from "./IDecryptionVerifier.sol"; */ struct E3 { uint256 seed; - uint32[2] threshold; + IEnclave.CommitteeSize committeeSize; uint256 requestBlock; uint256[2] inputWindow; bytes32 encryptionSchemeId; diff --git a/packages/enclave-contracts/contracts/interfaces/IEnclave.sol b/packages/enclave-contracts/contracts/interfaces/IEnclave.sol index 00e22f718d..031fd0cfc8 100644 --- a/packages/enclave-contracts/contracts/interfaces/IEnclave.sol +++ b/packages/enclave-contracts/contracts/interfaces/IEnclave.sol @@ -212,6 +212,14 @@ interface IEnclave { /// @notice Emitted when timeout config is updated event TimeoutConfigUpdated(E3TimeoutConfig config); + /// @notice Emitted when committee thresholds are updated + /// @param size The committee size enum value. + /// @param threshold The M/N threshold values. + event CommitteeThresholdsUpdated( + CommitteeSize indexed size, + uint32[2] threshold + ); + //////////////////////////////////////////////////////////// // // // Structs // @@ -451,4 +459,12 @@ interface IEnclave { /// @notice Set timeout configuration /// @param config The new timeout config function setTimeoutConfig(E3TimeoutConfig calldata config) external; + + /// @notice Set the threshold values for a committee size + /// @param size The committee size enum value + /// @param threshold The M/N threshold values [quorum, total] + function setCommitteeThresholds( + CommitteeSize size, + uint32[2] calldata threshold + ) external; } diff --git a/packages/enclave-contracts/scripts/deployEnclave.ts b/packages/enclave-contracts/scripts/deployEnclave.ts index afa35a4cc0..a3398e7898 100644 --- a/packages/enclave-contracts/scripts/deployEnclave.ts +++ b/packages/enclave-contracts/scripts/deployEnclave.ts @@ -209,6 +209,14 @@ export const deployEnclave = async (withMocks?: boolean) => { // E3RefundManager already has correct enclave from deployment + // Initialize committee size thresholds [threshold, total] + console.log("Setting committee thresholds..."); + // Micro: threshold=1, total=2 + await enclave.setCommitteeThresholds(0, [1, 2]); + // Small: threshold=2, total=5 + await enclave.setCommitteeThresholds(1, [2, 5]); + console.log("Committee thresholds set (Micro=[1,2], Small=[2,5])"); + if (shouldDeployMocks) { const { decryptionVerifierAddress, e3ProgramAddress } = await deployMocks(); diff --git a/packages/enclave-contracts/tasks/enclave.ts b/packages/enclave-contracts/tasks/enclave.ts index 7be1108a2c..7ffe5ef99f 100644 --- a/packages/enclave-contracts/tasks/enclave.ts +++ b/packages/enclave-contracts/tasks/enclave.ts @@ -21,15 +21,9 @@ export const requestCommittee = task( type: ArgumentType.STRING, }) .addOption({ - name: "thresholdQuorum", - description: "threshold quorum for committee", - defaultValue: 2, - type: ArgumentType.INT, - }) - .addOption({ - name: "thresholdTotal", - description: "threshold total for committee", - defaultValue: 2, + name: "committeeSize", + description: "committee size (0=Micro, 1=Small, 2=Medium, 3=Large)", + defaultValue: 1, type: ArgumentType.INT, }) .addOption({ @@ -71,8 +65,7 @@ export const requestCommittee = task( .setAction(async () => ({ default: async ( { - thresholdQuorum, - thresholdTotal, + committeeSize, inputWindowStart, inputWindowEnd, e3Address, @@ -155,7 +148,7 @@ export const requestCommittee = task( }); const requestParams = { - threshold: [thresholdQuorum, thresholdTotal] as [number, number], + committeeSize, inputWindow: [inputWindowStart, inputWindowEnd] as [ BigNumberish, BigNumberish, diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 70b6a5d8ae..7305607fb7 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -300,10 +300,16 @@ describe("Enclave", function () { await usdcToken.mint(ownerAddress, mintAmount); await usdcToken.mint(await notTheOwner.getAddress(), mintAmount); + // ── Committee Thresholds ────────────────────────────────────────────────── + // CommitteeSize.Micro = 0 → [1, 2] + await enclave.setCommitteeThresholds(0, [1, 2]); + // CommitteeSize.Small = 1 → [2, 5] + await enclave.setCommitteeThresholds(1, [2, 5]); + // ── Request ─────────────────────────────────────────────────────────────── const now = await time.latest(); const request = { - threshold: [2, 2] as [number, number], + committeeSize: 0, // Micro inputWindow: [now + 10, now + inputWindowDuration] as [number, number], e3Program: await e3Program.getAddress(), e3ProgramParams: encodedE3ProgramParams, @@ -483,7 +489,7 @@ describe("Enclave", function () { const { enclave, request, usdcToken } = await loadFixture(setup); await makeRequest(enclave, usdcToken, { - threshold: request.threshold, + committeeSize: request.committeeSize, inputWindow: request.inputWindow, e3Program: request.e3Program, e3ProgramParams: request.e3ProgramParams, @@ -493,7 +499,7 @@ describe("Enclave", function () { const e3 = await enclave.getE3(0); - expect(e3.threshold).to.deep.equal(request.threshold); + expect(e3.committeeSize).to.equal(request.committeeSize); expect(e3.inputWindow[0]).to.equal(request.inputWindow[0]); expect(e3.inputWindow[1]).to.equal(request.inputWindow[1]); expect(e3.e3Program).to.equal(request.e3Program); @@ -697,7 +703,7 @@ describe("Enclave", function () { const { enclave, request, usdcToken } = await loadFixture(setup); await expect( enclave.request({ - threshold: request.threshold, + committeeSize: request.committeeSize, inputWindow: request.inputWindow, e3Program: request.e3Program, e3ProgramParams: request.e3ProgramParams, @@ -706,36 +712,12 @@ describe("Enclave", function () { }), ).to.be.revertedWithCustomError(usdcToken, "ERC20InsufficientAllowance"); }); - it("reverts if threshold is 0", async function () { + it("reverts if committee size is not configured", async function () { const { enclave, request, usdcToken } = await loadFixture(setup); - const fee = await enclave.getE3Quote({ - threshold: [0, 2], - inputWindow: request.inputWindow, - e3Program: request.e3Program, - e3ProgramParams: request.e3ProgramParams, - computeProviderParams: request.computeProviderParams, - customParams: request.customParams, - }); - await usdcToken.approve(await enclave.getAddress(), fee); - await expect( - enclave.request({ - threshold: [0, 2], - inputWindow: request.inputWindow, - e3Program: request.e3Program, - e3ProgramParams: request.e3ProgramParams, - computeProviderParams: request.computeProviderParams, - customParams: request.customParams, - }), - ) - .to.be.revertedWithCustomError(enclave, "InvalidThreshold") - .withArgs([0, 2]); - }); - it("reverts if threshold is greater than number", async function () { - const { enclave, request, usdcToken } = await loadFixture(setup); - + // CommitteeSize.Large (3) is not configured in the fixture await expect( makeRequest(enclave, usdcToken, { - threshold: [3, 2], + committeeSize: 3, inputWindow: request.inputWindow, e3Program: request.e3Program, e3ProgramParams: request.e3ProgramParams, @@ -743,15 +725,15 @@ describe("Enclave", function () { customParams: request.customParams, }), ) - .to.be.revertedWithCustomError(enclave, "InvalidThreshold") - .withArgs([3, 2]); + .to.be.revertedWithCustomError(enclave, "CommitteeSizeNotConfigured") + .withArgs(3); }); it("reverts if total duration is greater than maxDuration", async function () { const { enclave, request, usdcToken } = await loadFixture(setup); await expect( makeRequest(enclave, usdcToken, { - threshold: [2, 3], + committeeSize: request.committeeSize, inputWindow: [ request.inputWindow[0], request.inputWindow[1] + time.duration.days(31), @@ -768,7 +750,7 @@ describe("Enclave", function () { await expect( makeRequest(enclave, usdcToken, { - threshold: [2, 3], + committeeSize: request.committeeSize, inputWindow: request.inputWindow, e3Program: ethers.ZeroAddress, e3ProgramParams: request.e3ProgramParams, @@ -784,7 +766,7 @@ describe("Enclave", function () { await enclave.disableEncryptionScheme(encryptionSchemeId); await expect( makeRequest(enclave, usdcToken, { - threshold: request.threshold, + committeeSize: request.committeeSize, inputWindow: request.inputWindow, e3Program: request.e3Program, e3ProgramParams: request.e3ProgramParams, @@ -799,7 +781,7 @@ describe("Enclave", function () { const { enclave, request, usdcToken } = await loadFixture(setup); await makeRequest(enclave, usdcToken, { - threshold: request.threshold, + committeeSize: request.committeeSize, inputWindow: request.inputWindow, e3Program: request.e3Program, e3ProgramParams: request.e3ProgramParams, @@ -810,7 +792,7 @@ describe("Enclave", function () { const e3 = await enclave.getE3(0); const block = await ethers.provider.getBlock("latest").catch((e) => e); - expect(e3.threshold).to.deep.equal(request.threshold); + expect(e3.committeeSize).to.equal(request.committeeSize); expect(e3.inputWindow[0]).to.equal(request.inputWindow[0]); expect(e3.inputWindow[1]).to.equal(request.inputWindow[1]); expect(e3.e3Program).to.equal(request.e3Program); @@ -825,7 +807,7 @@ describe("Enclave", function () { it("emits E3Requested event", async function () { const { enclave, request, usdcToken } = await loadFixture(setup); const tx = await makeRequest(enclave, usdcToken, { - threshold: request.threshold, + committeeSize: request.committeeSize, inputWindow: request.inputWindow, e3Program: request.e3Program, e3ProgramParams: request.e3ProgramParams, @@ -861,7 +843,7 @@ describe("Enclave", function () { const e3Id = 0; await makeRequest(enclave, usdcToken, { - threshold: request.threshold, + committeeSize: request.committeeSize, inputWindow: request.inputWindow, e3Program: request.e3Program, e3ProgramParams: request.e3ProgramParams, @@ -927,7 +909,7 @@ describe("Enclave", function () { const e3Id = 0; await makeRequest(enclave, usdcToken, { - threshold: request.threshold, + committeeSize: request.committeeSize, inputWindow: [(await time.latest()) + 20, (await time.latest()) + 100], e3Program: request.e3Program, e3ProgramParams: request.e3ProgramParams, diff --git a/packages/enclave-sdk/src/contracts/contract-client.ts b/packages/enclave-sdk/src/contracts/contract-client.ts index b22f988790..6413498792 100644 --- a/packages/enclave-sdk/src/contracts/contract-client.ts +++ b/packages/enclave-sdk/src/contracts/contract-client.ts @@ -149,7 +149,7 @@ export class ContractClient { functionName: 'request', args: [ { - threshold: params.threshold, + committeeSize: params.committeeSize, inputWindow: params.inputWindow, e3Program: params.e3Program, e3ProgramParams: params.e3ProgramParams, @@ -221,7 +221,7 @@ export class ContractClient { functionName: 'getE3Quote', args: [ { - threshold: requestParams.threshold, + committeeSize: requestParams.committeeSize, inputWindow: requestParams.inputWindow, e3Program: requestParams.e3Program, e3ProgramParams: requestParams.e3ProgramParams, diff --git a/packages/enclave-sdk/src/contracts/types.ts b/packages/enclave-sdk/src/contracts/types.ts index 6bdf06fc53..10c5ca5601 100644 --- a/packages/enclave-sdk/src/contracts/types.ts +++ b/packages/enclave-sdk/src/contracts/types.ts @@ -10,9 +10,16 @@ export interface ContractAddresses { feeToken: `0x${string}` } +export enum CommitteeSize { + Micro = 0, + Small = 1, + Medium = 2, + Large = 3, +} + export interface E3 { seed: bigint - threshold: readonly [number, number] + committeeSize: number requestBlock: bigint inputWindow: readonly [bigint, bigint] encryptionSchemeId: string @@ -29,7 +36,7 @@ export interface RequestParams { } export interface E3RequestParams extends RequestParams { - threshold: readonly [number, number] + committeeSize: number inputWindow: readonly [bigint, bigint] e3Program: `0x${string}` e3ProgramParams: `0x${string}` diff --git a/packages/enclave-sdk/src/enclave-sdk.ts b/packages/enclave-sdk/src/enclave-sdk.ts index ca4f99987d..2c213d9556 100644 --- a/packages/enclave-sdk/src/enclave-sdk.ts +++ b/packages/enclave-sdk/src/enclave-sdk.ts @@ -134,7 +134,7 @@ export class EnclaveSDK { } public async requestE3(params: { - threshold: [number, number] + committeeSize: number inputWindow: [bigint, bigint] e3Program: `0x${string}` e3ProgramParams: `0x${string}` diff --git a/packages/enclave-sdk/src/events/types.ts b/packages/enclave-sdk/src/events/types.ts index bd82143c5c..3d7504915b 100644 --- a/packages/enclave-sdk/src/events/types.ts +++ b/packages/enclave-sdk/src/events/types.ts @@ -36,7 +36,7 @@ export interface E3RequestedData { e3Id: bigint e3: { seed: bigint - threshold: readonly [number, number] + committeeSize: number requestBlock: bigint inputWindow: readonly [bigint, bigint] encryptionSchemeId: string diff --git a/packages/enclave-sdk/src/index.ts b/packages/enclave-sdk/src/index.ts index a1b14d5cbe..cd23bcbdbe 100644 --- a/packages/enclave-sdk/src/index.ts +++ b/packages/enclave-sdk/src/index.ts @@ -57,7 +57,7 @@ export type { } from './types' // Enums and constants -export { EnclaveEventType, RegistryEventType, ThresholdBfvParamsPresetNames, E3Stage, FailureReason } from './types' +export { EnclaveEventType, RegistryEventType, ThresholdBfvParamsPresetNames, E3Stage, FailureReason, CommitteeSize } from './types' export { DEFAULT_THRESHOLD_BFV_PARAMS_PRESET_NAME } from './constants' // Export utilities diff --git a/packages/enclave-sdk/src/types.ts b/packages/enclave-sdk/src/types.ts index a116fdd139..df7b8d4fc9 100644 --- a/packages/enclave-sdk/src/types.ts +++ b/packages/enclave-sdk/src/types.ts @@ -14,7 +14,7 @@ export type { BfvParams, ThresholdBfvParamsPresetName, VerifiableEncryptionResul export { ThresholdBfvParamsPresetNames } from './crypto/types' export type { ContractAddresses, E3, E3RequestParams } from './contracts/types' -export { E3Stage, FailureReason } from './contracts/types' +export { E3Stage, FailureReason, CommitteeSize } from './contracts/types' export { EnclaveEventType, RegistryEventType } from './events/types' diff --git a/packages/enclave-sdk/src/utils.ts b/packages/enclave-sdk/src/utils.ts index 3fc0cc9a0f..007feff9d6 100644 --- a/packages/enclave-sdk/src/utils.ts +++ b/packages/enclave-sdk/src/utils.ts @@ -78,8 +78,7 @@ export const DEFAULT_COMPUTE_PROVIDER_PARAMS: ComputeProviderParams = { // Default E3 configuration export const DEFAULT_E3_CONFIG = { - threshold_min: 2, - threshold_max: 5, + committeeSize: 1, // Small duration: 1800, // 30 minutes in seconds payment_amount: '0', // 0 ETH in wei } as const diff --git a/templates/default/client/src/pages/steps/RequestComputation.tsx b/templates/default/client/src/pages/steps/RequestComputation.tsx index efbb0015a2..0dd559cb52 100644 --- a/templates/default/client/src/pages/steps/RequestComputation.tsx +++ b/templates/default/client/src/pages/steps/RequestComputation.tsx @@ -16,6 +16,7 @@ import { calculateStartWindow, DEFAULT_COMPUTE_PROVIDER_PARAMS, DEFAULT_E3_CONFIG, + CommitteeSize, } from '@enclave-e3/sdk' import { getContractAddresses } from '@/utils/env-config' @@ -107,7 +108,7 @@ const RequestComputation: React.FC = () => { throw new Error('SDK not initialized') } - const threshold: [number, number] = [DEFAULT_E3_CONFIG.threshold_min, DEFAULT_E3_CONFIG.threshold_max] + const committeeSize = DEFAULT_E3_CONFIG.committeeSize const startWindow = calculateStartWindow(60) // 1 minute const duration = BigInt(60) // 1 minute const thresholdBfvParams = await sdk.getThresholdBfvParamsSet() @@ -116,7 +117,7 @@ const RequestComputation: React.FC = () => { console.log('requestE3') const hash = await requestE3({ - threshold, + committeeSize, startWindow, duration, e3Program: contracts.e3Program, diff --git a/templates/default/tests/integration.spec.ts b/templates/default/tests/integration.spec.ts index 39b22fadbd..ca75c31069 100644 --- a/templates/default/tests/integration.spec.ts +++ b/templates/default/tests/integration.spec.ts @@ -8,10 +8,10 @@ import { EnclaveSDK, calculateInputWindow, DEFAULT_COMPUTE_PROVIDER_PARAMS, - DEFAULT_E3_CONFIG, encodeBfvParams, encodeComputeProviderParams, decodePlaintextOutput, + CommitteeSize, } from '@enclave-e3/sdk' import { EnclaveEventType, RegistryEventType } from '@enclave-e3/sdk/events' import type { AllEventTypes, EnclaveEvent } from '@enclave-e3/sdk/events' @@ -188,8 +188,8 @@ describe('Integration', () => { it('should run an integration test', async () => { const { waitForEvent } = await setupEventListeners(sdk, store) - const threshold: [number, number] = [DEFAULT_E3_CONFIG.threshold_min, DEFAULT_E3_CONFIG.threshold_max] - const duration = 600 + const committeeSize = CommitteeSize.Micro + const duration = 300 const inputWindow = await calculateInputWindow(publicClient, duration) const thresholdBfvParams = await sdk.getThresholdBfvParamsSet() const e3ProgramParams = encodeBfvParams(thresholdBfvParams) @@ -204,7 +204,7 @@ describe('Integration', () => { // Verify fee quoting works const requestParams = { - threshold, + committeeSize, inputWindow, e3Program: contracts.e3Program, e3ProgramParams, diff --git a/tests/integration/base.sh b/tests/integration/base.sh index b885048494..16da63c9c6 100755 --- a/tests/integration/base.sh +++ b/tests/integration/base.sh @@ -74,8 +74,7 @@ pnpm committee:new \ --input-window-start "$INPUT_WINDOW_START" \ --input-window-end "$INPUT_WINDOW_END" \ --e3-params "$ENCODED_PARAMS" \ - --threshold-quorum 2 \ - --threshold-total 5 + --committee-size 0 waiton "$SCRIPT_DIR/output/pubkey.bin" diff --git a/tests/integration/persist.sh b/tests/integration/persist.sh index b36263188b..5cb592a01a 100755 --- a/tests/integration/persist.sh +++ b/tests/integration/persist.sh @@ -68,8 +68,7 @@ pnpm committee:new \ --input-window-start "$INPUT_WINDOW_START" \ --input-window-end "$INPUT_WINDOW_END" \ --e3-params "$ENCODED_PARAMS" \ - --threshold-quorum 2 \ - --threshold-total 5 + --committee-size 0 waiton "$SCRIPT_DIR/output/pubkey.bin" From 0f6debb8e82a1dea4ff2576d105216ace94bbe51 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Mar 2026 22:08:16 +0000 Subject: [PATCH 3/6] chore: update circuits and tests --- Cargo.lock | 2 + circuits/lib/src/configs/committee/micro.nr | 15 ++ circuits/lib/src/configs/committee/mod.nr | 1 + circuits/lib/src/configs/default/mod.nr | 2 +- circuits/lib/src/configs/insecure/dkg.nr | 12 +- circuits/lib/src/configs/secure/dkg.nr | 20 +-- crates/evm-helpers/Cargo.toml | 1 + crates/evm-helpers/src/events.rs | 10 +- crates/indexer/src/indexer.rs | 2 +- crates/indexer/src/models.rs | 2 +- .../indexer/tests/fixtures/fake_enclave.sol | 15 +- crates/keyshare/src/threshold_keyshare.rs | 2 +- crates/tests/tests/integration.rs | 72 +++++---- crates/zk-helpers/src/bin/zk_cli.rs | 28 +++- .../zk-helpers/src/ciphernodes_committee.rs | 6 +- examples/CRISP/Cargo.lock | 1 + .../contracts/Mocks/MockEnclave.sol | 4 +- .../crisp-contracts/deployed_contracts.json | 146 ++++++++++++++++++ examples/CRISP/server/.env.example | 6 +- .../enclave-contracts/contracts/Enclave.sol | 10 +- .../enclave-contracts/deployed_contracts.json | 132 ++++++++++++++++ .../scripts/deployEnclave.ts | 6 +- .../test/E3Lifecycle/E3Integration.spec.ts | 11 +- .../enclave-contracts/test/Enclave.spec.ts | 4 +- .../CiphernodeRegistryOwnable.spec.ts | 7 +- .../test/Slashing/CommitteeExpulsion.spec.ts | 29 ++-- templates/default/deployed_contracts.json | 58 +++---- 27 files changed, 467 insertions(+), 137 deletions(-) create mode 100644 circuits/lib/src/configs/committee/micro.nr diff --git a/Cargo.lock b/Cargo.lock index 28a55245e6..43fd8fa72d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3328,6 +3328,7 @@ dependencies = [ "e3-test-helpers", "e3-trbfv", "e3-utils", + "e3-zk-helpers", "futures-util", "hex", "num-bigint", @@ -3351,6 +3352,7 @@ dependencies = [ "futures", "futures-util", "once_cell", + "serde", "tokio", "tracing", ] diff --git a/circuits/lib/src/configs/committee/micro.nr b/circuits/lib/src/configs/committee/micro.nr new file mode 100644 index 0000000000..1c1dacb6af --- /dev/null +++ b/circuits/lib/src/configs/committee/micro.nr @@ -0,0 +1,15 @@ +// 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. + +/// Currently defaults to just small committee size. +/// In the future, we will add more committee sizes. + +/// Number of parties. +pub global N_PARTIES: u32 = 3; +/// Threshold. +pub global T: u32 = 1; +/// Number of honest parties. +pub global H: u32 = 3; diff --git a/circuits/lib/src/configs/committee/mod.nr b/circuits/lib/src/configs/committee/mod.nr index 3376ead18d..3bfb590e50 100644 --- a/circuits/lib/src/configs/committee/mod.nr +++ b/circuits/lib/src/configs/committee/mod.nr @@ -5,3 +5,4 @@ // or FITNESS FOR A PARTICULAR PURPOSE. pub mod small; +pub mod micro; diff --git a/circuits/lib/src/configs/default/mod.nr b/circuits/lib/src/configs/default/mod.nr index e1d4793718..784c388279 100644 --- a/circuits/lib/src/configs/default/mod.nr +++ b/circuits/lib/src/configs/default/mod.nr @@ -7,7 +7,7 @@ // Unico punto in cui si cambia il param-set: re-esporta insecure o production // (in futuro altri param-set). I circuiti usano tutti lib::configs::default::*. -pub use super::committee::small::{H, N_PARTIES, T}; +pub use super::committee::micro::{H, N_PARTIES, T}; pub use super::insecure::dkg; pub use super::insecure::threshold; diff --git a/circuits/lib/src/configs/insecure/dkg.nr b/circuits/lib/src/configs/insecure/dkg.nr index d4a5cf8d7e..6198b7cce5 100644 --- a/circuits/lib/src/configs/insecure/dkg.nr +++ b/circuits/lib/src/configs/insecure/dkg.nr @@ -28,14 +28,12 @@ pub global PK_BIT_PK: u32 = 50; pub global PARITY_MATRIX: [[[Field; N_PARTIES + 1]; N_PARTIES - T]; L_THRESHOLD] = [ [ - [68719403008, 3, 68719403006, 1, 0, 0], - [68719403006, 8, 68719403003, 0, 1, 0], - [68719403003, 15, 68719402999, 0, 0, 1], + [1, 68719403007, 1, 0], + [2, 68719403006, 0, 1], ], [ - [68719230976, 3, 68719230974, 1, 0, 0], - [68719230974, 8, 68719230971, 0, 1, 0], - [68719230971, 15, 68719230967, 0, 0, 1], + [1, 68719230975, 1, 0], + [2, 68719230974, 0, 1], ], ]; @@ -60,7 +58,7 @@ share_computation_e_sm (CIRCUIT 2b) ************************************/ // share_computation_e_sm - bit parameters -pub global SHARE_COMPUTATION_E_SM_BIT_SECRET: u32 = 24; +pub global SHARE_COMPUTATION_E_SM_BIT_SECRET: u32 = 23; // verify_shares - configs pub global SHARE_COMPUTATION_E_SM_CONFIGS: ShareComputationConfigs = diff --git a/circuits/lib/src/configs/secure/dkg.nr b/circuits/lib/src/configs/secure/dkg.nr index 84182954ef..de39431bee 100644 --- a/circuits/lib/src/configs/secure/dkg.nr +++ b/circuits/lib/src/configs/secure/dkg.nr @@ -29,24 +29,20 @@ pub global PK_BIT_PK: u32 = 56; pub global PARITY_MATRIX: [[[Field; N_PARTIES + 1]; N_PARTIES - T]; L_THRESHOLD] = [ [ - [2251799822204928, 3, 2251799822204926, 1, 0, 0], - [2251799822204926, 8, 2251799822204923, 0, 1, 0], - [2251799822204923, 15, 2251799822204919, 0, 0, 1], + [1, 2251799822204927, 1, 0], + [2, 2251799822204926, 0, 1], ], [ - [4503599627763712, 3, 4503599627763710, 1, 0, 0], - [4503599627763710, 8, 4503599627763707, 0, 1, 0], - [4503599627763707, 15, 4503599627763703, 0, 0, 1], + [1, 4503599627763711, 1, 0], + [2, 4503599627763710, 0, 1], ], [ - [4503599631433728, 3, 4503599631433726, 1, 0, 0], - [4503599631433726, 8, 4503599631433723, 0, 1, 0], - [4503599631433723, 15, 4503599631433719, 0, 0, 1], + [1, 4503599631433727, 1, 0], + [2, 4503599631433726, 0, 1], ], [ - [4503599634579456, 3, 4503599634579454, 1, 0, 0], - [4503599634579454, 8, 4503599634579451, 0, 1, 0], - [4503599634579451, 15, 4503599634579447, 0, 0, 1], + [1, 4503599634579455, 1, 0], + [2, 4503599634579454, 0, 1], ], ]; /************************************ diff --git a/crates/evm-helpers/Cargo.toml b/crates/evm-helpers/Cargo.toml index 4af5491548..81f3c3c077 100644 --- a/crates/evm-helpers/Cargo.toml +++ b/crates/evm-helpers/Cargo.toml @@ -11,6 +11,7 @@ alloy.workspace = true anyhow.workspace = true async-trait.workspace = true eyre.workspace = true +serde.workspace = true futures.workspace = true futures-util.workspace = true once_cell.workspace = true diff --git a/crates/evm-helpers/src/events.rs b/crates/evm-helpers/src/events.rs index 8d1d2b6896..1d25a33ac3 100644 --- a/crates/evm-helpers/src/events.rs +++ b/crates/evm-helpers/src/events.rs @@ -22,10 +22,18 @@ sol! { function verifyDecryption(bytes data) external view returns (bool); } + #[derive(Debug)] + enum CommitteeSize { + Micro, + Small, + Medium, + Large, + } + #[derive(Debug)] struct E3 { uint256 seed; - uint32[2] threshold; + CommitteeSize committeeSize; uint256 requestBlock; uint256[2] inputWindow; bytes32 encryptionSchemeId; diff --git a/crates/indexer/src/indexer.rs b/crates/indexer/src/indexer.rs index e6057d03e8..c30e80e0ae 100644 --- a/crates/indexer/src/indexer.rs +++ b/crates/indexer/src/indexer.rs @@ -364,7 +364,7 @@ impl EnclaveIndexer { request_block, seed, input_window, - committee_size: e3.committeSize, + committee_size: e3.committeeSize, requester: e3.requester.to_string(), }; diff --git a/crates/indexer/src/models.rs b/crates/indexer/src/models.rs index 306fc3f63e..e432da6fed 100644 --- a/crates/indexer/src/models.rs +++ b/crates/indexer/src/models.rs @@ -24,6 +24,6 @@ pub struct E3 { pub request_block: u64, pub seed: [u8; 32], pub input_window: [u64; 2], - pub commitee_size: CommitteeSize, + pub committee_size: CommitteeSize, pub requester: String, } diff --git a/crates/indexer/tests/fixtures/fake_enclave.sol b/crates/indexer/tests/fixtures/fake_enclave.sol index 87c4004cf1..ec8da583fb 100644 --- a/crates/indexer/tests/fixtures/fake_enclave.sol +++ b/crates/indexer/tests/fixtures/fake_enclave.sol @@ -36,7 +36,7 @@ contract FakeEnclave { function getE3(uint256 _e3Id) external view returns (E3 memory e3) { e3 = E3({ seed: 123456789012, - threshold: [uint32(2), uint32(3)], + committeeSize: CommitteeSize.Micro, requestBlock: 18750000, inputWindow: [uint256(18750100), uint256(18750200)], encryptionSchemeId: bytes32(keccak256("AES-256-GCM")), @@ -46,14 +46,22 @@ contract FakeEnclave { customParams: abi.encode("custom_params"), committeePublicKey: bytes32(keccak256("committee_public_key")), ciphertextOutput: bytes32(keccak256("encrypted_data")), - plaintextOutput: abi.encode("decrypted_result") + plaintextOutput: abi.encode("decrypted_result"), + requester: 0xdead000000000000000000000000000000000001 }); } } +enum CommitteeSize { + Micro, + Small, + Medium, + Large +} + struct E3 { uint256 seed; - uint32[2] threshold; + CommitteeSize committeeSize; uint256 requestBlock; uint256[2] inputWindow; bytes32 encryptionSchemeId; @@ -64,4 +72,5 @@ struct E3 { bytes32 committeePublicKey; bytes32 ciphertextOutput; bytes plaintextOutput; + address requester; } diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index 83f33b493b..c8493189da 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -1021,7 +1021,7 @@ impl ThresholdKeyshare { }; let derived_committee_size = - CiphernodesCommitteeSize::from_threshold(*threshold_m as usize, *threshold_n as usize); + CiphernodesCommitteeSize::from_threshold(threshold_m as usize, threshold_n as usize); // Get collected BFV public keys from all parties (from persisted state) let encryption_keys = &collected_encryption_keys; diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 2e33139bc5..c4a3691a20 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -102,19 +102,31 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .join("circuits") .join("bin"); - // Copy C0 (pk) circuit - let pk_circuit_dir = circuits_dir.join("dkg").join("pk"); + // Copy circuit artifacts under the "recursive" variant subdirectory, + // because prove() defaults to CircuitVariant::Recursive. + // + // The build system (build-circuits.ts) generates three VK flavors per circuit: + // - {name}.vk → evm verifier target (for on-chain verification) + // - {name}.vk_recursive → noir-recursive-no-zk target (Default variant) + // - {name}.vk_noir → noir-recursive target (Recursive variant) + // + // Each variant directory stores its VK as just ".vk", so for the recursive/ + // directory we must copy .vk_noir as .vk (matching what the build system does). + let recursive_dir = circuits_dir.join("recursive"); + + // Copy T0 (pk) circuit + let pk_circuit_dir = recursive_dir.join("dkg").join("pk"); tokio::fs::create_dir_all(&pk_circuit_dir).await.unwrap(); let dkg_target = circuits_build_root.join("dkg").join("target"); tokio::fs::copy(dkg_target.join("pk.json"), pk_circuit_dir.join("pk.json")) .await .unwrap(); - tokio::fs::copy(dkg_target.join("pk.vk"), pk_circuit_dir.join("pk.vk")) + tokio::fs::copy(dkg_target.join("pk.vk_noir"), pk_circuit_dir.join("pk.vk")) .await .unwrap(); // Copy C1 (pk_generation) circuit - let pk_gen_circuit_dir = circuits_dir.join("threshold").join("pk_generation"); + let pk_gen_circuit_dir = recursive_dir.join("threshold").join("pk_generation"); tokio::fs::create_dir_all(&pk_gen_circuit_dir) .await .unwrap(); @@ -126,14 +138,14 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); tokio::fs::copy( - threshold_target.join("pk_generation.vk"), + threshold_target.join("pk_generation.vk_noir"), pk_gen_circuit_dir.join("pk_generation.vk"), ) .await .unwrap(); // Copy C2a (sk_share_computation) circuit - let sk_share_comp_circuit_dir = circuits_dir.join("dkg").join("sk_share_computation"); + let sk_share_comp_circuit_dir = recursive_dir.join("dkg").join("sk_share_computation"); tokio::fs::create_dir_all(&sk_share_comp_circuit_dir) .await .unwrap(); @@ -144,14 +156,14 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); tokio::fs::copy( - dkg_target.join("sk_share_computation.vk"), + dkg_target.join("sk_share_computation.vk_noir"), sk_share_comp_circuit_dir.join("sk_share_computation.vk"), ) .await .unwrap(); // Copy C2b (e_sm_share_computation) circuit - let e_sm_share_comp_circuit_dir = circuits_dir.join("dkg").join("e_sm_share_computation"); + let e_sm_share_comp_circuit_dir = recursive_dir.join("dkg").join("e_sm_share_computation"); tokio::fs::create_dir_all(&e_sm_share_comp_circuit_dir) .await .unwrap(); @@ -162,14 +174,14 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); tokio::fs::copy( - dkg_target.join("e_sm_share_computation.vk"), + dkg_target.join("e_sm_share_computation.vk_noir"), e_sm_share_comp_circuit_dir.join("e_sm_share_computation.vk"), ) .await .unwrap(); // Copy C3 (share_encryption) circuit — single circuit used for both SK and E_SM - let share_enc_circuit_dir = circuits_dir.join("dkg").join("share_encryption"); + let share_enc_circuit_dir = recursive_dir.join("dkg").join("share_encryption"); tokio::fs::create_dir_all(&share_enc_circuit_dir) .await .unwrap(); @@ -180,14 +192,14 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); tokio::fs::copy( - dkg_target.join("share_encryption.vk"), + dkg_target.join("share_encryption.vk_noir"), share_enc_circuit_dir.join("share_encryption.vk"), ) .await .unwrap(); // Copy C4 (share_decryption) circuit — used for DKG share decryption proofs (Exchange #3) - let share_dec_circuit_dir = circuits_dir.join("dkg").join("share_decryption"); + let share_dec_circuit_dir = recursive_dir.join("dkg").join("share_decryption"); tokio::fs::create_dir_all(&share_dec_circuit_dir) .await .unwrap(); @@ -198,14 +210,14 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); tokio::fs::copy( - dkg_target.join("share_decryption.vk"), + dkg_target.join("share_decryption.vk_noir"), share_dec_circuit_dir.join("share_decryption.vk"), ) .await .unwrap(); // Copy C5 (pk_aggregation) circuit - let pk_agg_circuit_dir = circuits_dir.join("threshold").join("pk_aggregation"); + let pk_agg_circuit_dir = recursive_dir.join("threshold").join("pk_aggregation"); tokio::fs::create_dir_all(&pk_agg_circuit_dir) .await .unwrap(); @@ -216,7 +228,7 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); tokio::fs::copy( - threshold_target.join("pk_aggregation.vk"), + threshold_target.join("pk_aggregation.vk_noir"), pk_agg_circuit_dir.join("pk_aggregation.vk"), ) .await @@ -224,7 +236,7 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { // Copy C6 (threshold/share_decryption) circuit for C6 verification let threshold_share_dec_circuit_dir = - circuits_dir.join("threshold").join("share_decryption"); + recursive_dir.join("threshold").join("share_decryption"); tokio::fs::create_dir_all(&threshold_share_dec_circuit_dir) .await .unwrap(); @@ -235,14 +247,14 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); tokio::fs::copy( - threshold_target.join("share_decryption.vk"), + threshold_target.join("share_decryption.vk_noir"), threshold_share_dec_circuit_dir.join("share_decryption.vk"), ) .await .unwrap(); // Copy C7 (decrypted_shares_aggregation_mod) circuit - let dsa_circuit_dir = circuits_dir + let dsa_circuit_dir = recursive_dir .join("threshold") .join("decrypted_shares_aggregation_mod"); tokio::fs::create_dir_all(&dsa_circuit_dir).await.unwrap(); @@ -253,7 +265,7 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); tokio::fs::copy( - threshold_target.join("decrypted_shares_aggregation_mod.vk"), + threshold_target.join("decrypted_shares_aggregation_mod.vk_noir"), dsa_circuit_dir.join("decrypted_shares_aggregation_mod.vk"), ) .await @@ -457,7 +469,7 @@ async fn test_trbfv_actor() -> Result<()> { // - E3Router // - ThresholdKeyshare // - Multithread actor - // - 7 nodes (so as to check for some nodes not getting selected) + // - 20 nodes (so as to check for some nodes not getting selected) // - Loopback libp2p simulation /////////////////////////////////////////////////////////////////////////////////// @@ -477,8 +489,8 @@ async fn test_trbfv_actor() -> Result<()> { let params = ArcBytes::from_bytes(&encode_bfv_params(¶ms_raw.clone())); // round information - let threshold_m = 2; - let threshold_n = 5; + let threshold_m = 1; + let threshold_n = 3; let esi_per_ct = 1; // WARNING: INSECURE SECURITY PARAMETER LAMBDA. @@ -509,7 +521,7 @@ async fn test_trbfv_actor() -> Result<()> { let (zk_backend, _zk_temp) = setup_test_zk_backend().await; let nodes = CiphernodeSystemBuilder::new() - // Adding 20 total nodes: 5 for committee + 4 buffer = 9 selected, 11 unselected + // Adding 20 total nodes: 3 for committee + 3 buffer = 6 selected, 14 unselected .add_group(1, || async { let addr = rand_eth_addr(&rng); println!("Building collector {}!", addr); @@ -583,8 +595,8 @@ async fn test_trbfv_actor() -> Result<()> { /////////////////////////////////////////////////////////////////////////////////// // 2. Trigger E3Requested // - // - m=2. - // - n=5 + // - m=1. + // - n=3 // - lambda=2 // - error_size -> calculate using calculate_error_size // - esi_per_ciphertext = 1 @@ -642,7 +654,7 @@ async fn test_trbfv_actor() -> Result<()> { )); // Wait for KeyshareCreated + C1 verification + C5 proof + PublicKeyAggregated - // - KeyshareCreated × 5 (forwarded from committee nodes) + // - KeyshareCreated × 3 (forwarded from committee nodes) // - ShareVerificationDispatched (C1 proof verification dispatched by PublicKeyAggregator) // - ComputeRequest (C1 ZK verification) // - ComputeResponse (C1 ZK verification result) @@ -657,8 +669,6 @@ async fn test_trbfv_actor() -> Result<()> { let h = nodes .expect_events_with_timeouts( &[ - "KeyshareCreated", - "KeyshareCreated", "KeyshareCreated", "KeyshareCreated", "KeyshareCreated", @@ -751,7 +761,7 @@ async fn test_trbfv_actor() -> Result<()> { // Lets grab decryption share events // The collector sees: // - 1 CiphertextOutputPublished (from shared bus) - // - 5 DecryptionshareCreated (from simulate_libp2p, passes is_forwardable_event) + // - 3 DecryptionshareCreated (from simulate_libp2p, passes is_forwardable_event) // - 1 ShareVerificationDispatched (C6 verification dispatched by ThresholdPlaintextAggregator) // - 1 ComputeRequest (C6 ZK verification) // - 1 ComputeResponse (C6 ZK verification result) @@ -766,8 +776,8 @@ async fn test_trbfv_actor() -> Result<()> { // - 1 PlaintextAggregated (with C7 proofs) // Internal events from committee nodes (ComputeRequest/Response for CalculateDecryptionShare) // stay on their local buses. - // Total: 1 + 5 + 1 + 2 + 15 + 1 + 2 + 1 + 2 + 1 + 1 = 32 events - let expected_count = 1 + 5 + 1 + 2 + 15 + 1 + 2 + 1 + 2 + 1 + 1; + // Total: 1 + 3 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 1 = 15 events + let expected_count = 1 + 3 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 1; let h = nodes .take_history_with_timeouts( diff --git a/crates/zk-helpers/src/bin/zk_cli.rs b/crates/zk-helpers/src/bin/zk_cli.rs index 95fc3616e5..b4ee45332b 100644 --- a/crates/zk-helpers/src/bin/zk_cli.rs +++ b/crates/zk-helpers/src/bin/zk_cli.rs @@ -53,6 +53,16 @@ enum DkgInputTypeArg { SmudgingNoise, } +fn parse_committee(s: &str) -> Result { + match s.trim().to_lowercase().as_str() { + "micro" => Ok(CiphernodesCommitteeSize::Micro), + "small" => Ok(CiphernodesCommitteeSize::Small), + _ => Err(anyhow!( + "unknown committee: {s}. Use \"micro\" or \"small\"" + )), + } +} + fn parse_input_type(s: &str) -> Result { match s.trim().to_lowercase().as_str() { "secret-key" => Ok(DkgInputTypeArg::SecretKey), @@ -73,6 +83,7 @@ fn clear_terminal() { fn print_generation_info( circuit: &str, preset: BfvPreset, + committee_size: CiphernodesCommitteeSize, has_inputs: bool, dkg_input_type: DkgInputType, output: &std::path::Path, @@ -80,13 +91,18 @@ fn print_generation_info( no_configs: bool, ) { let meta = preset.metadata(); - println!(" Circuit: {}", circuit); + let committee = committee_size.values(); + println!(" Circuit: {}", circuit); println!( - " Preset: {} (degree {}, {} moduli)", + " Preset: {} (degree {}, {} moduli)", meta.security.as_config_str(), meta.degree, meta.num_moduli ); + println!( + " Committee: {:?} (n={}, t={}, h={})", + committee_size, committee.n, committee.threshold, committee.h + ); if has_inputs { println!( " Inputs: {}", @@ -154,6 +170,9 @@ struct Cli { /// For share-computation only: inputs type "secret-key" or "smudging-noise". Required when writing Prover.toml for share-computation. Ignored for pk (always secret key). #[arg(long)] inputs: Option, + /// Committee size: "micro" or "small". + #[arg(long, default_value = "micro")] + committee: String, /// Output directory for generated artifacts. #[arg(long, default_value = "output")] output: PathBuf, @@ -254,10 +273,13 @@ fn main() -> Result<()> { DkgInputType::SecretKey }; + let committee_size = parse_committee(&args.committee)?; + clear_terminal(); print_generation_info( &circuit, preset, + committee_size, has_inputs_type, dkg_input_type.clone(), &args.output, @@ -267,7 +289,7 @@ fn main() -> Result<()> { run_with_spinner(|| { let circuit_name = circuit_meta.name(); - let committee = CiphernodesCommitteeSize::Small.values(); + let committee = committee_size.values(); let artifacts = match circuit_name { name if name == ::NAME => { let sample = PkCircuitData::generate_sample(preset)?; diff --git a/crates/zk-helpers/src/ciphernodes_committee.rs b/crates/zk-helpers/src/ciphernodes_committee.rs index 39bf50c5be..3e801407b2 100644 --- a/crates/zk-helpers/src/ciphernodes_committee.rs +++ b/crates/zk-helpers/src/ciphernodes_committee.rs @@ -35,7 +35,7 @@ impl CiphernodesCommitteeSize { /// Derives the committee size from threshold values (M, N). pub fn from_threshold(threshold_m: usize, threshold_n: usize) -> Self { match (threshold_m, threshold_n) { - (1, 2) => Self::Micro, + (1, 3) => Self::Micro, (2, 5) => Self::Small, _ => panic!( "Unknown committee size for threshold ({}, {})", @@ -48,8 +48,8 @@ impl CiphernodesCommitteeSize { pub fn values(self) -> CiphernodesCommittee { match self { CiphernodesCommitteeSize::Micro => CiphernodesCommittee { - n: 2, - h: 2, + n: 3, + h: 3, threshold: 1, }, CiphernodesCommitteeSize::Small => CiphernodesCommittee { diff --git a/examples/CRISP/Cargo.lock b/examples/CRISP/Cargo.lock index bbac5fd2f3..bf660e249c 100644 --- a/examples/CRISP/Cargo.lock +++ b/examples/CRISP/Cargo.lock @@ -2398,6 +2398,7 @@ dependencies = [ "futures 0.3.31", "futures-util", "once_cell", + "serde", "tokio", "tracing", ] diff --git a/examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockEnclave.sol b/examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockEnclave.sol index a9433e3998..abe10da5a7 100644 --- a/examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockEnclave.sol +++ b/examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockEnclave.sol @@ -21,7 +21,7 @@ contract MockEnclave { function request(address program) external { e3s[nextE3Id] = E3({ seed: 0, - threshold: [uint32(1), uint32(2)], + committeeSize: IEnclave.CommitteeSize.Micro, requestBlock: 0, inputWindow: [uint256(0), uint256(0)], encryptionSchemeId: bytes32(0), @@ -56,7 +56,7 @@ contract MockEnclave { return E3({ seed: 0, - threshold: [uint32(1), uint32(2)], + committeeSize: IEnclave.CommitteeSize.Micro, requestBlock: 0, inputWindow: [uint256(0), block.timestamp + 100], encryptionSchemeId: bytes32(0), diff --git a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json index 4977199db4..a60954fb1d 100644 --- a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json +++ b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json @@ -152,5 +152,151 @@ "address": "0xCF32Da38ac52BadbFb278bc070B738C37D09a1E0", "blockNumber": 10395680 } + }, + "localhost": { + "PoseidonT3": { + "blockNumber": 6, + "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" + }, + "MockUSDC": { + "constructorArgs": { + "initialSupply": "1000000" + }, + "blockNumber": 8, + "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" + }, + "EnclaveToken": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 10, + "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + }, + "EnclaveTicketToken": { + "constructorArgs": { + "baseToken": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "registry": "0x0000000000000000000000000000000000000001", + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 13, + "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" + }, + "SlashingManager": { + "constructorArgs": { + "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 15, + "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" + }, + "CiphernodeRegistryOwnable": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "submissionWindow": "10" + }, + "proxyRecords": { + "initData": "0xcd6dc687000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000a", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "proxyAdminAddress": "0x9bd03768a7DCc129555dE410FF8E85528A4F88b5", + "implementationAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F" + }, + "blockNumber": 16, + "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + }, + "BondingRegistry": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ticketToken": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", + "licenseToken": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", + "registry": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "slashedFundsTreasury": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ticketPrice": "10000000", + "licenseRequiredBond": "100000000000000000000", + "minTicketBalance": "1", + "exitDelay": "604800" + }, + "proxyRecords": { + "initData": "0x7333fa82000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000dc64a140aa3e981100a9beca4e685f962f0cf6c90000000000000000000000009fe46736679d2d9a65f0992f2272de9f3c7fa6e0000000000000000000000000a513e6e4b8f2a923d98304ec87f64353c4d5c853000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000009896800000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000093a80", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318", + "proxyAdminAddress": "0x8aCd85898458400f7Db866d53FCFF6f0D49741FF", + "implementationAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + }, + "blockNumber": 19, + "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + }, + "Enclave": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "registry": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "bondingRegistry": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318", + "e3RefundManager": "0x0000000000000000000000000000000000000001", + "feeToken": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "maxDuration": "2592000", + "timeoutConfig": "{\"committeeFormationWindow\":3600,\"dkgWindow\":7200,\"computeWindow\":86400,\"decryptionWindow\":3600}", + "params": [ + "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000ffffee0010000000000000000000000000000000000000000000000000000000ffffc400100000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000" + ] + }, + "proxyRecords": { + "initData": "0x01d12f1c000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000a513e6e4b8f2a923d98304ec87f64353c4d5c8530000000000000000000000008a791620dd6260079bf849dc5567adc3f2fdc3180000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f05120000000000000000000000000000000000000000000000000000000000278d000000000000000000000000000000000000000000000000000000000000001c2000000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000e100000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000ffffee0010000000000000000000000000000000000000000000000000000000ffffc400100000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + "proxyAdminAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", + "implementationAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" + }, + "blockNumber": 22, + "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" + }, + "E3RefundManager": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "enclave": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + "treasury": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "proxyRecords": { + "initData": "0xc0c53b8b000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000b7f8bc63bbcad18155201308c8f3540b07f84f5e000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82", + "proxyAdminAddress": "0x32467b43BFa67273FC7dDda0999Ee9A12F2AaA08", + "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" + }, + "blockNumber": 25, + "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" + }, + "MockComputeProvider": { + "blockNumber": 30, + "address": "0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E" + }, + "MockDecryptionVerifier": { + "blockNumber": 31, + "address": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" + }, + "MockE3Program": { + "blockNumber": 33, + "address": "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB" + }, + "MockRISC0Verifier": { + "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8", + "blockNumber": 36 + }, + "HonkVerifier": { + "address": "0xf5059a5D33d5853360D16C683c16e67980206f36", + "blockNumber": 37 + }, + "CRISPProgram": { + "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778", + "blockNumber": 37, + "constructorArgs": { + "enclave": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + "verifierAddress": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8", + "honkVerifierAddress": "0xf5059a5D33d5853360D16C683c16e67980206f36", + "imageId": "0x23734b77b0f76e85623a88d7a82f24c34c94834f2501964ea123b7a2027013a2" + } + }, + "MockVotingToken": { + "address": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49", + "blockNumber": 39 + } } } \ No newline at end of file diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index f842f0c5a2..3eb5946195 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -15,7 +15,7 @@ CRON_API_KEY=1234567890 # Based on Default Anvil Deployments (Only for testing) ENCLAVE_ADDRESS="0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" CIPHERNODE_REGISTRY_ADDRESS="0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" -E3_PROGRAM_ADDRESS="0x851356ae760d987E095750cCeb3bC6014560891C" +E3_PROGRAM_ADDRESS="0x95401dc811bb5740090279Ba06cfA8fcF6113778" FEE_TOKEN_ADDRESS="0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" # E3 Config @@ -23,9 +23,9 @@ FEE_TOKEN_ADDRESS="0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" # After this interval, the computation phase starts automatically # After activation + this interval, ciphernodes are then not responsing to # any more decryption requests -E3_DURATION=250 +E3_DURATION=300 # 0=Micro, 1=Small, 2=Medium, 3=Large -E3_COMMITTEE_SIZE=1 +E3_COMMITTEE_SIZE=0 # E3 Compute Provider Config E3_COMPUTE_PROVIDER_NAME="RISC0" diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index 2b032181b1..743c8c3ba4 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -288,7 +288,9 @@ contract Enclave is IEnclave, OwnableUpgradeable { E3RequestParams calldata requestParams ) external returns (uint256 e3Id, E3 memory e3) { // Resolve committee size to threshold values - uint32[2] memory threshold = committeeThresholds[requestParams.committeeSize]; + uint32[2] memory threshold = committeeThresholds[ + requestParams.committeeSize + ]; require( threshold[1] > 0, CommitteeSizeNotConfigured(requestParams.committeeSize) @@ -368,11 +370,7 @@ contract Enclave is IEnclave, OwnableUpgradeable { _e3FeeTokens[e3Id] = feeToken; require( - ciphernodeRegistry.requestCommittee( - e3Id, - seed, - threshold - ), + ciphernodeRegistry.requestCommittee(e3Id, seed, threshold), CommitteeSelectionFailed() ); diff --git a/packages/enclave-contracts/deployed_contracts.json b/packages/enclave-contracts/deployed_contracts.json index c2966c48e9..48454f75b3 100644 --- a/packages/enclave-contracts/deployed_contracts.json +++ b/packages/enclave-contracts/deployed_contracts.json @@ -130,5 +130,137 @@ "blockNumber": 10395629, "address": "0x6997eCF2926a94c439599f7275219Cb2BfBA300C" } + }, + "localhost": { + "PoseidonT3": { + "blockNumber": 3, + "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" + }, + "MockUSDC": { + "constructorArgs": { + "initialSupply": "1000000" + }, + "blockNumber": 4, + "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + }, + "EnclaveToken": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 5, + "address": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" + }, + "EnclaveTicketToken": { + "constructorArgs": { + "baseToken": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", + "registry": "0x0000000000000000000000000000000000000001", + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 7, + "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" + }, + "SlashingManager": { + "constructorArgs": { + "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 8, + "address": "0x0165878A594ca255338adfa4d48449f69242Eb8F" + }, + "CiphernodeRegistryOwnable": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "submissionWindow": "10" + }, + "proxyRecords": { + "initData": "0xcd6dc687000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000a", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", + "proxyAdminAddress": "0x94099942864EA81cCF197E9D71ac53310b1468D8", + "implementationAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + }, + "blockNumber": 9, + "address": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + }, + "BondingRegistry": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ticketToken": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707", + "licenseToken": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9", + "registry": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", + "slashedFundsTreasury": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ticketPrice": "10000000", + "licenseRequiredBond": "100000000000000000000", + "minTicketBalance": "1", + "exitDelay": "604800" + }, + "proxyRecords": { + "initData": "0x7333fa82000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000005fc8d32690cc91d4c39d9d3abcbd16989f875707000000000000000000000000cf7ed3acca5a467e9e704c703e8d87f634fb0fc90000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe6000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000009896800000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000093a80", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788", + "proxyAdminAddress": "0x6F1216D1BFe15c98520CA1434FC1d9D57AC95321", + "implementationAddress": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + }, + "blockNumber": 10, + "address": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" + }, + "Enclave": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "registry": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6", + "bondingRegistry": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788", + "e3RefundManager": "0x0000000000000000000000000000000000000001", + "feeToken": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", + "maxDuration": "2592000", + "timeoutConfig": "{\"committeeFormationWindow\":3600,\"dkgWindow\":7200,\"computeWindow\":86400,\"decryptionWindow\":3600}", + "params": [ + "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000ffffee0010000000000000000000000000000000000000000000000000000000ffffc400100000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000" + ] + }, + "proxyRecords": { + "initData": "0x01d12f1c000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe6000000000000000000000000610178da211fef7d417bc0e6fed39f05609ad78800000000000000000000000000000000000000000000000000000000000000010000000000000000000000009fe46736679d2d9a65f0992f2272de9f3c7fa6e00000000000000000000000000000000000000000000000000000000000278d000000000000000000000000000000000000000000000000000000000000001c2000000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000e100000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000ffffee0010000000000000000000000000000000000000000000000000000000ffffc400100000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", + "proxyAdminAddress": "0x1F708C24a0D3A740cD47cC0444E9480899f3dA7D", + "implementationAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" + }, + "blockNumber": 13, + "address": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" + }, + "E3RefundManager": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "enclave": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", + "treasury": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "proxyRecords": { + "initData": "0xc0c53b8b000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000a51c1fc2f0d1a1b8494ed1fe312d7c3a78ed91c0000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0x9A676e781A523b5d0C0e43731313A708CB607508", + "proxyAdminAddress": "0x8e80FFe6Dc044F4A766Afd6e5a8732Fe0977A493", + "implementationAddress": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" + }, + "blockNumber": 15, + "address": "0x9A676e781A523b5d0C0e43731313A708CB607508" + }, + "MockComputeProvider": { + "blockNumber": 33, + "address": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" + }, + "MockDecryptionVerifier": { + "blockNumber": 34, + "address": "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB" + }, + "MockE3Program": { + "blockNumber": 35, + "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" + }, + "ZKTranscriptLib": { + "blockNumber": 38, + "address": "0x851356ae760d987E095750cCeb3bC6014560891C" + }, + "DkgPkVerifier": { + "blockNumber": 39, + "address": "0xf5059a5D33d5853360D16C683c16e67980206f36" + } } } \ No newline at end of file diff --git a/packages/enclave-contracts/scripts/deployEnclave.ts b/packages/enclave-contracts/scripts/deployEnclave.ts index a3398e7898..5ed168b295 100644 --- a/packages/enclave-contracts/scripts/deployEnclave.ts +++ b/packages/enclave-contracts/scripts/deployEnclave.ts @@ -211,11 +211,11 @@ export const deployEnclave = async (withMocks?: boolean) => { // Initialize committee size thresholds [threshold, total] console.log("Setting committee thresholds..."); - // Micro: threshold=1, total=2 - await enclave.setCommitteeThresholds(0, [1, 2]); + // Micro: threshold=1, total=3 + await enclave.setCommitteeThresholds(0, [1, 3]); // Small: threshold=2, total=5 await enclave.setCommitteeThresholds(1, [2, 5]); - console.log("Committee thresholds set (Micro=[1,2], Small=[2,5])"); + console.log("Committee thresholds set (Micro=[1,3], Small=[2,5])"); if (shouldDeployMocks) { const { decryptionVerifierAddress, e3ProgramAddress } = await deployMocks(); diff --git a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts index 06f5c98cae..32ff6ba7b0 100644 --- a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts +++ b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts @@ -314,6 +314,9 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await decryptionVerifier.getAddress(), ); + // Set up committee thresholds + await enclave.setCommitteeThresholds(0, [1, 3]); // Micro + await bondingRegistry.setRewardDistributor(enclaveAddress); await bondingRegistry.setSlashingManager( await slashingManager.getAddress(), @@ -352,12 +355,12 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { // ── Helpers ──────────────────────────────────────────────────────────────── const makeRequest = async ( signer: Signer = requester, - threshold: [number, number] = [2, 2], + committeeSize: number = 0, ): Promise<{ e3Id: number }> => { const startTime = (await time.latest()) + 100; const requestParams = { - threshold, + committeeSize, inputWindow: [startTime + 100, startTime + ONE_DAY] as [number, number], e3Program: await e3Program.getAddress(), e3ProgramParams: encodedE3ProgramParams, @@ -1268,7 +1271,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const makeRequestN = async (n: number) => { const startTime = (await time.latest()) + 100; const requestParams = { - threshold: [2, 2] as [number, number], + committeeSize: 0, inputWindow: [startTime, startTime + ONE_DAY] as [number, number], e3Program: await e3Program.getAddress(), e3ProgramParams: encodedE3ProgramParams, @@ -1348,7 +1351,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { for (let i = 0; i < 2; i++) { const startTime = (await time.latest()) + 100; const requestParams = { - threshold: [2, 2] as [number, number], + committeeSize: 0, inputWindow: [startTime, startTime + ONE_DAY] as [number, number], e3Program: await e3Program.getAddress(), e3ProgramParams: encodedE3ProgramParams, diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 7305607fb7..55a9479c28 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -301,8 +301,8 @@ describe("Enclave", function () { await usdcToken.mint(await notTheOwner.getAddress(), mintAmount); // ── Committee Thresholds ────────────────────────────────────────────────── - // CommitteeSize.Micro = 0 → [1, 2] - await enclave.setCommitteeThresholds(0, [1, 2]); + // CommitteeSize.Micro = 0 → [1, 3] + await enclave.setCommitteeThresholds(0, [1, 3]); // CommitteeSize.Small = 1 → [2, 5] await enclave.setCommitteeThresholds(1, [2, 5]); diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts index fa1508a7a0..97c3650dfd 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -229,6 +229,9 @@ describe("CiphernodeRegistryOwnable", function () { await mockDecryptionVerifier.getAddress(), ); + // Set up committee thresholds + await enclave.setCommitteeThresholds(0, [1, 3]); // Micro + // ── Operators ────────────────────────────────────────────────────────────── await licenseToken.setTransferRestriction(false); @@ -260,7 +263,7 @@ describe("CiphernodeRegistryOwnable", function () { mockDecryptionVerifier, request: { e3Id: 0, - threshold: [2, 2] as [number, number], + committeeSize: 0, }, }; } @@ -284,7 +287,7 @@ describe("CiphernodeRegistryOwnable", function () { const currentTime = await networkHelpers.time.latest(); const requestParams = { - threshold: [2, 2] as [number, number], + committeeSize: 0, inputWindow: [currentTime + 100, currentTime + 300] as [number, number], e3Program: await mockE3Program.getAddress(), e3ProgramParams: encodedE3ProgramParams, diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index 82f063927f..c94646ffd8 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -317,6 +317,11 @@ describe("Committee Expulsion & Fault Tolerance", function () { ); await enclave.setSlashingManager(await slashingManager.getAddress()); + // Set up committee thresholds + await enclave.setCommitteeThresholds(0, [1, 3]); // Micro + await enclave.setCommitteeThresholds(1, [2, 3]); // Small + await enclave.setCommitteeThresholds(2, [2, 4]); // Medium + await bondingRegistry.setRewardDistributor(enclaveAddress); await bondingRegistry.setSlashingManager( await slashingManager.getAddress(), @@ -379,10 +384,10 @@ describe("Committee Expulsion & Fault Tolerance", function () { await bondingRegistry.connect(operator).addTicketBalance(ticketAmount); } - async function makeRequest(threshold: [number, number] = [2, 3]) { + async function makeRequest(committeeSize: number = 1) { const startTime = (await time.latest()) + 100; const requestParams = { - threshold, + committeeSize, inputWindow: [startTime + 100, startTime + ONE_DAY] as [number, number], e3Program: await e3Program.getAddress(), e3ProgramParams: encodedE3ProgramParams, @@ -457,8 +462,8 @@ describe("Committee Expulsion & Fault Tolerance", function () { await setupOperator(operator2); await setupOperator(operator3); - // threshold [2, 3] means M=2, N=3 - await makeRequest([2, 3]); + // Small (committeeSize=1) means M=2, N=3 + await makeRequest(1); await finalizeCommitteeWithOperators(0, [ operator1, operator2, @@ -511,7 +516,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await setupOperator(operator2); await setupOperator(operator3); - await makeRequest([2, 3]); // M=2, N=3 + await makeRequest(1); // Small: M=2, N=3 await finalizeCommitteeWithOperators(0, [ operator1, operator2, @@ -579,7 +584,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { const SLASHER_ROLE = await slashingManager.SLASHER_ROLE(); await slashingManager.grantRole(SLASHER_ROLE, await owner.getAddress()); - await makeRequest([2, 3]); // M=2, N=3 + await makeRequest(1); // Small: M=2, N=3 await finalizeCommitteeWithOperators(0, [ operator1, operator2, @@ -648,7 +653,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await setupOperator(operator2); await setupOperator(operator3); - await makeRequest([2, 3]); + await makeRequest(1); await finalizeCommitteeWithOperators(0, [ operator1, operator2, @@ -708,7 +713,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await setupOperator(operator2); await setupOperator(operator3); - await makeRequest([2, 3]); + await makeRequest(1); await finalizeCommitteeWithOperators(0, [ operator1, operator2, @@ -761,7 +766,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await setupOperator(operator3); await setupOperator(operator4); - await makeRequest([2, 4]); // M=2, N=4 + await makeRequest(2); // Medium: M=2, N=4 await finalizeCommitteeWithOperators(0, [ operator1, operator2, @@ -843,7 +848,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { const SLASHER_ROLE = await slashingManager.SLASHER_ROLE(); await slashingManager.grantRole(SLASHER_ROLE, await owner.getAddress()); - await makeRequest([2, 2]); // M=2, N=2 — no room for error + await makeRequest(0); // Micro: M=1, N=3 — no room for error await finalizeCommitteeWithOperators(0, [operator1, operator2]); // Lane A cannot slash at M active when the accused is excluded from voting. @@ -891,7 +896,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await setupOperator(operator3); await setupOperator(operator4); - await makeRequest([2, 4]); // M=2, N=4 + await makeRequest(2); // Medium: M=2, N=4 await finalizeCommitteeWithOperators(0, [ operator1, operator2, @@ -978,7 +983,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await setupOperator(operator2); await setupOperator(operator3); - await makeRequest([2, 3]); + await makeRequest(1); await finalizeCommitteeWithOperators(0, [ operator1, operator2, diff --git a/templates/default/deployed_contracts.json b/templates/default/deployed_contracts.json index 1069806d9f..59e74926eb 100644 --- a/templates/default/deployed_contracts.json +++ b/templates/default/deployed_contracts.json @@ -1,41 +1,21 @@ { - "sepolia": { - "Enclave": { - "constructorArgs": { - "params": "0x000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000fc00100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000003fffffff000001", - "owner": "0x4f1f3a157073A35515C4fC4A8af2F1Af088f0676", - "maxDuration": "2592000", - "registry": "0x0000000000000000000000000000000000000001" - }, - "blockNumber": 9181738, - "address": "0xD43c70A343E631475d8470d0403e0c4b43c76927" - }, - "CiphernodeRegistry": { - "constructorArgs": { - "enclaveAddress": "0xD43c70A343E631475d8470d0403e0c4b43c76927", - "owner": "0x4f1f3a157073A35515C4fC4A8af2F1Af088f0676" - }, - "blockNumber": 9181748, - "address": "0x5ABDfCbA0366ABF2893D3f2465F4C97908488A6d" - } - }, "localhost": { "PoseidonT3": { - "blockNumber": 14, + "blockNumber": 10, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { "constructorArgs": { "initialSupply": "1000000" }, - "blockNumber": 15, + "blockNumber": 11, "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" }, "EnclaveToken": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 16, + "blockNumber": 12, "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" }, "EnclaveTicketToken": { @@ -44,14 +24,14 @@ "registry": "0x0000000000000000000000000000000000000001", "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 18, + "blockNumber": 14, "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" }, "SlashingManager": { "constructorArgs": { "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 19, + "blockNumber": 15, "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" }, "CiphernodeRegistryOwnable": { @@ -66,7 +46,7 @@ "proxyAdminAddress": "0x9bd03768a7DCc129555dE410FF8E85528A4F88b5", "implementationAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F" }, - "blockNumber": 20, + "blockNumber": 16, "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" }, "BondingRegistry": { @@ -88,7 +68,7 @@ "proxyAdminAddress": "0x8aCd85898458400f7Db866d53FCFF6f0D49741FF", "implementationAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" }, - "blockNumber": 21, + "blockNumber": 17, "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" }, "Enclave": { @@ -111,7 +91,7 @@ "proxyAdminAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", "implementationAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" }, - "blockNumber": 24, + "blockNumber": 20, "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "E3RefundManager": { @@ -127,28 +107,28 @@ "proxyAdminAddress": "0x32467b43BFa67273FC7dDda0999Ee9A12F2AaA08", "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" }, - "blockNumber": 26, + "blockNumber": 22, "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" }, "MockComputeProvider": { - "blockNumber": 30, - "address": "0xc5a5C42992dECbae36851359345FE25997F5C42d" + "blockNumber": 25, + "address": "0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E" }, "MockDecryptionVerifier": { - "blockNumber": 31, - "address": "0x67d269191c92Caf3cD7723F116c85e6E9bf55933" + "blockNumber": 26, + "address": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" }, "MockE3Program": { - "blockNumber": 32, - "address": "0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E" + "blockNumber": 27, + "address": "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB" }, "ImageID": { - "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9", - "blockNumber": 36 + "address": "0x851356ae760d987E095750cCeb3bC6014560891C", + "blockNumber": 31 }, "MyProgram": { - "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8", - "blockNumber": 38 + "address": "0xf5059a5D33d5853360D16C683c16e67980206f36", + "blockNumber": 33 } } } \ No newline at end of file From 397ac5d01f8086e01b19b6a4022c392fc45085a3 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 13 Mar 2026 08:28:20 +0000 Subject: [PATCH 4/6] chore: tests and linting --- circuits/lib/src/configs/insecure/dkg.nr | 10 +- circuits/lib/src/configs/secure/dkg.nr | 20 +-- .../IBondingRegistry.json | 2 +- .../ICiphernodeRegistry.json | 2 +- .../interfaces/IEnclave.sol/IEnclave.json | 93 ++++++++---- .../ISlashingManager.json | 2 +- .../test/E3Lifecycle/E3Integration.spec.ts | 75 +++++++++- .../enclave-contracts/test/Enclave.spec.ts | 133 ++++++++++++------ .../CiphernodeRegistryOwnable.spec.ts | 51 +++++-- .../test/Slashing/CommitteeExpulsion.spec.ts | 27 +++- 10 files changed, 292 insertions(+), 123 deletions(-) diff --git a/circuits/lib/src/configs/insecure/dkg.nr b/circuits/lib/src/configs/insecure/dkg.nr index 6198b7cce5..cb34cdaade 100644 --- a/circuits/lib/src/configs/insecure/dkg.nr +++ b/circuits/lib/src/configs/insecure/dkg.nr @@ -27,14 +27,8 @@ pk (CIRCUIT 0) pub global PK_BIT_PK: u32 = 50; pub global PARITY_MATRIX: [[[Field; N_PARTIES + 1]; N_PARTIES - T]; L_THRESHOLD] = [ - [ - [1, 68719403007, 1, 0], - [2, 68719403006, 0, 1], - ], - [ - [1, 68719230975, 1, 0], - [2, 68719230974, 0, 1], - ], + [[1, 68719403007, 1, 0], [2, 68719403006, 0, 1]], + [[1, 68719230975, 1, 0], [2, 68719230974, 0, 1]], ]; /************************************ diff --git a/circuits/lib/src/configs/secure/dkg.nr b/circuits/lib/src/configs/secure/dkg.nr index de39431bee..216418a4d6 100644 --- a/circuits/lib/src/configs/secure/dkg.nr +++ b/circuits/lib/src/configs/secure/dkg.nr @@ -28,22 +28,10 @@ pk (CIRCUIT 0) pub global PK_BIT_PK: u32 = 56; pub global PARITY_MATRIX: [[[Field; N_PARTIES + 1]; N_PARTIES - T]; L_THRESHOLD] = [ - [ - [1, 2251799822204927, 1, 0], - [2, 2251799822204926, 0, 1], - ], - [ - [1, 4503599627763711, 1, 0], - [2, 4503599627763710, 0, 1], - ], - [ - [1, 4503599631433727, 1, 0], - [2, 4503599631433726, 0, 1], - ], - [ - [1, 4503599634579455, 1, 0], - [2, 4503599634579454, 0, 1], - ], + [[1, 2251799822204927, 1, 0], [2, 2251799822204926, 0, 1]], + [[1, 4503599627763711, 1, 0], [2, 4503599627763710, 0, 1]], + [[1, 4503599631433727, 1, 0], [2, 4503599631433726, 0, 1]], + [[1, 4503599634579455, 1, 0], [2, 4503599634579454, 0, 1]], ]; /************************************ ------------------------------------- diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index c3e3f47993..7f9de47db8 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -940,5 +940,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-b29e2e20df477e5829743192d3e53aa86d40cfe2" + "buildInfoId": "solc-0_8_28-dbd94fb7bf5c147a4effe4d89ec8d62bae78112f" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index 50ecc0c4f9..6407b1521d 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -782,5 +782,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-b29e2e20df477e5829743192d3e53aa86d40cfe2" + "buildInfoId": "solc-0_8_28-dbd94fb7bf5c147a4effe4d89ec8d62bae78112f" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index 7cd80998ad..5c5263a687 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -87,6 +87,25 @@ "name": "CommitteeFormed", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "enum IEnclave.CommitteeSize", + "name": "size", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint32[2]", + "name": "threshold", + "type": "uint32[2]" + } + ], + "name": "CommitteeThresholdsUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -206,9 +225,9 @@ "type": "uint256" }, { - "internalType": "uint32[2]", - "name": "threshold", - "type": "uint32[2]" + "internalType": "enum IEnclave.CommitteeSize", + "name": "committeeSize", + "type": "uint8" }, { "internalType": "uint256", @@ -675,9 +694,9 @@ "type": "uint256" }, { - "internalType": "uint32[2]", - "name": "threshold", - "type": "uint32[2]" + "internalType": "enum IEnclave.CommitteeSize", + "name": "committeeSize", + "type": "uint8" }, { "internalType": "uint256", @@ -748,9 +767,9 @@ { "components": [ { - "internalType": "uint32[2]", - "name": "threshold", - "type": "uint32[2]" + "internalType": "enum IEnclave.CommitteeSize", + "name": "committeeSize", + "type": "uint8" }, { "internalType": "uint256[2]", @@ -908,7 +927,7 @@ "type": "uint256" } ], - "name": "processE3Failure", + "name": "onCommitteeFinalized", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -919,9 +938,14 @@ "internalType": "uint256", "name": "e3Id", "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "committeePublicKeyHash", + "type": "bytes32" } ], - "name": "onCommitteeFinalized", + "name": "onCommitteePublished", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -934,12 +958,12 @@ "type": "uint256" }, { - "internalType": "bytes32", - "name": "committeePublicKeyHash", - "type": "bytes32" + "internalType": "uint8", + "name": "reason", + "type": "uint8" } ], - "name": "onCommitteePublished", + "name": "onE3Failed", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -950,14 +974,9 @@ "internalType": "uint256", "name": "e3Id", "type": "uint256" - }, - { - "internalType": "uint8", - "name": "reason", - "type": "uint8" } ], - "name": "onE3Failed", + "name": "processE3Failure", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1038,9 +1057,9 @@ { "components": [ { - "internalType": "uint32[2]", - "name": "threshold", - "type": "uint32[2]" + "internalType": "enum IEnclave.CommitteeSize", + "name": "committeeSize", + "type": "uint8" }, { "internalType": "uint256[2]", @@ -1088,9 +1107,9 @@ "type": "uint256" }, { - "internalType": "uint32[2]", - "name": "threshold", - "type": "uint32[2]" + "internalType": "enum IEnclave.CommitteeSize", + "name": "committeeSize", + "type": "uint8" }, { "internalType": "uint256", @@ -1182,6 +1201,24 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "enum IEnclave.CommitteeSize", + "name": "size", + "type": "uint8" + }, + { + "internalType": "uint32[2]", + "name": "threshold", + "type": "uint32[2]" + } + ], + "name": "setCommitteeThresholds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1276,5 +1313,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-aabfcbf9eb9905bf16af29953ad33064524f63a7" + "buildInfoId": "solc-0_8_28-dbd94fb7bf5c147a4effe4d89ec8d62bae78112f" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index 738e479e5f..48541a975b 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -954,5 +954,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-13a9a691eee81d28d467846ed639e157d0edfd2b" + "buildInfoId": "solc-0_8_28-dbd94fb7bf5c147a4effe4d89ec8d62bae78112f" } \ No newline at end of file diff --git a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts index 32ff6ba7b0..cbbf2aeaef 100644 --- a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts +++ b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts @@ -440,11 +440,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { requester, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); await makeRequest(); @@ -466,11 +468,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { makeRequest, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); // Make a request first await makeRequest(); @@ -482,6 +486,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { // Submit tickets for sortition await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); + await registry.connect(operator3).submitTicket(0, 1); // Fast forward past submission window await time.increase(SORTITION_SUBMISSION_WINDOW + 1); @@ -493,6 +498,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const nodes = [ await operator1.getAddress(), await operator2.getAddress(), + await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const publicKeyHash = ethers.keccak256(publicKey); @@ -515,11 +521,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { makeRequest, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); // Make a request await makeRequest(); @@ -527,6 +535,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { // Complete sortition process await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); + await registry.connect(operator3).submitTicket(0, 1); await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); @@ -534,6 +543,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const nodes = [ await operator1.getAddress(), await operator2.getAddress(), + await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const publicKeyHash = ethers.keccak256(publicKey); @@ -554,11 +564,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { makeRequest, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); await makeRequest(); @@ -587,11 +599,18 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { }); it("reverts if E3 not in failed state", async function () { - const { enclave, makeRequest, operator1, operator2, setupOperator } = - await loadFixture(setup); + const { + enclave, + makeRequest, + operator1, + operator2, + operator3, + setupOperator, + } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); await makeRequest(); @@ -608,11 +627,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { makeRequest, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); await makeRequest(); @@ -645,11 +666,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { usdcToken, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); await makeRequest(); @@ -673,11 +696,18 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { }); it("reverts if trying to process failure twice", async function () { - const { enclave, makeRequest, operator1, operator2, setupOperator } = - await loadFixture(setup); + const { + enclave, + makeRequest, + operator1, + operator2, + operator3, + setupOperator, + } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); await makeRequest(); @@ -699,11 +729,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { requester, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); await makeRequest(); @@ -727,11 +759,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { requester, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); await makeRequest(); @@ -765,7 +799,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await setupOperator(operator3); // 1. Request E3, form committee, publish key - await makeRequest(requester, [2, 3]); + await makeRequest(requester, 0); await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); await registry.connect(operator3).submitTicket(0, 1); @@ -870,7 +904,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await setupOperator(operator3); // 1. Request E3, form committee, publish key - await makeRequest(undefined, [2, 3]); + await makeRequest(undefined, 0); await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); await registry.connect(operator3).submitTicket(0, 1); @@ -938,11 +972,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { owner, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); await makeRequest(); @@ -987,11 +1023,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { owner, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); await makeRequest(); @@ -1038,11 +1076,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { requester, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); // 1. Make request await makeRequest(); @@ -1052,6 +1092,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { // 2. Complete sortition (committee finalized, DKG starts) await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); + await registry.connect(operator3).submitTicket(0, 1); await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); @@ -1102,11 +1143,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { requester, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); // 1. Make request await makeRequest(); @@ -1116,12 +1159,14 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { // 2. Complete sortition and DKG await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); + await registry.connect(operator3).submitTicket(0, 1); await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); const nodes = [ await operator1.getAddress(), await operator2.getAddress(), + await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const publicKeyHash = ethers.keccak256(publicKey); @@ -1177,11 +1222,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { requester, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); // 1. Make request await makeRequest(); @@ -1191,12 +1238,14 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { // 2. Complete sortition and DKG await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); + await registry.connect(operator3).submitTicket(0, 1); await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); const nodes = [ await operator1.getAddress(), await operator2.getAddress(), + await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const publicKeyHash = ethers.keccak256(publicKey); @@ -1259,11 +1308,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { decryptionVerifier, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); const enclaveAddress = await enclave.getAddress(); @@ -1339,11 +1390,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { decryptionVerifier, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); const enclaveAddress = await enclave.getAddress(); @@ -1424,7 +1477,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await setupOperator(operator3); // 1. Request E3, form committee, publish key - await makeRequest(undefined, [2, 3]); + await makeRequest(undefined, 0); await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); await registry.connect(operator3).submitTicket(0, 1); @@ -1541,11 +1594,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { makeRequest, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); // 1. Make request await makeRequest(); @@ -1554,6 +1609,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { // 2. Complete sortition and publish committee (CommitteeFinalized -> KeyPublished) await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); + await registry.connect(operator3).submitTicket(0, 1); await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); @@ -1562,6 +1618,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const nodes = [ await operator1.getAddress(), await operator2.getAddress(), + await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const publicKeyHash = ethers.keccak256(publicKey); @@ -1599,11 +1656,13 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { requester, operator1, operator2, + operator3, setupOperator, } = await loadFixture(setup); await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); // Complete full E3 flow await makeRequest(); @@ -1611,12 +1670,14 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { // Complete sortition await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); + await registry.connect(operator3).submitTicket(0, 1); await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); const nodes = [ await operator1.getAddress(), await operator2.getAddress(), + await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const publicKeyHash = ethers.keccak256(publicKey); diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 55a9479c28..035f4e0154 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -72,11 +72,11 @@ describe("Enclave", function () { e3Id: number, nodes: string[], publicKey: string, - operator1: Signer, - operator2: Signer, + operators: Signer[], ): Promise => { - await registry.connect(operator1).submitTicket(e3Id, 1); - await registry.connect(operator2).submitTicket(e3Id, 1); + for (const operator of operators) { + await registry.connect(operator).submitTicket(e3Id, 1); + } await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(e3Id); const publicKeyHash = ethers.id(publicKey); @@ -134,7 +134,7 @@ describe("Enclave", function () { const setup = async () => { // ── Signers ────────────────────────────────────────────────────────────── - const [owner, notTheOwner, operator1, operator2] = + const [owner, notTheOwner, operator1, operator2, operator3] = await ethers.getSigners(); const ownerAddress = await owner.getAddress(); @@ -283,7 +283,7 @@ describe("Enclave", function () { // ── Operators ───────────────────────────────────────────────────────────── await licenseToken.setTransferRestriction(false); - for (const operator of [operator1, operator2]) { + for (const operator of [operator1, operator2, operator3]) { await setupOperatorForSortition( operator, bondingRegistry, @@ -329,6 +329,7 @@ describe("Enclave", function () { notTheOwner, operator1, operator2, + operator3, enclave, ciphernodeRegistryContract, bondingRegistry, @@ -839,6 +840,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -854,10 +856,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await mine(2, { interval: inputWindowDuration }); @@ -874,6 +879,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -885,10 +891,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await mine(2, { interval: inputWindowDuration + timeoutConfig.computeWindow, @@ -905,6 +914,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -920,10 +930,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await mine(2, { interval: inputWindowDuration }); await expect( @@ -938,6 +951,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -949,10 +963,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await mine(2, { interval: inputWindowDuration }); expect(await enclave.publishCiphertextOutput(e3Id, data, proof)); @@ -967,6 +984,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -978,10 +996,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await mine(2, { interval: inputWindowDuration }); expect( @@ -996,6 +1017,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -1007,10 +1029,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await mine(2, { interval: inputWindowDuration }); await expect(enclave.publishCiphertextOutput(e3Id, data, proof)) @@ -1037,6 +1062,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -1048,10 +1074,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await expect( enclave.publishPlaintextOutput(e3Id, data, "0x"), @@ -1065,6 +1094,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -1076,10 +1106,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); @@ -1096,6 +1129,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -1107,10 +1141,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); @@ -1126,6 +1163,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -1137,10 +1175,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); @@ -1157,6 +1198,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -1168,10 +1210,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); @@ -1187,6 +1232,7 @@ describe("Enclave", function () { ciphernodeRegistryContract, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; @@ -1198,10 +1244,13 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, - operator1, - operator2, + [operator1, operator2, operator3], ); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts index 97c3650dfd..b3a40f8e4f 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -77,7 +77,7 @@ describe("CiphernodeRegistryOwnable", function () { } async function setup() { // ── Signers ──────────────────────────────────────────────────────────────── - const [owner, notTheOwner, operator1, operator2] = + const [owner, notTheOwner, operator1, operator2, operator3] = await ethers.getSigners(); const ownerAddress = await owner.getAddress(); @@ -235,7 +235,7 @@ describe("CiphernodeRegistryOwnable", function () { // ── Operators ────────────────────────────────────────────────────────────── await licenseToken.setTransferRestriction(false); - for (const operator of [operator1, operator2]) { + for (const operator of [operator1, operator2, operator3]) { await setupOperatorForSortition( operator, bondingRegistry, @@ -253,6 +253,7 @@ describe("CiphernodeRegistryOwnable", function () { notTheOwner, operator1, operator2, + operator3, registry, enclave, bondingRegistry, @@ -441,6 +442,7 @@ describe("CiphernodeRegistryOwnable", function () { notTheOwner, operator1, operator2, + operator3, } = await loadFixture(setup); await makeRequest( enclave, @@ -451,6 +453,7 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); + await registry.connect(operator3).submitTicket(0, 1); await finalizeCommitteeAfterWindow(registry, 0); await expect( @@ -458,7 +461,11 @@ describe("CiphernodeRegistryOwnable", function () { .connect(notTheOwner) .publishCommittee( 0, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, dataHash, ), @@ -473,6 +480,7 @@ describe("CiphernodeRegistryOwnable", function () { mockDecryptionVerifier, operator1, operator2, + operator3, } = await loadFixture(setup); await makeRequest( enclave, @@ -481,15 +489,18 @@ describe("CiphernodeRegistryOwnable", function () { mockDecryptionVerifier, ); - await networkHelpers.mine(1); - await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); + await registry.connect(operator3).submitTicket(0, 1); await finalizeCommitteeAfterWindow(registry, 0); await registry.publishCommittee( 0, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, dataHash, ); @@ -504,6 +515,7 @@ describe("CiphernodeRegistryOwnable", function () { mockDecryptionVerifier, operator1, operator2, + operator3, } = await loadFixture(setup); await makeRequest( enclave, @@ -512,15 +524,20 @@ describe("CiphernodeRegistryOwnable", function () { mockDecryptionVerifier, ); - // Submit tickets from both operators and finalize + // Submit tickets from all operators and finalize await registry.connect(operator1).submitTicket(0, 1); await registry.connect(operator2).submitTicket(0, 1); + await registry.connect(operator3).submitTicket(0, 1); await finalizeCommitteeAfterWindow(registry, 0); await expect( await registry.publishCommittee( 0, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, dataHash, ), @@ -528,7 +545,11 @@ describe("CiphernodeRegistryOwnable", function () { .to.emit(registry, "CommitteePublished") .withArgs( 0, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, ); }); @@ -636,6 +657,7 @@ describe("CiphernodeRegistryOwnable", function () { mockDecryptionVerifier, operator1, operator2, + operator3, } = await loadFixture(setup); const e3Id = 0; await makeRequest( @@ -647,11 +669,16 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator1).submitTicket(e3Id, 1); await registry.connect(operator2).submitTicket(e3Id, 1); + await registry.connect(operator3).submitTicket(e3Id, 1); await finalizeCommitteeAfterWindow(registry, e3Id); await registry.publishCommittee( e3Id, - [await operator1.getAddress(), await operator2.getAddress()], + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], data, dataHash, ); @@ -731,8 +758,8 @@ describe("CiphernodeRegistryOwnable", function () { describe("treeSize()", function () { it("returns the size of the ciphernode registry merkle tree", async function () { const { registry } = await loadFixture(setup); - // Two operators registered in setup - expect(await registry.treeSize()).to.equal(2); + // Three operators registered in setup + expect(await registry.treeSize()).to.equal(3); }); }); }); diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index c94646ffd8..554eb556b6 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -822,6 +822,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { owner, operator1, operator2, + operator3, setupOperator, makeRequest, finalizeCommitteeWithOperators, @@ -829,6 +830,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await setupOperator(operator1); await setupOperator(operator2); + await setupOperator(operator3); // Lane B evidence-based policy with no appeal window const REASON_EVIDENCE = ethers.keccak256( @@ -848,17 +850,27 @@ describe("Committee Expulsion & Fault Tolerance", function () { const SLASHER_ROLE = await slashingManager.SLASHER_ROLE(); await slashingManager.grantRole(SLASHER_ROLE, await owner.getAddress()); - await makeRequest(0); // Micro: M=1, N=3 — no room for error - await finalizeCommitteeWithOperators(0, [operator1, operator2]); + await makeRequest(1); // Small: M=2, N=3 + await finalizeCommitteeWithOperators(0, [operator1, operator2, operator3]); - // Lane A cannot slash at M active when the accused is excluded from voting. - // With M=2, N=2: expelling 1 member needs M=2 non-accused votes, but only - // 1 non-accused active voter exists. Lane B (SLASHER_ROLE) is required. - // TODO: See GitHub issue — "Lane B governance flow for M-threshold slashing" + // Step 1: Lane A slash op1 — still viable (3→2 active, >= M=2) + const laneAProof = await signAndEncodeAttestation( + [operator2, operator3], + 0, + await operator1.getAddress(), + ); + await slashingManager.proposeSlash( + 0, + await operator1.getAddress(), + laneAProof, + ); + + // Step 2: Lane A cannot slash op2 (only op3 can vote, 1 < M=2). + // Lane B (SLASHER_ROLE) is required for the final expulsion. const nextProposalId = await slashingManager.totalProposals(); await slashingManager.proposeSlashEvidence( 0, - await operator1.getAddress(), + await operator2.getAddress(), REASON_EVIDENCE, ethers.toUtf8Bytes("evidence-data"), ); @@ -870,6 +882,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await expect(tx).to.emit(enclave, "E3Failed"); // Should emit CommitteeViabilityUpdated(viable=false) + // activeCount drops to 1, which is < M=2 await expect(tx) .to.emit(registry, "CommitteeViabilityUpdated") .withArgs(0, 1, 2, false); From ccb5585120094916fde9fd2c4de30b36d85bd8b7 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 13 Mar 2026 09:47:30 +0000 Subject: [PATCH 5/6] chore: pr comments --- crates/evm/src/enclave_sol_reader.rs | 37 +++++++++---------- crates/keyshare/src/threshold_keyshare.rs | 2 +- crates/tests/tests/integration.rs | 10 ++--- crates/zk-helpers/src/bin/zk_cli.rs | 4 +- .../zk-helpers/src/ciphernodes_committee.rs | 14 ++++--- .../src/circuits/recursive_aggregation/mod.rs | 4 +- crates/zk-prover/tests/local_e2e_tests.rs | 16 ++++---- examples/CRISP/server/.env.example | 2 +- examples/CRISP/server/src/cli/commands.rs | 4 +- .../DkgPkVerifier.sol/DkgPkVerifier.json | 10 ++--- .../DkgPkVerifier.sol/ZKTranscriptLib.json | 2 +- .../contracts/verifier/DkgPkVerifier.sol | 2 +- .../scripts/deployEnclave.ts | 1 + packages/enclave-contracts/tasks/enclave.ts | 8 +++- .../test/Slashing/CommitteeExpulsion.spec.ts | 6 ++- .../src/pages/steps/RequestComputation.tsx | 11 +++--- templates/default/deployed_contracts.json | 28 +++++++------- templates/default/enclave.config.yaml | 12 +++--- 18 files changed, 94 insertions(+), 79 deletions(-) diff --git a/crates/evm/src/enclave_sol_reader.rs b/crates/evm/src/enclave_sol_reader.rs index 8505ddb824..5b5d448603 100644 --- a/crates/evm/src/enclave_sol_reader.rs +++ b/crates/evm/src/enclave_sol_reader.rs @@ -27,15 +27,17 @@ sol!( struct E3RequestedWithChainId(pub IEnclave::E3Requested, pub u64); -impl From for e3_events::E3Requested { - fn from(value: E3RequestedWithChainId) -> Self { - let params_bytes = value.0.e3.e3ProgramParams.to_vec(); +impl E3RequestedWithChainId { + fn try_into_e3_requested(self) -> anyhow::Result { + let params_bytes = self.0.e3.e3ProgramParams.to_vec(); // Derive threshold values from committee size enum - let committee_size = match value.0.e3.committeeSize { + let committee_size = match self.0.e3.committeeSize { 0 => CiphernodesCommitteeSize::Micro, 1 => CiphernodesCommitteeSize::Small, - _ => panic!("Unsupported committee size: {}", value.0.e3.committeeSize), + 2 => CiphernodesCommitteeSize::Medium, + 3 => CiphernodesCommitteeSize::Large, + other => anyhow::bail!("Unsupported committee size: {}", other), }; let committee = committee_size.values(); let threshold_m = committee.threshold; @@ -76,22 +78,15 @@ impl From for e3_events::E3Requested { } }; - e3_events::E3Requested { + Ok(e3_events::E3Requested { params: ArcBytes::from_bytes(¶ms_bytes), threshold_m, threshold_n, - seed: value.0.e3.seed.into(), + seed: self.0.e3.seed.into(), error_size, esi_per_ct, - e3_id: E3id::new(value.0.e3Id.to_string(), value.1), - } - } -} - -impl From for EnclaveEventData { - fn from(value: E3RequestedWithChainId) -> Self { - let payload: e3_events::E3Requested = value.into(); - payload.into() + e3_id: E3id::new(self.0.e3Id.to_string(), self.1), + }) } } @@ -193,9 +188,13 @@ pub fn extractor(data: &LogData, topic: Option<&B256>, chain_id: u64) -> Option< error!("Error parsing event E3Requested after topic matched!"); return None; }; - Some(EnclaveEventData::from(E3RequestedWithChainId( - event, chain_id, - ))) + match E3RequestedWithChainId(event, chain_id).try_into_e3_requested() { + Ok(payload) => Some(payload.into()), + Err(e) => { + error!("Error processing E3Requested event: {}", e); + None + } + } } Some(&IEnclave::CiphertextOutputPublished::SIGNATURE_HASH) => { let Ok(event) = IEnclave::CiphertextOutputPublished::decode_log_data(data) else { diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index c8493189da..97a8471f02 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -1021,7 +1021,7 @@ impl ThresholdKeyshare { }; let derived_committee_size = - CiphernodesCommitteeSize::from_threshold(threshold_m as usize, threshold_n as usize); + CiphernodesCommitteeSize::from_threshold(threshold_m as usize, threshold_n as usize)?; // Get collected BFV public keys from all parties (from persisted state) let encryption_keys = &collected_encryption_keys; diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index c4a3691a20..97b162aeb9 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -658,7 +658,7 @@ async fn test_trbfv_actor() -> Result<()> { // - ShareVerificationDispatched (C1 proof verification dispatched by PublicKeyAggregator) // - ComputeRequest (C1 ZK verification) // - ComputeResponse (C1 ZK verification result) - // - ProofVerificationPassed × 5 (one per party's C1 proof) + // - ProofVerificationPassed × 3 (one per party's C1 proof) // - ShareVerificationComplete (C1 verification done) // - PkAggregationProofPending (C5 proof requested by PublicKeyAggregator) // - ComputeRequest (C5 proof generation) @@ -678,8 +678,6 @@ async fn test_trbfv_actor() -> Result<()> { "ProofVerificationPassed", "ProofVerificationPassed", "ProofVerificationPassed", - "ProofVerificationPassed", - "ProofVerificationPassed", "ShareVerificationComplete", "PkAggregationProofPending", "ComputeRequest", @@ -765,7 +763,7 @@ async fn test_trbfv_actor() -> Result<()> { // - 1 ShareVerificationDispatched (C6 verification dispatched by ThresholdPlaintextAggregator) // - 1 ComputeRequest (C6 ZK verification) // - 1 ComputeResponse (C6 ZK verification result) - // - 15 ProofVerificationPassed (5 parties × 3 C6 proofs per ciphertext) + // - 9 ProofVerificationPassed (3 parties × 3 C6 proofs per ciphertext) // - 1 ShareVerificationComplete (C6 verification done) // - 1 ComputeRequest (TrBFV CalculateThresholdDecryption) // - 1 ComputeResponse (TrBFV CalculateThresholdDecryption) @@ -776,8 +774,8 @@ async fn test_trbfv_actor() -> Result<()> { // - 1 PlaintextAggregated (with C7 proofs) // Internal events from committee nodes (ComputeRequest/Response for CalculateDecryptionShare) // stay on their local buses. - // Total: 1 + 3 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 1 = 15 events - let expected_count = 1 + 3 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 1; + // Total: 1 + 3 + 1 + 2 + 9 + 1 + 2 + 1 + 2 + 1 + 1 = 24 events + let expected_count = 1 + 3 + 1 + 2 + 9 + 1 + 2 + 1 + 2 + 1 + 1; let h = nodes .take_history_with_timeouts( diff --git a/crates/zk-helpers/src/bin/zk_cli.rs b/crates/zk-helpers/src/bin/zk_cli.rs index b4ee45332b..56711a818c 100644 --- a/crates/zk-helpers/src/bin/zk_cli.rs +++ b/crates/zk-helpers/src/bin/zk_cli.rs @@ -57,8 +57,10 @@ fn parse_committee(s: &str) -> Result { match s.trim().to_lowercase().as_str() { "micro" => Ok(CiphernodesCommitteeSize::Micro), "small" => Ok(CiphernodesCommitteeSize::Small), + "medium" => Ok(CiphernodesCommitteeSize::Medium), + "large" => Ok(CiphernodesCommitteeSize::Large), _ => Err(anyhow!( - "unknown committee: {s}. Use \"micro\" or \"small\"" + "unknown committee: {s}. Use \"micro\", \"small\", \"medium\", or \"large\"" )), } } diff --git a/crates/zk-helpers/src/ciphernodes_committee.rs b/crates/zk-helpers/src/ciphernodes_committee.rs index 3e801407b2..b3096b686a 100644 --- a/crates/zk-helpers/src/ciphernodes_committee.rs +++ b/crates/zk-helpers/src/ciphernodes_committee.rs @@ -4,6 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use anyhow::{bail, Result}; use serde::{Deserialize, Serialize}; /// @todo this must be integrated inside Ciphernodes & Smart Contract @@ -33,13 +34,16 @@ pub struct CiphernodesCommittee { impl CiphernodesCommitteeSize { /// Derives the committee size from threshold values (M, N). - pub fn from_threshold(threshold_m: usize, threshold_n: usize) -> Self { + pub fn from_threshold(threshold_m: usize, threshold_n: usize) -> Result { match (threshold_m, threshold_n) { - (1, 3) => Self::Micro, - (2, 5) => Self::Small, - _ => panic!( + (1, 3) => Ok(Self::Micro), + (2, 5) => Ok(Self::Small), + (4, 10) => Ok(Self::Medium), + (7, 20) => Ok(Self::Large), + _ => bail!( "Unknown committee size for threshold ({}, {})", - threshold_m, threshold_n + threshold_m, + threshold_n ), } } diff --git a/crates/zk-prover/src/circuits/recursive_aggregation/mod.rs b/crates/zk-prover/src/circuits/recursive_aggregation/mod.rs index 62fed5eb01..754750c789 100644 --- a/crates/zk-prover/src/circuits/recursive_aggregation/mod.rs +++ b/crates/zk-prover/src/circuits/recursive_aggregation/mod.rs @@ -377,7 +377,7 @@ mod tests { } let preset = BfvPreset::InsecureThreshold512; - let committee = CiphernodesCommitteeSize::Small.values(); + let committee = CiphernodesCommitteeSize::Micro.values(); let sample_a = ShareDecryptionCircuitData::generate_sample( preset, committee.clone(), @@ -476,7 +476,7 @@ mod tests { } let preset = BfvPreset::InsecureThreshold512; - let committee = CiphernodesCommitteeSize::Small.values(); + let committee = CiphernodesCommitteeSize::Micro.values(); let sd = preset.search_defaults().expect("search_defaults"); let pk_sample = diff --git a/crates/zk-prover/tests/local_e2e_tests.rs b/crates/zk-prover/tests/local_e2e_tests.rs index f28d68b521..641140aed9 100644 --- a/crates/zk-prover/tests/local_e2e_tests.rs +++ b/crates/zk-prover/tests/local_e2e_tests.rs @@ -49,7 +49,7 @@ async fn setup_share_encryption_e_sm_test() -> Option<( BfvPreset, &'static str, )> { - let committee = CiphernodesCommitteeSize::Small.values(); + let committee = CiphernodesCommitteeSize::Micro.values(); let preset = BfvPreset::InsecureThreshold512; let bb = find_bb().await?; let (backend, temp) = setup_test_prover(&bb).await; @@ -89,7 +89,7 @@ async fn setup_share_encryption_sk_test() -> Option<( BfvPreset, &'static str, )> { - let committee = CiphernodesCommitteeSize::Small.values(); + let committee = CiphernodesCommitteeSize::Micro.values(); let preset = BfvPreset::InsecureThreshold512; let bb = find_bb().await?; let (backend, temp) = setup_test_prover(&bb).await; @@ -129,7 +129,7 @@ async fn setup_share_computation_sk_test() -> Option<( BfvPreset, &'static str, )> { - let committee = CiphernodesCommitteeSize::Small.values(); + let committee = CiphernodesCommitteeSize::Micro.values(); let preset = BfvPreset::InsecureThreshold512; let bb = find_bb().await?; let (backend, temp) = setup_test_prover(&bb).await; @@ -161,7 +161,7 @@ async fn setup_share_computation_e_sm_test() -> Option<( BfvPreset, &'static str, )> { - let committee = CiphernodesCommitteeSize::Small.values(); + let committee = CiphernodesCommitteeSize::Micro.values(); let preset = BfvPreset::InsecureThreshold512; let bb = find_bb().await?; let (backend, temp) = setup_test_prover(&bb).await; @@ -196,7 +196,7 @@ async fn setup_pk_generation_test() -> Option<( BfvPreset, &'static str, )> { - let committee = CiphernodesCommitteeSize::Small.values(); + let committee = CiphernodesCommitteeSize::Micro.values(); let preset = BfvPreset::InsecureThreshold512; let bb = find_bb().await?; let (backend, temp) = setup_test_prover(&bb).await; @@ -226,7 +226,7 @@ async fn setup_share_decryption_test() -> Option<( BfvPreset, &'static str, )> { - let committee = CiphernodesCommitteeSize::Small.values(); + let committee = CiphernodesCommitteeSize::Micro.values(); let preset = BfvPreset::InsecureThreshold512; let bb = find_bb().await?; let (backend, temp) = setup_test_prover(&bb).await; @@ -256,7 +256,7 @@ async fn setup_pk_aggregation_test() -> Option<( BfvPreset, &'static str, )> { - let committee = CiphernodesCommitteeSize::Small.values(); + let committee = CiphernodesCommitteeSize::Micro.values(); let preset = BfvPreset::InsecureThreshold512; let bb = find_bb().await?; let (backend, temp) = setup_test_prover(&bb).await; @@ -286,7 +286,7 @@ async fn setup_decrypted_shares_aggregation_test() -> Option<( BfvPreset, &'static str, )> { - let committee = CiphernodesCommitteeSize::Small.values(); + let committee = CiphernodesCommitteeSize::Micro.values(); let preset = BfvPreset::InsecureThreshold512; let bb = find_bb().await?; let (backend, temp) = setup_test_prover(&bb).await; diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index 3eb5946195..fdf0065727 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -23,7 +23,7 @@ FEE_TOKEN_ADDRESS="0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" # After this interval, the computation phase starts automatically # After activation + this interval, ciphernodes are then not responsing to # any more decryption requests -E3_DURATION=300 +E3_DURATION=225 # 0=Micro, 1=Small, 2=Medium, 3=Large E3_COMMITTEE_SIZE=0 diff --git a/examples/CRISP/server/src/cli/commands.rs b/examples/CRISP/server/src/cli/commands.rs index 1d61f95fc8..af341dd7dd 100644 --- a/examples/CRISP/server/src/cli/commands.rs +++ b/examples/CRISP/server/src/cli/commands.rs @@ -135,7 +135,9 @@ pub async fn initialize_crisp_round( 1 => CommitteeSize::Small, 2 => CommitteeSize::Medium, 3 => CommitteeSize::Large, - _ => panic!("Invalid committee size: {}", CONFIG.e3_committee_size), + invalid => { + return Err(anyhow::anyhow!("Invalid committee size: {}", invalid).into()); + } }; let e3_params = Bytes::from(encode_bfv_params(&generate_bfv_parameters())); let compute_provider_params = ComputeProviderParams { diff --git a/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.json b/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.json index 929cd00796..4f248bff8c 100644 --- a/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.json +++ b/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.json @@ -102,7 +102,7 @@ } }, "immutableReferences": { - "32880": [ + "17861": [ { "length": 32, "start": 91 @@ -152,13 +152,13 @@ "start": 11165 } ], - "32882": [ + "17863": [ { "length": 32, "start": 398 } ], - "32884": [ + "17865": [ { "length": 32, "start": 432 @@ -168,7 +168,7 @@ "start": 2303 } ], - "32886": [ + "17867": [ { "length": 32, "start": 3156 @@ -184,5 +184,5 @@ ] }, "inputSourceName": "project/contracts/verifier/DkgPkVerifier.sol", - "buildInfoId": "solc-0_8_28-0b0bc0ce22be7ef0abc41e82d9a0bfe02856ddc5" + "buildInfoId": "solc-0_8_28-608af4cb791a4cb30d57991d993af95c6fcfa77a" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json b/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json index b715329508..3aea254962 100644 --- a/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json +++ b/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json @@ -391,5 +391,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/verifier/DkgPkVerifier.sol", - "buildInfoId": "solc-0_8_28-0b0bc0ce22be7ef0abc41e82d9a0bfe02856ddc5" + "buildInfoId": "solc-0_8_28-608af4cb791a4cb30d57991d993af95c6fcfa77a" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/verifier/DkgPkVerifier.sol b/packages/enclave-contracts/contracts/verifier/DkgPkVerifier.sol index 784dbd2f4f..b10314e942 100644 --- a/packages/enclave-contracts/contracts/verifier/DkgPkVerifier.sol +++ b/packages/enclave-contracts/contracts/verifier/DkgPkVerifier.sol @@ -3,7 +3,7 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -pragma solidity >=0.8.27; +pragma solidity >=0.8.21; uint256 constant N = 8192; uint256 constant LOG_N = 13; diff --git a/packages/enclave-contracts/scripts/deployEnclave.ts b/packages/enclave-contracts/scripts/deployEnclave.ts index 5ed168b295..83ee4b8026 100644 --- a/packages/enclave-contracts/scripts/deployEnclave.ts +++ b/packages/enclave-contracts/scripts/deployEnclave.ts @@ -215,6 +215,7 @@ export const deployEnclave = async (withMocks?: boolean) => { await enclave.setCommitteeThresholds(0, [1, 3]); // Small: threshold=2, total=5 await enclave.setCommitteeThresholds(1, [2, 5]); + // Medium and Large can be set later as needed console.log("Committee thresholds set (Micro=[1,3], Small=[2,5])"); if (shouldDeployMocks) { diff --git a/packages/enclave-contracts/tasks/enclave.ts b/packages/enclave-contracts/tasks/enclave.ts index 7ffe5ef99f..18ecf3c34c 100644 --- a/packages/enclave-contracts/tasks/enclave.ts +++ b/packages/enclave-contracts/tasks/enclave.ts @@ -23,7 +23,7 @@ export const requestCommittee = task( .addOption({ name: "committeeSize", description: "committee size (0=Micro, 1=Small, 2=Medium, 3=Large)", - defaultValue: 1, + defaultValue: 0, type: ArgumentType.INT, }) .addOption({ @@ -75,6 +75,12 @@ export const requestCommittee = task( }, hre, ) => { + if (![0, 1, 2, 3].includes(committeeSize)) { + throw new Error( + "Invalid committee size - expected 0 (Micro), 1 (Small), 2 (Medium), or 3 (Large).", + ); + } + const connection = await hre.network.connect(); const { ethers } = connection; diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index 554eb556b6..39c0c61b1d 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -851,7 +851,11 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.grantRole(SLASHER_ROLE, await owner.getAddress()); await makeRequest(1); // Small: M=2, N=3 - await finalizeCommitteeWithOperators(0, [operator1, operator2, operator3]); + await finalizeCommitteeWithOperators(0, [ + operator1, + operator2, + operator3, + ]); // Step 1: Lane A slash op1 — still viable (3→2 active, >= M=2) const laneAProof = await signAndEncodeAttestation( diff --git a/templates/default/client/src/pages/steps/RequestComputation.tsx b/templates/default/client/src/pages/steps/RequestComputation.tsx index 0dd559cb52..eabfb108e0 100644 --- a/templates/default/client/src/pages/steps/RequestComputation.tsx +++ b/templates/default/client/src/pages/steps/RequestComputation.tsx @@ -13,10 +13,9 @@ import { useWizard, WizardStep } from '../../context/WizardContext' import { encodeBfvParams, encodeComputeProviderParams, - calculateStartWindow, DEFAULT_COMPUTE_PROVIDER_PARAMS, DEFAULT_E3_CONFIG, - CommitteeSize, + calculateInputWindow, } from '@enclave-e3/sdk' import { getContractAddresses } from '@/utils/env-config' @@ -109,8 +108,9 @@ const RequestComputation: React.FC = () => { } const committeeSize = DEFAULT_E3_CONFIG.committeeSize - const startWindow = calculateStartWindow(60) // 1 minute - const duration = BigInt(60) // 1 minute + const publicClient = sdk.sdk.getPublicClient() + + const inputWindow = await calculateInputWindow(publicClient, 60) // 1 minute const thresholdBfvParams = await sdk.getThresholdBfvParamsSet() const e3ProgramParams = encodeBfvParams(thresholdBfvParams) const computeProviderParams = encodeComputeProviderParams(DEFAULT_COMPUTE_PROVIDER_PARAMS) @@ -118,8 +118,7 @@ const RequestComputation: React.FC = () => { console.log('requestE3') const hash = await requestE3({ committeeSize, - startWindow, - duration, + inputWindow, e3Program: contracts.e3Program, e3ProgramParams, computeProviderParams, diff --git a/templates/default/deployed_contracts.json b/templates/default/deployed_contracts.json index 59e74926eb..571aa5234b 100644 --- a/templates/default/deployed_contracts.json +++ b/templates/default/deployed_contracts.json @@ -1,21 +1,21 @@ { "localhost": { "PoseidonT3": { - "blockNumber": 10, + "blockNumber": 16, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { "constructorArgs": { "initialSupply": "1000000" }, - "blockNumber": 11, + "blockNumber": 17, "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" }, "EnclaveToken": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 12, + "blockNumber": 18, "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" }, "EnclaveTicketToken": { @@ -24,14 +24,14 @@ "registry": "0x0000000000000000000000000000000000000001", "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 14, + "blockNumber": 20, "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" }, "SlashingManager": { "constructorArgs": { "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 15, + "blockNumber": 21, "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" }, "CiphernodeRegistryOwnable": { @@ -46,7 +46,7 @@ "proxyAdminAddress": "0x9bd03768a7DCc129555dE410FF8E85528A4F88b5", "implementationAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F" }, - "blockNumber": 16, + "blockNumber": 22, "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" }, "BondingRegistry": { @@ -68,7 +68,7 @@ "proxyAdminAddress": "0x8aCd85898458400f7Db866d53FCFF6f0D49741FF", "implementationAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" }, - "blockNumber": 17, + "blockNumber": 23, "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" }, "Enclave": { @@ -91,7 +91,7 @@ "proxyAdminAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", "implementationAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" }, - "blockNumber": 20, + "blockNumber": 26, "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "E3RefundManager": { @@ -107,28 +107,28 @@ "proxyAdminAddress": "0x32467b43BFa67273FC7dDda0999Ee9A12F2AaA08", "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" }, - "blockNumber": 22, + "blockNumber": 28, "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" }, "MockComputeProvider": { - "blockNumber": 25, + "blockNumber": 30, "address": "0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E" }, "MockDecryptionVerifier": { - "blockNumber": 26, + "blockNumber": 31, "address": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" }, "MockE3Program": { - "blockNumber": 27, + "blockNumber": 32, "address": "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB" }, "ImageID": { "address": "0x851356ae760d987E095750cCeb3bC6014560891C", - "blockNumber": 31 + "blockNumber": 36 }, "MyProgram": { "address": "0xf5059a5D33d5853360D16C683c16e67980206f36", - "blockNumber": 33 + "blockNumber": 38 } } } \ No newline at end of file diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 2c2657fe95..f801e278de 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -4,22 +4,22 @@ chains: contracts: enclave: address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" - deploy_block: 16 + deploy_block: 26 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 12 + deploy_block: 22 bonding_registry: address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" - deploy_block: 13 + deploy_block: 23 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" deploy_block: 13 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 7 + deploy_block: 17 e3_program: - address: "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" - deploy_block: 28 + address: "0xf5059a5D33d5853360D16C683c16e67980206f36" + deploy_block: 38 program: dev: true nodes: From 25c80f1c65792659edd1dc24a21b16cbe96320e5 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 13 Mar 2026 09:53:46 +0000 Subject: [PATCH 6/6] chore: use micro as default committee sizd --- packages/enclave-sdk/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/enclave-sdk/src/utils.ts b/packages/enclave-sdk/src/utils.ts index 007feff9d6..c1a9990158 100644 --- a/packages/enclave-sdk/src/utils.ts +++ b/packages/enclave-sdk/src/utils.ts @@ -78,7 +78,7 @@ export const DEFAULT_COMPUTE_PROVIDER_PARAMS: ComputeProviderParams = { // Default E3 configuration export const DEFAULT_E3_CONFIG = { - committeeSize: 1, // Small + committeeSize: 0, // Micro duration: 1800, // 30 minutes in seconds payment_amount: '0', // 0 ETH in wei } as const