diff --git a/packages/ciphernode/core/src/ciphernode.rs b/packages/ciphernode/core/src/ciphernode.rs index 7f3bebe2..e289c42a 100644 --- a/packages/ciphernode/core/src/ciphernode.rs +++ b/packages/ciphernode/core/src/ciphernode.rs @@ -3,7 +3,7 @@ use crate::{ eventbus::EventBus, events::{ComputationRequested, EnclaveEvent, KeyshareCreated}, fhe::{Fhe, GenerateKeyshare}, - Subscribe, + DecryptionRequested, Subscribe, }; use actix::prelude::*; use anyhow::Result; @@ -85,3 +85,18 @@ async fn on_computation_requested( Ok(()) } + +async fn on_decryption_requested( + fhe: Addr, + data: Addr, + bus: Addr, + event: DecryptionRequested, +) -> Result<()> { + let DecryptionRequested { e3_id, ciphertext } = event; + + // get secret key by id from data + + // TODO: Complete this + + Ok(()) +} diff --git a/packages/ciphernode/core/src/committee.rs b/packages/ciphernode/core/src/committee.rs index 836dc534..220bca21 100644 --- a/packages/ciphernode/core/src/committee.rs +++ b/packages/ciphernode/core/src/committee.rs @@ -69,7 +69,8 @@ impl Handler for CommitteeManager { key.do_send(Die); self.keys.remove(&data.e3_id); - } // _ => (), + }, + _ => (), } } } diff --git a/packages/ciphernode/core/src/eventbus.rs b/packages/ciphernode/core/src/eventbus.rs index 50cd14f8..fe908caf 100644 --- a/packages/ciphernode/core/src/eventbus.rs +++ b/packages/ciphernode/core/src/eventbus.rs @@ -22,6 +22,10 @@ impl Subscribe { #[rtype(result = "Vec")] pub struct GetHistory; +#[derive(Message)] +#[rtype(result = "()")] +pub struct ResetHistory; + /// Central EventBus for each node. Actors publish events to this bus by sending it EnclaveEvents. /// All events sent to this bus are assumed to be published over the network via pubsub. @@ -73,6 +77,14 @@ impl Handler for EventBus { self.history.clone() } } +impl Handler for EventBus { + type Result = (); + + fn handle(&mut self, _: ResetHistory, _: &mut Context) { + self.history.clear() + } +} + impl Handler for EventBus { type Result = (); diff --git a/packages/ciphernode/core/src/events.rs b/packages/ciphernode/core/src/events.rs index ce062dae..f0298264 100644 --- a/packages/ciphernode/core/src/events.rs +++ b/packages/ciphernode/core/src/events.rs @@ -1,4 +1,7 @@ -use crate::fhe::{WrappedPublicKey, WrappedPublicKeyShare}; +use crate::{ + fhe::{WrappedPublicKey, WrappedPublicKeyShare}, + WrappedCiphertext, +}; use actix::Message; use bincode; use serde::{Deserialize, Serialize}; @@ -64,6 +67,10 @@ pub enum EnclaveEvent { id: EventId, data: PublicKeyAggregated, }, + DecryptionRequested { + id: EventId, + data: DecryptionRequested + } // CommitteeSelected, // OutputDecrypted, // CiphernodeRegistered, @@ -90,6 +97,7 @@ impl From for EventId { EnclaveEvent::KeyshareCreated { id, .. } => id, EnclaveEvent::ComputationRequested { id, .. } => id, EnclaveEvent::PublicKeyAggregated { id, .. } => id, + EnclaveEvent::DecryptionRequested { id, .. } => id, } } } @@ -121,6 +129,18 @@ impl From for EnclaveEvent { } } + +impl From for EnclaveEvent { + fn from(data: DecryptionRequested) -> Self { + EnclaveEvent::DecryptionRequested { + id: EventId::from(data.clone()), + data: data.clone(), + } + } +} + + + impl fmt::Display for EnclaveEvent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&format!("{}({})", self.event_type(), self.get_id())) @@ -154,6 +174,13 @@ pub struct ComputationRequested { // availability_duration: ??, // TODO: } +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct DecryptionRequested { + pub e3_id: E3id, + pub ciphertext: WrappedCiphertext, +} + fn extract_enclave_event_name(s: &str) -> &str { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { diff --git a/packages/ciphernode/core/src/fhe.rs b/packages/ciphernode/core/src/fhe.rs index 7a5b59f7..79904279 100644 --- a/packages/ciphernode/core/src/fhe.rs +++ b/packages/ciphernode/core/src/fhe.rs @@ -4,7 +4,7 @@ use crate::ordered_set::OrderedSet; use actix::{Actor, Context, Handler, Message}; use anyhow::*; use fhe::{ - bfv::{BfvParameters, BfvParametersBuilder, PublicKey, SecretKey}, + bfv::{BfvParameters, BfvParametersBuilder, Ciphertext, PublicKey, SecretKey}, mbfv::{AggregateIter, CommonRandomPoly, PublicKeyShare}, }; use fhe_traits::{Deserialize, DeserializeParametrized, Serialize}; @@ -123,7 +123,6 @@ impl serde::Serialize for WrappedPublicKeyShare { } } - /// Wrapped PublicKey. This is wrapped to provide an inflection point /// as we use this library elsewhere we only implement traits as we need them /// and avoid exposing underlying structures from fhe.rs @@ -214,6 +213,60 @@ impl WrappedSecretKey { } } +/// Wrapped Ciphertext. This is wrapped to provide an inflection point +/// as we use this library elsewhere we only implement traits as we need them +/// and avoid exposing underlying structures from fhe.rs +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct WrappedCiphertext { + inner: Ciphertext, + params: Arc, +} + +impl WrappedCiphertext { + pub fn from_fhe_rs(inner: Ciphertext, params: Arc) -> Self { + Self { inner, params } + } +} + +impl Hash for WrappedCiphertext { + fn hash(&self, state: &mut H) { + self.inner.to_bytes().hash(state) + } +} + +/// Deserialize from serde to WrappedPublicKey +impl<'de> serde::Deserialize<'de> for WrappedCiphertext { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + // Intermediate struct of bytes for deserialization + #[derive(serde::Deserialize)] + struct DeserializedBytes { + par: Vec, + bytes: Vec, + } + let DeserializedBytes { par, bytes } = DeserializedBytes::deserialize(deserializer)?; + let params = Arc::new(BfvParameters::try_deserialize(&par).unwrap()); + let inner = Ciphertext::from_bytes(&bytes, ¶ms).map_err(serde::de::Error::custom)?; + std::result::Result::Ok(WrappedCiphertext::from_fhe_rs(inner, params)) + } +} +impl serde::Serialize for WrappedCiphertext { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeStruct; + let bytes = self.inner.to_bytes(); + let par_bytes = self.params.to_bytes(); + // Intermediate struct of bytes + let mut state = serializer.serialize_struct("Ciphertext", 2)?; + state.serialize_field("par_bytes", &par_bytes)?; + state.serialize_field("bytes", &bytes)?; + state.end() + } +} /// Fhe library adaptor. All FHE computations should happen through this actor. pub struct Fhe { params: Arc, @@ -274,7 +327,7 @@ impl Handler for Fhe { } } -fn serialize_box_i64(boxed: Box<[i64]>) -> Vec { +pub fn serialize_box_i64(boxed: Box<[i64]>) -> Vec { let vec = boxed.into_vec(); let mut bytes = Vec::with_capacity(vec.len() * mem::size_of::()); for &num in &vec { diff --git a/packages/ciphernode/core/src/lib.rs b/packages/ciphernode/core/src/lib.rs index 772f0789..6e0c4562 100644 --- a/packages/ciphernode/core/src/lib.rs +++ b/packages/ciphernode/core/src/lib.rs @@ -10,31 +10,31 @@ mod enclave_contract; mod eventbus; mod events; mod fhe; +mod logger; mod ordered_set; mod p2p; -mod logger; // TODO: this is too permissive -pub use data::*; +pub use actix::prelude::*; pub use ciphernode::*; pub use committee::*; pub use committee_key::*; +pub use data::*; pub use eventbus::*; pub use events::*; pub use fhe::*; -pub use p2p::*; -pub use actix::prelude::*; pub use logger::*; +pub use p2p::*; -pub use data::*; +pub use actix::prelude::*; pub use ciphernode::*; pub use committee::*; pub use committee_key::*; +pub use data::*; pub use eventbus::*; pub use events::*; pub use fhe::*; pub use p2p::*; -pub use actix::prelude::*; // pub struct Core { // pub name: String, @@ -53,6 +53,7 @@ pub use actix::prelude::*; // } // } +// TODO: move these out to a test folder #[cfg(test)] mod tests { use std::{sync::Arc, time::Duration}; @@ -65,13 +66,15 @@ mod tests { events::{ComputationRequested, E3id, EnclaveEvent, KeyshareCreated, PublicKeyAggregated}, fhe::{Fhe, WrappedPublicKey, WrappedPublicKeyShare}, p2p::P2p, + DecryptionRequested, ResetHistory, WrappedCiphertext, }; use actix::prelude::*; use anyhow::*; use fhe::{ - bfv::{BfvParameters, BfvParametersBuilder, PublicKey, SecretKey}, + bfv::{BfvParameters, BfvParametersBuilder, Encoding, Plaintext, PublicKey, SecretKey}, mbfv::{AggregateIter, CommonRandomPoly, PublicKeyShare}, }; + use fhe_traits::{FheEncoder, FheEncrypter}; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; use tokio::sync::Mutex; @@ -144,24 +147,29 @@ mod tests { plaintext_modulus: u64, rng1: ChaCha20Rng, rng2: ChaCha20Rng, - ) -> Result> { + ) -> Result<(Addr, Arc, CommonRandomPoly)> { let (params, crp) = setup_bfv_params(&moduli, degree, plaintext_modulus, rng1)?; - Ok(Fhe::new(params, crp, rng2)?.start()) + Ok(( + Fhe::new(params.clone(), crp.clone(), rng2)?.start(), + params, + crp, + )) } #[actix::test] - async fn test_public_key_aggregation() -> Result<()> { + async fn test_public_key_aggregation_and_decryption() -> Result<()> { // Setup EventBus let bus = EventBus::new(true).start(); // Setup global FHE actor - let fhe = setup_global_fhe_actor( + let (fhe, ..) = setup_global_fhe_actor( &vec![0x3FFFFFFF000001], 2048, 1032193, ChaCha20Rng::seed_from_u64(42), ChaCha20Rng::seed_from_u64(42), )?; + setup_local_ciphernode(bus.clone(), fhe.clone(), true); setup_local_ciphernode(bus.clone(), fhe.clone(), true); setup_local_ciphernode(bus.clone(), fhe.clone(), true); @@ -196,7 +204,7 @@ mod tests { let (p2, rng) = generate_pk_share(params.clone(), crp.clone(), rng)?; let (p3, _) = generate_pk_share(params.clone(), crp.clone(), rng)?; - let aggregated: PublicKey = vec![p1.clone(), p2.clone(), p3.clone()] + let pubkey: PublicKey = vec![p1.clone(), p2.clone(), p3.clone()] .iter() .map(|k| k.clone_inner()) .aggregate()?; @@ -224,12 +232,27 @@ mod tests { e3_id: e3_id.clone() }), EnclaveEvent::from(PublicKeyAggregated { - pubkey: WrappedPublicKey::from_fhe_rs(aggregated, params), + pubkey: WrappedPublicKey::from_fhe_rs(pubkey.clone(), params.clone()), e3_id: e3_id.clone() }) ] ); + // Aggregate decryption + bus.send(ResetHistory).await?; + + let yes = 12376213u64; + let no = 873827u64; + + let pt = Plaintext::try_encode(&vec![yes, no], Encoding::poly(), ¶ms)?; + + let ciphertext = pubkey.try_encrypt(&pt, &mut ChaCha20Rng::seed_from_u64(42))?; + + bus.do_send(EnclaveEvent::from(DecryptionRequested { + ciphertext: WrappedCiphertext::from_fhe_rs(ciphertext, params), + e3_id: e3_id.clone(), + })); + Ok(()) } @@ -268,7 +291,7 @@ mod tests { bus.do_send(evt_1.clone()); bus.do_send(evt_2.clone()); - + sleep(Duration::from_millis(1)).await; // need to push to next tick // check the history of the event bus @@ -280,7 +303,11 @@ mod tests { "P2p did not transmit events to the network" ); - assert_eq!(history, vec![evt_1, evt_2], "P2p must not retransmit forwarded event to event bus"); + assert_eq!( + history, + vec![evt_1, evt_2], + "P2p must not retransmit forwarded event to event bus" + ); Ok(()) }