From 202b40f3cede22789d83e1132a4b69b397a78802 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Tue, 23 Dec 2025 17:56:19 +0100 Subject: [PATCH 01/13] refactor: pass public key commitment for proof verification --- Cargo.lock | 29 ++- crates/aggregator/Cargo.toml | 1 + crates/aggregator/src/publickey_aggregator.rs | 9 + crates/bfv-helpers/Cargo.toml | 2 +- crates/bfv-helpers/src/client.rs | 28 +++ crates/bfv-helpers/src/utils/greco.rs | 191 +++++++++++++++++- .../src/enclave_event/publickey_aggregated.rs | 1 + crates/evm-helpers/src/contracts.rs | 20 +- crates/evm/src/ciphernode_registry_sol.rs | 19 +- crates/tests/Cargo.toml | 1 + crates/tests/tests/integration_legacy.rs | 23 +++ examples/CRISP/Cargo.lock | 29 ++- examples/CRISP/circuits/src/main.nr | 2 +- .../contracts/CRISPProgram.sol | 14 +- .../contracts/CRISPVerifier.sol | 98 ++++----- .../contracts/Mocks/MockEnclave.sol | 7 +- .../tests/crisp.contracts.test.ts | 5 +- examples/CRISP/packages/crisp-sdk/src/vote.ts | 4 +- examples/CRISP/server/src/cli/approve.rs | 2 +- examples/CRISP/server/src/cli/commands.rs | 16 +- examples/CRISP/server/src/server/indexer.rs | 23 ++- .../src/server/token_holders/etherscan.rs | 1 + .../ICiphernodeRegistry.json | 7 +- .../interfaces/IEnclave.sol/IEnclave.json | 20 +- .../enclave-contracts/contracts/Enclave.sol | 11 +- .../interfaces/ICiphernodeRegistry.sol | 4 +- .../contracts/interfaces/IEnclave.sol | 4 +- .../registry/CiphernodeRegistryOwnable.sol | 5 +- .../contracts/test/MockCiphernodeRegistry.sol | 6 +- packages/enclave-contracts/tasks/enclave.ts | 18 +- .../enclave-contracts/test/Enclave.spec.ts | 83 ++++---- .../CiphernodeRegistryOwnable.spec.ts | 6 +- packages/enclave-sdk/src/contract-client.ts | 4 +- packages/enclave-sdk/src/enclave-sdk.ts | 4 +- 34 files changed, 559 insertions(+), 138 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b859ce0653..461e2237e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2741,6 +2741,7 @@ dependencies = [ "anyhow", "async-trait", "bincode", + "e3-bfv-helpers", "e3-config", "e3-data", "e3-events", @@ -2775,7 +2776,7 @@ dependencies = [ "strum", "thiserror 1.0.69", "zkfhe-greco", - "zkfhe-shared", + "zkfhe-shared 0.1.0 (git+https://github.com/gnosisguild/zkfhe-generator?rev=e2c02e6ef42348b2141293cc75f61a211c801a94)", ] [[package]] @@ -3296,6 +3297,7 @@ dependencies = [ "bincode", "clap", "e3-aggregator", + "e3-bfv-helpers", "e3-ciphernode-builder", "e3-crypto", "e3-data", @@ -9346,7 +9348,30 @@ dependencies = [ "serde_json", "tempfile", "toml", - "zkfhe-shared", + "zkfhe-shared 0.1.0 (git+https://github.com/gnosisguild/zkfhe-generator)", +] + +[[package]] +name = "zkfhe-shared" +version = "0.1.0" +source = "git+https://github.com/gnosisguild/zkfhe-generator?rev=e2c02e6ef42348b2141293cc75f61a211c801a94#e2c02e6ef42348b2141293cc75f61a211c801a94" +dependencies = [ + "anyhow", + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "bigint-poly", + "chrono", + "fhe", + "fhe-math", + "fhe-traits", + "num-bigint", + "num-traits", + "rand 0.8.5", + "safe 0.1.7 (git+https://github.com/gnosisguild/enclave)", + "serde", + "serde_json", + "thiserror 1.0.69", + "toml", ] [[package]] diff --git a/crates/aggregator/Cargo.toml b/crates/aggregator/Cargo.toml index f41f43110a..8b5ad04afc 100644 --- a/crates/aggregator/Cargo.toml +++ b/crates/aggregator/Cargo.toml @@ -18,6 +18,7 @@ e3-evm = { workspace = true } e3-fhe = { workspace = true } e3-multithread = { workspace = true } e3-trbfv = { workspace = true } +e3-bfv-helpers = { workspace = true } e3-request = { workspace = true } e3-sortition = { workspace = true } e3-utils = { workspace = true } diff --git a/crates/aggregator/src/publickey_aggregator.rs b/crates/aggregator/src/publickey_aggregator.rs index 1d022b1fec..2d8fe09f7c 100644 --- a/crates/aggregator/src/publickey_aggregator.rs +++ b/crates/aggregator/src/publickey_aggregator.rs @@ -6,6 +6,7 @@ use actix::prelude::*; use anyhow::Result; +use e3_bfv_helpers::client::compute_pk_commitment; use e3_data::Persistable; use e3_events::{ prelude::*, BusHandle, Die, E3id, EnclaveEvent, EnclaveEventData, KeyshareCreated, OrderedSet, @@ -181,6 +182,13 @@ impl Handler for PublicKeyAggregator { keyshares: msg.keyshares, })?; + let public_key_hash = compute_pk_commitment( + pubkey.clone(), + self.fhe.params.degree(), + self.fhe.params.plaintext(), + self.fhe.params.moduli().to_vec(), + )?; + // Update the local state self.set_pubkey(pubkey)?; @@ -194,6 +202,7 @@ impl Handler for PublicKeyAggregator { info!("Sending PublicKeyAggregated..."); let event = PublicKeyAggregated { pubkey, + public_key_hash, e3_id: msg.e3_id, nodes, }; diff --git a/crates/bfv-helpers/Cargo.toml b/crates/bfv-helpers/Cargo.toml index 9f3113408d..e7f7e45852 100644 --- a/crates/bfv-helpers/Cargo.toml +++ b/crates/bfv-helpers/Cargo.toml @@ -19,7 +19,7 @@ itertools = "0.14.0" ndarray = "0.15" num-bigint = { workspace = true } num-traits = "0.2" -shared = { package = "zkfhe-shared", git = "https://github.com/gnosisguild/zkfhe-generator" } +shared = { package = "zkfhe-shared", git = "https://github.com/gnosisguild/zkfhe-generator", rev = "e2c02e6ef42348b2141293cc75f61a211c801a94" } strum.workspace = true rand.workspace = true thiserror = { workspace = true } diff --git a/crates/bfv-helpers/src/client.rs b/crates/bfv-helpers/src/client.rs index 0eaba232f9..908e244398 100644 --- a/crates/bfv-helpers/src/client.rs +++ b/crates/bfv-helpers/src/client.rs @@ -5,6 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::build_bfv_params_arc; +use crate::utils::greco::bfv_public_key_to_greco; use anyhow::{anyhow, Result}; use fhe::bfv::{Encoding, Plaintext, PublicKey}; use fhe::Error as FheError; @@ -129,6 +130,33 @@ where }) } +pub fn compute_pk_commitment( + public_key: Vec, + degree: usize, + plaintext_modulus: u64, + moduli: Vec, +) -> Result<[u8; 32]> { + use shared::commitments::compute_pk_commitment as compute_pk_commitment_shared; + + let params = build_bfv_params_arc(degree, plaintext_modulus, &moduli, None); + + let public_key = PublicKey::from_bytes(&public_key, ¶ms) + .map_err(|e| anyhow!("Error deserializing public key: {}", e))?; + + let (_, bounds) = GrecoBounds::compute(¶ms, 0)?; + let bit_pk = shared::template::calculate_bit_width(&bounds.pk_bounds[0].to_string())?; + + let (pk0is, pk1is) = bfv_public_key_to_greco(&public_key, ¶ms); + let commitment_bigint = compute_pk_commitment_shared(&pk0is, &pk1is, bit_pk); + + let bytes = commitment_bigint.to_bytes_be().1; + let public_key_hash: [u8; 32] = bytes + .try_into() + .map_err(|_| anyhow!("Commitment must be exactly 32 bytes"))?; + + Ok(public_key_hash) +} + #[cfg(test)] mod tests { use crate::BfvParamSets; diff --git a/crates/bfv-helpers/src/utils/greco.rs b/crates/bfv-helpers/src/utils/greco.rs index 7b250a1b39..3bc32003b5 100644 --- a/crates/bfv-helpers/src/utils/greco.rs +++ b/crates/bfv-helpers/src/utils/greco.rs @@ -6,7 +6,7 @@ use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_primitives::I256; -use fhe::bfv::{BfvParameters, Ciphertext}; +use fhe::bfv::{BfvParameters, Ciphertext, PublicKey}; use fhe_math::rq::{traits::TryConvertFrom, Poly, Representation}; use itertools::izip; use ndarray::Array2; @@ -182,6 +182,106 @@ pub fn abi_decode_greco_ciphertext( (ct0is, ct1is) } +/// Converts a BFV coefficient (in [0, qi)) to centered format [-(qi-1)/2, (qi-1)/2]. +fn convert_bfv_coefficient_to_centered(coeff: u64, qi: u64) -> BigInt { + let qi_bigint = BigInt::from(qi); + let coeff_bigint = BigInt::from(coeff); + + // Center: convert from [0, qi) to [-(qi-1)/2, (qi-1)/2] + // If coeff > qi/2, it represents a negative centered value + if coeff > (qi / 2) { + &coeff_bigint - &qi_bigint + } else { + coeff_bigint + } +} + +/// Converts BFV coefficients to greco-formatted coefficients (centered, reversed, standard form). +fn convert_bfv_coefficients_to_greco( + bfv_coeffs: &[u64], + qi: u64, + zkp_modulus: &BigInt, +) -> Vec { + bfv_coeffs + .iter() + .rev() + .map(|coeff| { + let centered = convert_bfv_coefficient_to_centered(*coeff, qi); + // Reduce mod ZKP modulus to get standard form + // Handle negative values correctly + let centered_mod = centered % zkp_modulus; + if centered_mod < BigInt::from(0) { + centered_mod + zkp_modulus + } else { + centered_mod + } + }) + .collect() +} + +/// Converts a BFV public key to Greco format. +/// +/// Takes a BFV public key and converts it to Greco format, returning pk0is and pk1is +/// as vectors of coefficient vectors (one vector per modulus, standard form). +/// +/// # Arguments +/// * `pk` - BFV public key +/// * `params` - BFV parameters +/// +/// # Returns +/// A tuple of (pk0is, pk1is) where each is Vec> (one vector per modulus) +pub fn bfv_public_key_to_greco( + pk: &PublicKey, + params: &Arc, +) -> (Vec>, Vec>) { + let moduli = params.moduli(); + let degree = params.degree(); + let zkp_modulus = get_zkp_modulus(); + + // Access pk0 and pk1 polynomials from the public key + // PublicKey has a .c field that is a Ciphertext, which contains .c with the polynomials + let pk0_poly = &pk.c.c[0]; + let pk1_poly = &pk.c.c[1]; + + // Convert polynomials to power basis representation to access coefficients + let mut pk0_power = pk0_poly.clone(); + let mut pk1_power = pk1_poly.clone(); + pk0_power.change_representation(Representation::PowerBasis); + pk1_power.change_representation(Representation::PowerBasis); + + let mut pk0is = Vec::with_capacity(moduli.len()); + let mut pk1is = Vec::with_capacity(moduli.len()); + + // Extract coefficients for each modulus + for (i, qi) in moduli.iter().enumerate() { + let mut pk0_modulus = Vec::with_capacity(degree); + let mut pk1_modulus = Vec::with_capacity(degree); + + for j in 0..degree { + // Access coefficient at (modulus_index, coefficient_index) + let pk0_coeff = pk0_power.coefficients()[(i, j)]; + let pk1_coeff = pk1_power.coefficients()[(i, j)]; + + pk0_modulus.push(pk0_coeff); + pk1_modulus.push(pk1_coeff); + } + + // Convert to greco format (centers, reverses, and reduces mod ZKP modulus) + pk0is.push(convert_bfv_coefficients_to_greco( + &pk0_modulus, + *qi, + &zkp_modulus, + )); + pk1is.push(convert_bfv_coefficients_to_greco( + &pk1_modulus, + *qi, + &zkp_modulus, + )); + } + + (pk0is, pk1is) +} + /// Converts bytes32 (signed 256-bit, two's complement, big-endian) to BigInt fn bytes32_to_bigint(bytes: &[u8; 32]) -> BigInt { // Use I256::from_be_bytes which handles two's complement conversion automatically @@ -281,4 +381,93 @@ mod tests { assert_eq!(reconstructed_ct.c.len(), original_ct.c.len()); assert_eq!(reconstructed_ct.level, original_ct.level); } + + #[test] + fn test_bfv_public_key_to_greco() { + use crate::{BfvParamSet, BfvParamSets}; + let params = BfvParamSet::from(BfvParamSets::InsecureSet512_10_1).build_arc(); + + let mut rng = thread_rng(); + let sk = SecretKey::random(¶ms, &mut rng); + let pk = PublicKey::new(&sk, &mut rng); + + // Get expected pk0is and pk1is from GrecoVectors + let (_, bounds) = GrecoBounds::compute(¶ms, 0).unwrap(); + let bit_pk = + shared::template::calculate_bit_width(&bounds.pk_bounds[0].to_string()).unwrap(); + + let vote = vec![1u64, 0u64, 0u64]; + let pt = Plaintext::try_encode(&vote, Encoding::poly(), ¶ms).unwrap(); + let (ct, u_rns, e0_rns, e1_rns) = pk.try_encrypt_extended(&pt, &mut rng).unwrap(); + + let greco_vectors = + GrecoVectors::compute(&pt, &u_rns, &e0_rns, &e1_rns, &ct, &pk, ¶ms, bit_pk) + .unwrap(); + let standard_vectors = greco_vectors.standard_form(); + let expected_pk0is = &standard_vectors.pk0is; + let expected_pk1is = &standard_vectors.pk1is; + + // Convert using our function + let (actual_pk0is, actual_pk1is) = bfv_public_key_to_greco(&pk, ¶ms); + + // Verify the structure matches + assert_eq!(actual_pk0is.len(), expected_pk0is.len()); + assert_eq!(actual_pk1is.len(), expected_pk1is.len()); + assert_eq!(actual_pk0is.len(), params.moduli().len()); + + // Verify coefficients match for each modulus + for (i, (actual_pk0i, expected_pk0i)) in + actual_pk0is.iter().zip(expected_pk0is.iter()).enumerate() + { + assert_eq!( + actual_pk0i.len(), + expected_pk0i.len(), + "pk0is[{}] length mismatch", + i + ); + assert_eq!( + actual_pk0i.len(), + params.degree(), + "pk0is[{}] should have degree coefficients", + i + ); + + for (j, (actual_coeff, expected_coeff)) in + actual_pk0i.iter().zip(expected_pk0i.iter()).enumerate() + { + assert_eq!( + actual_coeff, expected_coeff, + "pk0is[{}][{}] coefficient mismatch", + i, j + ); + } + } + + for (i, (actual_pk1i, expected_pk1i)) in + actual_pk1is.iter().zip(expected_pk1is.iter()).enumerate() + { + assert_eq!( + actual_pk1i.len(), + expected_pk1i.len(), + "pk1is[{}] length mismatch", + i + ); + assert_eq!( + actual_pk1i.len(), + params.degree(), + "pk1is[{}] should have degree coefficients", + i + ); + + for (j, (actual_coeff, expected_coeff)) in + actual_pk1i.iter().zip(expected_pk1i.iter()).enumerate() + { + assert_eq!( + actual_coeff, expected_coeff, + "pk1is[{}][{}] coefficient mismatch", + i, j + ); + } + } + } } diff --git a/crates/events/src/enclave_event/publickey_aggregated.rs b/crates/events/src/enclave_event/publickey_aggregated.rs index e359f266e1..9190fb6573 100644 --- a/crates/events/src/enclave_event/publickey_aggregated.rs +++ b/crates/events/src/enclave_event/publickey_aggregated.rs @@ -16,6 +16,7 @@ use std::fmt::{self, Display}; pub struct PublicKeyAggregated { #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] pub pubkey: Vec, + pub public_key_hash: [u8; 32], pub e3_id: E3id, pub nodes: OrderedSet, } diff --git a/crates/evm-helpers/src/contracts.rs b/crates/evm-helpers/src/contracts.rs index 82926e8088..ca401c6d0c 100644 --- a/crates/evm-helpers/src/contracts.rs +++ b/crates/evm-helpers/src/contracts.rs @@ -7,7 +7,7 @@ use alloy::providers::fillers::BlobGasFiller; use alloy::{ network::{Ethereum, EthereumWallet}, - primitives::{Address, Bytes, U256}, + primitives::{Address, Bytes, FixedBytes, U256}, providers::fillers::{ ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller, }, @@ -75,7 +75,7 @@ sol! { mapping(uint256 e3Id => bytes params) public e3Params; mapping(address e3Program => bool allowed) public e3Programs; function request(E3RequestParams calldata requestParams) external returns (uint256 e3Id, E3 memory e3); - function activate(uint256 e3Id,bytes calldata publicKey) external returns (bool success); + function activate(uint256 e3Id,bytes calldata publicKey,bytes32 publicKeyHash) external returns (bool success); function enableE3Program(address e3Program) public onlyOwner returns (bool success); function publishInput(uint256 e3Id, bytes calldata data) external returns (bool success); function publishCiphertextOutput(uint256 e3Id, bytes calldata ciphertextOutput, bytes calldata proof) external returns (bool success); @@ -138,7 +138,12 @@ pub trait EnclaveWrite { ) -> Result<(TransactionReceipt, U256)>; /// Activate an E3 with a public key - async fn activate(&self, e3_id: U256, pub_key: Bytes) -> Result; + async fn activate( + &self, + e3_id: U256, + pub_key: Bytes, + pub_key_hash: FixedBytes<32>, + ) -> Result; /// Enable an E3 program async fn enable_e3_program(&self, e3_program: Address) -> Result; @@ -400,7 +405,12 @@ impl EnclaveWrite for EnclaveContract { Ok((receipt, e3_id)) } - async fn activate(&self, e3_id: U256, pub_key: Bytes) -> Result { + async fn activate( + &self, + e3_id: U256, + pub_key: Bytes, + pub_key_hash: FixedBytes<32>, + ) -> Result { let _guard = NONCE_LOCK.lock().await; let wallet_addr = self .wallet_address @@ -408,7 +418,7 @@ impl EnclaveWrite for EnclaveContract { let nonce = get_next_nonce(&*self.provider, wallet_addr).await?; let contract = Enclave::new(self.contract_address, &self.provider); - let builder = contract.activate(e3_id, pub_key).nonce(nonce); + let builder = contract.activate(e3_id, pub_key, pub_key_hash).nonce(nonce); let receipt = builder.send().await?.get_receipt().await?; Ok(receipt) diff --git a/crates/evm/src/ciphernode_registry_sol.rs b/crates/evm/src/ciphernode_registry_sol.rs index d02192b0e2..02d8760d73 100644 --- a/crates/evm/src/ciphernode_registry_sol.rs +++ b/crates/evm/src/ciphernode_registry_sol.rs @@ -9,7 +9,7 @@ use crate::{ }; use actix::prelude::*; use alloy::{ - primitives::{Address, Bytes, LogData, B256, U256}, + primitives::{Address, Bytes, FixedBytes, LogData, B256, U256}, providers::{Provider, WalletProvider}, rpc::types::TransactionReceipt, sol, @@ -406,15 +406,22 @@ impl Handler Self::Result { let e3_id = msg.e3_id.clone(); let pubkey = msg.pubkey.clone(); + let public_key_hash = msg.public_key_hash.clone(); let nodes = msg.nodes.clone(); let contract_address = self.contract_address; let provider = self.provider.clone(); let bus = self.bus.clone(); Box::pin(async move { - let result = - publish_committee_to_registry(provider, contract_address, e3_id, nodes, pubkey) - .await; + let result = publish_committee_to_registry( + provider, + contract_address, + e3_id, + nodes, + pubkey, + public_key_hash, + ) + .await; match result { Ok(receipt) => { info!(tx=%receipt.transaction_hash, "Committee published to registry"); @@ -483,10 +490,12 @@ pub async fn publish_committee_to_registry e3_id: E3id, nodes: OrderedSet, public_key: Vec, + public_key_hash: [u8; 32], ) -> Result { info!("Calling: contract.publishCommittee(..)"); let e3_id: U256 = e3_id.try_into()?; let public_key = Bytes::from(public_key); + let public_key_hash = FixedBytes::from(public_key_hash); let nodes_vec: Vec
= nodes .into_iter() .filter_map(|node| node.parse().ok()) @@ -499,7 +508,7 @@ pub async fn publish_committee_to_registry .await?; let contract = ICiphernodeRegistry::new(contract_address, provider.provider()); let builder = contract - .publishCommittee(e3_id, nodes_vec, public_key) + .publishCommittee(e3_id, nodes_vec, public_key, public_key_hash) .nonce(current_nonce); let receipt = builder.send().await?.get_receipt().await?; Ok(receipt) diff --git a/crates/tests/Cargo.toml b/crates/tests/Cargo.toml index ee84285db7..8c910328d9 100644 --- a/crates/tests/Cargo.toml +++ b/crates/tests/Cargo.toml @@ -31,6 +31,7 @@ e3-sdk = { workspace = true } e3-sortition = { workspace = true } e3-test-helpers = { workspace = true } e3-trbfv = { workspace = true } +e3-bfv-helpers = { workspace = true } e3-utils = { workspace = true } fhe-traits = { workspace = true } fhe-util = { workspace = true } diff --git a/crates/tests/tests/integration_legacy.rs b/crates/tests/tests/integration_legacy.rs index 4bfc160924..ebdcb67727 100644 --- a/crates/tests/tests/integration_legacy.rs +++ b/crates/tests/tests/integration_legacy.rs @@ -8,6 +8,7 @@ use actix::prelude::*; use actix::Actor; use alloy::primitives::{FixedBytes, I256, U256}; use anyhow::*; +use e3_bfv_helpers::client::compute_pk_commitment; use e3_ciphernode_builder::CiphernodeBuilder; use e3_ciphernode_builder::CiphernodeHandle; use e3_ciphernode_builder::EventSystem; @@ -220,9 +221,16 @@ async fn test_public_key_aggregation_and_decryption() -> Result<()> { let rng_test = create_shared_rng_from_u64(42); let test_shares = generate_pk_shares(¶ms, &crpoly, &rng_test, ð_addrs)?; let test_pubkey = aggregate_public_key(&test_shares)?; + let public_key_hash = compute_pk_commitment( + test_pubkey.to_bytes(), + params.degree(), + params.plaintext(), + params.moduli().to_vec(), + )?; let expected_aggregated_event = PublicKeyAggregated { pubkey: test_pubkey.to_bytes(), + public_key_hash, e3_id: e3_id.clone(), nodes: OrderedSet::from(eth_addrs.clone()), }; @@ -561,6 +569,12 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { let test_pubkey = aggregate_public_key(&generate_pk_shares( ¶ms, &crpoly, &rng_test, ð_addrs, )?)?; + let public_key_hash = compute_pk_commitment( + test_pubkey.to_bytes(), + params.degree(), + params.plaintext(), + params.moduli().to_vec(), + )?; let history_collector = ciphernodes.last().unwrap().history().unwrap(); let history = history_collector @@ -571,6 +585,7 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { history.last().cloned().unwrap().into_data(), PublicKeyAggregated { pubkey: test_pubkey.to_bytes(), + public_key_hash, e3_id: E3id::new("1234", 1), nodes: OrderedSet::from(eth_addrs.clone()), } @@ -597,6 +612,13 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { ¶ms, &crpoly, &rng_test, ð_addrs, )?)?; + let public_key_hash = compute_pk_commitment( + test_pubkey.to_bytes(), + params.degree(), + params.plaintext(), + params.moduli().to_vec(), + )?; + let history = history_collector .send(TakeEvents::::new(8)) .await?; @@ -605,6 +627,7 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { history.last().cloned().unwrap().into_data(), PublicKeyAggregated { pubkey: test_pubkey.to_bytes(), + public_key_hash, e3_id: E3id::new("1234", 2), nodes: OrderedSet::from(eth_addrs.clone()), } diff --git a/examples/CRISP/Cargo.lock b/examples/CRISP/Cargo.lock index 64ba384aa2..fe80157b45 100644 --- a/examples/CRISP/Cargo.lock +++ b/examples/CRISP/Cargo.lock @@ -2353,7 +2353,7 @@ dependencies = [ "strum", "thiserror 1.0.69", "zkfhe-greco", - "zkfhe-shared", + "zkfhe-shared 0.1.0 (git+https://github.com/gnosisguild/zkfhe-generator?rev=e2c02e6ef42348b2141293cc75f61a211c801a94)", ] [[package]] @@ -6663,7 +6663,7 @@ dependencies = [ "serde", "serde_json", "zkfhe-greco", - "zkfhe-shared", + "zkfhe-shared 0.1.0 (git+https://github.com/gnosisguild/zkfhe-generator)", ] [[package]] @@ -6699,7 +6699,30 @@ dependencies = [ "serde_json", "tempfile", "toml", - "zkfhe-shared", + "zkfhe-shared 0.1.0 (git+https://github.com/gnosisguild/zkfhe-generator)", +] + +[[package]] +name = "zkfhe-shared" +version = "0.1.0" +source = "git+https://github.com/gnosisguild/zkfhe-generator?rev=e2c02e6ef42348b2141293cc75f61a211c801a94#e2c02e6ef42348b2141293cc75f61a211c801a94" +dependencies = [ + "anyhow", + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "bigint-poly", + "chrono", + "fhe", + "fhe-math", + "fhe-traits", + "num-bigint", + "num-traits", + "rand 0.8.5", + "safe", + "serde", + "serde_json", + "thiserror 1.0.69", + "toml", ] [[package]] diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index dfd60e4ba1..012decb959 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -53,7 +53,7 @@ fn main( sum_r1is: [Polynomial<512>; 2], // Greco Section. params: Params<512, 2>, // todo: make this pub - pk_commitment: Field, // todo: make this pub + pk_commitment: pub Field, pk0is: [Polynomial<512>; 2], pk1is: [Polynomial<512>; 2], ct0is: [Polynomial<512>; 2], diff --git a/examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol b/examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol index b8b9f9b8ce..111b156947 100644 --- a/examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol +++ b/examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol @@ -135,13 +135,17 @@ contract CRISPProgram is IE3Program, Ownable { (uint40 voteIndex, bool isFirstVote) = _processVote(e3Id, slotAddress, voteBytes); // Set public inputs for the proof. Order must match Noir circuit. - bytes32[] memory noirPublicInputs = new bytes32[](3 + vote.length); + bytes32[] memory noirPublicInputs = new bytes32[](4 + vote.length); - noirPublicInputs[0] = bytes32(e3Data[e3Id].merkleRoot); - noirPublicInputs[1] = bytes32(uint256(uint160(slotAddress))); - noirPublicInputs[2] = bytes32(uint256(isFirstVote ? 1 : 0)); + // Fetch E3 to get committee public key + E3 memory e3 = enclave.getE3(e3Id); + + noirPublicInputs[0] = e3.committeePublicKey; + noirPublicInputs[1] = bytes32(e3Data[e3Id].merkleRoot); + noirPublicInputs[2] = bytes32(uint256(uint160(slotAddress))); + noirPublicInputs[3] = bytes32(uint256(isFirstVote ? 1 : 0)); for (uint256 i = 0; i < vote.length; i++) { - noirPublicInputs[i + 3] = vote[i]; + noirPublicInputs[i + 4] = vote[i]; } // Check if the ciphertext was encrypted correctly diff --git a/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol b/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol index f264956abf..24bda8bdb0 100644 --- a/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol +++ b/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol @@ -7,85 +7,85 @@ pragma solidity >=0.8.21; uint256 constant N = 262144; uint256 constant LOG_N = 18; -uint256 constant NUMBER_OF_PUBLIC_INPUTS = 2067; -uint256 constant VK_HASH = 0x1f342bc97d6645639cb2b424af12caaabefd2ac0d090e8edcc304a2111c023eb; +uint256 constant NUMBER_OF_PUBLIC_INPUTS = 2068; +uint256 constant VK_HASH = 0x1ca8d8a2b64dc27e11e756a83b5efcf951b1dddf2ecb023022dcbf4ba1005de2; library HonkVerificationKey { function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(262144), logCircuitSize: uint256(18), - publicInputsSize: uint256(2067), + publicInputsSize: uint256(2068), ql: Honk.G1Point({ - x: uint256(0x00f29150a23230e646cb136a94902ad257110fc4304b698ddc4999b86e3f5141), - y: uint256(0x2220dc38c8a64e8c3d014808a3dcd2c13c113144c871f71bcc1bc85bd98f30de) + x: uint256(0x0c3acef8966ceb2984e47121b763f16a862f3cdb33c24d4b03009a9f737408f6), + y: uint256(0x0da7bce94c82ecdbd4ae789e9753251fd8afc89d9605932b1154387a3d8c00e6) }), qr: Honk.G1Point({ - x: uint256(0x24647d1ca59027437dbe4e54129e79b3d5553f766595ed2b856651735e60123f), - y: uint256(0x04c1465aac2f24da6cda7f1dad49c0478334ee94015f573e2c711e0a37f1fc50) + x: uint256(0x2e41f2c832adad1d96df181c43ca52c717835470c7e471c087067197e33860d6), + y: uint256(0x25d16a68285523a3e9d8a42344a7489d493b8ddd51ed24b0c566651ca06eb8dc) }), qo: Honk.G1Point({ - x: uint256(0x2cca07970aaccdcf3082c0e878c8c3807ee98b35bee26ef13a7636cab75df4ae), - y: uint256(0x2ca851747e0aaddfaff5a837912eb616be85d7142c7034dc859d35a90be75b24) + x: uint256(0x0c0e53fdc05955a112f7b1efaa77afb15271c8ee842163186bbca2a110a6deba), + y: uint256(0x05a9f03fe46a2347ef8aceb395dbc221cf4e180f63bd6e9de07cded19378fdb2) }), q4: Honk.G1Point({ - x: uint256(0x2ef4f4db61395e49b96dfd6fc0dbdf9dc0a8515ff4d79767475de6bef5fcb1fd), - y: uint256(0x28a5ea00f49bb92903b381602f160a86620afc1bdb19d0661259e8e758244da5) + x: uint256(0x1e1cc841a9a3716e0689dca8bcc18f2b9e290bfeaef1f78c28cdcedb13f29d40), + y: uint256(0x12774484a5526ca36a7e91d0d53ec3e115321f609a662b5050ad378a024311e5) }), qm: Honk.G1Point({ - x: uint256(0x25ab10004d39d7a7b03b1690a3294771952cef36908c876bebdf38c81aa9cb5b), - y: uint256(0x28b7ac6b40aadf8605c47c1b485e9ba1e306875fc08ea3222b7ed52bc8df232f) + x: uint256(0x251c96f789fb3fc5258489bec45ec137c054966c8674132e27feeccf5a81cf25), + y: uint256(0x20491536be9bafbc08346c0a71a3ed3beffb13f2e9f092008230397a1c5fc98d) }), qc: Honk.G1Point({ - x: uint256(0x2a606b4de6181e512e53da202e9a5a422b57b04aef4dd24c63b7710583228587), - y: uint256(0x1ec117b91cd29c02e28012c3b329780e180eb4c4a78c989909df85f9a582d991) + x: uint256(0x0936620bea178cd901115b75f458c8de74899315505e3e52852f668b6fd37674), + y: uint256(0x1ea7f29d26f0d4a3ecd04724b76fe410c7a30e6eb4d8e44f09c5beeaa5f78780) }), qLookup: Honk.G1Point({ - x: uint256(0x0dfe6661307987506b0ebbc76de9a2d7adb3dffb6dbf0612c06befb4a793cd62), - y: uint256(0x2dd8ea30b3abc2adb29e2ad5fc2492d6b253ae39fb0f9b711b371c014762f9dd) + x: uint256(0x21ed84b63d62aef10a8cd203641cd62f5a42825efd4807a47a4f5d006e44b6af), + y: uint256(0x306347f78166296722434632227c5ac52adfcdf24ee09169a6fb79d4258c7cca) }), qArith: Honk.G1Point({ - x: uint256(0x3026f7e4a08adb74978832bb85010b5f0ccfef67ccf2737ef74ba24164823e28), - y: uint256(0x03d2d596658cc5fdf1fec581c56b74fa4a0b73ac262664d459b00cac87f02081) + x: uint256(0x053507710ff715cf9363741dc46cc5d686fdd09012e3ccf83a38e3f79f491bb0), + y: uint256(0x0d004c2714d1630398938e93adc23622cb4ae4ca78141a68fb9e365295650cde) }), qDeltaRange: Honk.G1Point({ - x: uint256(0x209c50a6cb8d7c07ca391a5f1c9d8ed819ac370d5e58ae91916180132aea4138), - y: uint256(0x04a4637666c572e8f366f4417bda553daf2e9f83a062c10ecc439dafed7e3801) + x: uint256(0x0ddde3ce2b24613aed779dde885daa094fb9087cba224838118a08fa197139db), + y: uint256(0x0030cdcf467265124848353360e5e29839ebd846b687a512f99ae7f8a2a54490) }), qElliptic: Honk.G1Point({ - x: uint256(0x1f14d0331b1194ddd9864a4bef26346afd65f7822d582b4dd9c0f7495f19f63d), - y: uint256(0x1348e481b3d1147f944893cdd429f10395c4009aa0e8888620ed4ec07314f2df) + x: uint256(0x0214eea9bd774ccbaea3cd9753e77adb6af9bfc2c5ef210160d6af8520204de9), + y: uint256(0x0ac1443dca135c5ddc9a1c38a4323d54a554867a0db1b82b752f9416b6eb8301) }), qMemory: Honk.G1Point({ - x: uint256(0x2da1c69a6a5fd038351c57a6928b4991c378dcba0ffaf5a73d3db99a26625228), - y: uint256(0x1684d420be93fd2096b67c81fa97a2feec4975a71bf959427bf60352161271dc) + x: uint256(0x1cb6ea41a55bb0a307ebbbc130d80e6039a971ac4664ed84a2968b8bf2ebbf49), + y: uint256(0x0047bc403498ca5f6ed5c9d15827dcd65dae3030a28002112e1d789680eaac2e) }), qNnf: Honk.G1Point({ - x: uint256(0x0833926efd6eb4eed73876ca39cd0f809406e6e3d7600431fbca085ff4746cf1), - y: uint256(0x28687103c3efe96aecf53ef207768cd5bf37f74b104df83d9e32c594ec398740) + x: uint256(0x2c45a53bef969d29924c1585f468d78c3f81a9a2026a37029e4651b8099f61b2), + y: uint256(0x1ff421b8b79665b7ad9c598f4143d155db08e0120f2a8bdd93fae19c5c8caca8) }), qPoseidon2External: Honk.G1Point({ - x: uint256(0x1f811c4432f7e04d5ae8efa8c484233c587e63ae6962f6fb874712b83095d0d7), - y: uint256(0x01da61bcdf7bf688bccb7613c40f845f2df5ee48d0cef74a0da700215ace9e91) + x: uint256(0x29a806a3d3deae47f9de89d35a3200f2f66c4b78bab19c0dfb5fe8348f669582), + y: uint256(0x0dc3b792592ccdde2d18bae94cca0e8d499814c70e8bfaeaa63dd902ea7ad008) }), qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x15ca529979d15aaa85980d68f5b581d26ce131569c49005ebf18692cbdfd7f7b), - y: uint256(0x2f96460f70d8ac40b30fcb9a55b6439e751782be4464d7c3d634716f6e249cc7) + x: uint256(0x05024f6de65a4dcced2738308dbda5326cbaafad2981f8b67526eb0eb1cce144), + y: uint256(0x07ec05f2076b4369362030c2c7acbef7efca4eea33e2b13c8a5b92b02a8e34c3) }), s1: Honk.G1Point({ - x: uint256(0x139d3cde0e0abe526fe49eaf44a509c471381727ce151a506458b843d2d5c913), - y: uint256(0x2fc2ebf6b18b284fc015bbec5879641c9026ed360fcab4eb3fb545b8f7ad6ab1) + x: uint256(0x01e80e00cc438bef1c0311d8e860d214f1f4f8c030cb97110c64fdb2dbb24f51), + y: uint256(0x17441e3965eacb1bedc71d9983aeb0558222c093f142e597fc66a0b7e9973907) }), s2: Honk.G1Point({ - x: uint256(0x072357d5c70595bc6c4ec6e714a3b5eccdc548e7f318d7f162d4014491042f5e), - y: uint256(0x2dfc881f2d3013e738e1eca79830e1aa42f58e4f60f2b20e65fb037b732ef0d9) + x: uint256(0x0d4e58d4f2698d73f7d0980c9e07a20d0587292bbc97212f9f85a786e03847bc), + y: uint256(0x27d389ca0c65494fab3c321aafe3f289ad39b778b76b524fa396c6b51004f812) }), s3: Honk.G1Point({ - x: uint256(0x27daf23dfd9dd006e51c71bacdeb5029bd1b599519bf7e1189e806fec146c9a7), - y: uint256(0x1b8679946f37e9075e63099f65fda5951ccb1c4f8db2bc251d3c3ad92e0fa8f2) + x: uint256(0x27e70f21603a4902a769ea6822a6c06d0715a9f39795fe18fdb6327bf41abca0), + y: uint256(0x1fedc45c7c852a9ebcb12ad5aa0a6df0c8f9576bed8a6d51ba8187b8936d4bd5) }), s4: Honk.G1Point({ - x: uint256(0x2df479bd76ee03ac303c5b3b8eacaa3d073dd5fc27921a73a839b77a03e52a11), - y: uint256(0x0ac3d76ade86fe99357493ebc3c8e45649ae84b377960d63fd96931a4b34c930) + x: uint256(0x1ff580dc873878337c5f7697ceb402342ce1b821c37d766f97cedd93b63d0359), + y: uint256(0x196d7d19376846c104f0ef472a6ed732fd243e356908ba33f6c68901ffe8ce13) }), t1: Honk.G1Point({ x: uint256(0x1f16b037f0b4c96ea2a30a118a44e139881c0db8a4d6c9fde7db5c1c1738e61f), @@ -104,28 +104,28 @@ library HonkVerificationKey { y: uint256(0x2d7e8c1ecb92e2490049b50efc811df63f1ca97e58d5e82852dbec0c29715d71) }), id1: Honk.G1Point({ - x: uint256(0x1a073546a9dc93862bbc18424ed313a3ee656c599370a0d0c1f6c94db76501ab), - y: uint256(0x1398feed57f99023a2c4efa2613430c59e7460478dd05a46315fd70adeba6ef2) + x: uint256(0x0fc864a6c1236d620b1c09c9278c95610758a8f5fea80f681fd1587c9406ecbf), + y: uint256(0x077911571c3508dbc02c65c15ae1cb3af33b2b69fe73f460925f5c5f906fb51e) }), id2: Honk.G1Point({ - x: uint256(0x191d3b40da5beabbfd51fa9cdbeff7fcdd521d69252035c143abd24d79b841a1), - y: uint256(0x1c8bb1ccc7222e8b268986f1d6e6245de0de6151cc0e1870574a9ccc06adfbcd) + x: uint256(0x23760b478cf3aaefe6782ad269e6bed4dbad373b85cf73685b5eb77ec9498bcf), + y: uint256(0x12b6be9b4f5b3cce944a695ad7517218ffb6648ec1a74d4bd028b053a565de67) }), id3: Honk.G1Point({ - x: uint256(0x1e971e497ef3b06f73dbb32f8788d2976bf15276d41c511bd518015608da183a), - y: uint256(0x116e7946a86db35275a791b3e5afd355b063f818b77cec1acdaa9e45dc02fed6) + x: uint256(0x144205f099b5c04c0a4a51e30bf20bd5fff8a82be5eac9a059961db089cb34dc), + y: uint256(0x256a6fdb5a8a8a5dfb285dcaeefb4bf590f58900fb648ba715e465cd3f745918) }), id4: Honk.G1Point({ - x: uint256(0x1e1dc5856d7b89f67a615c4e27361073bbd9803889ae429b7d9d72a22e7fde62), - y: uint256(0x213db7d35402d3318cf071829641c56f34a3ce68185fe088d4512826f6ce02b7) + x: uint256(0x1e1ea32eea4d1afcee4572ecca811099f54e90fcf09b4037f8505248cb65f2c6), + y: uint256(0x10df7dee70bc3946d428fe27811eedbe58a9a9565c9b734268e646b30aed50b8) }), lagrangeFirst: Honk.G1Point({ x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), lagrangeLast: Honk.G1Point({ - x: uint256(0x029dfa2b4411d997a90152aaca71774e248d435e84fa886b5f88d9653d397d3f), - y: uint256(0x06c195f01273254b7fd868060dea058a7266047ae13a64c2685fa1a9ff6af6fc) + x: uint256(0x2e8854320df8d7e2bc104a691285ebb73e6a6aeb5834d8818ddfb90779ef7b4a), + y: uint256(0x2a1a85f62dd5371e29e46b650f9b995e91b864d6bebd1bcb0604635f5a5c4270) }) }); return vk; diff --git a/examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockEnclave.sol b/examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockEnclave.sol index 605a756e85..b555ff3ce2 100644 --- a/examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockEnclave.sol +++ b/examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockEnclave.sol @@ -11,11 +11,16 @@ import { IDecryptionVerifier } from "@enclave-e3/contracts/contracts/interfaces/ contract MockEnclave { bytes public plaintextOutput; + bytes32 public committeePublicKey; function setPlaintextOutput(bytes memory plaintext) external { plaintextOutput = plaintext; } + function setCommitteePublicKey(bytes32 publicKeyHash) external { + committeePublicKey = publicKeyHash; + } + function getE3(uint256 e3Id) external view returns (E3 memory) { return E3({ @@ -30,7 +35,7 @@ contract MockEnclave { e3ProgramParams: bytes(""), customParams: bytes(""), decryptionVerifier: IDecryptionVerifier(address(0)), - committeePublicKey: bytes32(0), + committeePublicKey: committeePublicKey, ciphertextOutput: bytes32(0), plaintextOutput: plaintextOutput }); diff --git a/examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts b/examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts index 59bf2650ef..5b86070fce 100644 --- a/examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts +++ b/examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts @@ -71,7 +71,8 @@ describe('CRISP Contracts', function () { // It needs some time to generate the proof. this.timeout(60000) - const crispProgram = await deployCRISPProgram() + const mockEnclave = await deployMockEnclave() + const crispProgram = await deployCRISPProgram({ mockEnclave }) const [signer] = await ethers.getSigners() const e3Id = 1n @@ -93,6 +94,8 @@ describe('CRISP Contracts', function () { slotAddress: address, }) + await mockEnclave.setCommitteePublicKey(proof.publicInputs[0]) + const encodedProof = encodeSolidityProof(proof) // Call next functions with fake data for testing. diff --git a/examples/CRISP/packages/crisp-sdk/src/vote.ts b/examples/CRISP/packages/crisp-sdk/src/vote.ts index ef0468e1c9..2fbb2770f1 100644 --- a/examples/CRISP/packages/crisp-sdk/src/vote.ts +++ b/examples/CRISP/packages/crisp-sdk/src/vote.ts @@ -280,8 +280,8 @@ export const verifyProof = async (proof: ProofData): Promise => { * @returns The encoded proof data as a hex string. */ export const encodeSolidityProof = (proof: ProofData): Hex => { - const vote = proof.publicInputs.slice(3) as `0x${string}`[] - const slotAddress = getAddress(numberToHex(BigInt(proof.publicInputs[1]), { size: 20 })) + const vote = proof.publicInputs.slice(4) as `0x${string}`[] + const slotAddress = getAddress(numberToHex(BigInt(proof.publicInputs[2]), { size: 20 })) return encodeAbiParameters(parseAbiParameters('bytes, bytes32[], address'), [bytesToHex(proof.proof), vote, slotAddress]) } diff --git a/examples/CRISP/server/src/cli/approve.rs b/examples/CRISP/server/src/cli/approve.rs index 9cc528fd72..4925f19671 100644 --- a/examples/CRISP/server/src/cli/approve.rs +++ b/examples/CRISP/server/src/cli/approve.rs @@ -25,7 +25,7 @@ pub async fn approve_token( amount: U256, ) -> Result<()> { use alloy::network::EthereumWallet; - use alloy::providers::{ProviderBuilder}; + use alloy::providers::ProviderBuilder; use alloy::signers::local::PrivateKeySigner; let token_address: Address = token_address.parse()?; diff --git a/examples/CRISP/server/src/cli/commands.rs b/examples/CRISP/server/src/cli/commands.rs index 9c1df45a9a..ac9541034d 100644 --- a/examples/CRISP/server/src/cli/commands.rs +++ b/examples/CRISP/server/src/cli/commands.rs @@ -12,10 +12,11 @@ use serde::{Deserialize, Serialize}; use super::approve; use super::CLI_DB; -use alloy::primitives::{Address, Bytes, U256}; +use alloy::primitives::{Address, Bytes, FixedBytes, U256}; use alloy::providers::{Provider, ProviderBuilder}; use alloy::sol_types::SolValue; use crisp::config::CONFIG; +use e3_sdk::bfv_helpers::client::compute_pk_commitment; use e3_sdk::bfv_helpers::{build_bfv_params_from_set_arc, encode_bfv_params}; use e3_sdk::evm_helpers::contracts::{EnclaveContract, EnclaveRead, EnclaveWrite, E3}; use fhe::bfv::{BfvParameters, Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}; @@ -207,7 +208,18 @@ pub async fn activate_e3_round() -> Result<(), Box>, ) -> eyre::Result<()> { // If not activated activate - let tx = ctx.contract().activate(event.e3Id, event.publicKey).await?; + let params = get_default_paramset(); + + let public_key_hash = compute_pk_commitment( + event.publicKey.to_vec(), + params.degree, + params.plaintext_modulus, + params.moduli.to_vec(), + ) + .map_err(|e| eyre::eyre!("Failed to compute PK commitment: {}", e))?; + + let tx = ctx + .contract() + .activate( + event.e3Id, + event.publicKey, + FixedBytes::from(public_key_hash), + ) + .await?; info!( "[e3_id={}] E3 activated with tx: {:?}", event.e3Id, tx.transaction_hash diff --git a/examples/CRISP/server/src/server/token_holders/etherscan.rs b/examples/CRISP/server/src/server/token_holders/etherscan.rs index 3b490040fc..408a2f5302 100644 --- a/examples/CRISP/server/src/server/token_holders/etherscan.rs +++ b/examples/CRISP/server/src/server/token_holders/etherscan.rs @@ -618,6 +618,7 @@ pub fn get_mock_token_holders() -> Vec { #[cfg(test)] mod tests { use super::*; + use crate::server::CONFIG; #[test] fn test_extract_addresses() { 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 63328f690e..abb231f6ab 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -372,6 +372,11 @@ "internalType": "bytes", "name": "publicKey", "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "publicKeyHash", + "type": "bytes32" } ], "name": "publishCommittee", @@ -535,5 +540,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-572e77328edd43ea3643262ca5f82babf769c493" + "buildInfoId": "solc-0_8_28-c50eb34c88f83554dbf0f8f704a3681d5dfc5322" } \ 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 0719c20302..7c4306f7a4 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -347,6 +347,11 @@ "internalType": "bytes", "name": "publicKey", "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "publicKeyHash", + "type": "bytes32" } ], "name": "activate", @@ -417,6 +422,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "feeToken", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -924,5 +942,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-15da934c0854938352efc3fea207d9956917d5c6" + "buildInfoId": "solc-0_8_28-c50eb34c88f83554dbf0f8f704a3681d5dfc5322" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index 12c7176b12..8221453b00 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -297,7 +297,8 @@ contract Enclave is IEnclave, OwnableUpgradeable { /// @inheritdoc IEnclave function activate( uint256 e3Id, - bytes calldata publicKey + bytes calldata publicKey, + bytes32 publicKeyHash ) external returns (bool success) { E3 memory e3 = getE3(e3Id); @@ -306,14 +307,16 @@ contract Enclave is IEnclave, OwnableUpgradeable { // TODO: handle what happens to the payment if the start window has passed. require(e3.startWindow[1] >= block.timestamp, E3Expired()); - bytes32 publicKeyHash = ciphernodeRegistry.committeePublicKey(e3Id); + bytes32 registryPublicKeyHash = ciphernodeRegistry.committeePublicKey( + e3Id + ); require( - keccak256(publicKey) == publicKeyHash, + publicKeyHash == registryPublicKeyHash, CommitteeSelectionFailed() ); uint256 expiresAt = block.timestamp + e3.duration; e3s[e3Id].expiration = expiresAt; - e3s[e3Id].committeePublicKey = keccak256(publicKey); + e3s[e3Id].committeePublicKey = publicKeyHash; emit E3Activated(e3Id, expiresAt, publicKey); diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index f294ff4ec3..4b8d11937f 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -155,10 +155,12 @@ interface ICiphernodeRegistry { /// @param e3Id ID of the E3 for which to select the committee. /// @param nodes Array of ciphernode addresses selected for the committee. /// @param publicKey The public key generated by the given committee. + /// @param publicKeyHash The hash of the public key. function publishCommittee( uint256 e3Id, address[] calldata nodes, - bytes calldata publicKey + bytes calldata publicKey, + bytes32 publicKeyHash ) external; /// @notice This function should be called by the Enclave contract to get the public key of a committee. diff --git a/packages/enclave-contracts/contracts/interfaces/IEnclave.sol b/packages/enclave-contracts/contracts/interfaces/IEnclave.sol index e95d7223ec..8c690f46df 100644 --- a/packages/enclave-contracts/contracts/interfaces/IEnclave.sol +++ b/packages/enclave-contracts/contracts/interfaces/IEnclave.sol @@ -152,10 +152,12 @@ interface IEnclave { /// @dev This function MUST revert if the selected node committee has not yet published a public key. /// @param e3Id ID of the E3. /// @param publicKey Public key of the committee. + /// @param publicKeyHash Hash of the committee's public key. /// @return success True if the E3 was successfully activated. function activate( uint256 e3Id, - bytes calldata publicKey + bytes calldata publicKey, + bytes32 publicKeyHash ) external returns (bool success); /// @notice This function should be called to publish input data for Encrypted Execution Environment (E3). diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 549ae3cea3..33e995df4e 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -239,10 +239,12 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { /// @param e3Id ID of the E3 computation /// @param nodes Array of ciphernode addresses selected for the committee /// @param publicKey Aggregated public key of the committee + /// @param publicKeyHash The hash of the public key function publishCommittee( uint256 e3Id, address[] calldata nodes, - bytes calldata publicKey + bytes calldata publicKey, + bytes32 publicKeyHash ) external onlyOwner { Committee storage c = committees[e3Id]; @@ -253,7 +255,6 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { // TODO: Currently we trust the owner to publish the correct committee. // TODO: Need a Proof that the public key is generated from the committee - bytes32 publicKeyHash = keccak256(publicKey); c.publicKey = publicKeyHash; publicKeyHashes[e3Id] = publicKeyHash; emit CommitteePublished(e3Id, nodes, publicKey); diff --git a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol index a3b7695b4c..3f1b0f64d8 100644 --- a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol @@ -41,7 +41,8 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { function publishCommittee( uint256, address[] calldata, - bytes calldata + bytes calldata, + bytes32 ) external pure {} // solhint-disable-line no-empty-blocks function getCommitteeNodes( @@ -117,7 +118,8 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { function publishCommittee( uint256, address[] calldata, - bytes calldata + bytes calldata, + bytes32 ) external pure {} // solhint-disable-line no-empty-blocks function getCommitteeNodes( diff --git a/packages/enclave-contracts/tasks/enclave.ts b/packages/enclave-contracts/tasks/enclave.ts index d301db9971..bb033f11ef 100644 --- a/packages/enclave-contracts/tasks/enclave.ts +++ b/packages/enclave-contracts/tasks/enclave.ts @@ -255,8 +255,14 @@ export const publishCommittee = task( defaultValue: "", type: ArgumentType.STRING, }) + .addOption({ + name: "publicKeyHash", + description: "hash of the public key (bytes32)", + defaultValue: "", + type: ArgumentType.STRING, + }) .setAction(async () => ({ - default: async ({ e3Id, nodes, publicKey }, hre) => { + default: async ({ e3Id, nodes, publicKey, publicKeyHash }, hre) => { const { deployAndSaveCiphernodeRegistryOwnable } = await import( "../scripts/deployAndSave/ciphernodeRegistryOwnable" ); @@ -281,10 +287,15 @@ export const publishCommittee = task( throw new Error("Invalid nodes format: no valid addresses found"); } + if (!publicKeyHash) { + throw new Error("publicKeyHash is required"); + } + const tx = await ciphernodeRegistry.publishCommittee( e3Id, nodesToSend, publicKey, + publicKeyHash, ); console.log("Publishing committee... ", tx.hash); @@ -319,6 +330,8 @@ export const activateE3 = task("e3:activate", "Activate an E3 program") publicKeyArg || (publicKeyFile ? fs.readFileSync(publicKeyFile, "utf8").trim() : "") || process.env.PUBLIC_KEY; + const connection = await hre.network.connect(); + const { ethers } = connection; if (!publicKey) throw new Error("No public key provided!"); @@ -330,7 +343,8 @@ export const activateE3 = task("e3:activate", "Activate an E3 program") hre, }); - const tx = await enclave.activate(e3Id, publicKey); + const publicKeyHash = ethers.id(publicKey); + const tx = await enclave.activate(e3Id, publicKey, publicKeyHash); console.log("Activating E3 program... ", tx.hash); await tx.wait(); diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 517d668693..586732fb7e 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -55,7 +55,7 @@ describe("Enclave", function () { ); const data = "0xda7a"; - const dataHash = ethers.keccak256(data); + const dataHash = ethers.id(data); const proof = "0x1337"; // Hash function used to compute the tree nodes. @@ -73,7 +73,8 @@ describe("Enclave", function () { await registry.connect(operator2).submitTicket(e3Id, 1); await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(e3Id); - await registry.publishCommittee(e3Id, nodes, publicKey); + const publicKeyHash = ethers.id(publicKey); + await registry.publishCommittee(e3Id, nodes, publicKey, publicKeyHash); }; // Helper function to approve USDC and make request @@ -938,7 +939,9 @@ describe("Enclave", function () { it("reverts if E3 does not exist", async function () { const { enclave } = await loadFixture(setup); - await expect(enclave.activate(0, ethers.ZeroHash)) + await expect( + enclave.activate(0, ethers.ZeroHash, ethers.id(ethers.ZeroHash)), + ) .to.be.revertedWithCustomError(enclave, "E3DoesNotExist") .withArgs(0); }); @@ -972,8 +975,10 @@ describe("Enclave", function () { ); await expect(enclave.getE3(0)).to.not.be.revert(ethers); - await expect(enclave.activate(0, data)).to.not.be.revert(ethers); - await expect(enclave.activate(0, data)) + await expect(enclave.activate(0, data, dataHash)).to.not.be.revert( + ethers, + ); + await expect(enclave.activate(0, data, dataHash)) .to.be.revertedWithCustomError(enclave, "E3AlreadyActivated") .withArgs(0); }); @@ -995,7 +1000,7 @@ describe("Enclave", function () { }); await expect( - enclave.activate(0, ethers.ZeroHash), + enclave.activate(0, ethers.ZeroHash, ethers.id(ethers.ZeroHash)), ).to.be.revertedWithCustomError(enclave, "E3NotReady"); }); it("reverts if E3 start has expired", async function () { @@ -1030,10 +1035,9 @@ describe("Enclave", function () { await mine(2, { interval: 2000 }); - await expect(enclave.activate(e3Id, data)).to.be.revertedWithCustomError( - enclave, - "E3Expired", - ); + await expect( + enclave.activate(e3Id, data, dataHash), + ).to.be.revertedWithCustomError(enclave, "E3Expired"); }); it("reverts if ciphernodeRegistry does not return a public key", async function () { const { enclave, request, usdcToken } = await loadFixture(setup); @@ -1053,7 +1057,7 @@ describe("Enclave", function () { }); await expect( - enclave.activate(0, ethers.ZeroHash), + enclave.activate(0, ethers.ZeroHash, ethers.id(ethers.ZeroHash)), ).to.be.revertedWithCustomError(enclave, "E3NotReady"); }); it("reverts if E3 start has expired", async function () { @@ -1085,10 +1089,9 @@ describe("Enclave", function () { await time.increaseTo(currentTime + request.duration + 100); - await expect(enclave.activate(e3Id, data)).to.be.revertedWithCustomError( - enclave, - "E3Expired", - ); + await expect( + enclave.activate(e3Id, data, dataHash), + ).to.be.revertedWithCustomError(enclave, "E3Expired"); }); it("reverts if ciphernodeRegistry does not return a public key", async function () { const { @@ -1111,7 +1114,7 @@ describe("Enclave", function () { await enclave.setCiphernodeRegistry(nextRegistry); await expect( - enclave.activate(0, ethers.ZeroHash), + enclave.activate(0, ethers.ZeroHash, ethers.id(ethers.ZeroHash)), ).to.be.revertedWithCustomError(enclave, "CommitteeSelectionFailed"); await enclave.setCiphernodeRegistry(prevRegistry); @@ -1125,7 +1128,9 @@ describe("Enclave", function () { operator2, ); - await expect(enclave.activate(0, data)).not.to.be.revert(ethers); + await expect(enclave.activate(0, data, dataHash)).not.to.be.revert( + ethers, + ); }); it("sets committeePublicKey correctly", async () => { @@ -1165,7 +1170,7 @@ describe("Enclave", function () { let e3 = await enclave.getE3(e3Id); expect(e3.committeePublicKey).to.not.equal(publicKey); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); e3 = await enclave.getE3(e3Id); expect(e3.committeePublicKey).to.equal(publicKey); @@ -1201,7 +1206,9 @@ describe("Enclave", function () { operator2, ); - expect(await enclave.activate.staticCall(e3Id, data)).to.be.equal(true); + expect( + await enclave.activate.staticCall(e3Id, data, dataHash), + ).to.be.equal(true); }); it("emits E3Activated event", async () => { const { @@ -1234,7 +1241,7 @@ describe("Enclave", function () { operator2, ); - await expect(enclave.activate(e3Id, data)).to.emit( + await expect(enclave.activate(e3Id, data, dataHash)).to.emit( enclave, "E3Activated", ); @@ -1299,7 +1306,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(0, data); + await enclave.activate(0, data, dataHash); await expect( enclave.publishInput(0, "0xaabbcc"), ).to.be.revertedWithCustomError(enclave, "InvalidInput"); @@ -1333,7 +1340,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(0, data); + await enclave.activate(0, data, dataHash); await mine(2, { interval: request.duration }); @@ -1373,7 +1380,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(0, data); + await enclave.activate(0, data, dataHash); await enclave.publishInput(0, inputData); await makeRequest(enclave, usdcToken, { @@ -1394,7 +1401,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(1, data); + await enclave.activate(1, data, dataHash); await enclave.publishInput(1, inputData); }); it("returns true if input is published successfully", async function () { @@ -1426,7 +1433,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(0, data); + await enclave.activate(0, data, dataHash); expect(await enclave.publishInput.staticCall(0, inputData)).to.equal( true, @@ -1483,7 +1490,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await expect( enclave.publishCiphertextOutput(e3Id, "0x", "0x"), @@ -1518,7 +1525,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await mine(2, { interval: request.duration }); expect(await enclave.publishCiphertextOutput(e3Id, data, proof)); await expect(enclave.publishCiphertextOutput(e3Id, data, proof)) @@ -1557,7 +1564,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await mine(2, { interval: request.duration }); await expect( enclave.publishCiphertextOutput(e3Id, "0x", "0x"), @@ -1587,11 +1594,11 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await mine(2, { interval: request.duration }); expect(await enclave.publishCiphertextOutput(e3Id, data, proof)); const e3 = await enclave.getE3(e3Id); - expect(e3.ciphertextOutput).to.equal(dataHash); + expect(e3.ciphertextOutput).to.equal(ethers.keccak256(data)); }); it("returns true if output is published successfully", async function () { const { @@ -1617,7 +1624,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await mine(2, { interval: request.duration }); expect( await enclave.publishCiphertextOutput.staticCall(e3Id, data, proof), @@ -1647,7 +1654,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await mine(2, { interval: request.duration }); await expect(enclave.publishCiphertextOutput(e3Id, data, proof)) .to.emit(enclave, "CiphertextOutputPublished") @@ -1701,7 +1708,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await expect(enclave.publishPlaintextOutput(e3Id, data, "0x")) .to.be.revertedWithCustomError(enclave, "CiphertextOutputNotPublished") .withArgs(e3Id); @@ -1730,7 +1737,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await mine(2, { interval: request.duration }); await enclave.publishCiphertextOutput(e3Id, data, proof); await enclave.publishPlaintextOutput(e3Id, data, proof); @@ -1765,7 +1772,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await mine(2, { interval: request.duration }); await enclave.publishCiphertextOutput(e3Id, data, proof); await expect(enclave.publishPlaintextOutput(e3Id, data, "0x")) @@ -1796,7 +1803,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await mine(2, { interval: request.duration }); await enclave.publishCiphertextOutput(e3Id, data, proof); expect(await enclave.publishPlaintextOutput(e3Id, data, proof)); @@ -1828,7 +1835,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await mine(2, { interval: request.duration }); await enclave.publishCiphertextOutput(e3Id, data, proof); expect( @@ -1859,7 +1866,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data); + await enclave.activate(e3Id, data, dataHash); await mine(2, { interval: request.duration }); await enclave.publishCiphertextOutput(e3Id, data, proof); await expect(await enclave.publishPlaintextOutput(e3Id, data, proof)) diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts index d250f2f6b2..98b70fb739 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -28,7 +28,7 @@ const { ethers, networkHelpers, ignition } = await network.connect(); const { loadFixture } = networkHelpers; const data = "0xda7a"; -const dataHash = ethers.keccak256(data); +const dataHash = ethers.id(data); const SORTITION_SUBMISSION_WINDOW = 3; // Hash function used to compute the tree nodes. @@ -343,6 +343,7 @@ describe("CiphernodeRegistryOwnable", function () { request.e3Id, [await operator1.getAddress(), await operator2.getAddress()], data, + dataHash, ), ).to.be.revertedWithCustomError(registry, "OwnableUnauthorizedAccount"); }); @@ -361,6 +362,7 @@ describe("CiphernodeRegistryOwnable", function () { request.e3Id, [await operator1.getAddress(), await operator2.getAddress()], data, + dataHash, ); expect(await registry.committeePublicKey(request.e3Id)).to.equal( dataHash, @@ -381,6 +383,7 @@ describe("CiphernodeRegistryOwnable", function () { request.e3Id, [await operator1.getAddress(), await operator2.getAddress()], data, + dataHash, ), ) .to.emit(registry, "CommitteePublished") @@ -507,6 +510,7 @@ describe("CiphernodeRegistryOwnable", function () { request.e3Id, [await operator1.getAddress(), await operator2.getAddress()], data, + dataHash, ); expect(await registry.committeePublicKey(request.e3Id)).to.equal( dataHash, diff --git a/packages/enclave-sdk/src/contract-client.ts b/packages/enclave-sdk/src/contract-client.ts index b61916f6e8..dc290acc4b 100644 --- a/packages/enclave-sdk/src/contract-client.ts +++ b/packages/enclave-sdk/src/contract-client.ts @@ -161,7 +161,7 @@ export class ContractClient { * Activate an E3 computation * activate(uint256 e3Id, bytes memory publicKey) */ - public async activateE3(e3Id: bigint, publicKey: `0x${string}`, gasLimit?: bigint): Promise { + public async activateE3(e3Id: bigint, publicKey: `0x${string}`, publicKeyHash: `0x${string}`, gasLimit?: bigint): Promise { if (!this.walletClient) { throw new SDKError('Wallet client required for write operations', 'NO_WALLET') } @@ -180,7 +180,7 @@ export class ContractClient { address: this.addresses.enclave, abi: Enclave__factory.abi, functionName: 'activate', - args: [e3Id, publicKey], + args: [e3Id, publicKey, publicKeyHash], account, gas: gasLimit, }) diff --git a/packages/enclave-sdk/src/enclave-sdk.ts b/packages/enclave-sdk/src/enclave-sdk.ts index b3c2dbc681..82b7082255 100644 --- a/packages/enclave-sdk/src/enclave-sdk.ts +++ b/packages/enclave-sdk/src/enclave-sdk.ts @@ -326,12 +326,12 @@ export class EnclaveSDK { /** * Activate an E3 computation */ - public async activateE3(e3Id: bigint, publicKey: `0x${string}`, gasLimit?: bigint): Promise { + public async activateE3(e3Id: bigint, publicKey: `0x${string}`, publicKeyHash: `0x${string}`, gasLimit?: bigint): Promise { if (!this.initialized) { await this.initialize() } - return this.contractClient.activateE3(e3Id, publicKey, gasLimit) + return this.contractClient.activateE3(e3Id, publicKey, publicKeyHash, gasLimit) } /** From 912007be5ee775751aaf759dd3221215ac1f4f49 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Tue, 23 Dec 2025 18:00:06 +0100 Subject: [PATCH 02/13] chore: update zkfhe-generator dependency --- Cargo.lock | 27 ++------------------------- crates/bfv-helpers/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 461e2237e0..717e390ed8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2776,7 +2776,7 @@ dependencies = [ "strum", "thiserror 1.0.69", "zkfhe-greco", - "zkfhe-shared 0.1.0 (git+https://github.com/gnosisguild/zkfhe-generator?rev=e2c02e6ef42348b2141293cc75f61a211c801a94)", + "zkfhe-shared", ] [[package]] @@ -9348,30 +9348,7 @@ dependencies = [ "serde_json", "tempfile", "toml", - "zkfhe-shared 0.1.0 (git+https://github.com/gnosisguild/zkfhe-generator)", -] - -[[package]] -name = "zkfhe-shared" -version = "0.1.0" -source = "git+https://github.com/gnosisguild/zkfhe-generator?rev=e2c02e6ef42348b2141293cc75f61a211c801a94#e2c02e6ef42348b2141293cc75f61a211c801a94" -dependencies = [ - "anyhow", - "ark-bn254 0.5.0", - "ark-ff 0.5.0", - "bigint-poly", - "chrono", - "fhe", - "fhe-math", - "fhe-traits", - "num-bigint", - "num-traits", - "rand 0.8.5", - "safe 0.1.7 (git+https://github.com/gnosisguild/enclave)", - "serde", - "serde_json", - "thiserror 1.0.69", - "toml", + "zkfhe-shared", ] [[package]] diff --git a/crates/bfv-helpers/Cargo.toml b/crates/bfv-helpers/Cargo.toml index e7f7e45852..9f3113408d 100644 --- a/crates/bfv-helpers/Cargo.toml +++ b/crates/bfv-helpers/Cargo.toml @@ -19,7 +19,7 @@ itertools = "0.14.0" ndarray = "0.15" num-bigint = { workspace = true } num-traits = "0.2" -shared = { package = "zkfhe-shared", git = "https://github.com/gnosisguild/zkfhe-generator", rev = "e2c02e6ef42348b2141293cc75f61a211c801a94" } +shared = { package = "zkfhe-shared", git = "https://github.com/gnosisguild/zkfhe-generator" } strum.workspace = true rand.workspace = true thiserror = { workspace = true } From 515200a973566cdac99f8c5cf38b867647e9c4f4 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Tue, 23 Dec 2025 18:15:33 +0100 Subject: [PATCH 03/13] chore: update lockfile --- Cargo.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 717e390ed8..ac0119a267 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1147,7 +1147,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1158,7 +1158,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2524,7 +2524,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 2.0.111", + "syn 1.0.109", ] [[package]] @@ -2677,7 +2677,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3538,7 +3538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3625,7 +3625,7 @@ dependencies = [ [[package]] name = "fhe" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "bincode", "doc-comment", @@ -3651,7 +3651,7 @@ dependencies = [ [[package]] name = "fhe-math" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "ethnum", "fhe-traits", @@ -3674,7 +3674,7 @@ dependencies = [ [[package]] name = "fhe-traits" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "rand 0.8.5", ] @@ -3682,7 +3682,7 @@ dependencies = [ [[package]] name = "fhe-util" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "itertools 0.12.1", "num-bigint-dig", @@ -6037,7 +6037,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -7116,7 +7116,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -7213,7 +7213,7 @@ dependencies = [ [[package]] name = "safe" version = "0.1.7" -source = "git+https://github.com/gnosisguild/enclave#c9b72d1e42d8e37a19473f423978ef76f92454d6" +source = "git+https://github.com/gnosisguild/enclave#ad8c74becdb97608f3279f6a6cb1feba9e68dd1c" dependencies = [ "ark-bn254 0.5.0", "ark-ff 0.5.0", @@ -7893,7 +7893,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -9328,7 +9328,7 @@ dependencies = [ [[package]] name = "zkfhe-greco" version = "0.1.0" -source = "git+https://github.com/gnosisguild/zkfhe-generator#46d8579f8fc3c53d2c74e89e7c75c280f63e432f" +source = "git+https://github.com/gnosisguild/zkfhe-generator#9acbf0eaa73214646e472ef8df7499d1fce72ce0" dependencies = [ "anyhow", "ark-bn254 0.5.0", @@ -9343,7 +9343,6 @@ dependencies = [ "num-traits", "rand 0.8.5", "rayon", - "safe 0.1.7 (git+https://github.com/gnosisguild/enclave)", "serde", "serde_json", "tempfile", @@ -9354,7 +9353,7 @@ dependencies = [ [[package]] name = "zkfhe-shared" version = "0.1.0" -source = "git+https://github.com/gnosisguild/zkfhe-generator#46d8579f8fc3c53d2c74e89e7c75c280f63e432f" +source = "git+https://github.com/gnosisguild/zkfhe-generator#9acbf0eaa73214646e472ef8df7499d1fce72ce0" dependencies = [ "anyhow", "ark-bn254 0.5.0", @@ -9367,6 +9366,7 @@ dependencies = [ "num-bigint", "num-traits", "rand 0.8.5", + "safe 0.1.7 (git+https://github.com/gnosisguild/enclave)", "serde", "serde_json", "thiserror 1.0.69", From 22059c3c5e678ff5ea9d8e814398707d8a491b8f Mon Sep 17 00:00:00 2001 From: Cedoor Date: Tue, 23 Dec 2025 18:20:47 +0100 Subject: [PATCH 04/13] chore: update lockfile for default template --- templates/default/Cargo.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/templates/default/Cargo.lock b/templates/default/Cargo.lock index ea520e45d5..ed02af23e4 100644 --- a/templates/default/Cargo.lock +++ b/templates/default/Cargo.lock @@ -1343,7 +1343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1393,7 +1393,7 @@ dependencies = [ [[package]] name = "fhe" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "bincode", "doc-comment", @@ -1419,7 +1419,7 @@ dependencies = [ [[package]] name = "fhe-math" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "ethnum", "fhe-traits", @@ -1442,7 +1442,7 @@ dependencies = [ [[package]] name = "fhe-traits" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "rand 0.8.5", ] @@ -1450,7 +1450,7 @@ dependencies = [ [[package]] name = "fhe-util" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "itertools 0.12.1", "num-bigint-dig", @@ -3084,7 +3084,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3147,7 +3147,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safe" version = "0.1.7" -source = "git+https://github.com/gnosisguild/enclave#c9b72d1e42d8e37a19473f423978ef76f92454d6" +source = "git+https://github.com/gnosisguild/enclave#ad8c74becdb97608f3279f6a6cb1feba9e68dd1c" dependencies = [ "ark-bn254 0.5.0", "ark-ff 0.5.0", @@ -3559,7 +3559,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4427,7 +4427,7 @@ dependencies = [ [[package]] name = "zkfhe-greco" version = "0.1.0" -source = "git+https://github.com/gnosisguild/zkfhe-generator#46d8579f8fc3c53d2c74e89e7c75c280f63e432f" +source = "git+https://github.com/gnosisguild/zkfhe-generator#9acbf0eaa73214646e472ef8df7499d1fce72ce0" dependencies = [ "anyhow", "ark-bn254 0.5.0", @@ -4442,7 +4442,6 @@ dependencies = [ "num-traits", "rand 0.8.5", "rayon", - "safe", "serde", "serde_json", "tempfile", @@ -4453,7 +4452,7 @@ dependencies = [ [[package]] name = "zkfhe-shared" version = "0.1.0" -source = "git+https://github.com/gnosisguild/zkfhe-generator#46d8579f8fc3c53d2c74e89e7c75c280f63e432f" +source = "git+https://github.com/gnosisguild/zkfhe-generator#9acbf0eaa73214646e472ef8df7499d1fce72ce0" dependencies = [ "anyhow", "ark-bn254 0.5.0", @@ -4466,6 +4465,7 @@ dependencies = [ "num-bigint", "num-traits", "rand 0.8.5", + "safe", "serde", "serde_json", "thiserror", From a50066dd3012abe6349249861f0c18565f9754c2 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 24 Dec 2025 10:36:55 +0100 Subject: [PATCH 05/13] chore: update lockfile for CRISP example --- examples/CRISP/Cargo.lock | 119 +++++++++++++++----------------------- 1 file changed, 48 insertions(+), 71 deletions(-) diff --git a/examples/CRISP/Cargo.lock b/examples/CRISP/Cargo.lock index fe80157b45..da9fadaae2 100644 --- a/examples/CRISP/Cargo.lock +++ b/examples/CRISP/Cargo.lock @@ -915,9 +915,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "954d1b2533b9b2c7959652df3076954ecb1122a28cc740aa84e7b0a49f6ac0a9" +checksum = "af67a0b0dcebe14244fc92002cd8d96ecbf65db4639d479f5fcd5805755a4c27" dependencies = [ "serde", "winnow", @@ -1014,9 +1014,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3412d52bb97c6c6cc27ccc28d4e6e8cf605469101193b50b0bd5813b1f990b5" +checksum = "2b77b56af09ead281337d06b1d036c88e2dc8a2e45da512a532476dbee94912b" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -1687,9 +1687,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byte-slice-cast" @@ -1738,9 +1738,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.49" +version = "1.2.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" dependencies = [ "find-msvc-tools", "jobserver", @@ -2228,18 +2228,18 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case 0.10.0", "proc-macro2 1.0.103", @@ -2353,7 +2353,7 @@ dependencies = [ "strum", "thiserror 1.0.69", "zkfhe-greco", - "zkfhe-shared 0.1.0 (git+https://github.com/gnosisguild/zkfhe-generator?rev=e2c02e6ef42348b2141293cc75f61a211c801a94)", + "zkfhe-shared", ] [[package]] @@ -2643,7 +2643,7 @@ dependencies = [ [[package]] name = "fhe" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "bincode", "doc-comment", @@ -2669,7 +2669,7 @@ dependencies = [ [[package]] name = "fhe-math" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "ethnum", "fhe-traits", @@ -2692,7 +2692,7 @@ dependencies = [ [[package]] name = "fhe-traits" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "rand 0.8.5", ] @@ -2700,7 +2700,7 @@ dependencies = [ [[package]] name = "fhe-util" version = "0.1.0-beta.7" -source = "git+https://github.com/gnosisguild/fhe.rs#f588e090d69488a9cffa2a891bd068a295553348" +source = "git+https://github.com/gnosisguild/fhe.rs#3824c52cb457c55551ffcdaeeaef9f3c53145a93" dependencies = [ "itertools 0.12.1", "num-bigint-dig", @@ -3509,9 +3509,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" [[package]] name = "jiff" @@ -4257,9 +4257,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd" [[package]] name = "portable-atomic-util" @@ -4332,7 +4332,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.23.9", + "toml_edit 0.23.10+spec-1.0.0", ] [[package]] @@ -4896,9 +4896,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags 2.10.0", "errno", @@ -4923,9 +4923,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "web-time", "zeroize", @@ -4962,14 +4962,14 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" [[package]] name = "safe" version = "0.1.7" -source = "git+https://github.com/gnosisguild/enclave#c9b72d1e42d8e37a19473f423978ef76f92454d6" +source = "git+https://github.com/gnosisguild/enclave#d4c7e455a8cce813760b454289c3e269710add81" dependencies = [ "ark-bn254 0.5.0", "ark-ff 0.5.0", @@ -5432,9 +5432,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff790eb176cc81bb8936aed0f7b9f14fc4670069a2d371b3e3b0ecce908b2cb3" +checksum = "5f92d01b5de07eaf324f7fca61cc6bd3d82bbc1de5b6c963e6fe79e86f36580d" dependencies = [ "paste", "proc-macro2 1.0.103", @@ -5485,9 +5485,9 @@ dependencies = [ [[package]] name = "taceo-poseidon2" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbf106fb8682ee4e057872a18f431828bd467c28d2ead469e4c84dbf6ce5ec6" +checksum = "ac59d3df4c827b3496bff929aebd6440997a5c2e946f46ff4fdd76f318447581" dependencies = [ "ark-bn254 0.5.0", "ark-ff 0.5.0", @@ -5504,9 +5504,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom 0.3.4", @@ -5753,9 +5753,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] @@ -5776,21 +5776,21 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.9" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap 2.12.1", - "toml_datetime 0.7.3", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "winnow", ] [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ "winnow", ] @@ -5871,9 +5871,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] @@ -6663,7 +6663,7 @@ dependencies = [ "serde", "serde_json", "zkfhe-greco", - "zkfhe-shared 0.1.0 (git+https://github.com/gnosisguild/zkfhe-generator)", + "zkfhe-shared", ] [[package]] @@ -6679,7 +6679,7 @@ dependencies = [ [[package]] name = "zkfhe-greco" version = "0.1.0" -source = "git+https://github.com/gnosisguild/zkfhe-generator#46d8579f8fc3c53d2c74e89e7c75c280f63e432f" +source = "git+https://github.com/gnosisguild/zkfhe-generator#9acbf0eaa73214646e472ef8df7499d1fce72ce0" dependencies = [ "anyhow", "ark-bn254 0.5.0", @@ -6694,18 +6694,17 @@ dependencies = [ "num-traits", "rand 0.8.5", "rayon", - "safe", "serde", "serde_json", "tempfile", "toml", - "zkfhe-shared 0.1.0 (git+https://github.com/gnosisguild/zkfhe-generator)", + "zkfhe-shared", ] [[package]] name = "zkfhe-shared" version = "0.1.0" -source = "git+https://github.com/gnosisguild/zkfhe-generator?rev=e2c02e6ef42348b2141293cc75f61a211c801a94#e2c02e6ef42348b2141293cc75f61a211c801a94" +source = "git+https://github.com/gnosisguild/zkfhe-generator#9acbf0eaa73214646e472ef8df7499d1fce72ce0" dependencies = [ "anyhow", "ark-bn254 0.5.0", @@ -6725,28 +6724,6 @@ dependencies = [ "toml", ] -[[package]] -name = "zkfhe-shared" -version = "0.1.0" -source = "git+https://github.com/gnosisguild/zkfhe-generator#46d8579f8fc3c53d2c74e89e7c75c280f63e432f" -dependencies = [ - "anyhow", - "ark-bn254 0.5.0", - "ark-ff 0.5.0", - "bigint-poly", - "chrono", - "fhe", - "fhe-math", - "fhe-traits", - "num-bigint", - "num-traits", - "rand 0.8.5", - "serde", - "serde_json", - "thiserror 1.0.69", - "toml", -] - [[package]] name = "zstd" version = "0.13.3" From 1c89819fa16da90af50216c7cc42807de9596ba3 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 24 Dec 2025 11:59:39 +0100 Subject: [PATCH 06/13] refactor: update activate functions in sdk and contracts --- crates/bfv-helpers/src/client.rs | 7 +++--- crates/wasm/src/lib.rs | 28 ++++++++++++++++++++- packages/enclave-contracts/package.json | 1 + packages/enclave-contracts/tasks/enclave.ts | 23 ++++++++++++++--- packages/enclave-sdk/src/enclave-sdk.ts | 19 ++++++++++++-- pnpm-lock.yaml | 3 +++ tests/integration/base.sh | 12 ++++----- tests/integration/persist.sh | 12 ++++----- 8 files changed, 83 insertions(+), 22 deletions(-) diff --git a/crates/bfv-helpers/src/client.rs b/crates/bfv-helpers/src/client.rs index 908e244398..c89948e179 100644 --- a/crates/bfv-helpers/src/client.rs +++ b/crates/bfv-helpers/src/client.rs @@ -136,7 +136,8 @@ pub fn compute_pk_commitment( plaintext_modulus: u64, moduli: Vec, ) -> Result<[u8; 32]> { - use shared::commitments::compute_pk_commitment as compute_pk_commitment_shared; + use shared::commitments::compute_pk_commitment as _compute_pk_commitment; + use shared::template::calculate_bit_width; let params = build_bfv_params_arc(degree, plaintext_modulus, &moduli, None); @@ -144,10 +145,10 @@ pub fn compute_pk_commitment( .map_err(|e| anyhow!("Error deserializing public key: {}", e))?; let (_, bounds) = GrecoBounds::compute(¶ms, 0)?; - let bit_pk = shared::template::calculate_bit_width(&bounds.pk_bounds[0].to_string())?; + let bit_pk = calculate_bit_width(&bounds.pk_bounds[0].to_string())?; let (pk0is, pk1is) = bfv_public_key_to_greco(&public_key, ¶ms); - let commitment_bigint = compute_pk_commitment_shared(&pk0is, &pk1is, bit_pk); + let commitment_bigint = _compute_pk_commitment(&pk0is, &pk1is, bit_pk); let bytes = commitment_bigint.to_bytes_be().1; let public_key_hash: [u8; 32] = bytes diff --git a/crates/wasm/src/lib.rs b/crates/wasm/src/lib.rs index 33dc069ce5..2de2751d95 100644 --- a/crates/wasm/src/lib.rs +++ b/crates/wasm/src/lib.rs @@ -5,7 +5,9 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use e3_bfv_helpers::{ - client::{bfv_encrypt, bfv_verifiable_encrypt}, + client::{ + bfv_encrypt, bfv_verifiable_encrypt, compute_pk_commitment as _compute_pk_commitment, + }, BfvParamSet, BfvParamSets, }; use serde::{Deserialize, Serialize}; @@ -41,6 +43,30 @@ pub fn bfv_encrypt_number( Ok(encrypted_data) } +#[wasm_bindgen] +/// A function to compute the public key commitment for a given public key. +/// +/// # Arguments +/// +/// * `public_key` - The public key to compute the commitment for +/// +/// # Returns +/// Returns a `Result, JsValue>` containing the commitment and any errors. +/// +/// # Panics +/// +/// Panics if the public key cannot be computed +pub fn compute_pk_commitment( + public_key: Vec, + degree: usize, + plaintext_modulus: u64, + moduli: Vec, +) -> Result, JsValue> { + let commitment = _compute_pk_commitment(public_key, degree, plaintext_modulus, moduli) + .map_err(|e| JsValue::from_str(&format!("{}", e)))?; + Ok(commitment.to_vec()) +} + #[wasm_bindgen] /// A function to encrypt a Vec value using BFV and default params. /// diff --git a/packages/enclave-contracts/package.json b/packages/enclave-contracts/package.json index 44b671f69b..2d068f9dd8 100644 --- a/packages/enclave-contracts/package.json +++ b/packages/enclave-contracts/package.json @@ -79,6 +79,7 @@ }, "devDependencies": { "@enclave-e3/config": "workspace:*", + "@enclave-e3/wasm": "workspace:*", "@nomicfoundation/hardhat-ethers": "4", "@nomicfoundation/hardhat-ethers-chai-matchers": "^3.0.0", "@nomicfoundation/hardhat-ignition": "^3.0.0", diff --git a/packages/enclave-contracts/tasks/enclave.ts b/packages/enclave-contracts/tasks/enclave.ts index bb033f11ef..08c20890ac 100644 --- a/packages/enclave-contracts/tasks/enclave.ts +++ b/packages/enclave-contracts/tasks/enclave.ts @@ -3,6 +3,7 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +import { compute_pk_commitment, get_bfv_params } from "@enclave-e3/wasm"; import { ZeroAddress, zeroPadValue } from "ethers"; import fs from "fs"; import { task } from "hardhat/config"; @@ -324,14 +325,21 @@ export const activateE3 = task("e3:activate", "Activate an E3 program") defaultValue: "", type: ArgumentType.STRING, }) + .addOption({ + name: "bfvParams", + description: "BFV parameters for the scheme", + defaultValue: "", + type: ArgumentType.STRING, + }) .setAction(async () => ({ - default: async ({ e3Id, publicKey: publicKeyArg, publicKeyFile }, hre) => { + default: async ( + { e3Id, publicKey: publicKeyArg, publicKeyFile, bfvParams }, + hre, + ) => { const publicKey = publicKeyArg || (publicKeyFile ? fs.readFileSync(publicKeyFile, "utf8").trim() : "") || process.env.PUBLIC_KEY; - const connection = await hre.network.connect(); - const { ethers } = connection; if (!publicKey) throw new Error("No public key provided!"); @@ -343,7 +351,14 @@ export const activateE3 = task("e3:activate", "Activate an E3 program") hre, }); - const publicKeyHash = ethers.id(publicKey); + const params = get_bfv_params(bfvParams); + const publicKeyHash = await compute_pk_commitment( + new Uint8Array(Buffer.from(publicKey, "hex")), + params.degree, + params.plaintextModulus, + params.moduli, + ); + const tx = await enclave.activate(e3Id, publicKey, publicKeyHash); console.log("Activating E3 program... ", tx.hash); diff --git a/packages/enclave-sdk/src/enclave-sdk.ts b/packages/enclave-sdk/src/enclave-sdk.ts index 82b7082255..e322322375 100644 --- a/packages/enclave-sdk/src/enclave-sdk.ts +++ b/packages/enclave-sdk/src/enclave-sdk.ts @@ -30,6 +30,7 @@ import { bfv_encrypt_vector, bfv_verifiable_encrypt_number, bfv_verifiable_encrypt_vector, + compute_pk_commitment, get_bfv_params, } from '@enclave-e3/wasm' import { generateProof } from './greco' @@ -132,6 +133,18 @@ export class EnclaveSDK { } } + public async computePublicKeyCommitment(publicKey: Uint8Array): Promise { + await initializeWasm() + const protocolParams = await this.getProtocolParams() + + return compute_pk_commitment( + publicKey, + protocolParams.degree, + protocolParams.plaintextModulus, + BigUint64Array.from(protocolParams.moduli), + ) + } + /** * Encrypt a number using the configured protocol * @param data - The number to encrypt @@ -326,12 +339,14 @@ export class EnclaveSDK { /** * Activate an E3 computation */ - public async activateE3(e3Id: bigint, publicKey: `0x${string}`, publicKeyHash: `0x${string}`, gasLimit?: bigint): Promise { + public async activateE3(e3Id: bigint, publicKey: `0x${string}`, gasLimit?: bigint): Promise { if (!this.initialized) { await this.initialize() } - return this.contractClient.activateE3(e3Id, publicKey, publicKeyHash, gasLimit) + const publicKeyHash = await this.computePublicKeyCommitment(new Uint8Array(Buffer.from(publicKey, 'hex'))) + + return this.contractClient.activateE3(e3Id, publicKey, `0x${Buffer.from(publicKeyHash).toString('hex')}`, gasLimit) } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c117a0cb98..49c1d34a77 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -452,6 +452,9 @@ importers: '@enclave-e3/config': specifier: workspace:* version: link:../enclave-config + '@enclave-e3/wasm': + specifier: workspace:* + version: link:../../crates/wasm '@nomicfoundation/hardhat-ethers': specifier: '4' version: 4.0.3(bufferutil@4.0.9)(hardhat@3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) diff --git a/tests/integration/base.sh b/tests/integration/base.sh index 1a9fb9bcd9..9f8b2897f0 100755 --- a/tests/integration/base.sh +++ b/tests/integration/base.sh @@ -54,12 +54,12 @@ pnpm ciphernode:add --ciphernode-address $CIPHERNODE_ADDRESS_5 --network localho heading "Request Committee" ENCODED_PARAMS=0x$($SCRIPT_DIR/lib/pack_e3_params.sh \ - --moduli 0x800000022a0001 \ - --moduli 0x800000021a0001 \ - --moduli 0x80000002120001 \ - --moduli 0x80000001f60001 \ + --moduli 0x00800000022a0001 \ + --moduli 0x00800000021a0001 \ + --moduli 0x0080000002120001 \ + --moduli 0x0080000001f60001 \ --degree 8192 \ - --plaintext-modulus 1032193) + --plaintext-modulus 1000) sleep 4 @@ -80,7 +80,7 @@ $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output heading "Mock activate e3-id" PUBLIC_KEY_FILE=/tmp/enclave-public-key.txt echo "0x${PUBLIC_KEY}" > $PUBLIC_KEY_FILE -pnpm -s e3:activate --e3-id 0 --network localhost --public-key-file $PUBLIC_KEY_FILE +pnpm -s e3:activate --e3-id 0 --network localhost --public-key-file $PUBLIC_KEY_FILE --bfv-params "SET_8192_1000_4" heading "Mock publish input e3-id" pnpm e3:publishInput --network localhost --e3-id 0 --data 0x12345678 diff --git a/tests/integration/persist.sh b/tests/integration/persist.sh index 16187d3fc6..f633103d33 100755 --- a/tests/integration/persist.sh +++ b/tests/integration/persist.sh @@ -50,12 +50,12 @@ pnpm ciphernode:add --ciphernode-address $CIPHERNODE_ADDRESS_5 --network localho heading "Request Committee" ENCODED_PARAMS=0x$($SCRIPT_DIR/lib/pack_e3_params.sh \ - --moduli 0x800000022a0001 \ - --moduli 0x800000021a0001 \ - --moduli 0x80000002120001 \ - --moduli 0x80000001f60001 \ + --moduli 0x00800000022a0001 \ + --moduli 0x00800000021a0001 \ + --moduli 0x0080000002120001 \ + --moduli 0x0080000001f60001 \ --degree 8192 \ - --plaintext-modulus 1032193) + --plaintext-modulus 1000) pnpm committee:new \ --network localhost \ @@ -85,7 +85,7 @@ heading "Mock activate e3-id" PUBLIC_KEY_FILE=/tmp/enclave-public-key.txt echo "0x${PUBLIC_KEY}" > $PUBLIC_KEY_FILE -pnpm -s e3:activate --e3-id 0 --network localhost --public-key-file $PUBLIC_KEY_FILE +pnpm -s e3:activate --e3-id 0 --network localhost --public-key-file $PUBLIC_KEY_FILE --bfv-params "SET_8192_1000_4" heading "Mock publish input e3-id" pnpm e3:publishInput --network localhost --e3-id 0 --data 0x12345678 From 219e3c69cf99a9caf1c0d178c099e42ce6de0cce Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 24 Dec 2025 13:18:45 +0100 Subject: [PATCH 07/13] refactor: remove public key from activate function --- crates/evm-helpers/src/contracts.rs | 20 +--- crates/indexer/src/indexer.rs | 4 +- examples/CRISP/server/src/cli/commands.rs | 13 +- examples/CRISP/server/src/server/indexer.rs | 23 +--- .../interfaces/IEnclave.sol/IEnclave.json | 16 +-- .../enclave-contracts/contracts/Enclave.sol | 17 +-- .../contracts/interfaces/IE3.sol | 2 +- .../contracts/interfaces/IEnclave.sol | 12 +- .../contracts/test/MockCiphernodeRegistry.sol | 4 +- packages/enclave-contracts/package.json | 1 - packages/enclave-contracts/tasks/enclave.ts | 41 +------ .../enclave-contracts/test/Enclave.spec.ts | 112 +++++++----------- packages/enclave-sdk/src/contract-client.ts | 6 +- packages/enclave-sdk/src/enclave-sdk.ts | 6 +- pnpm-lock.yaml | 3 - .../client/src/pages/steps/ActivateE3.tsx | 2 +- templates/default/tests/integration.spec.ts | 4 +- tests/integration/base.sh | 6 +- tests/integration/persist.sh | 6 +- 19 files changed, 80 insertions(+), 218 deletions(-) diff --git a/crates/evm-helpers/src/contracts.rs b/crates/evm-helpers/src/contracts.rs index ca401c6d0c..e0f7b3cc32 100644 --- a/crates/evm-helpers/src/contracts.rs +++ b/crates/evm-helpers/src/contracts.rs @@ -75,7 +75,7 @@ sol! { mapping(uint256 e3Id => bytes params) public e3Params; mapping(address e3Program => bool allowed) public e3Programs; function request(E3RequestParams calldata requestParams) external returns (uint256 e3Id, E3 memory e3); - function activate(uint256 e3Id,bytes calldata publicKey,bytes32 publicKeyHash) external returns (bool success); + function activate(uint256 e3Id) external returns (bool success); function enableE3Program(address e3Program) public onlyOwner returns (bool success); function publishInput(uint256 e3Id, bytes calldata data) external returns (bool success); function publishCiphertextOutput(uint256 e3Id, bytes calldata ciphertextOutput, bytes calldata proof) external returns (bool success); @@ -137,13 +137,8 @@ pub trait EnclaveWrite { custom_params: Bytes, ) -> Result<(TransactionReceipt, U256)>; - /// Activate an E3 with a public key - async fn activate( - &self, - e3_id: U256, - pub_key: Bytes, - pub_key_hash: FixedBytes<32>, - ) -> Result; + /// Activate an E3 + async fn activate(&self, e3_id: U256) -> Result; /// Enable an E3 program async fn enable_e3_program(&self, e3_program: Address) -> Result; @@ -405,12 +400,7 @@ impl EnclaveWrite for EnclaveContract { Ok((receipt, e3_id)) } - async fn activate( - &self, - e3_id: U256, - pub_key: Bytes, - pub_key_hash: FixedBytes<32>, - ) -> Result { + async fn activate(&self, e3_id: U256) -> Result { let _guard = NONCE_LOCK.lock().await; let wallet_addr = self .wallet_address @@ -418,7 +408,7 @@ impl EnclaveWrite for EnclaveContract { let nonce = get_next_nonce(&*self.provider, wallet_addr).await?; let contract = Enclave::new(self.contract_address, &self.provider); - let builder = contract.activate(e3_id, pub_key, pub_key_hash).nonce(nonce); + let builder = contract.activate(e3_id).nonce(nonce); let receipt = builder.send().await?.get_receipt().await?; Ok(receipt) diff --git a/crates/indexer/src/indexer.rs b/crates/indexer/src/indexer.rs index 155823c962..68e6eeb431 100644 --- a/crates/indexer/src/indexer.rs +++ b/crates/indexer/src/indexer.rs @@ -336,10 +336,10 @@ impl EnclaveIndexer { let db = ctx.store(); let enclave_address = ctx.enclave_address(); println!( - "E3Activated: id={}, expiration={}, pubkey=0x{}...", + "E3Activated: id={}, expiration={}, pubkey_hash=0x{}...", e.e3Id, e.expiration, - hex::encode(&e.committeePublicKey[..8.min(e.committeePublicKey.len())]) + hex::encode(&e.committeePublicKey[..16.min(e.committeePublicKey.len())]) ); let e3_id = u64_try_from(e.e3Id)?; let e3 = contract.get_e3(e.e3Id).await?; diff --git a/examples/CRISP/server/src/cli/commands.rs b/examples/CRISP/server/src/cli/commands.rs index ac9541034d..63c610993b 100644 --- a/examples/CRISP/server/src/cli/commands.rs +++ b/examples/CRISP/server/src/cli/commands.rs @@ -206,20 +206,9 @@ pub async fn activate_e3_round() -> Result<(), Box>, ) -> eyre::Result<()> { // If not activated activate - let params = get_default_paramset(); - - let public_key_hash = compute_pk_commitment( - event.publicKey.to_vec(), - params.degree, - params.plaintext_modulus, - params.moduli.to_vec(), - ) - .map_err(|e| eyre::eyre!("Failed to compute PK commitment: {}", e))?; - - let tx = ctx - .contract() - .activate( - event.e3Id, - event.publicKey, - FixedBytes::from(public_key_hash), - ) - .await?; + let tx = ctx.contract().activate(event.e3Id).await?; info!( "[e3_id={}] E3 activated with tx: {:?}", event.e3Id, tx.transaction_hash 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 7c4306f7a4..c68510e8de 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -78,9 +78,9 @@ }, { "indexed": false, - "internalType": "bytes", + "internalType": "bytes32", "name": "committeePublicKey", - "type": "bytes" + "type": "bytes32" } ], "name": "E3Activated", @@ -342,16 +342,6 @@ "internalType": "uint256", "name": "e3Id", "type": "uint256" - }, - { - "internalType": "bytes", - "name": "publicKey", - "type": "bytes" - }, - { - "internalType": "bytes32", - "name": "publicKeyHash", - "type": "bytes32" } ], "name": "activate", @@ -942,5 +932,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-c50eb34c88f83554dbf0f8f704a3681d5dfc5322" + "buildInfoId": "solc-0_8_28-88174d36ce3bdcc25dc9fa934311dccaec8cde55" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index 8221453b00..0f0406a07a 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -295,11 +295,7 @@ contract Enclave is IEnclave, OwnableUpgradeable { } /// @inheritdoc IEnclave - function activate( - uint256 e3Id, - bytes calldata publicKey, - bytes32 publicKeyHash - ) external returns (bool success) { + function activate(uint256 e3Id) external returns (bool success) { E3 memory e3 = getE3(e3Id); require(e3.expiration == 0, E3AlreadyActivated(e3Id)); @@ -307,18 +303,13 @@ contract Enclave is IEnclave, OwnableUpgradeable { // TODO: handle what happens to the payment if the start window has passed. require(e3.startWindow[1] >= block.timestamp, E3Expired()); - bytes32 registryPublicKeyHash = ciphernodeRegistry.committeePublicKey( - e3Id - ); - require( - publicKeyHash == registryPublicKeyHash, - CommitteeSelectionFailed() - ); + bytes32 publicKeyHash = ciphernodeRegistry.committeePublicKey(e3Id); + uint256 expiresAt = block.timestamp + e3.duration; e3s[e3Id].expiration = expiresAt; e3s[e3Id].committeePublicKey = publicKeyHash; - emit E3Activated(e3Id, expiresAt, publicKey); + emit E3Activated(e3Id, expiresAt, publicKeyHash); return true; } diff --git a/packages/enclave-contracts/contracts/interfaces/IE3.sol b/packages/enclave-contracts/contracts/interfaces/IE3.sol index e18023d8df..7f8270cbf4 100644 --- a/packages/enclave-contracts/contracts/interfaces/IE3.sol +++ b/packages/enclave-contracts/contracts/interfaces/IE3.sol @@ -24,7 +24,7 @@ import { IDecryptionVerifier } from "./IDecryptionVerifier.sol"; * @param e3ProgramParams ABI encoded computation parameters specific to the E3 program * @param customParams Arbitrary ABI-encoded application-defined parameters. * @param decryptionVerifier Address of the output verifier contract for decryption verification - * @param committeePublicKey The public key of the selected committee for this computation + * @param committeePublicKey Hash of the public key of the selected committee for this computation * @param ciphertextOutput Hash of the encrypted output data produced by the computation * @param plaintextOutput Decrypted output data after committee decryption */ diff --git a/packages/enclave-contracts/contracts/interfaces/IEnclave.sol b/packages/enclave-contracts/contracts/interfaces/IEnclave.sol index 8c690f46df..ba95e0da84 100644 --- a/packages/enclave-contracts/contracts/interfaces/IEnclave.sol +++ b/packages/enclave-contracts/contracts/interfaces/IEnclave.sol @@ -27,11 +27,11 @@ interface IEnclave { /// @notice This event MUST be emitted when an Encrypted Execution Environment (E3) is successfully activated. /// @param e3Id ID of the E3. /// @param expiration Timestamp when committee duties expire. - /// @param committeePublicKey Public key of the committee. + /// @param committeePublicKey Hash of the public key of the committee. event E3Activated( uint256 e3Id, uint256 expiration, - bytes committeePublicKey + bytes32 committeePublicKey ); /// @notice This event MUST be emitted when an input to an Encrypted Execution Environment (E3) is @@ -151,14 +151,8 @@ interface IEnclave { /// @dev This function MUST revert if the given E3 has not yet been requested. /// @dev This function MUST revert if the selected node committee has not yet published a public key. /// @param e3Id ID of the E3. - /// @param publicKey Public key of the committee. - /// @param publicKeyHash Hash of the committee's public key. /// @return success True if the E3 was successfully activated. - function activate( - uint256 e3Id, - bytes calldata publicKey, - bytes32 publicKeyHash - ) external returns (bool success); + function activate(uint256 e3Id) external returns (bool success); /// @notice This function should be called to publish input data for Encrypted Execution Environment (E3). /// @dev This function MUST revert if the E3 is not yet activated. diff --git a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol index 3f1b0f64d8..8d2a19ce53 100644 --- a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol @@ -89,6 +89,8 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { } contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { + error CommitteeNotPublished(); + function requestCommittee( uint256, uint256, @@ -102,7 +104,7 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { } function committeePublicKey(uint256) external pure returns (bytes32) { - return bytes32(0); + revert CommitteeNotPublished(); } function isCiphernodeEligible(address) external pure returns (bool) { diff --git a/packages/enclave-contracts/package.json b/packages/enclave-contracts/package.json index 2d068f9dd8..44b671f69b 100644 --- a/packages/enclave-contracts/package.json +++ b/packages/enclave-contracts/package.json @@ -79,7 +79,6 @@ }, "devDependencies": { "@enclave-e3/config": "workspace:*", - "@enclave-e3/wasm": "workspace:*", "@nomicfoundation/hardhat-ethers": "4", "@nomicfoundation/hardhat-ethers-chai-matchers": "^3.0.0", "@nomicfoundation/hardhat-ignition": "^3.0.0", diff --git a/packages/enclave-contracts/tasks/enclave.ts b/packages/enclave-contracts/tasks/enclave.ts index 08c20890ac..6a2717049f 100644 --- a/packages/enclave-contracts/tasks/enclave.ts +++ b/packages/enclave-contracts/tasks/enclave.ts @@ -3,7 +3,6 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -import { compute_pk_commitment, get_bfv_params } from "@enclave-e3/wasm"; import { ZeroAddress, zeroPadValue } from "ethers"; import fs from "fs"; import { task } from "hardhat/config"; @@ -313,36 +312,8 @@ export const activateE3 = task("e3:activate", "Activate an E3 program") defaultValue: 0, type: ArgumentType.INT, }) - .addOption({ - name: "publicKey", - description: "public key of the committee", - defaultValue: "", - type: ArgumentType.STRING, - }) - .addOption({ - name: "publicKeyFile", - description: "path to file containing the public key", - defaultValue: "", - type: ArgumentType.STRING, - }) - .addOption({ - name: "bfvParams", - description: "BFV parameters for the scheme", - defaultValue: "", - type: ArgumentType.STRING, - }) .setAction(async () => ({ - default: async ( - { e3Id, publicKey: publicKeyArg, publicKeyFile, bfvParams }, - hre, - ) => { - const publicKey = - publicKeyArg || - (publicKeyFile ? fs.readFileSync(publicKeyFile, "utf8").trim() : "") || - process.env.PUBLIC_KEY; - - if (!publicKey) throw new Error("No public key provided!"); - + default: async ({ e3Id }, hre) => { const { deployAndSaveEnclave } = await import( "../scripts/deployAndSave/enclave" ); @@ -351,15 +322,7 @@ export const activateE3 = task("e3:activate", "Activate an E3 program") hre, }); - const params = get_bfv_params(bfvParams); - const publicKeyHash = await compute_pk_commitment( - new Uint8Array(Buffer.from(publicKey, "hex")), - params.degree, - params.plaintextModulus, - params.moduli, - ); - - const tx = await enclave.activate(e3Id, publicKey, publicKeyHash); + const tx = await enclave.activate(e3Id); console.log("Activating E3 program... ", tx.hash); await tx.wait(); diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 586732fb7e..d3517c8d69 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -939,9 +939,7 @@ describe("Enclave", function () { it("reverts if E3 does not exist", async function () { const { enclave } = await loadFixture(setup); - await expect( - enclave.activate(0, ethers.ZeroHash, ethers.id(ethers.ZeroHash)), - ) + await expect(enclave.activate(0)) .to.be.revertedWithCustomError(enclave, "E3DoesNotExist") .withArgs(0); }); @@ -975,10 +973,8 @@ describe("Enclave", function () { ); await expect(enclave.getE3(0)).to.not.be.revert(ethers); - await expect(enclave.activate(0, data, dataHash)).to.not.be.revert( - ethers, - ); - await expect(enclave.activate(0, data, dataHash)) + await expect(enclave.activate(0)).to.not.be.revert(ethers); + await expect(enclave.activate(0)) .to.be.revertedWithCustomError(enclave, "E3AlreadyActivated") .withArgs(0); }); @@ -999,9 +995,10 @@ describe("Enclave", function () { customParams: request.customParams, }); - await expect( - enclave.activate(0, ethers.ZeroHash, ethers.id(ethers.ZeroHash)), - ).to.be.revertedWithCustomError(enclave, "E3NotReady"); + await expect(enclave.activate(0)).to.be.revertedWithCustomError( + enclave, + "E3NotReady", + ); }); it("reverts if E3 start has expired", async function () { const { @@ -1035,9 +1032,10 @@ describe("Enclave", function () { await mine(2, { interval: 2000 }); - await expect( - enclave.activate(e3Id, data, dataHash), - ).to.be.revertedWithCustomError(enclave, "E3Expired"); + await expect(enclave.activate(e3Id)).to.be.revertedWithCustomError( + enclave, + "E3Expired", + ); }); it("reverts if ciphernodeRegistry does not return a public key", async function () { const { enclave, request, usdcToken } = await loadFixture(setup); @@ -1056,9 +1054,10 @@ describe("Enclave", function () { customParams: request.customParams, }); - await expect( - enclave.activate(0, ethers.ZeroHash, ethers.id(ethers.ZeroHash)), - ).to.be.revertedWithCustomError(enclave, "E3NotReady"); + await expect(enclave.activate(0)).to.be.revertedWithCustomError( + enclave, + "E3NotReady", + ); }); it("reverts if E3 start has expired", async function () { const { @@ -1089,19 +1088,13 @@ describe("Enclave", function () { await time.increaseTo(currentTime + request.duration + 100); - await expect( - enclave.activate(e3Id, data, dataHash), - ).to.be.revertedWithCustomError(enclave, "E3Expired"); + await expect(enclave.activate(e3Id)).to.be.revertedWithCustomError( + enclave, + "E3Expired", + ); }); it("reverts if ciphernodeRegistry does not return a public key", async function () { - const { - enclave, - request, - ciphernodeRegistryContract, - usdcToken, - operator1, - operator2, - } = await loadFixture(setup); + const { enclave, request, usdcToken } = await loadFixture(setup); await makeRequest(enclave, usdcToken, request); @@ -1113,24 +1106,12 @@ describe("Enclave", function () { await enclave.setCiphernodeRegistry(nextRegistry); - await expect( - enclave.activate(0, ethers.ZeroHash, ethers.id(ethers.ZeroHash)), - ).to.be.revertedWithCustomError(enclave, "CommitteeSelectionFailed"); - - await enclave.setCiphernodeRegistry(prevRegistry); - - await setupAndPublishCommittee( - ciphernodeRegistryContract, - 0, - [await operator1.getAddress(), await operator2.getAddress()], - data, - operator1, - operator2, + await expect(enclave.activate(0)).to.be.revertedWithCustomError( + reg.mockCiphernodeRegistryEmptyKey, + "CommitteeNotPublished", ); - await expect(enclave.activate(0, data, dataHash)).not.to.be.revert( - ethers, - ); + await enclave.setCiphernodeRegistry(prevRegistry); }); it("sets committeePublicKey correctly", async () => { @@ -1170,7 +1151,7 @@ describe("Enclave", function () { let e3 = await enclave.getE3(e3Id); expect(e3.committeePublicKey).to.not.equal(publicKey); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); e3 = await enclave.getE3(e3Id); expect(e3.committeePublicKey).to.equal(publicKey); @@ -1206,9 +1187,7 @@ describe("Enclave", function () { operator2, ); - expect( - await enclave.activate.staticCall(e3Id, data, dataHash), - ).to.be.equal(true); + expect(await enclave.activate.staticCall(e3Id)).to.be.equal(true); }); it("emits E3Activated event", async () => { const { @@ -1241,10 +1220,7 @@ describe("Enclave", function () { operator2, ); - await expect(enclave.activate(e3Id, data, dataHash)).to.emit( - enclave, - "E3Activated", - ); + await expect(enclave.activate(e3Id)).to.emit(enclave, "E3Activated"); }); }); @@ -1306,7 +1282,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(0, data, dataHash); + await enclave.activate(0); await expect( enclave.publishInput(0, "0xaabbcc"), ).to.be.revertedWithCustomError(enclave, "InvalidInput"); @@ -1340,7 +1316,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(0, data, dataHash); + await enclave.activate(0); await mine(2, { interval: request.duration }); @@ -1380,7 +1356,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(0, data, dataHash); + await enclave.activate(0); await enclave.publishInput(0, inputData); await makeRequest(enclave, usdcToken, { @@ -1401,7 +1377,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(1, data, dataHash); + await enclave.activate(1); await enclave.publishInput(1, inputData); }); it("returns true if input is published successfully", async function () { @@ -1433,7 +1409,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(0, data, dataHash); + await enclave.activate(0); expect(await enclave.publishInput.staticCall(0, inputData)).to.equal( true, @@ -1490,7 +1466,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await expect( enclave.publishCiphertextOutput(e3Id, "0x", "0x"), @@ -1525,7 +1501,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await mine(2, { interval: request.duration }); expect(await enclave.publishCiphertextOutput(e3Id, data, proof)); await expect(enclave.publishCiphertextOutput(e3Id, data, proof)) @@ -1564,7 +1540,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await mine(2, { interval: request.duration }); await expect( enclave.publishCiphertextOutput(e3Id, "0x", "0x"), @@ -1594,7 +1570,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await mine(2, { interval: request.duration }); expect(await enclave.publishCiphertextOutput(e3Id, data, proof)); const e3 = await enclave.getE3(e3Id); @@ -1624,7 +1600,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await mine(2, { interval: request.duration }); expect( await enclave.publishCiphertextOutput.staticCall(e3Id, data, proof), @@ -1654,7 +1630,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await mine(2, { interval: request.duration }); await expect(enclave.publishCiphertextOutput(e3Id, data, proof)) .to.emit(enclave, "CiphertextOutputPublished") @@ -1708,7 +1684,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await expect(enclave.publishPlaintextOutput(e3Id, data, "0x")) .to.be.revertedWithCustomError(enclave, "CiphertextOutputNotPublished") .withArgs(e3Id); @@ -1737,7 +1713,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await mine(2, { interval: request.duration }); await enclave.publishCiphertextOutput(e3Id, data, proof); await enclave.publishPlaintextOutput(e3Id, data, proof); @@ -1772,7 +1748,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await mine(2, { interval: request.duration }); await enclave.publishCiphertextOutput(e3Id, data, proof); await expect(enclave.publishPlaintextOutput(e3Id, data, "0x")) @@ -1803,7 +1779,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await mine(2, { interval: request.duration }); await enclave.publishCiphertextOutput(e3Id, data, proof); expect(await enclave.publishPlaintextOutput(e3Id, data, proof)); @@ -1835,7 +1811,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await mine(2, { interval: request.duration }); await enclave.publishCiphertextOutput(e3Id, data, proof); expect( @@ -1866,7 +1842,7 @@ describe("Enclave", function () { operator1, operator2, ); - await enclave.activate(e3Id, data, dataHash); + await enclave.activate(e3Id); await mine(2, { interval: request.duration }); await enclave.publishCiphertextOutput(e3Id, data, proof); await expect(await enclave.publishPlaintextOutput(e3Id, data, proof)) diff --git a/packages/enclave-sdk/src/contract-client.ts b/packages/enclave-sdk/src/contract-client.ts index dc290acc4b..5271ed9edd 100644 --- a/packages/enclave-sdk/src/contract-client.ts +++ b/packages/enclave-sdk/src/contract-client.ts @@ -159,9 +159,9 @@ export class ContractClient { /** * Activate an E3 computation - * activate(uint256 e3Id, bytes memory publicKey) + * activate(uint256 e3Id) */ - public async activateE3(e3Id: bigint, publicKey: `0x${string}`, publicKeyHash: `0x${string}`, gasLimit?: bigint): Promise { + public async activateE3(e3Id: bigint, gasLimit?: bigint): Promise { if (!this.walletClient) { throw new SDKError('Wallet client required for write operations', 'NO_WALLET') } @@ -180,7 +180,7 @@ export class ContractClient { address: this.addresses.enclave, abi: Enclave__factory.abi, functionName: 'activate', - args: [e3Id, publicKey, publicKeyHash], + args: [e3Id], account, gas: gasLimit, }) diff --git a/packages/enclave-sdk/src/enclave-sdk.ts b/packages/enclave-sdk/src/enclave-sdk.ts index e322322375..a19e98c545 100644 --- a/packages/enclave-sdk/src/enclave-sdk.ts +++ b/packages/enclave-sdk/src/enclave-sdk.ts @@ -339,14 +339,12 @@ export class EnclaveSDK { /** * Activate an E3 computation */ - public async activateE3(e3Id: bigint, publicKey: `0x${string}`, gasLimit?: bigint): Promise { + public async activateE3(e3Id: bigint, gasLimit?: bigint): Promise { if (!this.initialized) { await this.initialize() } - const publicKeyHash = await this.computePublicKeyCommitment(new Uint8Array(Buffer.from(publicKey, 'hex'))) - - return this.contractClient.activateE3(e3Id, publicKey, `0x${Buffer.from(publicKeyHash).toString('hex')}`, gasLimit) + return this.contractClient.activateE3(e3Id, gasLimit) } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 49c1d34a77..c117a0cb98 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -452,9 +452,6 @@ importers: '@enclave-e3/config': specifier: workspace:* version: link:../enclave-config - '@enclave-e3/wasm': - specifier: workspace:* - version: link:../../crates/wasm '@nomicfoundation/hardhat-ethers': specifier: '4' version: 4.0.3(bufferutil@4.0.9)(hardhat@3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) diff --git a/templates/default/client/src/pages/steps/ActivateE3.tsx b/templates/default/client/src/pages/steps/ActivateE3.tsx index f71da5d0ec..bd7277b046 100644 --- a/templates/default/client/src/pages/steps/ActivateE3.tsx +++ b/templates/default/client/src/pages/steps/ActivateE3.tsx @@ -71,7 +71,7 @@ const ActivateE3: React.FC = () => { setRequestError(null) try { - const hash = await activateE3(e3State.id, e3State.publicKey) + const hash = await activateE3(e3State.id) setLocalTransactionHash(hash) setLastTransactionHash(hash) setRequestSuccess(true) diff --git a/templates/default/tests/integration.spec.ts b/templates/default/tests/integration.spec.ts index c9c6abe08b..c2d9ec21b9 100644 --- a/templates/default/tests/integration.spec.ts +++ b/templates/default/tests/integration.spec.ts @@ -217,11 +217,11 @@ describe('Integration', () => { assert.strictEqual(state.type, 'committee_published') assert.strictEqual(state.publicKey, event.data.publicKey) - let { e3Id, publicKey } = state + let { e3Id } = state // ACTIVATION phase event = await waitForEvent(EnclaveEventType.E3_ACTIVATED, async () => { - await sdk.activateE3(e3Id, publicKey) + await sdk.activateE3(e3Id) }) state = store.get(0n) diff --git a/tests/integration/base.sh b/tests/integration/base.sh index 9f8b2897f0..7bf9ca09ff 100755 --- a/tests/integration/base.sh +++ b/tests/integration/base.sh @@ -72,15 +72,11 @@ pnpm committee:new \ waiton "$SCRIPT_DIR/output/pubkey.bin" -PUBLIC_KEY=$(xxd -p -c 10000000 "$SCRIPT_DIR/output/pubkey.bin") - heading "Mock encrypted plaintext" $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" heading "Mock activate e3-id" -PUBLIC_KEY_FILE=/tmp/enclave-public-key.txt -echo "0x${PUBLIC_KEY}" > $PUBLIC_KEY_FILE -pnpm -s e3:activate --e3-id 0 --network localhost --public-key-file $PUBLIC_KEY_FILE --bfv-params "SET_8192_1000_4" +pnpm -s e3:activate --e3-id 0 --network localhost heading "Mock publish input e3-id" pnpm e3:publishInput --network localhost --e3-id 0 --data 0x12345678 diff --git a/tests/integration/persist.sh b/tests/integration/persist.sh index f633103d33..4cd4f7b45a 100755 --- a/tests/integration/persist.sh +++ b/tests/integration/persist.sh @@ -65,8 +65,6 @@ pnpm committee:new \ --threshold-total 5 waiton "$SCRIPT_DIR/output/pubkey.bin" -PUBLIC_KEY=$(xxd -p -c 10000000 "$SCRIPT_DIR/output/pubkey.bin") - # kill aggregator enclave_nodes_stop ag @@ -83,9 +81,7 @@ $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output heading "Mock activate e3-id" -PUBLIC_KEY_FILE=/tmp/enclave-public-key.txt -echo "0x${PUBLIC_KEY}" > $PUBLIC_KEY_FILE -pnpm -s e3:activate --e3-id 0 --network localhost --public-key-file $PUBLIC_KEY_FILE --bfv-params "SET_8192_1000_4" +pnpm -s e3:activate --e3-id 0 --network localhost heading "Mock publish input e3-id" pnpm e3:publishInput --network localhost --e3-id 0 --data 0x12345678 From 56e6c519b4b95ebc58d66e35e2dadef3870f2c38 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 24 Dec 2025 13:20:07 +0100 Subject: [PATCH 08/13] chore: fix linting errors --- packages/enclave-contracts/test/Enclave.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index d3517c8d69..2eaaa491e2 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -55,7 +55,6 @@ describe("Enclave", function () { ); const data = "0xda7a"; - const dataHash = ethers.id(data); const proof = "0x1337"; // Hash function used to compute the tree nodes. From 866c45f78c586a2a4a8f48de031c998b9b32e552 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 24 Dec 2025 14:17:33 +0100 Subject: [PATCH 09/13] test: update bfv params --- tests/integration/base.sh | 10 +++++----- tests/integration/persist.sh | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/integration/base.sh b/tests/integration/base.sh index 7bf9ca09ff..1d5b06a072 100755 --- a/tests/integration/base.sh +++ b/tests/integration/base.sh @@ -54,12 +54,12 @@ pnpm ciphernode:add --ciphernode-address $CIPHERNODE_ADDRESS_5 --network localho heading "Request Committee" ENCODED_PARAMS=0x$($SCRIPT_DIR/lib/pack_e3_params.sh \ - --moduli 0x00800000022a0001 \ - --moduli 0x00800000021a0001 \ - --moduli 0x0080000002120001 \ - --moduli 0x0080000001f60001 \ + --moduli 0x800000022a0001 \ + --moduli 0x800000021a0001 \ + --moduli 0x80000002120001 \ + --moduli 0x80000001f60001 \ --degree 8192 \ - --plaintext-modulus 1000) + --plaintext-modulus 1032193) sleep 4 diff --git a/tests/integration/persist.sh b/tests/integration/persist.sh index 4cd4f7b45a..809092120c 100755 --- a/tests/integration/persist.sh +++ b/tests/integration/persist.sh @@ -50,12 +50,12 @@ pnpm ciphernode:add --ciphernode-address $CIPHERNODE_ADDRESS_5 --network localho heading "Request Committee" ENCODED_PARAMS=0x$($SCRIPT_DIR/lib/pack_e3_params.sh \ - --moduli 0x00800000022a0001 \ - --moduli 0x00800000021a0001 \ - --moduli 0x0080000002120001 \ - --moduli 0x0080000001f60001 \ + --moduli 0x800000022a0001 \ + --moduli 0x800000021a0001 \ + --moduli 0x80000002120001 \ + --moduli 0x80000001f60001 \ --degree 8192 \ - --plaintext-modulus 1000) + --plaintext-modulus 1032193) pnpm committee:new \ --network localhost \ From 70376051e4e6c909ce80e72c5360516ec59a2d90 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 24 Dec 2025 15:52:18 +0100 Subject: [PATCH 10/13] fix: set correct public key in E3Activated event --- crates/bfv-helpers/src/client.rs | 16 +++++++-- crates/evm-helpers/src/events.rs | 2 +- crates/indexer/src/indexer.rs | 61 ++++++++++++++++++++++++++++---- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/crates/bfv-helpers/src/client.rs b/crates/bfv-helpers/src/client.rs index c89948e179..4b2eebf979 100644 --- a/crates/bfv-helpers/src/client.rs +++ b/crates/bfv-helpers/src/client.rs @@ -151,9 +151,21 @@ pub fn compute_pk_commitment( let commitment_bigint = _compute_pk_commitment(&pk0is, &pk1is, bit_pk); let bytes = commitment_bigint.to_bytes_be().1; - let public_key_hash: [u8; 32] = bytes + + if bytes.len() > 32 { + return Err(anyhow!( + "Commitment must be at most 32 bytes, got {}", + bytes.len() + )); + } + + let mut padded_bytes = vec![0u8; 32]; + let start_idx = 32 - bytes.len(); + padded_bytes[start_idx..].copy_from_slice(&bytes); + + let public_key_hash: [u8; 32] = padded_bytes .try_into() - .map_err(|_| anyhow!("Commitment must be exactly 32 bytes"))?; + .map_err(|_| anyhow!("Failed to convert padded bytes to array"))?; Ok(public_key_hash) } diff --git a/crates/evm-helpers/src/events.rs b/crates/evm-helpers/src/events.rs index cfeb6136b1..3706890623 100644 --- a/crates/evm-helpers/src/events.rs +++ b/crates/evm-helpers/src/events.rs @@ -10,7 +10,7 @@ use alloy::sol; sol! { #[derive(Debug)] - event E3Activated(uint256 e3Id, uint256 expiration, bytes committeePublicKey); + event E3Activated(uint256 e3Id, uint256 expiration, bytes32 committeePublicKey); #[derive(Debug)] event E3Requested(uint256 e3Id, E3 e3, IE3Program indexed e3Program); diff --git a/crates/indexer/src/indexer.rs b/crates/indexer/src/indexer.rs index 68e6eeb431..6795fc9b9a 100644 --- a/crates/indexer/src/indexer.rs +++ b/crates/indexer/src/indexer.rs @@ -19,7 +19,9 @@ use e3_evm_helpers::{ EnclaveContract, EnclaveContractFactory, EnclaveRead, ProviderType, ReadOnly, ReadWrite, }, event_listener::EventListener, - events::{CiphertextOutputPublished, E3Activated, PlaintextOutputPublished}, + events::{ + CiphertextOutputPublished, CommitteePublished, E3Activated, PlaintextOutputPublished, + }, }; use eyre::eyre; use eyre::Result; @@ -329,19 +331,65 @@ impl EnclaveIndexer { .await; } + async fn register_committee_published(&mut self) -> Result<()> { + self.add_event_handler(move |e: CommitteePublished, ctx| { + async move { + let db = ctx.store(); + let e3_id = u64_try_from(e.e3Id)?; + info!( + "CommitteePublished: id={}, public_key_len={}", + e.e3Id, + e.publicKey.len() + ); + + // Store the public key temporarily to use when E3Activated happens + let temp_key = format!("_committee_pubkey:{e3_id}"); + let mut db_clone = db.clone(); + db_clone + .insert(&temp_key, &e.publicKey.to_vec()) + .await + .map_err(|e| eyre::eyre!("Failed to store committee public key: {}", e))?; + info!("Stored committee_public_key temporarily for E3 {}", e3_id); + Ok(()) + } + }) + .await; + Ok(()) + } + async fn register_e3_activated(&mut self) -> Result<()> { self.add_event_handler(move |e: E3Activated, ctx| { async move { let contract = ctx.contract(); let db = ctx.store(); let enclave_address = ctx.enclave_address(); - println!( - "E3Activated: id={}, expiration={}, pubkey_hash=0x{}...", + let e3_id = u64_try_from(e.e3Id)?; + + // Get the actual public key from CommitteePublished event + // CommitteePublished always happens before E3Activated, so it should always be in temporary storage + let temp_key = format!("_committee_pubkey:{e3_id}"); + let committee_public_key = db + .get::>(&temp_key) + .await + .map_err(|e| eyre::eyre!("Failed to get committee public key: {}", e))? + .ok_or_else(|| { + eyre::eyre!( + "CommitteePublished event not found for E3 {} - this should not happen", + e3_id + ) + })?; + + info!( + "E3Activated: id={}, expiration={}, using actual public_key (len={})", e.e3Id, e.expiration, - hex::encode(&e.committeePublicKey[..16.min(e.committeePublicKey.len())]) + committee_public_key.len() ); - let e3_id = u64_try_from(e.e3Id)?; + + // Remove the temporary storage + let mut db_clone = db.clone(); + let _ = db_clone.modify::, _>(&temp_key, |_| None).await; + let e3 = contract.get_e3(e.e3Id).await?; let duration = u64_try_from(e3.duration)?; let expiration = u64_try_from(e.expiration)?; @@ -357,7 +405,7 @@ impl EnclaveIndexer { chain_id: ctx.chain_id(), ciphertext_inputs: vec![], ciphertext_output: vec![], - committee_public_key: e.committeePublicKey.to_vec(), + committee_public_key, duration, custom_params: e3.customParams.to_vec(), e3_params: e3.e3ProgramParams.to_vec(), @@ -441,6 +489,7 @@ impl EnclaveIndexer { async fn setup_listeners(&mut self) -> Result<()> { info!("Setting up listeners for EnclaveIndexer..."); + self.register_committee_published().await?; self.register_e3_activated().await?; self.register_ciphertext_output_published().await?; self.register_plaintext_output_published().await?; From 25a1916b616b5beeebba738b54d7bf6c89048478 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 2 Jan 2026 13:31:40 +0000 Subject: [PATCH 11/13] test: fix integration tests for indexer --- Cargo.lock | 4 +++ crates/evm-helpers/src/contracts.rs | 2 +- crates/indexer/Cargo.toml | 6 ++++ crates/indexer/src/indexer.rs | 1 + .../indexer/tests/fixtures/fake_enclave.sol | 9 +++--- crates/indexer/tests/integration.rs | 32 +++++++++++++++++-- examples/CRISP/server/src/cli/commands.rs | 1 - 7 files changed, 46 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac0119a267..5f0c540b24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3071,8 +3071,12 @@ dependencies = [ "alloy", "async-trait", "bincode", + "e3-bfv-helpers", "e3-evm-helpers", "eyre", + "fhe", + "fhe-traits", + "rand 0.8.5", "serde", "thiserror 1.0.69", "tokio", diff --git a/crates/evm-helpers/src/contracts.rs b/crates/evm-helpers/src/contracts.rs index e0f7b3cc32..4921e21991 100644 --- a/crates/evm-helpers/src/contracts.rs +++ b/crates/evm-helpers/src/contracts.rs @@ -7,7 +7,7 @@ use alloy::providers::fillers::BlobGasFiller; use alloy::{ network::{Ethereum, EthereumWallet}, - primitives::{Address, Bytes, FixedBytes, U256}, + primitives::{Address, Bytes, U256}, providers::fillers::{ ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller, }, diff --git a/crates/indexer/Cargo.toml b/crates/indexer/Cargo.toml index d1dbfca2df..545b796206 100644 --- a/crates/indexer/Cargo.toml +++ b/crates/indexer/Cargo.toml @@ -16,3 +16,9 @@ serde.workspace = true thiserror.workspace = true tokio.workspace = true tracing.workspace = true + +[dev-dependencies] +e3-bfv-helpers.workspace = true +fhe.workspace = true +fhe-traits.workspace = true +rand.workspace = true diff --git a/crates/indexer/src/indexer.rs b/crates/indexer/src/indexer.rs index 6795fc9b9a..a2a968b3f2 100644 --- a/crates/indexer/src/indexer.rs +++ b/crates/indexer/src/indexer.rs @@ -399,6 +399,7 @@ impl EnclaveIndexer { u64_try_from(e3.startWindow[0])?, u64_try_from(e3.startWindow[1])?, ]; + // NOTE: we are only saving protocol specific info // here and not CRISP specific info so E3 corresponds to the solidity E3 let e3_obj = E3 { diff --git a/crates/indexer/tests/fixtures/fake_enclave.sol b/crates/indexer/tests/fixtures/fake_enclave.sol index 38e1c9d481..fa830792f6 100644 --- a/crates/indexer/tests/fixtures/fake_enclave.sol +++ b/crates/indexer/tests/fixtures/fake_enclave.sol @@ -7,14 +7,14 @@ pragma solidity >=0.4.24; contract FakeEnclave { - event E3Activated(uint256 e3Id, uint256 expiration, bytes committeePublicKey); + event E3Activated(uint256 e3Id, uint256 expiration, bytes32 committeePublicKey); event InputPublished(uint256 indexed e3Id, bytes data, uint256 inputHash, uint256 index); event CiphertextOutputPublished(uint256 indexed e3Id, bytes ciphertextOutput); event PlaintextOutputPublished(uint256 indexed e3Id, bytes plaintextOutput); - event CommitteePublished(uint256 indexed e3Id, bytes publicKey); + event CommitteePublished(uint256 indexed e3Id, address[] nodes, bytes publicKey); // Emit E3Activated event with passed test data - function emitE3Activated(uint256 e3Id, uint256 expiration, bytes memory committeePublicKey) public { + function emitE3Activated(uint256 e3Id, uint256 expiration, bytes32 committeePublicKey) public { emit E3Activated(e3Id, expiration, committeePublicKey); } @@ -35,7 +35,8 @@ contract FakeEnclave { // Emit CommitteePublished event with passed test data function emitCommitteePublished(uint256 e3Id, bytes memory publicKey) public { - emit CommitteePublished(e3Id, publicKey); + address[] memory nodes = new address[](1); + emit CommitteePublished(e3Id, nodes, publicKey); } function getE3(uint256 _e3Id) external view returns (E3 memory e3) { diff --git a/crates/indexer/tests/integration.rs b/crates/indexer/tests/integration.rs index ebf31046d2..63f02523fb 100644 --- a/crates/indexer/tests/integration.rs +++ b/crates/indexer/tests/integration.rs @@ -6,13 +6,17 @@ mod helpers; use alloy::{ - primitives::{Bytes, Uint}, + primitives::{Bytes, FixedBytes, Uint}, sol, }; +use e3_bfv_helpers::{build_bfv_params_from_set_arc, client::compute_pk_commitment, BfvParamSets}; use e3_evm_helpers::contracts::ReadOnly; use e3_indexer::{DataStore, EnclaveIndexer, InMemoryStore}; use eyre::Result; +use fhe::bfv::{PublicKey, SecretKey}; +use fhe_traits::Serialize; use helpers::setup_two_contracts; +use rand::thread_rng; use std::{ sync::{Arc, Mutex}, time::Duration, @@ -39,6 +43,9 @@ async fn test_indexer() -> Result<()> { const THRESHOLD: u64 = 10; const INDEXER_DELAY_MS: u64 = 10; + let param_set = BfvParamSets::InsecureSet2048_1032193_1.into(); + let params = build_bfv_params_from_set_arc(param_set); + let ( enclave_contract, enclave_address, @@ -87,16 +94,34 @@ async fn test_indexer() -> Result<()> { let indexer_listening = indexer.clone(); let _ = tokio::spawn(async move { indexer_listening.listen().await }); - let public_key = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; + let mut rng = thread_rng(); + let sk = SecretKey::random(¶ms, &mut rng); + let pk = PublicKey::new(&sk, &mut rng); + + let public_key_commitment = compute_pk_commitment( + pk.to_bytes(), + params.degree(), + params.plaintext(), + params.moduli().to_vec(), + ) + .expect("Failed to compute public key commitment"); let input_data = "Random data that wont actually be a string".to_string(); let input_data_bytes = Bytes::from(input_data.clone().into_bytes()); let ciphertext_output_data = vec![9, 8, 7, 6, 5, 4, 3, 2, 1]; + // first publish committee pk + enclave_contract + .emitCommitteePublished(Uint::from(E3_ID), Bytes::from(pk.to_bytes())) + .send() + .await? + .watch() + .await?; + enclave_contract .emitE3Activated( Uint::from(E3_ID), Uint::from(THRESHOLD), - Bytes::from(public_key.clone()), + FixedBytes::from(public_key_commitment), ) .send() .await? @@ -172,6 +197,7 @@ async fn test_indexer() -> Result<()> { sleep(Duration::from_millis(INDEXER_DELAY_MS)).await; let e3_state_after_output = indexer.get_e3(E3_ID).await?; + assert_eq!( e3_state_after_output.ciphertext_output, ciphertext_output_data diff --git a/examples/CRISP/server/src/cli/commands.rs b/examples/CRISP/server/src/cli/commands.rs index 63c610993b..d9aa9a7529 100644 --- a/examples/CRISP/server/src/cli/commands.rs +++ b/examples/CRISP/server/src/cli/commands.rs @@ -16,7 +16,6 @@ use alloy::primitives::{Address, Bytes, FixedBytes, U256}; use alloy::providers::{Provider, ProviderBuilder}; use alloy::sol_types::SolValue; use crisp::config::CONFIG; -use e3_sdk::bfv_helpers::client::compute_pk_commitment; use e3_sdk::bfv_helpers::{build_bfv_params_from_set_arc, encode_bfv_params}; use e3_sdk::evm_helpers::contracts::{EnclaveContract, EnclaveRead, EnclaveWrite, E3}; use fhe::bfv::{BfvParameters, Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}; From 01a21a65c04400f4fe5580f9fa4473a6db911f26 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:00:01 +0000 Subject: [PATCH 12/13] chore: pr comments --- crates/evm-helpers/tests/fixtures/fake_enclave.sol | 4 ++-- docs/pages/setting-up-server.mdx | 4 +--- packages/enclave-sdk/README.md | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/evm-helpers/tests/fixtures/fake_enclave.sol b/crates/evm-helpers/tests/fixtures/fake_enclave.sol index 38e1c9d481..151afd4046 100644 --- a/crates/evm-helpers/tests/fixtures/fake_enclave.sol +++ b/crates/evm-helpers/tests/fixtures/fake_enclave.sol @@ -7,14 +7,14 @@ pragma solidity >=0.4.24; contract FakeEnclave { - event E3Activated(uint256 e3Id, uint256 expiration, bytes committeePublicKey); + event E3Activated(uint256 e3Id, uint256 expiration, bytes32 committeePublicKey); event InputPublished(uint256 indexed e3Id, bytes data, uint256 inputHash, uint256 index); event CiphertextOutputPublished(uint256 indexed e3Id, bytes ciphertextOutput); event PlaintextOutputPublished(uint256 indexed e3Id, bytes plaintextOutput); event CommitteePublished(uint256 indexed e3Id, bytes publicKey); // Emit E3Activated event with passed test data - function emitE3Activated(uint256 e3Id, uint256 expiration, bytes memory committeePublicKey) public { + function emitE3Activated(uint256 e3Id, uint256 expiration, bytes32 committeePublicKey) public { emit E3Activated(e3Id, expiration, committeePublicKey); } diff --git a/docs/pages/setting-up-server.mdx b/docs/pages/setting-up-server.mdx index ca90ffa313..bf29d6bcb2 100644 --- a/docs/pages/setting-up-server.mdx +++ b/docs/pages/setting-up-server.mdx @@ -265,9 +265,7 @@ class E3CoordinationServer { console.log(`Auto-activating E3 ${e3Id}`) try { - // In a real implementation, you'd generate/retrieve the public key - const publicKey = '0x...' // Your FHE public key - await this.sdk.activateE3(e3Id, publicKey) + await this.sdk.activateE3(e3Id) } catch (error) { console.error(`Failed to activate E3 ${e3Id}:`, error) } diff --git a/packages/enclave-sdk/README.md b/packages/enclave-sdk/README.md index 85dfa9de69..140fdc149b 100644 --- a/packages/enclave-sdk/README.md +++ b/packages/enclave-sdk/README.md @@ -217,7 +217,7 @@ await sdk.requestE3({ }); // Activate an E3 computation -await sdk.activateE3(e3Id: bigint, publicKey: `0x${string}`, gasLimit?: bigint); +await sdk.activateE3(e3Id: bigint, gasLimit?: bigint); // Publish input data await sdk.publishInput(e3Id: bigint, data: `0x${string}`, gasLimit?: bigint); From f947c8bd4979b579dfd58bff3481efc20467483f Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:56:46 +0000 Subject: [PATCH 13/13] test: fix template tests --- templates/default/server/index.ts | 1 + templates/default/tests/integration.spec.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/default/server/index.ts b/templates/default/server/index.ts index 4a7f20a662..36de70d6b0 100644 --- a/templates/default/server/index.ts +++ b/templates/default/server/index.ts @@ -211,6 +211,7 @@ async function setupEventListeners() { console.log('📡 Setting up event listeners...') sdk.onEnclaveEvent(EnclaveEventType.E3_ACTIVATED, handleE3ActivatedEvent) + await listenToInputPublishedEvents(sdk.getPublicClient(), PROGRAM_ADDRESS as `0x${string}`, 0n) console.log('✅ Event listeners set up successfully') diff --git a/templates/default/tests/integration.spec.ts b/templates/default/tests/integration.spec.ts index c2d9ec21b9..522946c1f3 100644 --- a/templates/default/tests/integration.spec.ts +++ b/templates/default/tests/integration.spec.ts @@ -212,6 +212,8 @@ describe('Integration', () => { // Ciphernodes will publish a public key within the COMMITTEE_PUBLISHED event event = await waitForEvent(RegistryEventType.COMMITTEE_PUBLISHED) + const publicKeyBytes = hexToBytes(event.data.publicKey as `0x${string}`) + state = store.get(0n) assert(state, 'store should have E3State but it was falsey') assert.strictEqual(state.type, 'committee_published') @@ -232,7 +234,6 @@ describe('Integration', () => { console.log('PUBLISHING PRIVATE INPUT') const num1 = 12n const num2 = 21n - const publicKeyBytes = hexToBytes(state.publicKey) console.log('ENCRYPTING NUMBERS') const enc1 = await sdk.encryptNumber(num1, publicKeyBytes)