From a8f06a9202b8b0cb7396da4fdd0353f7f9f2fdfc Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 27 Dec 2024 14:54:57 +1100 Subject: [PATCH 01/15] Add comment to create tech debt branch --- packages/ciphernode/docs/user_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ciphernode/docs/user_guide.md b/packages/ciphernode/docs/user_guide.md index c7c1bb69..cad9b8c8 100644 --- a/packages/ciphernode/docs/user_guide.md +++ b/packages/ciphernode/docs/user_guide.md @@ -146,4 +146,4 @@ You can change the location of your keyfile by using the `key_file` option withi key_file: "/path/to/enclave/key" ``` - + From 256b5e41bfd208c9690689be691fc6b251b5d298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Fri, 27 Dec 2024 14:59:32 +1100 Subject: [PATCH 02/15] Add Persistable State Container (#214) * Ensure that snapshotting is fallable and prepare for persistable container * Formatting * Fix failing test by ensuring when we read 0 as bytes datastore returns None * Add docs * Add comments and expose repositories * Add Persistable to PlaintextAggregator * Remove deref * Add persistable to PublickeyAggregator * Add persistable to EventReader * Add persistable to KeyShare * Remove KeyshareState * Add persistable to Sortition * Hook up StoreKeys in anticipation for extraction later --- packages/ciphernode/Cargo.lock | 2 + packages/ciphernode/Cargo.toml | 2 + .../aggregator/src/plaintext_aggregator.rs | 108 ++--- .../aggregator/src/publickey_aggregator.rs | 104 ++--- packages/ciphernode/config/Cargo.toml | 1 + packages/ciphernode/config/src/lib.rs | 3 + packages/ciphernode/config/src/store_keys.rs | 53 +++ packages/ciphernode/data/README.md | 140 ++++++ packages/ciphernode/data/src/data_store.rs | 8 +- packages/ciphernode/data/src/lib.rs | 3 + packages/ciphernode/data/src/persistable.rs | 430 ++++++++++++++++++ packages/ciphernode/data/src/repository.rs | 18 +- .../ciphernode/data/src/repository_factory.rs | 49 ++ packages/ciphernode/data/src/snapshot.rs | 17 +- packages/ciphernode/evm/src/event_reader.rs | 116 ++--- packages/ciphernode/fhe/src/fhe.rs | 6 +- packages/ciphernode/keyshare/src/keyshare.rs | 60 +-- packages/ciphernode/router/Cargo.toml | 1 + packages/ciphernode/router/src/context.rs | 6 +- .../router/src/e3_request_router.rs | 6 +- packages/ciphernode/router/src/hooks.rs | 92 ++-- .../ciphernode/router/src/repositories.rs | 31 +- .../ciphernode/sortition/src/sortition.rs | 91 ++-- 23 files changed, 941 insertions(+), 406 deletions(-) create mode 100644 packages/ciphernode/config/src/store_keys.rs create mode 100644 packages/ciphernode/data/README.md create mode 100644 packages/ciphernode/data/src/persistable.rs create mode 100644 packages/ciphernode/data/src/repository_factory.rs diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 63966fe4..734202d5 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -1706,6 +1706,7 @@ dependencies = [ "alloy", "anyhow", "dirs", + "enclave-core", "figment", "serde", "shellexpand", @@ -5305,6 +5306,7 @@ dependencies = [ "async-trait", "bincode", "cipher 0.1.0", + "config", "data", "enclave-core", "evm", diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index ae75b191..1a4a4747 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -38,8 +38,10 @@ base64 = "0.22.1" clap = { version = "4.5.17", features = ["derive"] } cipher = { path = "./cipher" } compile-time = "0.2.0" +config = { path = "./config" } dirs = "5.0.1" data = { path = "./data" } +enclave-core = { path = "./core" } shellexpand = "3.1.0" figment = { version = "0.10.19", features = ["yaml", "test"] } fhe_rs = { package = "fhe", git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } diff --git a/packages/ciphernode/aggregator/src/plaintext_aggregator.rs b/packages/ciphernode/aggregator/src/plaintext_aggregator.rs index fc5bdf60..c2db077f 100644 --- a/packages/ciphernode/aggregator/src/plaintext_aggregator.rs +++ b/packages/ciphernode/aggregator/src/plaintext_aggregator.rs @@ -1,7 +1,6 @@ use actix::prelude::*; use anyhow::Result; -use async_trait::async_trait; -use data::{Checkpoint, FromSnapshotWithParams, Repository, Snapshot}; +use data::Persistable; use enclave_core::{ DecryptionshareCreated, Die, E3id, EnclaveEvent, EventBus, OrderedSet, PlaintextAggregated, Seed, @@ -50,28 +49,28 @@ struct ComputeAggregate { pub struct PlaintextAggregator { fhe: Arc, bus: Addr, - store: Repository, sortition: Addr, e3_id: E3id, - state: PlaintextAggregatorState, + state: Persistable, src_chain_id: u64, } pub struct PlaintextAggregatorParams { pub fhe: Arc, pub bus: Addr, - pub store: Repository, pub sortition: Addr, pub e3_id: E3id, pub src_chain_id: u64, } impl PlaintextAggregator { - pub fn new(params: PlaintextAggregatorParams, state: PlaintextAggregatorState) -> Self { + pub fn new( + params: PlaintextAggregatorParams, + state: Persistable, + ) -> Self { PlaintextAggregator { fhe: params.fhe, bus: params.bus, - store: params.store, sortition: params.sortition, e3_id: params.e3_id, src_chain_id: params.src_chain_id, @@ -79,36 +78,40 @@ impl PlaintextAggregator { } } - pub fn add_share(&mut self, share: Vec) -> Result { - let PlaintextAggregatorState::Collecting { - threshold_m, - shares, - ciphertext_output, - .. - } = &mut self.state - else { - return Err(anyhow::anyhow!("Can only add share in Collecting state")); - }; - - shares.insert(share); - if shares.len() == *threshold_m { - return Ok(PlaintextAggregatorState::Computing { - shares: shares.clone(), - ciphertext_output: ciphertext_output.to_vec(), - }); - } - - Ok(self.state.clone()) + pub fn add_share(&mut self, share: Vec) -> Result<()> { + self.state.try_mutate(|mut state| { + let PlaintextAggregatorState::Collecting { + threshold_m, + shares, + ciphertext_output, + .. + } = &mut state + else { + return Err(anyhow::anyhow!("Can only add share in Collecting state")); + }; + + shares.insert(share); + + if shares.len() == *threshold_m { + return Ok(PlaintextAggregatorState::Computing { + shares: shares.clone(), + ciphertext_output: ciphertext_output.to_vec(), + }); + } + + Ok(state) + }) } - pub fn set_decryption(&mut self, decrypted: Vec) -> Result { - let PlaintextAggregatorState::Computing { shares, .. } = &mut self.state else { - return Ok(self.state.clone()); - }; + pub fn set_decryption(&mut self, decrypted: Vec) -> Result<()> { + self.state.try_mutate(|mut state| { + let PlaintextAggregatorState::Computing { shares, .. } = &mut state else { + return Ok(state.clone()); + }; + let shares = shares.to_owned(); - let shares = shares.to_owned(); - - Ok(PlaintextAggregatorState::Complete { decrypted, shares }) + Ok(PlaintextAggregatorState::Complete { decrypted, shares }) + }) } } @@ -131,9 +134,9 @@ impl Handler for PlaintextAggregator { type Result = ResponseActFuture>; fn handle(&mut self, event: DecryptionshareCreated, _: &mut Self::Context) -> Self::Result { - let PlaintextAggregatorState::Collecting { + let Some(PlaintextAggregatorState::Collecting { threshold_m, seed, .. - } = self.state + }) = self.state.get() else { error!(state=?self.state, "Aggregator has been closed for collecting."); return Box::pin(fut::ready(Ok(()))); @@ -165,14 +168,13 @@ impl Handler for PlaintextAggregator { } // add the keyshare and - act.state = act.add_share(decryption_share)?; - act.checkpoint(); + act.add_share(decryption_share)?; // Check the state and if it has changed to the computing - if let PlaintextAggregatorState::Computing { + if let Some(PlaintextAggregatorState::Computing { shares, ciphertext_output, - } = &act.state + }) = &act.state.get() { ctx.notify(ComputeAggregate { shares: shares.clone(), @@ -195,8 +197,7 @@ impl Handler for PlaintextAggregator { })?; // Update the local state - self.state = self.set_decryption(decrypted_output.clone())?; - self.checkpoint(); + self.set_decryption(decrypted_output.clone())?; // Dispatch the PlaintextAggregated event let event = EnclaveEvent::from(PlaintextAggregated { @@ -217,26 +218,3 @@ impl Handler for PlaintextAggregator { ctx.stop() } } - -impl Snapshot for PlaintextAggregator { - type Snapshot = PlaintextAggregatorState; - - fn snapshot(&self) -> Self::Snapshot { - self.state.clone() - } -} - -#[async_trait] -impl FromSnapshotWithParams for PlaintextAggregator { - type Params = PlaintextAggregatorParams; - - async fn from_snapshot(params: Self::Params, snapshot: Self::Snapshot) -> Result { - Ok(PlaintextAggregator::new(params, snapshot)) - } -} - -impl Checkpoint for PlaintextAggregator { - fn repository(&self) -> &Repository { - &self.store - } -} diff --git a/packages/ciphernode/aggregator/src/publickey_aggregator.rs b/packages/ciphernode/aggregator/src/publickey_aggregator.rs index e5e3b640..f64d148f 100644 --- a/packages/ciphernode/aggregator/src/publickey_aggregator.rs +++ b/packages/ciphernode/aggregator/src/publickey_aggregator.rs @@ -1,7 +1,6 @@ use actix::prelude::*; use anyhow::Result; -use async_trait::async_trait; -use data::{Checkpoint, FromSnapshotWithParams, Repository, Snapshot}; +use data::Persistable; use enclave_core::{ Die, E3id, EnclaveEvent, EventBus, KeyshareCreated, OrderedSet, PublicKeyAggregated, Seed, }; @@ -53,17 +52,15 @@ struct NotifyNetwork { pub struct PublicKeyAggregator { fhe: Arc, bus: Addr, - store: Repository, sortition: Addr, e3_id: E3id, - state: PublicKeyAggregatorState, + state: Persistable, src_chain_id: u64, } pub struct PublicKeyAggregatorParams { pub fhe: Arc, pub bus: Addr, - pub store: Repository, pub sortition: Addr, pub e3_id: E3id, pub src_chain_id: u64, @@ -76,11 +73,13 @@ pub struct PublicKeyAggregatorParams { /// It is expected to change this mechanism as we work through adversarial scenarios and write tests /// for them. impl PublicKeyAggregator { - pub fn new(params: PublicKeyAggregatorParams, state: PublicKeyAggregatorState) -> Self { + pub fn new( + params: PublicKeyAggregatorParams, + state: Persistable, + ) -> Self { PublicKeyAggregator { fhe: params.fhe, bus: params.bus, - store: params.store, sortition: params.sortition, e3_id: params.e3_id, src_chain_id: params.src_chain_id, @@ -88,35 +87,39 @@ impl PublicKeyAggregator { } } - pub fn add_keyshare(&mut self, keyshare: Vec) -> Result { - let PublicKeyAggregatorState::Collecting { - threshold_m, - keyshares, - .. - } = &mut self.state - else { - return Err(anyhow::anyhow!("Can only add keyshare in Collecting state")); - }; - keyshares.insert(keyshare); - if keyshares.len() == *threshold_m { - return Ok(PublicKeyAggregatorState::Computing { - keyshares: keyshares.clone(), - }); - } - - Ok(self.state.clone()) + pub fn add_keyshare(&mut self, keyshare: Vec) -> Result<()> { + self.state.try_mutate(|mut state| { + let PublicKeyAggregatorState::Collecting { + threshold_m, + keyshares, + .. + } = &mut state + else { + return Err(anyhow::anyhow!("Can only add keyshare in Collecting state")); + }; + keyshares.insert(keyshare); + if keyshares.len() == *threshold_m { + return Ok(PublicKeyAggregatorState::Computing { + keyshares: keyshares.clone(), + }); + } + + Ok(state) + }) } - pub fn set_pubkey(&mut self, pubkey: Vec) -> Result { - let PublicKeyAggregatorState::Computing { keyshares } = &mut self.state else { - return Ok(self.state.clone()); - }; + pub fn set_pubkey(&mut self, pubkey: Vec) -> Result<()> { + self.state.try_mutate(|mut state| { + let PublicKeyAggregatorState::Computing { keyshares } = &mut state else { + return Ok(state); + }; - let keyshares = keyshares.to_owned(); + let keyshares = keyshares.to_owned(); - Ok(PublicKeyAggregatorState::Complete { - public_key: pubkey, - keyshares, + Ok(PublicKeyAggregatorState::Complete { + public_key: pubkey, + keyshares, + }) }) } } @@ -140,9 +143,9 @@ impl Handler for PublicKeyAggregator { type Result = ResponseActFuture>; fn handle(&mut self, event: KeyshareCreated, _: &mut Self::Context) -> Self::Result { - let PublicKeyAggregatorState::Collecting { + let Some(PublicKeyAggregatorState::Collecting { threshold_m, seed, .. - } = self.state.clone() + }) = self.state.get() else { error!(state=?self.state, "Aggregator has been closed for collecting keyshares."); return Box::pin(fut::ready(Ok(()))); @@ -176,11 +179,12 @@ impl Handler for PublicKeyAggregator { } // add the keyshare and - act.state = act.add_keyshare(pubkey)?; - act.checkpoint(); + act.add_keyshare(pubkey)?; // Check the state and if it has changed to the computing - if let PublicKeyAggregatorState::Computing { keyshares } = &act.state { + if let Some(PublicKeyAggregatorState::Computing { keyshares }) = + &act.state.get() + { ctx.notify(ComputeAggregate { keyshares: keyshares.clone(), e3_id, @@ -202,8 +206,7 @@ impl Handler for PublicKeyAggregator { })?; // Update the local state - self.state = self.set_pubkey(pubkey.clone())?; - self.checkpoint(); + self.set_pubkey(pubkey.clone())?; ctx.notify(NotifyNetwork { pubkey, @@ -242,26 +245,3 @@ impl Handler for PublicKeyAggregator { ctx.stop() } } - -impl Snapshot for PublicKeyAggregator { - type Snapshot = PublicKeyAggregatorState; - - fn snapshot(&self) -> Self::Snapshot { - self.state.clone() - } -} - -#[async_trait] -impl FromSnapshotWithParams for PublicKeyAggregator { - type Params = PublicKeyAggregatorParams; - - async fn from_snapshot(params: Self::Params, snapshot: Self::Snapshot) -> Result { - Ok(PublicKeyAggregator::new(params, snapshot)) - } -} - -impl Checkpoint for PublicKeyAggregator { - fn repository(&self) -> &Repository { - &self.store - } -} diff --git a/packages/ciphernode/config/Cargo.toml b/packages/ciphernode/config/Cargo.toml index e58c695f..0ce89132 100644 --- a/packages/ciphernode/config/Cargo.toml +++ b/packages/ciphernode/config/Cargo.toml @@ -11,6 +11,7 @@ figment = { workspace = true } alloy = { workspace = true } shellexpand = { workspace = true } url = { workspace = true } +enclave-core = { workspace = true } [dev-dependencies] tempfile = { workspace = true } diff --git a/packages/ciphernode/config/src/lib.rs b/packages/ciphernode/config/src/lib.rs index 83e06ce9..b6a71252 100644 --- a/packages/ciphernode/config/src/lib.rs +++ b/packages/ciphernode/config/src/lib.rs @@ -1,3 +1,6 @@ mod app_config; +mod store_keys; mod yaml; + pub use app_config::*; +pub use store_keys::*; diff --git a/packages/ciphernode/config/src/store_keys.rs b/packages/ciphernode/config/src/store_keys.rs new file mode 100644 index 00000000..3cdb653f --- /dev/null +++ b/packages/ciphernode/config/src/store_keys.rs @@ -0,0 +1,53 @@ +use enclave_core::E3id; + +pub struct StoreKeys; + +impl StoreKeys { + pub fn keyshare(e3_id: &E3id) -> String { + format!("//keyshare/{e3_id}") + } + + pub fn plaintext(e3_id: &E3id) -> String { + format!("//plaintext/{e3_id}") + } + + pub fn publickey(e3_id: &E3id) -> String { + format!("//publickey/{e3_id}") + } + + pub fn fhe(e3_id: &E3id) -> String { + format!("//fhe/{e3_id}") + } + + pub fn meta(e3_id: &E3id) -> String { + format!("//meta/{e3_id}") + } + + pub fn context(e3_id: &E3id) -> String { + format!("//context/{e3_id}") + } + + pub fn router() -> String { + String::from("//router") + } + + pub fn sortition() -> String { + String::from("//sortition") + } + + pub fn eth_private_key() -> String { + String::from("//eth_private_key") + } + + pub fn libp2p_keypair() -> String { + String::from("//libp2p/keypair") + } + + pub fn enclave_sol_reader(chain_id: u64) -> String { + format!("//evm_readers/enclave/{chain_id}") + } + + pub fn ciphernode_registry_reader(chain_id: u64) -> String { + format!("//evm_readers/ciphernode_registry/{chain_id}") + } +} diff --git a/packages/ciphernode/data/README.md b/packages/ciphernode/data/README.md new file mode 100644 index 00000000..490de7fa --- /dev/null +++ b/packages/ciphernode/data/README.md @@ -0,0 +1,140 @@ +# On Persistence patterns + +_The way persistence is managed within this codebase has a few elements to it. So here is the story as to how this works and why it has been done like this_ + +Persistence within an Actor Model tends to be based around the idea that actors need to be able to have their state persistable and hydratable upon restart. This enables in an ideal scenario any actor to be able to just crash on error and restart as required. + +We started persistence by creating an Actor that wraps the database which is good practice within an Actor Model. This has advantages because we can interleave database writes to become a stream of events enabling high throughput. We can create delivery guarantees by storing events in a persistent queue at a later point if need be. + +```mermaid +graph LR + DB[(SledDB)] + Client --insert--> SledStore + SledStore --insert--> DB + SledStore -.retry.-> SledStore +``` + +## DataStore + +Next we needed a way to polymorphically pick between a real database and an in memory database for testing - to do this we utilize Actix's `Recipient` trait which means we can accept any actor that is happy to receive an `Insert` or a `Get` message. This means we can create a Key Value Store struct and pass in either a `SledStore` or an `InMemStore` Actor to the `DataStore` actor to accomplish this. + +```rust +let store = DataStore::from(SledStore::from(SledDb::new())); +``` + +or for testing: + +```rust +let store = DataStore::from(InMemStore::new()); +``` + +```mermaid +graph LR + DB[(SledDB)] + Client --> DataStore + DataStore -.-> SledStore + DataStore -.-> InMemStore + InMemStore -.-> BTreeMap + SledStore --> DB +``` + +The `DataStore` actor also has some convenience methods within it where it is possible to scope the keys so that you can consider the information you are storing as more of a tree structure as opposed to a flat list. + +```rust +let store = DataStore::from(&addr); +let scoped = store.scope("//foo/bar/baz"); +scoped.write(some_data); +``` + +## Repository + +There was an attempt to use the `DataStore` throughout the app but it became apparent this was causing the knowledge of where and how the data was saved to be spread throughout the codebase. What we needed was for the components not to really care how their data was saved but for us to be able to easily have a sense of the different keys under which data was being saved in a centralized place. + +Also `DataStore` can take any type of serializable data to save at a key location but this means the data in the DataStore was effectively untyped. + +To solve this it made sense to create a typed `Repository` interface to encapsulate saving of data from within an actor or routine and in theory the repository could use whatever underlying mechanism requires to save the data. This could even be a SQL DB or the filesystem if required. Whatever it's type T the Repository knows how to save it. + +The tradeoff is we get a slightly deeper stack but each layer adds a responsibility to the data saving stack: + +```mermaid +graph LR + R["Repository<T>"] + DB[(SledDB)] + Client --"write()"--> R + R --> D[DataStore] + D -.-> SledStore + D -.-> InMemStore + InMemStore -.-> BTreeMap + SledStore --> DB +``` + + +| Layer | Functionality | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| `Repository` | Strongly typed Data persistence for a single item. Configured to know how to save its data. | +| `DataStore` | Flexible KV store. Client can scope to specific namespace. Can be backed by polymorphic data actor to handle testing scenarios. | +| `{InMem,Sled}Store` | Actor to receive `Insert` and `Get` requests can only save raw bytes. | + +## Snapshotting + +We had a way to save bytes data with the `DataStore` and had a way to specify where that could be saved but actors need to be restartable and be able to be hydrated and we needed a standard way to accomplish this. To do this in typical Rust fashion we created a set of traits: + +- [`Snapshot`](https://github.com/gnosisguild/enclave/blob/main/packages/ciphernode/data/src/snapshot.rs) for defining how an object can create a snapshot of it's state +- [`Checkpoint`](https://github.com/gnosisguild/enclave/blob/main/packages/ciphernode/data/src/snapshot.rs) for defining how to save that snapshot to a repository +- [`FromSnapshot`](https://github.com/gnosisguild/enclave/blob/main/packages/ciphernode/data/src/snapshot.rs) and [`FromSnapshotWithParams`](https://github.com/gnosisguild/enclave/blob/main/packages/ciphernode/data/src/snapshot.rs) for defining how an object could be reconstituted from a snapshot + +This worked well especially for objects who's persistable state needs to be derived from a subset of the saved state however there are a couple of problems: + +- `self.checkpoint()` needs to be called everytime you want to save the state +- Using these traits is very verbose and repeditive - especially for situations where the state was just a field on the actor which it often is. +- These traits mean you need to mix some persistence API within your business logic API unless you create a separate struct just for persistence. + +## Enter Persistable + +Persistable is a struct that connects a repository and some in memory state and ensures that every time the in memory state is mutated that the state is saved to the repository. + +This has several benefits: + +- Less verbose +- Centralized batching point for logical operations +- Can remove complex "snapshot" traits +- Simpler initialization +- No need to consider the underlying data saving mechanism - logic can be [persistence ignorant](https://www.linkedin.com/pulse/persistence-ignorance-domain-driven-design-ilkay-polat-atmae). + +```rust + +// Some how we get a repository for a type +let repo:Repository> = get_repo(); + +// We can use the load to create a persistable object from the contents of the persistance layer that the repository encapsulates +let persistable:Persistable> = repo.load().await?; + +// If we add a name to the list the list is automatically synced to the database +persistable.try_mutate(|mut list| { + list.push("Fred"); + Ok(list) +})?; + +// We can set new state +persistable.set(vec![String::from("Hello")]); + +// We can try and get the data if it is set on the object +if persistable.try_get()?.len() > 0 { + println!("Repo has names!") +} + +// We an clear the object which will clear the repo +persistable.clear(); + +assert_eq!(persistable.get(), None); +``` + +To use it we can just have it as a field on a struct or actor: + +```rust +struct MyActor { + state: Persistable> +} +``` + +We have also extracted the key calculation mechanism to a [`StoreKeys`](https://github.com/gnosisguild/enclave/blob/main/packages/ciphernode/config/src/store_keys.rs) struct. This is used in various places when creating repsitory factories for example [here](https://github.com/gnosisguild/enclave/blob/main/packages/ciphernode/aggregator/src/repositories.rs) diff --git a/packages/ciphernode/data/src/data_store.rs b/packages/ciphernode/data/src/data_store.rs index 0be873a5..b6f7bf56 100644 --- a/packages/ciphernode/data/src/data_store.rs +++ b/packages/ciphernode/data/src/data_store.rs @@ -49,7 +49,8 @@ impl Remove { } } -/// Generate proxy for the DB +/// Generate proxy for the DB / KV store +/// DataStore is scopable #[derive(Clone, Debug)] pub struct DataStore { scope: Vec, @@ -68,6 +69,11 @@ impl DataStore { return Ok(None); }; + // If we get a null value return None as this doesn't deserialize correctly + if bytes == [0] { + return Ok(None); + } + Ok(Some(bincode::deserialize(&bytes)?)) } diff --git a/packages/ciphernode/data/src/lib.rs b/packages/ciphernode/data/src/lib.rs index b3f1ea93..08b5f844 100644 --- a/packages/ciphernode/data/src/lib.rs +++ b/packages/ciphernode/data/src/lib.rs @@ -1,13 +1,16 @@ mod data_store; mod in_mem; mod into_key; +mod persistable; mod repository; +mod repository_factory; mod sled_store; mod snapshot; pub use data_store::*; pub use in_mem::*; pub use into_key::IntoKey; +pub use persistable::*; pub use repository::*; pub use sled_store::*; pub use snapshot::*; diff --git a/packages/ciphernode/data/src/persistable.rs b/packages/ciphernode/data/src/persistable.rs new file mode 100644 index 00000000..9353571c --- /dev/null +++ b/packages/ciphernode/data/src/persistable.rs @@ -0,0 +1,430 @@ +use crate::{Checkpoint, FromSnapshotWithParams, Repository, Snapshot}; +use anyhow::*; +use async_trait::async_trait; +use serde::{de::DeserializeOwned, Serialize}; + +pub trait PersistableData: Serialize + DeserializeOwned + Clone + Send + Sync + 'static {} +impl PersistableData for T where T: Serialize + DeserializeOwned + Clone + Send + Sync + 'static {} + +/// AutoPersist enables a repository to generate a persistable container +#[async_trait] +pub trait AutoPersist +where + T: PersistableData, +{ + /// Load the data from the repository into an auto persist container + async fn load(&self) -> Result>; + /// Create a new auto persist container and set some data on it to send back to the repository + fn send(&self, data: Option) -> Persistable; + /// Load the data from the repository into an auto persist container. If there is no persisted data then persist the given default data + async fn load_or_default(&self, default: T) -> Result>; + /// Load the data from the repository into an auto persist container. If there is no persisted data then persist the given default data + async fn load_or_else(&self, f: F) -> Result> + where + F: Send + FnOnce() -> Result; +} + +#[async_trait] +impl AutoPersist for Repository +where + T: PersistableData, +{ + /// Load the data from the repository into an auto persist container + async fn load(&self) -> Result> { + Ok(Persistable::load(self).await?) + } + + /// Create a new auto persist container and set some data on it to send back to the repository + fn send(&self, data: Option) -> Persistable { + Persistable::new(data, self).save() + } + + /// Load the data from the repository into an auto persist container. If there is no persisted data then persist the given default data + async fn load_or_default(&self, default: T) -> Result> { + Ok(Persistable::load_or_default(self, default).await?) + } + + /// Load the data from the repository into an auto persist container. If there is no persisted data then persist the result of the callback + async fn load_or_else(&self, f: F) -> Result> + where + F: Send + FnOnce() -> Result, + { + Ok(Persistable::load_or_else(self, f).await?) + } +} + +/// A container that automatically persists it's content every time it is mutated or changed. +#[derive(Debug)] +pub struct Persistable { + data: Option, + repo: Repository, +} + +impl Persistable +where + T: PersistableData, +{ + /// Create a new container with the given option data and repository + pub fn new(data: Option, repo: &Repository) -> Self { + Self { + data, + repo: repo.clone(), + } + } + + /// Load data from the repository to the container + pub async fn load(repo: &Repository) -> Result { + let data = repo.read().await?; + + Ok(Self::new(data, repo)) + } + + /// Load the data from the repo or save and sync the given default value + pub async fn load_or_default(repo: &Repository, default: T) -> Result { + let instance = Self::new(Some(repo.read().await?.unwrap_or(default)), repo); + + Ok(instance.save()) + } + + /// Load the data from the repo or save and sync the result of the given callback + pub async fn load_or_else(repo: &Repository, f: F) -> Result + where + F: FnOnce() -> Result, + { + let data = repo + .read() + .await? + .ok_or_else(|| anyhow!("Not found")) + .or_else(|_| f())?; + + let instance = Self::new(Some(data), repo); + Ok(instance.save()) + } + + /// Save the data in the container to the database + pub fn save(self) -> Self { + self.checkpoint(); + self + } + + /// Mutate the content if it is available or return an error if either the mutator function + /// fails or if the data has not been set. + pub fn try_mutate(&mut self, mutator: F) -> Result<()> + where + F: FnOnce(T) -> Result, + { + let content = self.data.clone().ok_or(anyhow!("Data has not been set"))?; + self.data = Some(mutator(content)?); + self.checkpoint(); + Ok(()) + } + + /// Set the data on both the persistable and the repository. + pub fn set(&mut self, data: T) { + self.data = Some(data); + self.checkpoint(); + } + + /// Clear the data from both the persistable and the repository. + pub fn clear(&mut self) { + self.data = None; + self.clear_checkpoint(); + } + + /// Get the data currently stored on the container as an Option + pub fn get(&self) -> Option { + self.data.clone() + } + + /// Get the data from the container or return an error. + pub fn try_get(&self) -> Result { + self.data + .clone() + .ok_or(anyhow!("Data was not set on container.")) + } + + /// Returns true if there is data on the container and false if there is not. + pub fn has(&self) -> bool { + self.data.is_some() + } + + /// Get an immutable reference to the data on the container if the data is not set on the + /// container return an error + pub fn try_with(&self, f: F) -> Result + where + F: FnOnce(&T) -> Result, + { + match &self.data { + Some(data) => f(data), + None => Err(anyhow!("Data was not set on container.")), + } + } +} + +impl Snapshot for Persistable +where + T: PersistableData, +{ + type Snapshot = T; + fn snapshot(&self) -> Result { + Ok(self + .data + .clone() + .ok_or(anyhow!("No data stored on container"))?) + } +} + +impl Checkpoint for Persistable +where + T: PersistableData, +{ + fn repository(&self) -> &Repository { + &self.repo + } +} + +#[async_trait] +impl FromSnapshotWithParams for Persistable +where + T: PersistableData, +{ + type Params = Repository; + async fn from_snapshot(params: Repository, snapshot: T) -> Result { + Ok(Persistable::new(Some(snapshot), ¶ms)) + } +} + +#[cfg(test)] +mod tests { + use crate::{AutoPersist, DataStore, GetLog, InMemStore, Repository}; + use actix::{Actor, Addr}; + use anyhow::{anyhow, Result}; + + fn get_repo() -> (Repository, Addr) { + let addr = InMemStore::new(true).start(); + let store = DataStore::from(&addr).scope("/"); + let repo: Repository = Repository::new(store); + (repo, addr) + } + + #[actix::test] + async fn persistable_loads_with_default() -> Result<()> { + let (repo, addr) = get_repo::>(); + let container = repo + .clone() + .load_or_default(vec!["berlin".to_string()]) + .await?; + + assert_eq!(addr.send(GetLog).await?.len(), 1); + assert_eq!(repo.read().await?, Some(vec!["berlin".to_string()])); + assert_eq!(container.get(), Some(vec!["berlin".to_string()])); + Ok(()) + } + + #[actix::test] + async fn persistable_loads_with_default_override() -> Result<()> { + let (repo, _) = get_repo::>(); + repo.write(&vec!["berlin".to_string()]); + let container = repo + .clone() + .load_or_default(vec!["amsterdam".to_string()]) + .await?; + + assert_eq!(repo.read().await?, Some(vec!["berlin".to_string()])); + assert_eq!(container.get(), Some(vec!["berlin".to_string()])); + Ok(()) + } + + #[actix::test] + async fn persistable_load() -> Result<()> { + let (repo, _) = get_repo::>(); + repo.write(&vec!["berlin".to_string()]); + let container = repo.clone().load().await?; + + assert_eq!(repo.read().await?, Some(vec!["berlin".to_string()])); + assert_eq!(container.get(), Some(vec!["berlin".to_string()])); + Ok(()) + } + + #[actix::test] + async fn persistable_send() -> Result<()> { + let (repo, _) = get_repo::>(); + repo.write(&vec!["amsterdam".to_string()]); + let container = repo.clone().send(Some(vec!["berlin".to_string()])); + + assert_eq!(repo.read().await?, Some(vec!["berlin".to_string()])); + assert_eq!(container.get(), Some(vec!["berlin".to_string()])); + Ok(()) + } + + #[actix::test] + async fn persistable_mutate() -> Result<()> { + let (repo, addr) = get_repo::>(); + + let mut container = repo.clone().send(Some(vec!["berlin".to_string()])); + + container.try_mutate(|mut list| { + list.push(String::from("amsterdam")); + Ok(list) + })?; + + assert_eq!( + repo.read().await?, + Some(vec!["berlin".to_string(), "amsterdam".to_string()]) + ); + + assert_eq!(addr.send(GetLog).await?.len(), 2); + + Ok(()) + } + + #[actix::test] + async fn test_clear_persistable() -> Result<()> { + let (repo, _) = get_repo::>(); + let repo_ref = &repo; + let mut container = repo_ref.send(Some(vec!["berlin".to_string()])); + + assert!(container.has()); + container.clear(); + assert!(!container.has()); + assert_eq!(repo_ref.read().await?, None); + Ok(()) + } + + #[actix::test] + async fn test_set_persistable() -> Result<()> { + let (repo, _) = get_repo::>(); + let mut container = repo.clone().send(None); + + container.set(vec!["amsterdam".to_string()]); + + assert!(container.has()); + assert_eq!(repo.read().await?, Some(vec!["amsterdam".to_string()])); + Ok(()) + } + + #[actix::test] + async fn test_try_get_with_data() -> Result<()> { + let (repo, _) = get_repo::>(); + let container = repo.clone().send(Some(vec!["berlin".to_string()])); + + let result = container.try_get()?; + assert_eq!(result, vec!["berlin".to_string()]); + Ok(()) + } + + #[actix::test] + async fn test_try_get_without_data() { + let (repo, _) = get_repo::>(); + let container = repo.clone().send(None); + + assert!(container.try_get().is_err()); + } + + #[actix::test] + async fn test_try_with_success() -> Result<()> { + let (repo, _) = get_repo::>(); + let container = repo.clone().send(Some(vec!["berlin".to_string()])); + + let length = container.try_with(|data| Ok(data.len()))?; + assert_eq!(length, 1); + Ok(()) + } + + #[actix::test] + async fn test_try_with_failure() { + let (repo, _) = get_repo::>(); + let container = repo.clone().send(None); + + let result = container.try_with(|data| Ok(data.len())); + assert!(result.is_err()); + } + + #[actix::test] + async fn test_try_mutate_failure() { + let (repo, _) = get_repo::>(); + let mut container = repo.clone().send(None); + + let result = container.try_mutate(|mut list| { + list.push(String::from("amsterdam")); + Ok(list) + }); + assert!(result.is_err()); + } + + #[actix::test] + async fn test_mutate_with_error() -> Result<()> { + let (repo, _) = get_repo::>(); + let mut container = repo.clone().send(Some(vec!["berlin".to_string()])); + + let result = + container.try_mutate(|_| -> Result> { Err(anyhow!("Mutation failed")) }); + + assert!(result.is_err()); + // Original data should remain unchanged + assert_eq!(container.try_get()?, vec!["berlin".to_string()]); + Ok(()) + } + + #[actix::test] + async fn test_load_or_else_success_with_empty_repo() -> Result<()> { + let (repo, _) = get_repo::>(); + + let container = repo + .clone() + .load_or_else(|| Ok(vec!["amsterdam".to_string()])) + .await?; + + assert_eq!(container.try_get()?, vec!["amsterdam".to_string()]); + assert_eq!(repo.read().await?, Some(vec!["amsterdam".to_string()])); + Ok(()) + } + + #[actix::test] + async fn test_load_or_else_skips_callback_when_data_exists() -> Result<()> { + let (repo, _) = get_repo::>(); + repo.write(&vec!["berlin".to_string()]); + + let container = repo + .clone() + .load_or_else(|| { + panic!("This callback should not be called!"); + #[allow(unreachable_code)] + Ok(vec!["amsterdam".to_string()]) + }) + .await?; + + assert_eq!(container.try_get()?, vec!["berlin".to_string()]); + Ok(()) + } + + #[actix::test] + async fn test_load_or_else_propagates_callback_error() -> Result<()> { + let (repo, _) = get_repo::>(); + + let result = repo + .clone() + .load_or_else(|| Err(anyhow!("Failed to create default data"))) + .await; + + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains("Failed to create default data")); + assert_eq!(repo.read().await?, None); + Ok(()) + } + + #[actix::test] + async fn test_load_or_else_custom_error_message() -> Result<()> { + let (repo, _) = get_repo::>(); + let error_msg = "Custom initialization error"; + + let result = repo.load_or_else(|| Err(anyhow!(error_msg))).await; + + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains(error_msg)); + Ok(()) + } +} diff --git a/packages/ciphernode/data/src/repository.rs b/packages/ciphernode/data/src/repository.rs index 4e04fd00..f7cf54af 100644 --- a/packages/ciphernode/data/src/repository.rs +++ b/packages/ciphernode/data/src/repository.rs @@ -6,7 +6,8 @@ use crate::DataStore; #[derive(Debug)] pub struct Repository { - store: DataStore, + /// store is currently set to be a scopeable key value store + store: DataStore, // this could change and be abstracted if need be _p: PhantomData, } @@ -19,13 +20,6 @@ impl Repository { } } -impl Deref for Repository { - type Target = DataStore; - fn deref(&self) -> &Self::Target { - &self.store - } -} - impl From> for DataStore { fn from(value: Repository) -> Self { value.store @@ -56,11 +50,15 @@ where self.store.read().await } + pub async fn has(&self) -> bool { + self.read().await.ok().flatten().is_some() + } + pub fn write(&self, value: &T) { - self.store.write(value); + self.store.write(value) } pub fn clear(&self) { - self.store.clear(); + self.store.write::>(None) } } diff --git a/packages/ciphernode/data/src/repository_factory.rs b/packages/ciphernode/data/src/repository_factory.rs new file mode 100644 index 00000000..3024b84a --- /dev/null +++ b/packages/ciphernode/data/src/repository_factory.rs @@ -0,0 +1,49 @@ +use crate::{DataStore, Repository}; + +// TODO: Naming here is confusing +pub struct Repositories { + pub store: DataStore, +} + +impl From for Repositories { + fn from(value: DataStore) -> Self { + Repositories { store: value } + } +} +impl From<&DataStore> for Repositories { + fn from(value: &DataStore) -> Self { + Repositories { + store: value.clone(), + } + } +} + +impl Repositories { + pub fn new(store: DataStore) -> Self { + Repositories { store } + } +} + +impl From> for Repositories { + fn from(value: Repository) -> Self { + let store: DataStore = value.into(); + store.into() + } +} + +pub trait RepositoriesFactory { + fn repositories(&self) -> Repositories; +} + +impl RepositoriesFactory for DataStore { + fn repositories(&self) -> Repositories { + self.into() + } +} + +impl RepositoriesFactory for Repository { + fn repositories(&self) -> Repositories { + let store: DataStore = self.into(); + store.repositories() + } +} diff --git a/packages/ciphernode/data/src/snapshot.rs b/packages/ciphernode/data/src/snapshot.rs index a0203f45..2bf91d9e 100644 --- a/packages/ciphernode/data/src/snapshot.rs +++ b/packages/ciphernode/data/src/snapshot.rs @@ -2,6 +2,7 @@ use crate::Repository; use anyhow::Result; use async_trait::async_trait; use serde::{de::DeserializeOwned, Serialize}; +use tracing::error; /// This trait enables the self type to report their state snapshot pub trait Snapshot @@ -14,7 +15,7 @@ where type Snapshot: Serialize + DeserializeOwned; /// Return the Snapshot object for the implementor - fn snapshot(&self) -> Self::Snapshot; + fn snapshot(&self) -> Result; } /// This trait enables the self type to checkpoint its state @@ -24,7 +25,19 @@ pub trait Checkpoint: Snapshot { /// Write the current snapshot to the `Repository` provided by `repository()` fn checkpoint(&self) { - self.repository().write(&self.snapshot()); + let snapshot = match self.snapshot() { + Ok(v) => v, + Err(err) => { + error!("Not saving data because '{}'", err); + return; + } + }; + + self.repository().write(&snapshot); + } + + fn clear_checkpoint(&self) { + self.repository().clear() } } diff --git a/packages/ciphernode/evm/src/event_reader.rs b/packages/ciphernode/evm/src/event_reader.rs index 4981c4f6..79c90029 100644 --- a/packages/ciphernode/evm/src/event_reader.rs +++ b/packages/ciphernode/evm/src/event_reader.rs @@ -8,8 +8,7 @@ use alloy::providers::Provider; use alloy::rpc::types::Filter; use alloy::transports::{BoxTransport, Transport}; use anyhow::{anyhow, Result}; -use async_trait::async_trait; -use data::{Checkpoint, FromSnapshotWithParams, Repository, Snapshot}; +use data::{AutoPersist, Persistable, Repository}; use enclave_core::{ get_tag, BusError, EnclaveErrorType, EnclaveEvent, EventBus, EventId, Subscribe, }; @@ -50,7 +49,7 @@ where contract_address: Address, start_block: Option, bus: Addr, - repository: Repository, + state: Persistable, } #[derive(Default, serde::Serialize, serde::Deserialize, Clone)] @@ -80,10 +79,8 @@ where start_block: Option, /// Event bus for error propagation bus: Addr, - /// The in memory state of the event reader - state: EvmEventReaderState, - /// Repository to save the state of the event reader - repository: Repository, + /// The auto persistable state of the event reader + state: Persistable, } impl EvmEventReader @@ -101,22 +98,10 @@ where shutdown_tx: Some(shutdown_tx), start_block: params.start_block, bus: params.bus, - state: EvmEventReaderState::default(), - repository: params.repository, + state: params.state, } } - #[instrument(name="evm_event_reader", skip_all, fields(id = get_tag()))] - pub async fn load(params: EvmEventReaderParams) -> Result { - Ok(if let Some(snapshot) = params.repository.read().await? { - info!("Loading from snapshot"); - Self::from_snapshot(params, snapshot).await? - } else { - info!("Loading from params"); - Self::new(params) - }) - } - pub async fn attach( provider: &WithChainId, extractor: ExtractorFn, @@ -125,15 +110,20 @@ where bus: &Addr, repository: &Repository, ) -> Result> { + let sync_state = repository + .clone() + .load_or_default(EvmEventReaderState::default()) + .await?; + let params = EvmEventReaderParams { provider: provider.clone(), extractor, contract_address: contract_address.parse()?, start_block, bus: bus.clone(), - repository: repository.clone(), + state: sync_state, }; - let addr = EvmEventReader::load(params).await?.start(); + let addr = EvmEventReader::new(params).start(); bus.do_send(Subscribe::new("Shutdown", addr.clone().into())); @@ -284,69 +274,33 @@ where #[instrument(name="evm_event_reader", skip_all, fields(id = get_tag()))] fn handle(&mut self, wrapped: EnclaveEvmEvent, _: &mut Self::Context) -> Self::Result { - let event_id = wrapped.get_id(); - info!("Processing event: {}", event_id); - info!("cache length: {}", self.state.ids.len()); - if self.state.ids.contains(&event_id) { - error!( - "Event id {} has already been seen and was not forwarded to the bus", - &event_id - ); - return; - } - let event_type = wrapped.event.event_type(); + match self.state.try_mutate(|mut state| { + let event_id = wrapped.get_id(); + info!("Processing event: {}", event_id); + info!("cache length: {}", state.ids.len()); + if state.ids.contains(&event_id) { + error!( + "Event id {} has already been seen and was not forwarded to the bus", + &event_id + ); + return Ok(state); + } - // Forward everything else to the event bus - self.bus.do_send(wrapped.event); + let event_type = wrapped.event.event_type(); - // Save processed ids - info!("Storing event(EVM) in cache {}({})", event_type, event_id); - self.state.ids.insert(event_id); - self.state.last_block = wrapped.block; - self.checkpoint(); - } -} + // Forward everything else to the event bus + self.bus.do_send(wrapped.event); -impl Snapshot for EvmEventReader -where - P: Provider + Clone + 'static, - T: Transport + Clone + Unpin, -{ - type Snapshot = EvmEventReaderState; - fn snapshot(&self) -> Self::Snapshot { - self.state.clone() - } -} + // Save processed ids + info!("Storing event(EVM) in cache {}({})", event_type, event_id); -impl Checkpoint for EvmEventReader -where - P: Provider + Clone + 'static, - T: Transport + Clone + Unpin, -{ - fn repository(&self) -> &Repository { - &self.repository - } -} + state.ids.insert(event_id); + state.last_block = wrapped.block; -#[async_trait] -impl FromSnapshotWithParams for EvmEventReader -where - P: Provider + Clone + 'static, - T: Transport + Clone + Unpin, -{ - type Params = EvmEventReaderParams; - async fn from_snapshot(params: Self::Params, snapshot: Self::Snapshot) -> Result { - let (shutdown_tx, shutdown_rx) = oneshot::channel(); - Ok(Self { - contract_address: params.contract_address, - provider: Some(params.provider), - extractor: params.extractor, - shutdown_rx: Some(shutdown_rx), - shutdown_tx: Some(shutdown_tx), - start_block: params.start_block, - bus: params.bus, - state: snapshot, - repository: params.repository, - }) + Ok(state) + }) { + Ok(_) => (), + Err(err) => self.bus.err(EnclaveErrorType::Evm, err), + } } } diff --git a/packages/ciphernode/fhe/src/fhe.rs b/packages/ciphernode/fhe/src/fhe.rs index f5131aec..84333de4 100644 --- a/packages/ciphernode/fhe/src/fhe.rs +++ b/packages/ciphernode/fhe/src/fhe.rs @@ -127,11 +127,11 @@ impl Fhe { impl Snapshot for Fhe { type Snapshot = FheSnapshot; - fn snapshot(&self) -> Self::Snapshot { - FheSnapshot { + fn snapshot(&self) -> Result { + Ok(FheSnapshot { crp: self.crp.to_bytes(), params: self.params.to_bytes(), - } + }) } } diff --git a/packages/ciphernode/keyshare/src/keyshare.rs b/packages/ciphernode/keyshare/src/keyshare.rs index a2e93c0e..dab8a0c8 100644 --- a/packages/ciphernode/keyshare/src/keyshare.rs +++ b/packages/ciphernode/keyshare/src/keyshare.rs @@ -1,22 +1,19 @@ use actix::prelude::*; use anyhow::{anyhow, Result}; -use async_trait::async_trait; use cipher::Cipher; -use data::{Checkpoint, FromSnapshotWithParams, Repository, Snapshot}; +use data::Persistable; use enclave_core::{ BusError, CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated, Die, E3RequestComplete, EnclaveErrorType, EnclaveEvent, EventBus, FromError, KeyshareCreated, }; use fhe::{DecryptCiphertext, Fhe}; -use serde::{Deserialize, Serialize}; use std::sync::Arc; use tracing::warn; pub struct Keyshare { fhe: Arc, - store: Repository, bus: Addr, - secret: Option>, + secret: Persistable>, address: String, cipher: Arc, } @@ -27,24 +24,18 @@ impl Actor for Keyshare { pub struct KeyshareParams { pub bus: Addr, - pub store: Repository, + pub secret: Persistable>, pub fhe: Arc, pub address: String, pub cipher: Arc, } -#[derive(Serialize, Deserialize)] -pub struct KeyshareState { - secret: Option>, -} - impl Keyshare { pub fn new(params: KeyshareParams) -> Self { Self { bus: params.bus, fhe: params.fhe, - store: params.store, - secret: None, + secret: params.secret, address: params.address, cipher: params.cipher, } @@ -53,7 +44,7 @@ impl Keyshare { fn set_secret(&mut self, mut data: Vec) -> Result<()> { let encrypted = self.cipher.encrypt_data(&mut data)?; - self.secret = Some(encrypted); + self.secret.set(encrypted); Ok(()) } @@ -61,8 +52,8 @@ impl Keyshare { fn get_secret(&self) -> Result> { let encrypted = self .secret - .as_ref() - .ok_or(anyhow!("No secret share available on Keyshare"))?; + .get() + .ok_or(anyhow!("State was not stored on keyshare"))?; let decrypted = self.cipher.decrypt_data(&encrypted)?; @@ -70,38 +61,7 @@ impl Keyshare { } fn clear_secret(&mut self) { - self.secret = None; - } -} - -impl Snapshot for Keyshare { - type Snapshot = KeyshareState; - - fn snapshot(&self) -> Self::Snapshot { - KeyshareState { - secret: self.secret.clone(), - } - } -} - -impl Checkpoint for Keyshare { - fn repository(&self) -> &Repository { - &self.store - } -} - -#[async_trait] -impl FromSnapshotWithParams for Keyshare { - type Params = KeyshareParams; - async fn from_snapshot(params: Self::Params, snapshot: Self::Snapshot) -> Result { - Ok(Self { - bus: params.bus, - fhe: params.fhe, - store: params.store, - secret: snapshot.secret, - address: params.address, - cipher: params.cipher, - }) + self.secret.clear(); } } @@ -148,9 +108,6 @@ impl Handler for Keyshare { e3_id, node: self.address.clone(), })); - - // Write the snapshot to the store - self.checkpoint() } } @@ -198,7 +155,6 @@ impl Handler for Keyshare { type Result = (); fn handle(&mut self, _: E3RequestComplete, ctx: &mut Self::Context) -> Self::Result { self.clear_secret(); - self.checkpoint(); ctx.notify(Die); } } diff --git a/packages/ciphernode/router/Cargo.toml b/packages/ciphernode/router/Cargo.toml index cb626737..d2dbacec 100644 --- a/packages/ciphernode/router/Cargo.toml +++ b/packages/ciphernode/router/Cargo.toml @@ -14,6 +14,7 @@ aggregator = { path = "../aggregator" } evm = { path = "../evm" } anyhow = { workspace = true } serde = { workspace = true } +config = { workspace = true } cipher = { path = "../cipher" } bincode = { workspace = true } async-trait = { workspace = true } diff --git a/packages/ciphernode/router/src/context.rs b/packages/ciphernode/router/src/context.rs index 506e106c..f869844c 100644 --- a/packages/ciphernode/router/src/context.rs +++ b/packages/ciphernode/router/src/context.rs @@ -151,15 +151,15 @@ impl RepositoriesFactory for E3RequestContext { impl Snapshot for E3RequestContext { type Snapshot = E3RequestContextSnapshot; - fn snapshot(&self) -> Self::Snapshot { - Self::Snapshot { + fn snapshot(&self) -> Result { + Ok(Self::Snapshot { e3_id: self.e3_id.clone(), meta: self.meta.is_some(), fhe: self.fhe.is_some(), publickey: self.publickey.is_some(), plaintext: self.plaintext.is_some(), keyshare: self.keyshare.is_some(), - } + }) } } diff --git a/packages/ciphernode/router/src/e3_request_router.rs b/packages/ciphernode/router/src/e3_request_router.rs index 0890d4e0..ba2eb39c 100644 --- a/packages/ciphernode/router/src/e3_request_router.rs +++ b/packages/ciphernode/router/src/e3_request_router.rs @@ -178,14 +178,14 @@ pub struct E3RequestRouterSnapshot { impl Snapshot for E3RequestRouter { type Snapshot = E3RequestRouterSnapshot; - fn snapshot(&self) -> Self::Snapshot { + fn snapshot(&self) -> Result { let contexts = self.contexts.keys().cloned().collect(); let completed = self.completed.clone(); - Self::Snapshot { + Ok(Self::Snapshot { completed, contexts, - } + }) } } diff --git a/packages/ciphernode/router/src/hooks.rs b/packages/ciphernode/router/src/hooks.rs index c2969214..5752542f 100644 --- a/packages/ciphernode/router/src/hooks.rs +++ b/packages/ciphernode/router/src/hooks.rs @@ -7,7 +7,7 @@ use aggregator::{ use anyhow::{anyhow, Result}; use async_trait::async_trait; use cipher::Cipher; -use data::{FromSnapshotWithParams, Snapshot}; +use data::{AutoPersist, FromSnapshotWithParams, Snapshot}; use enclave_core::{BusError, E3Requested, EnclaveErrorType, EnclaveEvent, EventBus}; use fhe::{Fhe, SharedRng}; use keyshare::{Keyshare, KeyshareParams}; @@ -57,7 +57,14 @@ impl E3Feature for FheFeature { let fhe = Arc::new(fhe_inner); // FHE doesn't implement Checkpoint so we are going to store it manually - ctx.repositories().fhe(&e3_id).write(&fhe.snapshot()); + let Ok(snapshot) = fhe.snapshot() else { + self.bus.err( + EnclaveErrorType::KeyGeneration, + anyhow!("Failed to get snapshot"), + ); + return; + }; + ctx.repositories().fhe(&e3_id).write(&snapshot); let _ = ctx.set_fhe(fhe); } @@ -120,11 +127,13 @@ impl E3Feature for KeyshareFeature { }; let e3_id = data.clone().e3_id; + let repo = ctx.repositories().keyshare(&e3_id); + let container = repo.send(None); ctx.set_keyshare( Keyshare::new(KeyshareParams { bus: self.bus.clone(), - store: ctx.repositories().keyshare(&e3_id), + secret: container, fhe: fhe.clone(), address: self.address.clone(), cipher: self.cipher.clone(), @@ -143,10 +152,10 @@ impl E3Feature for KeyshareFeature { return Ok(()); }; - let store = ctx.repositories().keyshare(&snapshot.e3_id); + let sync_secret = ctx.repositories().keyshare(&snapshot.e3_id).load().await?; - // No Snapshot returned from the store -> bail - let Some(snap) = store.read().await? else { + // No Snapshot returned from the sync_secret -> bail + if !sync_secret.has() { return Ok(()); }; @@ -160,17 +169,13 @@ impl E3Feature for KeyshareFeature { }; // Construct from snapshot - let value = Keyshare::from_snapshot( - KeyshareParams { - fhe, - bus: self.bus.clone(), - store, - address: self.address.clone(), - cipher: self.cipher.clone(), - }, - snap, - ) - .await? + let value = Keyshare::new(KeyshareParams { + fhe, + bus: self.bus.clone(), + secret: sync_secret, + address: self.address.clone(), + cipher: self.cipher.clone(), + }) .start(); // send to context @@ -179,7 +184,6 @@ impl E3Feature for KeyshareFeature { Ok(()) } } - pub struct PlaintextAggregatorFeature { bus: Addr, sortition: Addr, @@ -221,22 +225,23 @@ impl E3Feature for PlaintextAggregatorFeature { }; let e3_id = data.e3_id.clone(); - - let _ = ctx.set_plaintext( + let repo = ctx.repositories().plaintext(&e3_id); + let sync_state = repo.send(Some(PlaintextAggregatorState::init( + meta.threshold_m, + meta.seed, + data.ciphertext_output.clone(), + ))); + + ctx.set_plaintext( PlaintextAggregator::new( PlaintextAggregatorParams { fhe: fhe.clone(), bus: self.bus.clone(), - store: ctx.repositories().plaintext(&e3_id), sortition: self.sortition.clone(), - e3_id, + e3_id: e3_id.clone(), src_chain_id: meta.src_chain_id, }, - PlaintextAggregatorState::init( - meta.threshold_m, - meta.seed, - data.ciphertext_output.clone(), - ), + sync_state, ) .start(), ); @@ -252,10 +257,11 @@ impl E3Feature for PlaintextAggregatorFeature { return Ok(()); } - let store = ctx.repositories().plaintext(&snapshot.e3_id); + let repo = ctx.repositories().plaintext(&snapshot.e3_id); + let sync_state = repo.load().await?; // No Snapshot returned from the store -> bail - let Some(snap) = store.read().await? else { + if !sync_state.has() { return Ok(()); }; @@ -276,18 +282,16 @@ impl E3Feature for PlaintextAggregatorFeature { return Ok(()); }; - let value = PlaintextAggregator::from_snapshot( + let value = PlaintextAggregator::new( PlaintextAggregatorParams { fhe: fhe.clone(), bus: self.bus.clone(), - store, sortition: self.sortition.clone(), e3_id: ctx.e3_id.clone(), src_chain_id: meta.src_chain_id, }, - snap, + sync_state, ) - .await? .start(); // send to context @@ -338,18 +342,21 @@ impl E3Feature for PublicKeyAggregatorFeature { }; let e3_id = data.e3_id.clone(); - - let _ = ctx.set_publickey( + let repo = ctx.repositories().publickey(&e3_id); + let sync_state = repo.send(Some(PublicKeyAggregatorState::init( + meta.threshold_m, + meta.seed, + ))); + ctx.set_publickey( PublicKeyAggregator::new( PublicKeyAggregatorParams { fhe: fhe.clone(), bus: self.bus.clone(), - store: ctx.repositories().publickey(&e3_id), sortition: self.sortition.clone(), e3_id, src_chain_id: meta.src_chain_id, }, - PublicKeyAggregatorState::init(meta.threshold_m, meta.seed), + sync_state, ) .start(), ); @@ -365,10 +372,11 @@ impl E3Feature for PublicKeyAggregatorFeature { return Ok(()); }; - let repository = ctx.repositories().publickey(&ctx.e3_id); + let repo = ctx.repositories().publickey(&ctx.e3_id); + let sync_state = repo.load().await?; // No Snapshot returned from the store -> bail - let Some(snap) = repository.read().await? else { + if !sync_state.has() { return Ok(()); }; @@ -391,18 +399,16 @@ impl E3Feature for PublicKeyAggregatorFeature { return Ok(()); }; - let value = PublicKeyAggregator::from_snapshot( + let value = PublicKeyAggregator::new( PublicKeyAggregatorParams { fhe: fhe.clone(), bus: self.bus.clone(), - store: repository, sortition: self.sortition.clone(), e3_id: ctx.e3_id.clone(), src_chain_id: meta.src_chain_id, }, - snap, + sync_state, ) - .await? .start(); // send to context diff --git a/packages/ciphernode/router/src/repositories.rs b/packages/ciphernode/router/src/repositories.rs index d9ab879c..dc72c2b9 100644 --- a/packages/ciphernode/router/src/repositories.rs +++ b/packages/ciphernode/router/src/repositories.rs @@ -1,10 +1,10 @@ use crate::{CommitteeMeta, E3RequestContextSnapshot, E3RequestRouterSnapshot}; use aggregator::{PlaintextAggregatorState, PublicKeyAggregatorState}; +use config::StoreKeys; use data::{DataStore, Repository}; use enclave_core::E3id; use evm::EvmEventReaderState; use fhe::FheSnapshot; -use keyshare::KeyshareState; use sortition::SortitionModule; pub struct Repositories { @@ -38,56 +38,53 @@ impl From> for Repositories { } impl Repositories { - pub fn keyshare(&self, e3_id: &E3id) -> Repository { - Repository::new(self.store.scope(format!("//keyshare/{e3_id}"))) + pub fn keyshare(&self, e3_id: &E3id) -> Repository> { + Repository::new(self.store.scope(StoreKeys::keyshare(e3_id))) } pub fn plaintext(&self, e3_id: &E3id) -> Repository { - Repository::new(self.store.scope(format!("//plaintext/{e3_id}"))) + Repository::new(self.store.scope(StoreKeys::plaintext(e3_id))) } pub fn publickey(&self, e3_id: &E3id) -> Repository { - Repository::new(self.store.scope(format!("//publickey/{e3_id}"))) + Repository::new(self.store.scope(StoreKeys::publickey(e3_id))) } pub fn fhe(&self, e3_id: &E3id) -> Repository { - Repository::new(self.store.scope(format!("//fhe/{e3_id}"))) + Repository::new(self.store.scope(StoreKeys::fhe(e3_id))) } pub fn meta(&self, e3_id: &E3id) -> Repository { - Repository::new(self.store.scope(format!("//meta/{e3_id}"))) + Repository::new(self.store.scope(StoreKeys::meta(e3_id))) } pub fn context(&self, e3_id: &E3id) -> Repository { - Repository::new(self.store.scope(format!("//context/{e3_id}"))) + Repository::new(self.store.scope(StoreKeys::context(e3_id))) } pub fn router(&self) -> Repository { - Repository::new(self.store.scope(format!("//router"))) + Repository::new(self.store.scope(StoreKeys::router())) } pub fn sortition(&self) -> Repository { - Repository::new(self.store.scope(format!("//sortition"))) + Repository::new(self.store.scope(StoreKeys::sortition())) } pub fn eth_private_key(&self) -> Repository> { - Repository::new(self.store.scope(format!("//eth_private_key"))) + Repository::new(self.store.scope(StoreKeys::eth_private_key())) } pub fn libp2p_keypair(&self) -> Repository> { - Repository::new(self.store.scope(format!("//libp2p/keypair"))) + Repository::new(self.store.scope(StoreKeys::libp2p_keypair())) } pub fn enclave_sol_reader(&self, chain_id: u64) -> Repository { - Repository::new( - self.store - .scope(format!("//evm_readers/enclave/{chain_id}")), - ) + Repository::new(self.store.scope(StoreKeys::enclave_sol_reader(chain_id))) } pub fn ciphernode_registry_reader(&self, chain_id: u64) -> Repository { Repository::new( self.store - .scope(format!("//evm_readers/ciphernode_registry/{chain_id}")), + .scope(StoreKeys::ciphernode_registry_reader(chain_id)), ) } } diff --git a/packages/ciphernode/sortition/src/sortition.rs b/packages/ciphernode/sortition/src/sortition.rs index a41c9fa9..07ff6704 100644 --- a/packages/ciphernode/sortition/src/sortition.rs +++ b/packages/ciphernode/sortition/src/sortition.rs @@ -2,8 +2,7 @@ use crate::DistanceSortition; use actix::prelude::*; use alloy::primitives::Address; use anyhow::{anyhow, Result}; -use async_trait::async_trait; -use data::{Checkpoint, FromSnapshotWithParams, Repository, Snapshot}; +use data::{AutoPersist, Persistable, Repository}; use enclave_core::{ get_tag, BusError, CiphernodeAdded, CiphernodeRemoved, EnclaveErrorType, EnclaveEvent, EventBus, Seed, Subscribe, @@ -87,23 +86,21 @@ impl SortitionList for SortitionModule { pub struct GetNodes; pub struct Sortition { - list: SortitionModule, + list: Persistable, bus: Addr, - store: Repository, } #[derive(Debug)] pub struct SortitionParams { - pub bus: Addr, - pub store: Repository, + bus: Addr, + list: Persistable, } impl Sortition { pub fn new(params: SortitionParams) -> Self { Self { - list: SortitionModule::new(), + list: params.list, bus: params.bus, - store: params.store, } } @@ -112,29 +109,18 @@ impl Sortition { bus: &Addr, store: Repository, ) -> Result> { - let addr = Sortition::load(SortitionParams { + let list = store.load_or_default(SortitionModule::default()).await?; + let addr = Sortition::new(SortitionParams { bus: bus.clone(), - store, + list, }) - .await? .start(); bus.do_send(Subscribe::new("CiphernodeAdded", addr.clone().into())); Ok(addr) } - #[instrument(name="sortition", skip_all, fields(id = get_tag()))] - pub async fn load(params: SortitionParams) -> Result { - Ok(if let Some(snapshot) = params.store.read().await? { - info!("Loading from snapshot"); - Self::from_snapshot(params, snapshot).await? - } else { - info!("Loading from params"); - Self::new(params) - }) - } - pub fn get_nodes(&self) -> Vec { - self.list.nodes.clone().into_iter().collect() + self.list.get().unwrap().nodes.clone().into_iter().collect() } } @@ -142,38 +128,6 @@ impl Actor for Sortition { type Context = actix::Context; } -impl Snapshot for Sortition { - type Snapshot = SortitionModule; - fn snapshot(&self) -> Self::Snapshot { - self.list.clone() - } -} - -#[async_trait] -impl FromSnapshotWithParams for Sortition { - type Params = SortitionParams; - - #[instrument(name="sortition", skip_all, fields(id = get_tag()))] - async fn from_snapshot(params: Self::Params, snapshot: Self::Snapshot) -> Result { - info!("Loaded snapshot with {} nodes", snapshot.nodes().len()); - info!( - "Nodes:\n\n{:?}\n", - snapshot.nodes().into_iter().collect::>() - ); - Ok(Sortition { - bus: params.bus, - store: params.store, - list: snapshot, - }) - } -} - -impl Checkpoint for Sortition { - fn repository(&self) -> &Repository { - &self.store - } -} - impl Handler for Sortition { type Result = (); fn handle(&mut self, msg: EnclaveEvent, ctx: &mut Self::Context) -> Self::Result { @@ -191,8 +145,13 @@ impl Handler for Sortition { #[instrument(name="sortition", skip_all, fields(id = get_tag()))] fn handle(&mut self, msg: CiphernodeAdded, _ctx: &mut Self::Context) -> Self::Result { info!("Adding node: {}", msg.address); - self.list.add(msg.address); - self.checkpoint(); + match self.list.try_mutate(|mut list| { + list.add(msg.address); + Ok(list) + }) { + Err(err) => self.bus.err(EnclaveErrorType::Sortition, err), + _ => (), + }; } } @@ -202,8 +161,13 @@ impl Handler for Sortition { #[instrument(name="sortition", skip_all, fields(id = get_tag()))] fn handle(&mut self, msg: CiphernodeRemoved, _ctx: &mut Self::Context) -> Self::Result { info!("Removing node: {}", msg.address); - self.list.remove(msg.address); - self.checkpoint(); + match self.list.try_mutate(|mut list| { + list.remove(msg.address); + Ok(list) + }) { + Err(err) => self.bus.err(EnclaveErrorType::Sortition, err), + _ => (), + }; } } @@ -212,13 +176,12 @@ impl Handler for Sortition { #[instrument(name="sortition", skip_all, fields(id = get_tag()))] fn handle(&mut self, msg: GetHasNode, _ctx: &mut Self::Context) -> Self::Result { - match self.list.contains(msg.seed, msg.size, msg.address) { - Ok(val) => val, - Err(err) => { + self.list + .try_with(|list| list.contains(msg.seed, msg.size, msg.address)) + .unwrap_or_else(|err| { self.bus.err(EnclaveErrorType::Sortition, err); false - } - } + }) } } From acc12ad26d521b2fd08c879e518f1d3a227f4729 Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 27 Dec 2024 15:01:09 +1100 Subject: [PATCH 03/15] Revert "Add comment to create tech debt branch" This reverts commit a8f06a9202b8b0cb7396da4fdd0353f7f9f2fdfc. --- packages/ciphernode/docs/user_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ciphernode/docs/user_guide.md b/packages/ciphernode/docs/user_guide.md index cad9b8c8..c7c1bb69 100644 --- a/packages/ciphernode/docs/user_guide.md +++ b/packages/ciphernode/docs/user_guide.md @@ -146,4 +146,4 @@ You can change the location of your keyfile by using the `key_file` option withi key_file: "/path/to/enclave/key" ``` - + From 4b6b3107f091581cf28a84df3fa58e23fea4b626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Fri, 27 Dec 2024 23:46:43 +1100 Subject: [PATCH 04/15] Refactor router (#218) * Extract all repositories to separate crates * Fix tests * Extract keyshare and aggregator features to crates * Fix test imports * Remove comments * Add helpful comments * Use HetrogenousMap * Extract features out of router * Tidy up * Add docs --- packages/ciphernode/Cargo.lock | 17 +- packages/ciphernode/Cargo.toml | 3 + packages/ciphernode/aggregator/Cargo.toml | 2 + packages/ciphernode/aggregator/src/feature.rs | 257 +++++++++++ packages/ciphernode/aggregator/src/lib.rs | 6 + packages/ciphernode/aggregator/src/repo.rs | 25 ++ packages/ciphernode/data/src/lib.rs | 3 +- ...{repository_factory.rs => repositories.rs} | 1 - packages/ciphernode/enclave/Cargo.toml | 14 +- .../enclave/src/commands/net/generate.rs | 1 + .../enclave/src/commands/net/purge.rs | 1 + .../enclave/src/commands/net/set.rs | 1 + .../enclave/src/commands/wallet/set.rs | 1 + .../ciphernode/enclave_node/src/aggregator.rs | 17 +- .../ciphernode/enclave_node/src/ciphernode.rs | 19 +- .../ciphernode/enclave_node/src/datastore.rs | 2 +- packages/ciphernode/evm/src/lib.rs | 2 + packages/ciphernode/evm/src/repo.rs | 37 ++ packages/ciphernode/fhe/Cargo.toml | 3 + packages/ciphernode/fhe/src/feature.rs | 86 ++++ packages/ciphernode/fhe/src/lib.rs | 4 + packages/ciphernode/fhe/src/repo.rs | 15 + packages/ciphernode/keyshare/Cargo.toml | 2 + packages/ciphernode/keyshare/src/feature.rs | 111 +++++ packages/ciphernode/keyshare/src/lib.rs | 4 + packages/ciphernode/keyshare/src/repo.rs | 13 + packages/ciphernode/net/Cargo.toml | 1 + packages/ciphernode/net/src/lib.rs | 2 + packages/ciphernode/net/src/repo.rs | 12 + packages/ciphernode/router/Cargo.toml | 6 - .../ciphernode/router/src/committee_meta.rs | 13 +- packages/ciphernode/router/src/context.rs | 154 +++---- .../router/src/e3_request_router.rs | 32 +- .../ciphernode/router/src/hetrogenous_map.rs | 154 +++++++ packages/ciphernode/router/src/hooks.rs | 419 ------------------ .../ciphernode/router/src/keyshare_feature.rs | 0 packages/ciphernode/router/src/lib.rs | 10 +- packages/ciphernode/router/src/repo.rs | 35 ++ .../ciphernode/router/src/repositories.rs | 107 ----- packages/ciphernode/sortition/Cargo.toml | 1 + .../src/ciphernode_selector.rs | 2 +- packages/ciphernode/sortition/src/lib.rs | 4 + packages/ciphernode/sortition/src/repo.rs | 14 + .../tests/test_aggregation_and_decryption.rs | 20 +- 44 files changed, 956 insertions(+), 677 deletions(-) create mode 100644 packages/ciphernode/aggregator/src/feature.rs create mode 100644 packages/ciphernode/aggregator/src/repo.rs rename packages/ciphernode/data/src/{repository_factory.rs => repositories.rs} (96%) create mode 100644 packages/ciphernode/evm/src/repo.rs create mode 100644 packages/ciphernode/fhe/src/feature.rs create mode 100644 packages/ciphernode/fhe/src/repo.rs create mode 100644 packages/ciphernode/keyshare/src/feature.rs create mode 100644 packages/ciphernode/keyshare/src/repo.rs create mode 100644 packages/ciphernode/net/src/repo.rs create mode 100644 packages/ciphernode/router/src/hetrogenous_map.rs delete mode 100644 packages/ciphernode/router/src/hooks.rs delete mode 100644 packages/ciphernode/router/src/keyshare_feature.rs create mode 100644 packages/ciphernode/router/src/repo.rs delete mode 100644 packages/ciphernode/router/src/repositories.rs rename packages/ciphernode/{router => sortition}/src/ciphernode_selector.rs (98%) create mode 100644 packages/ciphernode/sortition/src/repo.rs diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 734202d5..6d30df34 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -116,9 +116,11 @@ dependencies = [ "anyhow", "async-trait", "bincode", + "config", "data", "enclave-core", "fhe 0.1.0", + "router", "serde", "sortition", "tracing", @@ -2186,8 +2188,10 @@ dependencies = [ "dirs", "enclave-core", "enclave_node", + "evm", "hex", "libp2p", + "net", "once_cell", "petname", "phf", @@ -2414,9 +2418,11 @@ dependencies = [ name = "fhe" version = "0.1.0" dependencies = [ + "actix", "anyhow", "async-trait", "bincode", + "config", "data", "enclave-core", "fhe 0.1.0-beta.7", @@ -2424,6 +2430,7 @@ dependencies = [ "fhe-util", "rand", "rand_chacha", + "router", "serde", ] @@ -3489,9 +3496,11 @@ dependencies = [ "anyhow", "async-trait", "cipher 0.1.0", + "config", "data", "enclave-core", "fhe 0.1.0", + "router", "serde", "tracing", ] @@ -4147,6 +4156,7 @@ dependencies = [ "async-std", "async-trait", "cipher 0.1.0", + "config", "data", "enclave-core", "futures", @@ -5301,19 +5311,13 @@ name = "router" version = "0.1.0" dependencies = [ "actix", - "aggregator", "anyhow", "async-trait", "bincode", - "cipher 0.1.0", "config", "data", "enclave-core", - "evm", - "fhe 0.1.0", - "keyshare", "serde", - "sortition", "tracing", ] @@ -5838,6 +5842,7 @@ dependencies = [ "alloy", "anyhow", "async-trait", + "config", "data", "enclave-core", "num", diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index 1a4a4747..34664799 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -42,6 +42,7 @@ config = { path = "./config" } dirs = "5.0.1" data = { path = "./data" } enclave-core = { path = "./core" } +evm = { path = "./evm" } shellexpand = "3.1.0" figment = { version = "0.10.19", features = ["yaml", "test"] } fhe_rs = { package = "fhe", git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } @@ -52,8 +53,10 @@ futures-util = "0.3" hex = "0.4.3" lazy_static = "1.5.0" num = "0.4.3" +net = { path = "./net" } rand_chacha = "0.3.1" rand = "0.8.5" +router = { path = "./router" } serde = { version = "1.0.208", features = ["derive"] } serde_json = { version = "1.0.133" } sled = "0.34.7" diff --git a/packages/ciphernode/aggregator/Cargo.toml b/packages/ciphernode/aggregator/Cargo.toml index 495a26a1..83de43a5 100644 --- a/packages/ciphernode/aggregator/Cargo.toml +++ b/packages/ciphernode/aggregator/Cargo.toml @@ -8,9 +8,11 @@ actix = { workspace = true } anyhow = { workspace = true } serde = { workspace = true } bincode = { workspace = true } +config = { workspace = true } async-trait = { workspace = true } enclave-core = { path = "../core" } fhe = { path = "../fhe" } sortition = { path = "../sortition" } +router = { workspace = true } data = { path = "../data" } tracing = { workspace = true } diff --git a/packages/ciphernode/aggregator/src/feature.rs b/packages/ciphernode/aggregator/src/feature.rs new file mode 100644 index 00000000..d0422b5c --- /dev/null +++ b/packages/ciphernode/aggregator/src/feature.rs @@ -0,0 +1,257 @@ +use crate::{ + PlaintextAggregator, PlaintextAggregatorParams, PlaintextAggregatorState, + PlaintextRepositoryFactory, PublicKeyAggregator, PublicKeyAggregatorParams, + PublicKeyAggregatorState, PublicKeyRepositoryFactory, +}; +use actix::{Actor, Addr}; +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use data::{AutoPersist, RepositoriesFactory}; +use enclave_core::{BusError, EnclaveErrorType, EnclaveEvent, EventBus}; +use fhe::FHE_KEY; +use router::{E3Feature, E3RequestContext, E3RequestContextSnapshot, META_KEY}; +use sortition::Sortition; + +pub struct PlaintextAggregatorFeature { + bus: Addr, + sortition: Addr, +} +impl PlaintextAggregatorFeature { + pub fn create(bus: &Addr, sortition: &Addr) -> Box { + Box::new(Self { + bus: bus.clone(), + sortition: sortition.clone(), + }) + } +} + +const ERROR_PLAINTEXT_FHE_MISSING:&str = "Could not create PlaintextAggregator because the fhe instance it depends on was not set on the context."; +const ERROR_PLAINTEXT_META_MISSING:&str = "Could not create PlaintextAggregator because the meta instance it depends on was not set on the context."; + +#[async_trait] +impl E3Feature for PlaintextAggregatorFeature { + fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent) { + // Save plaintext aggregator + let EnclaveEvent::CiphertextOutputPublished { data, .. } = evt else { + return; + }; + + let Some(fhe) = ctx.get_dependency(FHE_KEY) else { + self.bus.err( + EnclaveErrorType::PlaintextAggregation, + anyhow!(ERROR_PLAINTEXT_FHE_MISSING), + ); + return; + }; + + let Some(ref meta) = ctx.get_dependency(META_KEY) else { + self.bus.err( + EnclaveErrorType::PlaintextAggregation, + anyhow!(ERROR_PLAINTEXT_META_MISSING), + ); + return; + }; + + let e3_id = data.e3_id.clone(); + let repo = ctx.repositories().plaintext(&e3_id); + let sync_state = repo.send(Some(PlaintextAggregatorState::init( + meta.threshold_m, + meta.seed, + data.ciphertext_output.clone(), + ))); + + ctx.set_event_recipient( + "plaintext", + Some( + PlaintextAggregator::new( + PlaintextAggregatorParams { + fhe: fhe.clone(), + bus: self.bus.clone(), + sortition: self.sortition.clone(), + e3_id: e3_id.clone(), + src_chain_id: meta.src_chain_id, + }, + sync_state, + ) + .start() + .into(), + ), + ); + } + + async fn hydrate( + &self, + ctx: &mut E3RequestContext, + snapshot: &E3RequestContextSnapshot, + ) -> Result<()> { + // No ID on the snapshot -> bail + if !snapshot.contains("plaintext") { + return Ok(()); + } + + let repo = ctx.repositories().plaintext(&snapshot.e3_id); + let sync_state = repo.load().await?; + + // No Snapshot returned from the store -> bail + if !sync_state.has() { + return Ok(()); + }; + + // Get deps + let Some(fhe) = ctx.get_dependency(FHE_KEY) else { + self.bus.err( + EnclaveErrorType::PlaintextAggregation, + anyhow!(ERROR_PLAINTEXT_FHE_MISSING), + ); + return Ok(()); + }; + + let Some(ref meta) = ctx.get_dependency(META_KEY) else { + self.bus.err( + EnclaveErrorType::PlaintextAggregation, + anyhow!(ERROR_PLAINTEXT_META_MISSING), + ); + return Ok(()); + }; + + let value = PlaintextAggregator::new( + PlaintextAggregatorParams { + fhe: fhe.clone(), + bus: self.bus.clone(), + sortition: self.sortition.clone(), + e3_id: ctx.e3_id.clone(), + src_chain_id: meta.src_chain_id, + }, + sync_state, + ) + .start() + .into(); + + // send to context + ctx.set_event_recipient("plaintext", Some(value)); + + Ok(()) + } +} + +pub struct PublicKeyAggregatorFeature { + bus: Addr, + sortition: Addr, +} + +impl PublicKeyAggregatorFeature { + pub fn create(bus: &Addr, sortition: &Addr) -> Box { + Box::new(Self { + bus: bus.clone(), + sortition: sortition.clone(), + }) + } +} + +const ERROR_PUBKEY_FHE_MISSING:&str = "Could not create PublicKeyAggregator because the fhe instance it depends on was not set on the context."; +const ERROR_PUBKEY_META_MISSING:&str = "Could not create PublicKeyAggregator because the meta instance it depends on was not set on the context."; + +#[async_trait] +impl E3Feature for PublicKeyAggregatorFeature { + fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent) { + // Saving the publickey aggregator with deps on E3Requested + let EnclaveEvent::E3Requested { data, .. } = evt else { + return; + }; + + let Some(fhe) = ctx.get_dependency(FHE_KEY) else { + self.bus.err( + EnclaveErrorType::PublickeyAggregation, + anyhow!(ERROR_PUBKEY_FHE_MISSING), + ); + return; + }; + let Some(ref meta) = ctx.get_dependency(META_KEY) else { + self.bus.err( + EnclaveErrorType::PublickeyAggregation, + anyhow!(ERROR_PUBKEY_META_MISSING), + ); + return; + }; + + let e3_id = data.e3_id.clone(); + let repo = ctx.repositories().publickey(&e3_id); + let sync_state = repo.send(Some(PublicKeyAggregatorState::init( + meta.threshold_m, + meta.seed, + ))); + ctx.set_event_recipient( + "publickey", + Some( + PublicKeyAggregator::new( + PublicKeyAggregatorParams { + fhe: fhe.clone(), + bus: self.bus.clone(), + sortition: self.sortition.clone(), + e3_id, + src_chain_id: meta.src_chain_id, + }, + sync_state, + ) + .start() + .into(), + ), + ); + } + + async fn hydrate( + &self, + ctx: &mut E3RequestContext, + snapshot: &E3RequestContextSnapshot, + ) -> Result<()> { + // No ID on the snapshot -> bail + if !snapshot.contains("publickey") { + return Ok(()); + }; + + let repo = ctx.repositories().publickey(&ctx.e3_id); + let sync_state = repo.load().await?; + + // No Snapshot returned from the store -> bail + if !sync_state.has() { + return Ok(()); + }; + + // Get deps + let Some(fhe) = ctx.get_dependency(FHE_KEY) else { + self.bus.err( + EnclaveErrorType::PublickeyAggregation, + anyhow!(ERROR_PUBKEY_FHE_MISSING), + ); + + return Ok(()); + }; + + let Some(meta) = ctx.get_dependency(META_KEY) else { + self.bus.err( + EnclaveErrorType::PublickeyAggregation, + anyhow!(ERROR_PUBKEY_META_MISSING), + ); + + return Ok(()); + }; + + let value = PublicKeyAggregator::new( + PublicKeyAggregatorParams { + fhe: fhe.clone(), + bus: self.bus.clone(), + sortition: self.sortition.clone(), + e3_id: ctx.e3_id.clone(), + src_chain_id: meta.src_chain_id, + }, + sync_state, + ) + .start() + .into(); + + // send to context + ctx.set_event_recipient("publickey", Some(value)); + + Ok(()) + } +} diff --git a/packages/ciphernode/aggregator/src/lib.rs b/packages/ciphernode/aggregator/src/lib.rs index fef3eb91..8a7415a5 100644 --- a/packages/ciphernode/aggregator/src/lib.rs +++ b/packages/ciphernode/aggregator/src/lib.rs @@ -1,8 +1,14 @@ +mod feature; mod plaintext_aggregator; mod publickey_aggregator; +mod repo; + pub use plaintext_aggregator::{ PlaintextAggregator, PlaintextAggregatorParams, PlaintextAggregatorState, }; pub use publickey_aggregator::{ PublicKeyAggregator, PublicKeyAggregatorParams, PublicKeyAggregatorState, }; + +pub use feature::*; +pub use repo::*; diff --git a/packages/ciphernode/aggregator/src/repo.rs b/packages/ciphernode/aggregator/src/repo.rs new file mode 100644 index 00000000..a010b142 --- /dev/null +++ b/packages/ciphernode/aggregator/src/repo.rs @@ -0,0 +1,25 @@ +use config::StoreKeys; +use data::{Repositories, Repository}; +use enclave_core::E3id; + +use crate::{PlaintextAggregatorState, PublicKeyAggregatorState}; + +pub trait PlaintextRepositoryFactory { + fn plaintext(&self, e3_id: &E3id) -> Repository; +} + +impl PlaintextRepositoryFactory for Repositories { + fn plaintext(&self, e3_id: &E3id) -> Repository { + Repository::new(self.store.scope(StoreKeys::plaintext(e3_id))) + } +} + +pub trait PublicKeyRepositoryFactory { + fn publickey(&self, e3_id: &E3id) -> Repository; +} + +impl PublicKeyRepositoryFactory for Repositories { + fn publickey(&self, e3_id: &E3id) -> Repository { + Repository::new(self.store.scope(StoreKeys::publickey(e3_id))) + } +} diff --git a/packages/ciphernode/data/src/lib.rs b/packages/ciphernode/data/src/lib.rs index 08b5f844..48ed8475 100644 --- a/packages/ciphernode/data/src/lib.rs +++ b/packages/ciphernode/data/src/lib.rs @@ -2,8 +2,8 @@ mod data_store; mod in_mem; mod into_key; mod persistable; +mod repositories; mod repository; -mod repository_factory; mod sled_store; mod snapshot; @@ -11,6 +11,7 @@ pub use data_store::*; pub use in_mem::*; pub use into_key::IntoKey; pub use persistable::*; +pub use repositories::*; pub use repository::*; pub use sled_store::*; pub use snapshot::*; diff --git a/packages/ciphernode/data/src/repository_factory.rs b/packages/ciphernode/data/src/repositories.rs similarity index 96% rename from packages/ciphernode/data/src/repository_factory.rs rename to packages/ciphernode/data/src/repositories.rs index 3024b84a..be9aad25 100644 --- a/packages/ciphernode/data/src/repository_factory.rs +++ b/packages/ciphernode/data/src/repositories.rs @@ -1,6 +1,5 @@ use crate::{DataStore, Repository}; -// TODO: Naming here is confusing pub struct Repositories { pub store: DataStore, } diff --git a/packages/ciphernode/enclave/Cargo.toml b/packages/ciphernode/enclave/Cargo.toml index c2595c4b..fcf601d3 100644 --- a/packages/ciphernode/enclave/Cargo.toml +++ b/packages/ciphernode/enclave/Cargo.toml @@ -12,26 +12,28 @@ alloy = { workspace = true } anyhow = { workspace = true } cipher = { path = "../cipher" } clap = { workspace = true } +compile-time = { workspace = true } config = { path = "../config" } data = { path = "../data" } -dirs = { workspace = true } dialoguer = "0.11.0" +dirs = { workspace = true } enclave-core = { path = "../core" } enclave_node = { path = "../enclave_node" } +evm = { workspace = true } hex = { workspace = true } libp2p = { workspace = true } +net = { workspace = true } once_cell = "1.20.2" +petname = "2.0.2" +phf = { version = "0.11", features = ["macros"] } +rand = { workspace = true } router = { path = "../router" } -tokio = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } +tokio = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } zeroize = { workspace = true } -phf = { version = "0.11", features = ["macros"] } -compile-time = { workspace = true } -rand = { workspace = true } -petname = "2.0.2" [build-dependencies] serde_json = { workspace = true } diff --git a/packages/ciphernode/enclave/src/commands/net/generate.rs b/packages/ciphernode/enclave/src/commands/net/generate.rs index 1e747379..d582daab 100644 --- a/packages/ciphernode/enclave/src/commands/net/generate.rs +++ b/packages/ciphernode/enclave/src/commands/net/generate.rs @@ -5,6 +5,7 @@ use config::AppConfig; use enclave_core::{EventBus, GetErrors}; use enclave_node::get_repositories; use libp2p::identity::Keypair; +use net::NetRepositoryFactory; use zeroize::Zeroize; pub async fn execute(config: &AppConfig) -> Result<()> { diff --git a/packages/ciphernode/enclave/src/commands/net/purge.rs b/packages/ciphernode/enclave/src/commands/net/purge.rs index 520a75d5..a83cc46e 100644 --- a/packages/ciphernode/enclave/src/commands/net/purge.rs +++ b/packages/ciphernode/enclave/src/commands/net/purge.rs @@ -3,6 +3,7 @@ use anyhow::*; use config::AppConfig; use enclave_core::EventBus; use enclave_node::get_repositories; +use net::NetRepositoryFactory; pub async fn execute(config: &AppConfig) -> Result<()> { let bus = EventBus::new(true).start(); diff --git a/packages/ciphernode/enclave/src/commands/net/set.rs b/packages/ciphernode/enclave/src/commands/net/set.rs index 853ecde0..3a6739ca 100644 --- a/packages/ciphernode/enclave/src/commands/net/set.rs +++ b/packages/ciphernode/enclave/src/commands/net/set.rs @@ -7,6 +7,7 @@ use dialoguer::{theme::ColorfulTheme, Password}; use enclave_core::{EventBus, GetErrors}; use enclave_node::get_repositories; use libp2p::identity::Keypair; +use net::NetRepositoryFactory; pub fn create_keypair(input: &String) -> Result { match hex::check(input) { diff --git a/packages/ciphernode/enclave/src/commands/wallet/set.rs b/packages/ciphernode/enclave/src/commands/wallet/set.rs index fcafd38e..4c322628 100644 --- a/packages/ciphernode/enclave/src/commands/wallet/set.rs +++ b/packages/ciphernode/enclave/src/commands/wallet/set.rs @@ -6,6 +6,7 @@ use config::AppConfig; use dialoguer::{theme::ColorfulTheme, Password}; use enclave_core::{EventBus, GetErrors}; use enclave_node::get_repositories; +use evm::EthPrivateKeyRepositoryFactory; pub fn validate_private_key(input: &String) -> Result<()> { let bytes = diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index e9fbbf6a..178432c0 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -1,27 +1,28 @@ +use crate::setup_datastore; use actix::{Actor, Addr}; +use aggregator::{PlaintextAggregatorFeature, PublicKeyAggregatorFeature}; use anyhow::Result; use cipher::Cipher; use config::AppConfig; +use data::RepositoriesFactory; use enclave_core::EventBus; use evm::{ helpers::{get_signer_from_repository, ProviderConfig}, - CiphernodeRegistrySol, EnclaveSol, RegistryFilterSol, + CiphernodeRegistryReaderRepositoryFactory, CiphernodeRegistrySol, EnclaveSol, + EnclaveSolReaderRepositoryFactory, EthPrivateKeyRepositoryFactory, RegistryFilterSol, }; +use fhe::FheFeature; use logger::SimpleLogger; -use net::NetworkManager; +use net::{NetRepositoryFactory, NetworkManager}; use rand::SeedableRng; use rand_chacha::{rand_core::OsRng, ChaCha20Rng}; -use router::{ - E3RequestRouter, FheFeature, PlaintextAggregatorFeature, PublicKeyAggregatorFeature, - RepositoriesFactory, -}; +use router::E3RequestRouter; use sortition::Sortition; +use sortition::SortitionRepositoryFactory; use std::sync::{Arc, Mutex}; use test_helpers::{PlaintextWriter, PublicKeyWriter}; use tokio::task::JoinHandle; -use crate::setup_datastore; - pub async fn setup_aggregator( config: AppConfig, pubkey_write_path: Option<&str>, diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/ciphernode.rs index bf71c73c..77bd8cc4 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/ciphernode.rs @@ -1,24 +1,29 @@ +use crate::setup_datastore; use actix::{Actor, Addr}; use alloy::primitives::Address; use anyhow::Result; use cipher::Cipher; use config::AppConfig; +use data::RepositoriesFactory; use enclave_core::{get_tag, EventBus}; -use evm::{helpers::ProviderConfig, CiphernodeRegistrySol, EnclaveSolReader}; +use evm::{ + helpers::ProviderConfig, CiphernodeRegistryReaderRepositoryFactory, CiphernodeRegistrySol, + EnclaveSolReader, EnclaveSolReaderRepositoryFactory, +}; +use fhe::FheFeature; +use keyshare::KeyshareFeature; use logger::SimpleLogger; -use net::NetworkManager; +use net::{NetRepositoryFactory, NetworkManager}; use rand::SeedableRng; use rand_chacha::rand_core::OsRng; -use router::{ - CiphernodeSelector, E3RequestRouter, FheFeature, KeyshareFeature, RepositoriesFactory, -}; +use router::E3RequestRouter; +use sortition::CiphernodeSelector; use sortition::Sortition; +use sortition::SortitionRepositoryFactory; use std::sync::{Arc, Mutex}; use tokio::task::JoinHandle; use tracing::instrument; -use crate::setup_datastore; - #[instrument(name="app", skip_all,fields(id = get_tag()))] pub async fn setup_ciphernode( config: AppConfig, diff --git a/packages/ciphernode/enclave_node/src/datastore.rs b/packages/ciphernode/enclave_node/src/datastore.rs index 17a2be04..43ff182f 100644 --- a/packages/ciphernode/enclave_node/src/datastore.rs +++ b/packages/ciphernode/enclave_node/src/datastore.rs @@ -4,8 +4,8 @@ use actix::{Actor, Addr}; use anyhow::Result; use config::AppConfig; use data::{DataStore, InMemStore, SledStore}; +use data::{Repositories, RepositoriesFactory}; use enclave_core::EventBus; -use router::{Repositories, RepositoriesFactory}; pub fn get_sled_store(bus: &Addr, db_file: &PathBuf) -> Result { Ok((&SledStore::new(bus, db_file)?).into()) diff --git a/packages/ciphernode/evm/src/lib.rs b/packages/ciphernode/evm/src/lib.rs index e7fb10c9..6cecae10 100644 --- a/packages/ciphernode/evm/src/lib.rs +++ b/packages/ciphernode/evm/src/lib.rs @@ -5,6 +5,7 @@ mod enclave_sol_writer; mod event_reader; pub mod helpers; mod registry_filter_sol; +mod repo; pub use ciphernode_registry_sol::{CiphernodeRegistrySol, CiphernodeRegistrySolReader}; pub use enclave_sol::EnclaveSol; @@ -12,3 +13,4 @@ pub use enclave_sol_reader::EnclaveSolReader; pub use enclave_sol_writer::EnclaveSolWriter; pub use event_reader::{EnclaveEvmEvent, EvmEventReader, EvmEventReaderState, ExtractorFn}; pub use registry_filter_sol::{RegistryFilterSol, RegistryFilterSolWriter}; +pub use repo::*; diff --git a/packages/ciphernode/evm/src/repo.rs b/packages/ciphernode/evm/src/repo.rs new file mode 100644 index 00000000..f20b5d1e --- /dev/null +++ b/packages/ciphernode/evm/src/repo.rs @@ -0,0 +1,37 @@ +use config::StoreKeys; +use data::{Repositories, Repository}; + +use crate::EvmEventReaderState; + +pub trait EthPrivateKeyRepositoryFactory { + fn eth_private_key(&self) -> Repository>; +} + +impl EthPrivateKeyRepositoryFactory for Repositories { + fn eth_private_key(&self) -> Repository> { + Repository::new(self.store.scope(StoreKeys::eth_private_key())) + } +} + +pub trait EnclaveSolReaderRepositoryFactory { + fn enclave_sol_reader(&self, chain_id: u64) -> Repository; +} + +impl EnclaveSolReaderRepositoryFactory for Repositories { + fn enclave_sol_reader(&self, chain_id: u64) -> Repository { + Repository::new(self.store.scope(StoreKeys::enclave_sol_reader(chain_id))) + } +} + +pub trait CiphernodeRegistryReaderRepositoryFactory { + fn ciphernode_registry_reader(&self, chain_id: u64) -> Repository; +} + +impl CiphernodeRegistryReaderRepositoryFactory for Repositories { + fn ciphernode_registry_reader(&self, chain_id: u64) -> Repository { + Repository::new( + self.store + .scope(StoreKeys::ciphernode_registry_reader(chain_id)), + ) + } +} diff --git a/packages/ciphernode/fhe/Cargo.toml b/packages/ciphernode/fhe/Cargo.toml index 3272964a..aeca4615 100644 --- a/packages/ciphernode/fhe/Cargo.toml +++ b/packages/ciphernode/fhe/Cargo.toml @@ -4,9 +4,11 @@ version = "0.1.0" edition = "2021" [dependencies] +actix = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } bincode = { workspace = true } +config = { workspace = true } data = { path = "../data" } enclave-core = { path = "../core" } fhe-traits = { workspace = true } @@ -14,4 +16,5 @@ fhe-util = { workspace = true } fhe_rs = { workspace = true } rand = { workspace = true } rand_chacha = { workspace = true } +router = { workspace = true } serde = { workspace = true } diff --git a/packages/ciphernode/fhe/src/feature.rs b/packages/ciphernode/fhe/src/feature.rs new file mode 100644 index 00000000..adba1897 --- /dev/null +++ b/packages/ciphernode/fhe/src/feature.rs @@ -0,0 +1,86 @@ +use crate::{Fhe, FheRepositoryFactory, SharedRng}; +use actix::Addr; +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use data::{FromSnapshotWithParams, RepositoriesFactory, Snapshot}; +use enclave_core::{BusError, E3Requested, EnclaveErrorType, EnclaveEvent, EventBus}; +use router::{E3Feature, E3RequestContext, E3RequestContextSnapshot, TypedKey}; +use std::sync::Arc; + +pub const FHE_KEY: TypedKey> = TypedKey::new("fhe"); + +/// TODO: move these to each package with access on MyStruct::launcher() +pub struct FheFeature { + rng: SharedRng, + bus: Addr, +} + +impl FheFeature { + pub fn create(bus: &Addr, rng: &SharedRng) -> Box { + Box::new(Self { + rng: rng.clone(), + bus: bus.clone(), + }) + } +} + +const ERROR_FHE_FAILED_TO_DECODE: &str = "Failed to decode encoded FHE params"; + +#[async_trait] +impl E3Feature for FheFeature { + fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent) { + // Saving the fhe on Committee Requested + let EnclaveEvent::E3Requested { data, .. } = evt else { + return; + }; + + let E3Requested { + params, + seed, + e3_id, + .. + } = data.clone(); + + let Ok(fhe_inner) = Fhe::from_encoded(¶ms, seed, self.rng.clone()) else { + self.bus.err( + EnclaveErrorType::KeyGeneration, + anyhow!(ERROR_FHE_FAILED_TO_DECODE), + ); + return; + }; + + let fhe = Arc::new(fhe_inner); + + // FHE doesn't implement Checkpoint so we are going to store it manually + let Ok(snapshot) = fhe.snapshot() else { + self.bus.err( + EnclaveErrorType::KeyGeneration, + anyhow!("Failed to get snapshot"), + ); + return; + }; + ctx.repositories().fhe(&e3_id).write(&snapshot); + let _ = ctx.set_dependency(FHE_KEY, fhe); + } + + async fn hydrate( + &self, + ctx: &mut E3RequestContext, + snapshot: &E3RequestContextSnapshot, + ) -> Result<()> { + // No ID on the snapshot -> bail without reporting + if !snapshot.contains("fhe") { + return Ok(()); + }; + + // No Snapshot returned from the store -> bail without reporting + let Some(snap) = ctx.repositories().fhe(&ctx.e3_id).read().await? else { + return Ok(()); + }; + + let value = Arc::new(Fhe::from_snapshot(self.rng.clone(), snap).await?); + ctx.set_dependency(FHE_KEY, value); + + Ok(()) + } +} diff --git a/packages/ciphernode/fhe/src/lib.rs b/packages/ciphernode/fhe/src/lib.rs index 75b9bd75..f6a4203d 100644 --- a/packages/ciphernode/fhe/src/lib.rs +++ b/packages/ciphernode/fhe/src/lib.rs @@ -1,5 +1,9 @@ +mod feature; mod fhe; +mod repo; mod utils; +pub use feature::*; pub use fhe::*; +pub use repo::*; pub use utils::*; diff --git a/packages/ciphernode/fhe/src/repo.rs b/packages/ciphernode/fhe/src/repo.rs new file mode 100644 index 00000000..7e035b73 --- /dev/null +++ b/packages/ciphernode/fhe/src/repo.rs @@ -0,0 +1,15 @@ +use config::StoreKeys; +use data::{Repositories, Repository}; +use enclave_core::E3id; + +use crate::FheSnapshot; + +pub trait FheRepositoryFactory { + fn fhe(&self, e3_id: &E3id) -> Repository; +} + +impl FheRepositoryFactory for Repositories { + fn fhe(&self, e3_id: &E3id) -> Repository { + Repository::new(self.store.scope(StoreKeys::fhe(e3_id))) + } +} diff --git a/packages/ciphernode/keyshare/Cargo.toml b/packages/ciphernode/keyshare/Cargo.toml index bc05ba0b..b5a9c599 100644 --- a/packages/ciphernode/keyshare/Cargo.toml +++ b/packages/ciphernode/keyshare/Cargo.toml @@ -7,9 +7,11 @@ edition = "2021" actix = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } +config = { workspace = true } data = { path = "../data" } cipher = { path = "../cipher" } enclave-core = { path = "../core" } fhe = { path = "../fhe" } +router = { workspace = true } serde = { workspace = true } tracing = { workspace = true } diff --git a/packages/ciphernode/keyshare/src/feature.rs b/packages/ciphernode/keyshare/src/feature.rs new file mode 100644 index 00000000..e5db14ea --- /dev/null +++ b/packages/ciphernode/keyshare/src/feature.rs @@ -0,0 +1,111 @@ +use crate::{Keyshare, KeyshareParams, KeyshareRepositoryFactory}; +use actix::{Actor, Addr}; +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use cipher::Cipher; +use data::{AutoPersist, RepositoriesFactory}; +use enclave_core::{BusError, EnclaveErrorType, EnclaveEvent, EventBus}; +use fhe::FHE_KEY; +use router::{E3Feature, E3RequestContext, E3RequestContextSnapshot}; +use std::sync::Arc; + +pub struct KeyshareFeature { + bus: Addr, + address: String, + cipher: Arc, +} + +impl KeyshareFeature { + pub fn create(bus: &Addr, address: &str, cipher: &Arc) -> Box { + Box::new(Self { + bus: bus.clone(), + address: address.to_owned(), + cipher: cipher.to_owned(), + }) + } +} + +const ERROR_KEYSHARE_FHE_MISSING: &str = + "Could not create Keyshare because the fhe instance it depends on was not set on the context."; + +#[async_trait] +impl E3Feature for KeyshareFeature { + fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent) { + // if this is NOT a CiphernodeSelected event then ignore + let EnclaveEvent::CiphernodeSelected { data, .. } = evt else { + return; + }; + + // Has the FHE dependency been already setup? (hint: it should have) + let Some(fhe) = ctx.get_dependency(FHE_KEY) else { + self.bus.err( + EnclaveErrorType::KeyGeneration, + anyhow!(ERROR_KEYSHARE_FHE_MISSING), + ); + return; + }; + + let e3_id = data.clone().e3_id; + let repo = ctx.repositories().keyshare(&e3_id); + let container = repo.send(None); // New container with None + + ctx.set_event_recipient( + "keyshare", + Some( + Keyshare::new(KeyshareParams { + bus: self.bus.clone(), + secret: container, + fhe: fhe.clone(), + address: self.address.clone(), + cipher: self.cipher.clone(), + }) + .start() + .into(), + ), + ); + } + + async fn hydrate( + &self, + ctx: &mut E3RequestContext, + snapshot: &E3RequestContextSnapshot, + ) -> Result<()> { + // No keyshare on the snapshot -> bail + if !snapshot.contains("keyshare") { + return Ok(()); + }; + + // Get the saved state as a persistable + let sync_secret = ctx.repositories().keyshare(&snapshot.e3_id).load().await?; + + // No Snapshot returned from the sync_secret -> bail + if !sync_secret.has() { + return Ok(()); + }; + + // Has the FHE dependency been already setup? (hint: it should have) + let Some(fhe) = ctx.get_dependency(FHE_KEY) else { + self.bus.err( + EnclaveErrorType::KeyGeneration, + anyhow!(ERROR_KEYSHARE_FHE_MISSING), + ); + return Ok(()); + }; + + // Construct from snapshot + let value = Keyshare::new(KeyshareParams { + fhe: fhe.clone(), + bus: self.bus.clone(), + secret: sync_secret, + address: self.address.clone(), + cipher: self.cipher.clone(), + }) + .start() + .into(); + + // send to context + ctx.set_event_recipient("keyshare", Some(value)); + + Ok(()) + } +} diff --git a/packages/ciphernode/keyshare/src/lib.rs b/packages/ciphernode/keyshare/src/lib.rs index 46e4b5c9..a41ebf72 100644 --- a/packages/ciphernode/keyshare/src/lib.rs +++ b/packages/ciphernode/keyshare/src/lib.rs @@ -1,2 +1,6 @@ +mod feature; mod keyshare; +mod repo; +pub use feature::*; pub use keyshare::*; +pub use repo::*; diff --git a/packages/ciphernode/keyshare/src/repo.rs b/packages/ciphernode/keyshare/src/repo.rs new file mode 100644 index 00000000..772c5335 --- /dev/null +++ b/packages/ciphernode/keyshare/src/repo.rs @@ -0,0 +1,13 @@ +use config::StoreKeys; +use data::{Repositories, Repository}; +use enclave_core::E3id; + +pub trait KeyshareRepositoryFactory { + fn keyshare(&self, e3_id: &E3id) -> Repository>; +} + +impl KeyshareRepositoryFactory for Repositories { + fn keyshare(&self, e3_id: &E3id) -> Repository> { + Repository::new(self.store.scope(StoreKeys::keyshare(e3_id))) + } +} diff --git a/packages/ciphernode/net/Cargo.toml b/packages/ciphernode/net/Cargo.toml index 91ecb570..dba2c1d5 100644 --- a/packages/ciphernode/net/Cargo.toml +++ b/packages/ciphernode/net/Cargo.toml @@ -12,6 +12,7 @@ async-std = { workspace = true, features = ["attributes"] } async-trait = { workspace = true } futures = { workspace = true } cipher = { workspace = true } +config = { workspace = true } data = { workspace = true } libp2p = { workspace = true, features = [ "async-std", diff --git a/packages/ciphernode/net/src/lib.rs b/packages/ciphernode/net/src/lib.rs index 35d4f37e..b695df32 100644 --- a/packages/ciphernode/net/src/lib.rs +++ b/packages/ciphernode/net/src/lib.rs @@ -6,7 +6,9 @@ mod dialer; pub mod events; mod network_manager; mod network_peer; +mod repo; mod retry; pub use network_manager::*; pub use network_peer::*; +pub use repo::*; diff --git a/packages/ciphernode/net/src/repo.rs b/packages/ciphernode/net/src/repo.rs new file mode 100644 index 00000000..93b0abc2 --- /dev/null +++ b/packages/ciphernode/net/src/repo.rs @@ -0,0 +1,12 @@ +use config::StoreKeys; +use data::{Repositories, Repository}; + +pub trait NetRepositoryFactory { + fn libp2p_keypair(&self) -> Repository>; +} + +impl NetRepositoryFactory for Repositories { + fn libp2p_keypair(&self) -> Repository> { + Repository::new(self.store.scope(StoreKeys::libp2p_keypair())) + } +} diff --git a/packages/ciphernode/router/Cargo.toml b/packages/ciphernode/router/Cargo.toml index d2dbacec..f5e485c9 100644 --- a/packages/ciphernode/router/Cargo.toml +++ b/packages/ciphernode/router/Cargo.toml @@ -6,16 +6,10 @@ edition = "2021" [dependencies] actix = { workspace = true } enclave-core = { path = "../core" } -sortition = { path = "../sortition" } -fhe = { path = "../fhe" } data = { path = "../data" } -keyshare = { path = "../keyshare" } -aggregator = { path = "../aggregator" } -evm = { path = "../evm" } anyhow = { workspace = true } serde = { workspace = true } config = { workspace = true } -cipher = { path = "../cipher" } bincode = { workspace = true } async-trait = { workspace = true } tracing = { workspace = true } diff --git a/packages/ciphernode/router/src/committee_meta.rs b/packages/ciphernode/router/src/committee_meta.rs index eb9a7d2c..74c6cc2b 100644 --- a/packages/ciphernode/router/src/committee_meta.rs +++ b/packages/ciphernode/router/src/committee_meta.rs @@ -1,8 +1,13 @@ -use crate::{E3Feature, E3RequestContext, E3RequestContextSnapshot, RepositoriesFactory}; +use crate::{ + E3Feature, E3RequestContext, E3RequestContextSnapshot, MetaRepositoryFactory, TypedKey, +}; use anyhow::*; use async_trait::async_trait; +use data::RepositoriesFactory; use enclave_core::{E3Requested, EnclaveEvent, Seed}; +pub const META_KEY: TypedKey = TypedKey::new("meta"); + #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct CommitteeMeta { pub threshold_m: usize, @@ -39,7 +44,7 @@ impl E3Feature for CommitteeMetaFeature { src_chain_id, }; ctx.repositories().meta(&e3_id).write(&meta); - let _ = ctx.set_meta(meta); + let _ = ctx.set_dependency(META_KEY, meta); } async fn hydrate( @@ -48,7 +53,7 @@ impl E3Feature for CommitteeMetaFeature { snapshot: &E3RequestContextSnapshot, ) -> Result<()> { // No ID on the snapshot -> bail - if !snapshot.meta { + if !snapshot.contains("meta") { return Ok(()); }; @@ -59,7 +64,7 @@ impl E3Feature for CommitteeMetaFeature { return Ok(()); }; - ctx.set_meta(value); + ctx.set_dependency(META_KEY, value); Ok(()) } diff --git a/packages/ciphernode/router/src/context.rs b/packages/ciphernode/router/src/context.rs index f869844c..e1912c30 100644 --- a/packages/ciphernode/router/src/context.rs +++ b/packages/ciphernode/router/src/context.rs @@ -1,36 +1,53 @@ -use std::sync::Arc; - -use crate::{CommitteeMeta, E3Feature, EventBuffer, Repositories, RepositoriesFactory}; -use actix::{Addr, Recipient}; -use aggregator::{PlaintextAggregator, PublicKeyAggregator}; +use crate::{E3Feature, EventBuffer, HetrogenousMap, TypedKey}; +use actix::Recipient; use anyhow::Result; use async_trait::async_trait; -use data::{Checkpoint, FromSnapshotWithParams, Repository, Snapshot}; +use data::{ + Checkpoint, FromSnapshotWithParams, Repositories, RepositoriesFactory, Repository, Snapshot, +}; use enclave_core::{E3id, EnclaveEvent}; -use fhe::Fhe; -use keyshare::Keyshare; use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, sync::Arc}; + +/// Initialize the HashMap with a list of expected Recipients. In order to know whether or not we +/// should buffer we need to iterate over this list and determine which recipients are missing based +/// on the recipient value is why we set it here to have keys with empty values. +fn init_recipients() -> HashMap>> { + HashMap::from([ + ("keyshare".to_owned(), None), + ("plaintext".to_owned(), None), + ("publickey".to_owned(), None), + ]) +} /// Context that is set to each event hook. Hooks can use this context to gather dependencies if /// they need to instantiate struct instances or actors. +// TODO: remove Addr imports as we need to be able to move the features out of the hooks file +// without circular deps +// TODO: remove Arc import as we need to be able to move the Fhe feature out of the hooks +// file without circular deps pub struct E3RequestContext { + /// The E3Request's ID pub e3_id: E3id, - pub keyshare: Option>, - pub fhe: Option>, - pub plaintext: Option>, - pub publickey: Option>, - pub meta: Option, + /// A way to store EnclaveEvent recipients on the context + pub recipients: HashMap>>, // NOTE: can be a None value + /// A way to store a feature's dependencies on the context + pub dependencies: HetrogenousMap, + /// A Repository for storing this context's data snapshot pub store: Repository, } #[derive(Serialize, Deserialize)] pub struct E3RequestContextSnapshot { - pub keyshare: bool, pub e3_id: E3id, - pub fhe: bool, - pub plaintext: bool, - pub publickey: bool, - pub meta: bool, + pub recipients: Vec, + pub dependencies: Vec, +} + +impl E3RequestContextSnapshot { + pub fn contains(&self, key: &str) -> bool { + self.recipients.contains(&key.to_string()) || self.dependencies.contains(&key.to_string()) + } } pub struct E3RequestContextParams { @@ -44,29 +61,18 @@ impl E3RequestContext { Self { e3_id: params.e3_id, store: params.store, - fhe: None, - keyshare: None, - meta: None, - plaintext: None, - publickey: None, + recipients: init_recipients(), + dependencies: HetrogenousMap::new(), } } + /// Return a list of expected recipient keys alongside any values that have or have not been + /// set. fn recipients(&self) -> Vec<(String, Option>)> { - vec![ - ( - "keyshare".to_owned(), - self.keyshare.clone().map(|addr| addr.into()), - ), - ( - "plaintext".to_owned(), - self.plaintext.clone().map(|addr| addr.into()), - ), - ( - "publickey".to_owned(), - self.publickey.clone().map(|addr| addr.into()), - ), - ] + self.recipients + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect() } pub fn forward_message(&self, msg: &EnclaveEvent, buffer: &mut EventBuffer) { @@ -90,54 +96,34 @@ impl E3RequestContext { }); } - /// Accept a DataStore ID and a Keystore actor address - pub fn set_keyshare(&mut self, value: Addr) { - self.keyshare = Some(value); - self.checkpoint(); - } - - /// Accept a DataStore ID and a Keystore actor address - pub fn set_plaintext(&mut self, value: Addr) { - self.plaintext = Some(value); - self.checkpoint(); - } - - /// Accept a DataStore ID and a Keystore actor address - pub fn set_publickey(&mut self, value: Addr) { - self.publickey = Some(value); + pub fn set_event_recipient( + &mut self, + key: impl Into, + value: Option>, + ) { + self.recipients.insert(key.into(), value); self.checkpoint(); } - /// Accept a DataStore ID and an Arc instance of the Fhe wrapper - pub fn set_fhe(&mut self, value: Arc) { - self.fhe = Some(value.clone()); - self.checkpoint(); + pub fn get_event_recipient(&self, key: impl Into) -> Option<&Recipient> { + self.recipients + .get(&key.into()) + .and_then(|opt| opt.as_ref()) } - /// Accept a Datastore ID and a metadata object - pub fn set_meta(&mut self, value: CommitteeMeta) { - self.meta = Some(value.clone()); + pub fn set_dependency(&mut self, key: TypedKey, value: T) + where + T: Send + Sync + 'static, + { + self.dependencies.insert(key, value); self.checkpoint(); } - pub fn get_keyshare(&self) -> Option<&Addr> { - self.keyshare.as_ref() - } - - pub fn get_plaintext(&self) -> Option<&Addr> { - self.plaintext.as_ref() - } - - pub fn get_publickey(&self) -> Option<&Addr> { - self.publickey.as_ref() - } - - pub fn get_fhe(&self) -> Option<&Arc> { - self.fhe.as_ref() - } - - pub fn get_meta(&self) -> Option<&CommitteeMeta> { - self.meta.as_ref() + pub fn get_dependency(&self, key: TypedKey) -> Option<&T> + where + T: Send + Sync + 'static, + { + self.dependencies.get(key) } } @@ -154,11 +140,8 @@ impl Snapshot for E3RequestContext { fn snapshot(&self) -> Result { Ok(Self::Snapshot { e3_id: self.e3_id.clone(), - meta: self.meta.is_some(), - fhe: self.fhe.is_some(), - publickey: self.publickey.is_some(), - plaintext: self.plaintext.is_some(), - keyshare: self.keyshare.is_some(), + dependencies: self.dependencies.keys(), + recipients: self.recipients.keys().cloned().collect(), }) } } @@ -170,11 +153,8 @@ impl FromSnapshotWithParams for E3RequestContext { let mut ctx = Self { e3_id: params.e3_id, store: params.store, - fhe: None, - keyshare: None, - meta: None, - plaintext: None, - publickey: None, + recipients: init_recipients(), + dependencies: HetrogenousMap::new(), }; for feature in params.features.iter() { diff --git a/packages/ciphernode/router/src/e3_request_router.rs b/packages/ciphernode/router/src/e3_request_router.rs index ba2eb39c..9c29c81a 100644 --- a/packages/ciphernode/router/src/e3_request_router.rs +++ b/packages/ciphernode/router/src/e3_request_router.rs @@ -1,8 +1,9 @@ use crate::CommitteeMetaFeature; +use crate::ContextRepositoryFactory; use crate::E3RequestContext; use crate::E3RequestContextParams; use crate::E3RequestContextSnapshot; -use crate::RepositoriesFactory; +use crate::RouterRepositoryFactory; use actix::AsyncContext; use actix::{Actor, Addr, Context, Handler}; use anyhow::*; @@ -10,6 +11,7 @@ use async_trait::async_trait; use data::Checkpoint; use data::DataStore; use data::FromSnapshotWithParams; +use data::RepositoriesFactory; use data::Repository; use data::Snapshot; use enclave_core::E3RequestComplete; @@ -39,10 +41,24 @@ impl EventBuffer { } } -/// Format of the hook that needs to be passed to E3RequestRouter +/// Format of a Feature that can be passed to E3RequestRouter. E3Features listen for EnclaveEvents +/// that are braoadcast to know when to instantiate themselves. They define the events they respond +/// to using the `on_event` handler. Within this handler they will typically use the request's +/// context to construct a version of their requisite actors and save their addresses to the +/// context using the `set_event_recipient` method on the context. Event recipients once set will +/// then have all their events streamed to them from their buffer. Features can also reconstruct +/// Actors based on their persisted state using the context snapshot and relevant repositories. +/// Generally Features can ask the context to see if a dependency has already been set to know if +/// it has everything it needs to construct the Feature #[async_trait] pub trait E3Feature: Send + Sync + 'static { + /// This function is triggered when an EnclaveEvent is sent to the router. Use this to + /// initialize the receiver using `ctx.set_event_receiver(my_address.into())`. Typically this + /// means filtering for specific e3_id enabled events that give rise to actors that have to + /// handle certain behaviour. fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent); + + /// This function it triggered when the request context is being hydrated from snapshot. async fn hydrate( &self, ctx: &mut E3RequestContext, @@ -51,14 +67,16 @@ pub trait E3Feature: Send + Sync + 'static { } /// E3RequestRouter will register features that receive an E3_id specific context. After features -/// have run e3_id specific messages are forwarded to all instances on the context. This enables -/// features to lazily register instances that have the correct dependencies available per e3_id -/// request -// TODO: setup typestate pattern so that we have to place features within correct order of -// dependencies +/// have run e3_id specific messages are forwarded to all instances on the context as they come in. +/// This enables features to lazily register instances that have the correct dependencies available +/// per e3_id request. +// TODO: setup so that we have to place features within correct order of dependencies pub struct E3RequestRouter { + /// The context for every E3 request contexts: HashMap, + /// A list of completed requests completed: HashSet, + /// The features this instance of the router is configured to listen for features: Arc>>, buffer: EventBuffer, bus: Addr, diff --git a/packages/ciphernode/router/src/hetrogenous_map.rs b/packages/ciphernode/router/src/hetrogenous_map.rs new file mode 100644 index 00000000..c8c34383 --- /dev/null +++ b/packages/ciphernode/router/src/hetrogenous_map.rs @@ -0,0 +1,154 @@ +use std::any::Any; +use std::{collections::HashMap, marker::PhantomData}; + +/// A key that is associated to a type within the HetrogenousMap given by the generic parameter T +pub struct TypedKey { + name: &'static str, + _phantom: PhantomData, +} + +impl TypedKey { + pub const fn new(name: &'static str) -> Self { + Self { + name, + _phantom: PhantomData, + } + } +} + +/// A map that accepts hetrogenous data and stores it in a typesafe way using a typed key +pub struct HetrogenousMap { + storage: HashMap<&'static str, Box>, +} + +impl HetrogenousMap { + pub fn new() -> Self { + Self { + storage: HashMap::new(), + } + } + + /// Insert data of type T + pub fn insert(&mut self, key: TypedKey, dep: T) { + self.storage.insert(key.name, Box::new(dep)); + } + + /// Get data of type T + pub fn get(&self, key: TypedKey) -> Option<&T> { + self.storage.get(key.name)?.downcast_ref() + } + + /// Search for data that holds data under the given key name + pub fn contains(&self, name: &'static str) -> bool { + self.storage.contains_key(name) + } + + /// Get a list of all key names + pub fn keys(&self) -> Vec { + self.storage.keys().map(|&k| k.to_string()).collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Arc; + + // Define test keys + const STRING_KEY: TypedKey = TypedKey::new("string_value"); + const INT_KEY: TypedKey = TypedKey::new("int_value"); + const FLOAT_KEY: TypedKey = TypedKey::new("float_value"); + const VEC_KEY: TypedKey> = TypedKey::new("vec_value"); + const ARC_KEY: TypedKey> = TypedKey::new("arc_value"); + + #[test] + fn test_basic_insert_and_get() { + let mut map = HetrogenousMap::new(); + map.insert(STRING_KEY, "hello".to_string()); + map.insert(INT_KEY, 42); + + assert_eq!(map.get(STRING_KEY), Some(&"hello".to_string())); + assert_eq!(map.get(INT_KEY), Some(&42)); + } + + #[test] + fn test_overwrite_value() { + let mut map = HetrogenousMap::new(); + map.insert(INT_KEY, 42); + map.insert(INT_KEY, 24); + + assert_eq!(map.get(INT_KEY), Some(&24)); + } + + #[test] + fn test_get_nonexistent_key() { + let map = HetrogenousMap::new(); + assert_eq!(map.get(STRING_KEY), None); + } + + #[test] + fn test_contains() { + let mut map = HetrogenousMap::new(); + map.insert(STRING_KEY, "test".to_string()); + + assert!(map.contains("string_value")); + assert!(!map.contains("nonexistent")); + } + + #[test] + fn test_keys() { + let mut map = HetrogenousMap::new(); + map.insert(STRING_KEY, "test".to_string()); + map.insert(INT_KEY, 42); + + let mut keys = map.keys(); + keys.sort(); // Sort for deterministic comparison + assert_eq!(keys, vec!["int_value", "string_value"]); + } + + #[test] + fn test_complex_types() { + let mut map = HetrogenousMap::new(); + + // Test with Vec + let vec_data = vec![1, 2, 3]; + map.insert(VEC_KEY, vec_data.clone()); + assert_eq!(map.get(VEC_KEY), Some(&vec_data)); + + // Test with Arc + let arc_data = Arc::new("shared data".to_string()); + map.insert(ARC_KEY, arc_data.clone()); + assert_eq!(map.get(ARC_KEY).map(|a| a.as_str()), Some("shared data")); + } + + #[test] + fn test_multiple_types() { + let mut map = HetrogenousMap::new(); + + map.insert(STRING_KEY, "string".to_string()); + map.insert(INT_KEY, 42); + map.insert(FLOAT_KEY, 3.14); + + assert_eq!(map.get(STRING_KEY), Some(&"string".to_string())); + assert_eq!(map.get(INT_KEY), Some(&42)); + assert_eq!(map.get(FLOAT_KEY), Some(&3.14)); + } + + // This test verifies that Send + Sync bounds work correctly + #[test] + fn test_thread_safety() { + use std::thread; + + let mut map = HetrogenousMap::new(); + map.insert(STRING_KEY, "test".to_string()); + + let map_arc = Arc::new(map); + let map_clone = map_arc.clone(); + + let handle = thread::spawn(move || { + assert_eq!(map_clone.get(STRING_KEY), Some(&"test".to_string())); + }); + + handle.join().unwrap(); + } +} diff --git a/packages/ciphernode/router/src/hooks.rs b/packages/ciphernode/router/src/hooks.rs deleted file mode 100644 index 5752542f..00000000 --- a/packages/ciphernode/router/src/hooks.rs +++ /dev/null @@ -1,419 +0,0 @@ -use crate::{E3Feature, E3RequestContext, E3RequestContextSnapshot, RepositoriesFactory}; -use actix::{Actor, Addr}; -use aggregator::{ - PlaintextAggregator, PlaintextAggregatorParams, PlaintextAggregatorState, PublicKeyAggregator, - PublicKeyAggregatorParams, PublicKeyAggregatorState, -}; -use anyhow::{anyhow, Result}; -use async_trait::async_trait; -use cipher::Cipher; -use data::{AutoPersist, FromSnapshotWithParams, Snapshot}; -use enclave_core::{BusError, E3Requested, EnclaveErrorType, EnclaveEvent, EventBus}; -use fhe::{Fhe, SharedRng}; -use keyshare::{Keyshare, KeyshareParams}; -use sortition::Sortition; -use std::sync::Arc; - -/// TODO: move these to each package with access on MyStruct::launcher() -pub struct FheFeature { - rng: SharedRng, - bus: Addr, -} - -impl FheFeature { - pub fn create(bus: &Addr, rng: &SharedRng) -> Box { - Box::new(Self { - rng: rng.clone(), - bus: bus.clone(), - }) - } -} - -const ERROR_FHE_FAILED_TO_DECODE: &str = "Failed to decode encoded FHE params"; - -#[async_trait] -impl E3Feature for FheFeature { - fn on_event(&self, ctx: &mut crate::E3RequestContext, evt: &EnclaveEvent) { - // Saving the fhe on Committee Requested - let EnclaveEvent::E3Requested { data, .. } = evt else { - return; - }; - - let E3Requested { - params, - seed, - e3_id, - .. - } = data.clone(); - - let Ok(fhe_inner) = Fhe::from_encoded(¶ms, seed, self.rng.clone()) else { - self.bus.err( - EnclaveErrorType::KeyGeneration, - anyhow!(ERROR_FHE_FAILED_TO_DECODE), - ); - return; - }; - - let fhe = Arc::new(fhe_inner); - - // FHE doesn't implement Checkpoint so we are going to store it manually - let Ok(snapshot) = fhe.snapshot() else { - self.bus.err( - EnclaveErrorType::KeyGeneration, - anyhow!("Failed to get snapshot"), - ); - return; - }; - ctx.repositories().fhe(&e3_id).write(&snapshot); - - let _ = ctx.set_fhe(fhe); - } - - async fn hydrate( - &self, - ctx: &mut E3RequestContext, - snapshot: &E3RequestContextSnapshot, - ) -> Result<()> { - // No ID on the snapshot -> bail without reporting - if !snapshot.fhe { - return Ok(()); - }; - - // No Snapshot returned from the store -> bail without reporting - let Some(snap) = ctx.repositories().fhe(&ctx.e3_id).read().await? else { - return Ok(()); - }; - - let value = Arc::new(Fhe::from_snapshot(self.rng.clone(), snap).await?); - ctx.set_fhe(value); - - Ok(()) - } -} - -pub struct KeyshareFeature { - bus: Addr, - address: String, - cipher: Arc, -} - -impl KeyshareFeature { - pub fn create(bus: &Addr, address: &str, cipher: &Arc) -> Box { - Box::new(Self { - bus: bus.clone(), - address: address.to_owned(), - cipher: cipher.to_owned(), - }) - } -} - -const ERROR_KEYSHARE_FHE_MISSING: &str = - "Could not create Keyshare because the fhe instance it depends on was not set on the context."; - -#[async_trait] -impl E3Feature for KeyshareFeature { - fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent) { - // Save Ciphernode on CiphernodeSelected - let EnclaveEvent::CiphernodeSelected { data, .. } = evt else { - return; - }; - - let Some(fhe) = ctx.get_fhe() else { - self.bus.err( - EnclaveErrorType::KeyGeneration, - anyhow!(ERROR_KEYSHARE_FHE_MISSING), - ); - return; - }; - - let e3_id = data.clone().e3_id; - let repo = ctx.repositories().keyshare(&e3_id); - let container = repo.send(None); - - ctx.set_keyshare( - Keyshare::new(KeyshareParams { - bus: self.bus.clone(), - secret: container, - fhe: fhe.clone(), - address: self.address.clone(), - cipher: self.cipher.clone(), - }) - .start(), - ); - } - - async fn hydrate( - &self, - ctx: &mut E3RequestContext, - snapshot: &E3RequestContextSnapshot, - ) -> Result<()> { - // No ID on the snapshot -> bail - if !snapshot.keyshare { - return Ok(()); - }; - - let sync_secret = ctx.repositories().keyshare(&snapshot.e3_id).load().await?; - - // No Snapshot returned from the sync_secret -> bail - if !sync_secret.has() { - return Ok(()); - }; - - // Get deps - let Some(fhe) = ctx.fhe.clone() else { - self.bus.err( - EnclaveErrorType::KeyGeneration, - anyhow!(ERROR_KEYSHARE_FHE_MISSING), - ); - return Ok(()); - }; - - // Construct from snapshot - let value = Keyshare::new(KeyshareParams { - fhe, - bus: self.bus.clone(), - secret: sync_secret, - address: self.address.clone(), - cipher: self.cipher.clone(), - }) - .start(); - - // send to context - ctx.set_keyshare(value); - - Ok(()) - } -} -pub struct PlaintextAggregatorFeature { - bus: Addr, - sortition: Addr, -} -impl PlaintextAggregatorFeature { - pub fn create(bus: &Addr, sortition: &Addr) -> Box { - Box::new(Self { - bus: bus.clone(), - sortition: sortition.clone(), - }) - } -} - -const ERROR_PLAINTEXT_FHE_MISSING:&str = "Could not create PlaintextAggregator because the fhe instance it depends on was not set on the context."; -const ERROR_PLAINTEXT_META_MISSING:&str = "Could not create PlaintextAggregator because the meta instance it depends on was not set on the context."; - -#[async_trait] -impl E3Feature for PlaintextAggregatorFeature { - fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent) { - // Save plaintext aggregator - let EnclaveEvent::CiphertextOutputPublished { data, .. } = evt else { - return; - }; - - let Some(fhe) = ctx.get_fhe() else { - self.bus.err( - EnclaveErrorType::PlaintextAggregation, - anyhow!(ERROR_PLAINTEXT_FHE_MISSING), - ); - return; - }; - - let Some(ref meta) = ctx.get_meta() else { - self.bus.err( - EnclaveErrorType::PlaintextAggregation, - anyhow!(ERROR_PLAINTEXT_META_MISSING), - ); - return; - }; - - let e3_id = data.e3_id.clone(); - let repo = ctx.repositories().plaintext(&e3_id); - let sync_state = repo.send(Some(PlaintextAggregatorState::init( - meta.threshold_m, - meta.seed, - data.ciphertext_output.clone(), - ))); - - ctx.set_plaintext( - PlaintextAggregator::new( - PlaintextAggregatorParams { - fhe: fhe.clone(), - bus: self.bus.clone(), - sortition: self.sortition.clone(), - e3_id: e3_id.clone(), - src_chain_id: meta.src_chain_id, - }, - sync_state, - ) - .start(), - ); - } - - async fn hydrate( - &self, - ctx: &mut E3RequestContext, - snapshot: &E3RequestContextSnapshot, - ) -> Result<()> { - // No ID on the snapshot -> bail - if !snapshot.plaintext { - return Ok(()); - } - - let repo = ctx.repositories().plaintext(&snapshot.e3_id); - let sync_state = repo.load().await?; - - // No Snapshot returned from the store -> bail - if !sync_state.has() { - return Ok(()); - }; - - // Get deps - let Some(fhe) = ctx.get_fhe() else { - self.bus.err( - EnclaveErrorType::PlaintextAggregation, - anyhow!(ERROR_PLAINTEXT_FHE_MISSING), - ); - return Ok(()); - }; - - let Some(ref meta) = ctx.get_meta() else { - self.bus.err( - EnclaveErrorType::PlaintextAggregation, - anyhow!(ERROR_PLAINTEXT_META_MISSING), - ); - return Ok(()); - }; - - let value = PlaintextAggregator::new( - PlaintextAggregatorParams { - fhe: fhe.clone(), - bus: self.bus.clone(), - sortition: self.sortition.clone(), - e3_id: ctx.e3_id.clone(), - src_chain_id: meta.src_chain_id, - }, - sync_state, - ) - .start(); - - // send to context - ctx.set_plaintext(value); - - Ok(()) - } -} - -pub struct PublicKeyAggregatorFeature { - bus: Addr, - sortition: Addr, -} - -impl PublicKeyAggregatorFeature { - pub fn create(bus: &Addr, sortition: &Addr) -> Box { - Box::new(Self { - bus: bus.clone(), - sortition: sortition.clone(), - }) - } -} - -const ERROR_PUBKEY_FHE_MISSING:&str = "Could not create PublicKeyAggregator because the fhe instance it depends on was not set on the context."; -const ERROR_PUBKEY_META_MISSING:&str = "Could not create PublicKeyAggregator because the meta instance it depends on was not set on the context."; - -#[async_trait] -impl E3Feature for PublicKeyAggregatorFeature { - fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent) { - // Saving the publickey aggregator with deps on E3Requested - let EnclaveEvent::E3Requested { data, .. } = evt else { - return; - }; - - let Some(fhe) = ctx.get_fhe() else { - self.bus.err( - EnclaveErrorType::PublickeyAggregation, - anyhow!(ERROR_PUBKEY_FHE_MISSING), - ); - return; - }; - let Some(ref meta) = ctx.get_meta() else { - self.bus.err( - EnclaveErrorType::PublickeyAggregation, - anyhow!(ERROR_PUBKEY_META_MISSING), - ); - return; - }; - - let e3_id = data.e3_id.clone(); - let repo = ctx.repositories().publickey(&e3_id); - let sync_state = repo.send(Some(PublicKeyAggregatorState::init( - meta.threshold_m, - meta.seed, - ))); - ctx.set_publickey( - PublicKeyAggregator::new( - PublicKeyAggregatorParams { - fhe: fhe.clone(), - bus: self.bus.clone(), - sortition: self.sortition.clone(), - e3_id, - src_chain_id: meta.src_chain_id, - }, - sync_state, - ) - .start(), - ); - } - - async fn hydrate( - &self, - ctx: &mut E3RequestContext, - snapshot: &E3RequestContextSnapshot, - ) -> Result<()> { - // No ID on the snapshot -> bail - if !snapshot.publickey { - return Ok(()); - }; - - let repo = ctx.repositories().publickey(&ctx.e3_id); - let sync_state = repo.load().await?; - - // No Snapshot returned from the store -> bail - if !sync_state.has() { - return Ok(()); - }; - - // Get deps - let Some(fhe) = ctx.fhe.clone() else { - self.bus.err( - EnclaveErrorType::PublickeyAggregation, - anyhow!(ERROR_PUBKEY_FHE_MISSING), - ); - - return Ok(()); - }; - - let Some(meta) = ctx.meta.clone() else { - self.bus.err( - EnclaveErrorType::PublickeyAggregation, - anyhow!(ERROR_PUBKEY_META_MISSING), - ); - - return Ok(()); - }; - - let value = PublicKeyAggregator::new( - PublicKeyAggregatorParams { - fhe: fhe.clone(), - bus: self.bus.clone(), - sortition: self.sortition.clone(), - e3_id: ctx.e3_id.clone(), - src_chain_id: meta.src_chain_id, - }, - sync_state, - ) - .start(); - - // send to context - ctx.set_publickey(value); - - Ok(()) - } -} diff --git a/packages/ciphernode/router/src/keyshare_feature.rs b/packages/ciphernode/router/src/keyshare_feature.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/ciphernode/router/src/lib.rs b/packages/ciphernode/router/src/lib.rs index 77f7aa5c..a51bcee3 100644 --- a/packages/ciphernode/router/src/lib.rs +++ b/packages/ciphernode/router/src/lib.rs @@ -1,13 +1,11 @@ -mod ciphernode_selector; mod committee_meta; mod context; mod e3_request_router; -mod hooks; -mod repositories; +mod hetrogenous_map; +mod repo; -pub use ciphernode_selector::*; pub use committee_meta::*; pub use context::*; pub use e3_request_router::*; -pub use hooks::*; -pub use repositories::*; +pub use hetrogenous_map::*; +pub use repo::*; diff --git a/packages/ciphernode/router/src/repo.rs b/packages/ciphernode/router/src/repo.rs new file mode 100644 index 00000000..de6c11b2 --- /dev/null +++ b/packages/ciphernode/router/src/repo.rs @@ -0,0 +1,35 @@ +use config::StoreKeys; +use data::{Repositories, Repository}; +use enclave_core::E3id; + +use crate::{CommitteeMeta, E3RequestContextSnapshot, E3RequestRouterSnapshot}; + +pub trait MetaRepositoryFactory { + fn meta(&self, e3_id: &E3id) -> Repository; +} + +impl MetaRepositoryFactory for Repositories { + fn meta(&self, e3_id: &E3id) -> Repository { + Repository::new(self.store.scope(StoreKeys::meta(e3_id))) + } +} + +pub trait ContextRepositoryFactory { + fn context(&self, e3_id: &E3id) -> Repository; +} + +impl ContextRepositoryFactory for Repositories { + fn context(&self, e3_id: &E3id) -> Repository { + Repository::new(self.store.scope(StoreKeys::context(e3_id))) + } +} + +pub trait RouterRepositoryFactory { + fn router(&self) -> Repository; +} + +impl RouterRepositoryFactory for Repositories { + fn router(&self) -> Repository { + Repository::new(self.store.scope(StoreKeys::router())) + } +} diff --git a/packages/ciphernode/router/src/repositories.rs b/packages/ciphernode/router/src/repositories.rs deleted file mode 100644 index dc72c2b9..00000000 --- a/packages/ciphernode/router/src/repositories.rs +++ /dev/null @@ -1,107 +0,0 @@ -use crate::{CommitteeMeta, E3RequestContextSnapshot, E3RequestRouterSnapshot}; -use aggregator::{PlaintextAggregatorState, PublicKeyAggregatorState}; -use config::StoreKeys; -use data::{DataStore, Repository}; -use enclave_core::E3id; -use evm::EvmEventReaderState; -use fhe::FheSnapshot; -use sortition::SortitionModule; - -pub struct Repositories { - store: DataStore, -} - -impl From for Repositories { - fn from(value: DataStore) -> Self { - Repositories { store: value } - } -} -impl From<&DataStore> for Repositories { - fn from(value: &DataStore) -> Self { - Repositories { - store: value.clone(), - } - } -} - -impl Repositories { - pub fn new(store: DataStore) -> Self { - Repositories { store } - } -} - -impl From> for Repositories { - fn from(value: Repository) -> Self { - let store: DataStore = value.into(); - store.into() - } -} - -impl Repositories { - pub fn keyshare(&self, e3_id: &E3id) -> Repository> { - Repository::new(self.store.scope(StoreKeys::keyshare(e3_id))) - } - - pub fn plaintext(&self, e3_id: &E3id) -> Repository { - Repository::new(self.store.scope(StoreKeys::plaintext(e3_id))) - } - - pub fn publickey(&self, e3_id: &E3id) -> Repository { - Repository::new(self.store.scope(StoreKeys::publickey(e3_id))) - } - - pub fn fhe(&self, e3_id: &E3id) -> Repository { - Repository::new(self.store.scope(StoreKeys::fhe(e3_id))) - } - - pub fn meta(&self, e3_id: &E3id) -> Repository { - Repository::new(self.store.scope(StoreKeys::meta(e3_id))) - } - - pub fn context(&self, e3_id: &E3id) -> Repository { - Repository::new(self.store.scope(StoreKeys::context(e3_id))) - } - - pub fn router(&self) -> Repository { - Repository::new(self.store.scope(StoreKeys::router())) - } - - pub fn sortition(&self) -> Repository { - Repository::new(self.store.scope(StoreKeys::sortition())) - } - - pub fn eth_private_key(&self) -> Repository> { - Repository::new(self.store.scope(StoreKeys::eth_private_key())) - } - - pub fn libp2p_keypair(&self) -> Repository> { - Repository::new(self.store.scope(StoreKeys::libp2p_keypair())) - } - - pub fn enclave_sol_reader(&self, chain_id: u64) -> Repository { - Repository::new(self.store.scope(StoreKeys::enclave_sol_reader(chain_id))) - } - pub fn ciphernode_registry_reader(&self, chain_id: u64) -> Repository { - Repository::new( - self.store - .scope(StoreKeys::ciphernode_registry_reader(chain_id)), - ) - } -} - -pub trait RepositoriesFactory { - fn repositories(&self) -> Repositories; -} - -impl RepositoriesFactory for DataStore { - fn repositories(&self) -> Repositories { - self.into() - } -} - -impl RepositoriesFactory for Repository { - fn repositories(&self) -> Repositories { - let store: DataStore = self.into(); - store.repositories() - } -} diff --git a/packages/ciphernode/sortition/Cargo.toml b/packages/ciphernode/sortition/Cargo.toml index 383e1955..bed3b0a7 100644 --- a/packages/ciphernode/sortition/Cargo.toml +++ b/packages/ciphernode/sortition/Cargo.toml @@ -13,6 +13,7 @@ actix = { workspace = true } alloy = { workspace = true, features = ["full"] } anyhow = { workspace = true } async-trait = { workspace = true } +config = { workspace = true } data = { path = "../data" } enclave-core = { path = "../core" } num = { workspace = true } diff --git a/packages/ciphernode/router/src/ciphernode_selector.rs b/packages/ciphernode/sortition/src/ciphernode_selector.rs similarity index 98% rename from packages/ciphernode/router/src/ciphernode_selector.rs rename to packages/ciphernode/sortition/src/ciphernode_selector.rs index 96048a67..72ca1d50 100644 --- a/packages/ciphernode/router/src/ciphernode_selector.rs +++ b/packages/ciphernode/sortition/src/ciphernode_selector.rs @@ -1,8 +1,8 @@ +use crate::{GetHasNode, Sortition}; /// CiphernodeSelector is an actor that determines if a ciphernode is part of a committee and if so /// forwards a CiphernodeSelected event to the event bus use actix::prelude::*; use enclave_core::{CiphernodeSelected, E3Requested, EnclaveEvent, EventBus, Shutdown, Subscribe}; -use sortition::{GetHasNode, Sortition}; use tracing::info; pub struct CiphernodeSelector { diff --git a/packages/ciphernode/sortition/src/lib.rs b/packages/ciphernode/sortition/src/lib.rs index 792f64fc..7021bb9f 100644 --- a/packages/ciphernode/sortition/src/lib.rs +++ b/packages/ciphernode/sortition/src/lib.rs @@ -2,10 +2,14 @@ #![crate_type = "lib"] // #![warn(missing_docs, unused_imports)] +mod ciphernode_selector; mod distance; mod index; +mod repo; mod sortition; +pub use ciphernode_selector::*; pub use distance::*; pub use index::*; +pub use repo::*; pub use sortition::*; diff --git a/packages/ciphernode/sortition/src/repo.rs b/packages/ciphernode/sortition/src/repo.rs new file mode 100644 index 00000000..2b84c184 --- /dev/null +++ b/packages/ciphernode/sortition/src/repo.rs @@ -0,0 +1,14 @@ +use config::StoreKeys; +use data::{Repositories, Repository}; + +use crate::SortitionModule; + +pub trait SortitionRepositoryFactory { + fn sortition(&self) -> Repository; +} + +impl SortitionRepositoryFactory for Repositories { + fn sortition(&self) -> Repository { + Repository::new(self.store.scope(StoreKeys::sortition())) + } +} diff --git a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs index b2011ed5..2a04720a 100644 --- a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs +++ b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs @@ -1,4 +1,6 @@ +use aggregator::{PlaintextAggregatorFeature, PublicKeyAggregatorFeature}; use cipher::Cipher; +use data::RepositoriesFactory; use data::{DataStore, InMemStore}; use enclave_core::{ CiphernodeAdded, CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated, @@ -6,17 +8,16 @@ use enclave_core::{ KeyshareCreated, OrderedSet, PlaintextAggregated, PublicKeyAggregated, ResetHistory, Seed, Shutdown, }; -use fhe::{setup_crp_params, ParamsWithCrp, SharedRng}; +use fhe::{setup_crp_params, FheFeature, ParamsWithCrp, SharedRng}; +use keyshare::KeyshareFeature; use logger::SimpleLogger; -use net::{correlation_id::CorrelationId, events::NetworkPeerEvent, NetworkManager}; -use router::{ - CiphernodeSelector, E3RequestRouter, FheFeature, KeyshareFeature, PlaintextAggregatorFeature, - PublicKeyAggregatorFeature, RepositoriesFactory, -}; -use sortition::Sortition; +use net::{events::NetworkPeerEvent, NetworkManager}; +use router::E3RequestRouter; +use sortition::SortitionRepositoryFactory; +use sortition::{CiphernodeSelector, Sortition}; use actix::prelude::*; -use alloy::{primitives::Address, signers::k256::sha2::digest::Reset}; +use alloy::primitives::Address; use anyhow::*; use fhe_rs::{ bfv::{BfvParameters, Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}, @@ -26,7 +27,7 @@ use fhe_traits::{FheEncoder, FheEncrypter, Serialize}; use rand::Rng; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; -use std::{env, path::Path, sync::Arc, time::Duration}; +use std::{sync::Arc, time::Duration}; use tokio::sync::{broadcast, Mutex}; use tokio::{sync::mpsc, time::sleep}; @@ -456,7 +457,6 @@ async fn test_stopped_keyshares_retain_state() -> Result<()> { EnclaveEvent::PlaintextAggregated { data, .. } => Some(data.decrypted_output.clone()), _ => None, }); - assert_eq!(actual, Some(expected)); Ok(()) From adb23fbef524083c6c2c72472956c80e76481375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Sun, 29 Dec 2024 20:32:11 +1100 Subject: [PATCH 05/15] Rename `router` -> `e3_request` (#223) * router -> e3_request * E3RequestContextSnapshot -> ContextSnapshot * ContextSnapshot -> E3ContextSnapshot * E3RequestRouter -> E3Router * e3_request_router.rs -> router.rs * CommitteeMeta -> E3Meta * Rename file * Rename property * Add docs and warnings --- packages/ciphernode/Cargo.lock | 42 ++++---- packages/ciphernode/Cargo.toml | 5 +- packages/ciphernode/README.md | 4 +- packages/ciphernode/aggregator/Cargo.toml | 2 +- packages/ciphernode/aggregator/src/feature.rs | 18 +--- .../{router => e3_request}/Cargo.toml | 2 +- .../{router => e3_request}/src/context.rs | 36 +++---- .../src/hetrogenous_map.rs | 0 .../{router => e3_request}/src/lib.rs | 8 +- .../src/meta.rs} | 24 ++--- .../{router => e3_request}/src/repo.rs | 14 +-- .../src/router.rs} | 96 +++++++++---------- packages/ciphernode/enclave/Cargo.toml | 4 +- packages/ciphernode/enclave_node/Cargo.toml | 2 +- .../ciphernode/enclave_node/src/aggregator.rs | 4 +- .../ciphernode/enclave_node/src/ciphernode.rs | 4 +- packages/ciphernode/fhe/Cargo.toml | 2 +- packages/ciphernode/fhe/src/feature.rs | 10 +- packages/ciphernode/keyshare/Cargo.toml | 2 +- packages/ciphernode/keyshare/src/feature.rs | 10 +- packages/ciphernode/tests/Cargo.toml | 2 +- .../tests/test_aggregation_and_decryption.rs | 6 +- 22 files changed, 138 insertions(+), 159 deletions(-) rename packages/ciphernode/{router => e3_request}/Cargo.toml (94%) rename packages/ciphernode/{router => e3_request}/src/context.rs (86%) rename packages/ciphernode/{router => e3_request}/src/hetrogenous_map.rs (100%) rename packages/ciphernode/{router => e3_request}/src/lib.rs (52%) rename packages/ciphernode/{router/src/committee_meta.rs => e3_request/src/meta.rs} (69%) rename packages/ciphernode/{router => e3_request}/src/repo.rs (54%) rename packages/ciphernode/{router/src/e3_request_router.rs => e3_request/src/router.rs} (77%) diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 6d30df34..d017cd95 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -118,9 +118,9 @@ dependencies = [ "bincode", "config", "data", + "e3_request", "enclave-core", "fhe 0.1.0", - "router", "serde", "sortition", "tracing", @@ -2108,6 +2108,21 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "e3_request" +version = "0.1.0" +dependencies = [ + "actix", + "anyhow", + "async-trait", + "bincode", + "config", + "data", + "enclave-core", + "serde", + "tracing", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -2186,6 +2201,7 @@ dependencies = [ "data", "dialoguer", "dirs", + "e3_request", "enclave-core", "enclave_node", "evm", @@ -2196,7 +2212,6 @@ dependencies = [ "petname", "phf", "rand", - "router", "serde", "serde_json", "tokio", @@ -2237,6 +2252,7 @@ dependencies = [ "clap", "config", "data", + "e3_request", "enclave-core", "evm", "fhe 0.1.0", @@ -2245,7 +2261,6 @@ dependencies = [ "net", "rand", "rand_chacha", - "router", "serde", "sortition", "test-helpers", @@ -2424,13 +2439,13 @@ dependencies = [ "bincode", "config", "data", + "e3_request", "enclave-core", "fhe 0.1.0-beta.7", "fhe-traits", "fhe-util", "rand", "rand_chacha", - "router", "serde", ] @@ -3498,9 +3513,9 @@ dependencies = [ "cipher 0.1.0", "config", "data", + "e3_request", "enclave-core", "fhe 0.1.0", - "router", "serde", "tracing", ] @@ -5306,21 +5321,6 @@ dependencies = [ "rustc-hex", ] -[[package]] -name = "router" -version = "0.1.0" -dependencies = [ - "actix", - "anyhow", - "async-trait", - "bincode", - "config", - "data", - "enclave-core", - "serde", - "tracing", -] - [[package]] name = "rtnetlink" version = "0.10.1" @@ -6063,6 +6063,7 @@ dependencies = [ "cipher 0.1.0", "clap", "data", + "e3_request", "enclave-core", "evm", "fhe 0.1.0", @@ -6074,7 +6075,6 @@ dependencies = [ "net", "rand", "rand_chacha", - "router", "sortition", "test-helpers", "tokio", diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index 34664799..f5507284 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -8,7 +8,7 @@ members = [ "data", "evm", "fhe", - "router", + "e3_request", "keyshare", "aggregator", "test_helpers", @@ -42,6 +42,7 @@ config = { path = "./config" } dirs = "5.0.1" data = { path = "./data" } enclave-core = { path = "./core" } +enclave_node = { path = "./enclave_node" } evm = { path = "./evm" } shellexpand = "3.1.0" figment = { version = "0.10.19", features = ["yaml", "test"] } @@ -56,7 +57,7 @@ num = "0.4.3" net = { path = "./net" } rand_chacha = "0.3.1" rand = "0.8.5" -router = { path = "./router" } +e3_request = { path = "./e3_request" } serde = { version = "1.0.208", features = ["derive"] } serde_json = { version = "1.0.133" } sled = "0.34.7" diff --git a/packages/ciphernode/README.md b/packages/ciphernode/README.md index c619b253..9e998890 100644 --- a/packages/ciphernode/README.md +++ b/packages/ciphernode/README.md @@ -37,7 +37,7 @@ sequenceDiagram autonumber participant EVM as EVM participant CS as CiphernodeSelector - participant E3 as E3RequestRouter + participant E3 as E3Router participant KS as Keyshare participant PKA as PublicKeyAggregator participant S as Sortition @@ -63,7 +63,7 @@ sequenceDiagram sequenceDiagram autonumber participant EVM as EVM - participant E3 as E3RequestRouter + participant E3 as E3Router participant KS as Keyshare participant PTA as PlaintextAggregator participant S as Sortition diff --git a/packages/ciphernode/aggregator/Cargo.toml b/packages/ciphernode/aggregator/Cargo.toml index 83de43a5..491f51bd 100644 --- a/packages/ciphernode/aggregator/Cargo.toml +++ b/packages/ciphernode/aggregator/Cargo.toml @@ -13,6 +13,6 @@ async-trait = { workspace = true } enclave-core = { path = "../core" } fhe = { path = "../fhe" } sortition = { path = "../sortition" } -router = { workspace = true } +e3_request = { workspace = true } data = { path = "../data" } tracing = { workspace = true } diff --git a/packages/ciphernode/aggregator/src/feature.rs b/packages/ciphernode/aggregator/src/feature.rs index d0422b5c..e93de3d2 100644 --- a/packages/ciphernode/aggregator/src/feature.rs +++ b/packages/ciphernode/aggregator/src/feature.rs @@ -7,9 +7,9 @@ use actix::{Actor, Addr}; use anyhow::{anyhow, Result}; use async_trait::async_trait; use data::{AutoPersist, RepositoriesFactory}; +use e3_request::{E3Context, E3ContextSnapshot, E3Feature, META_KEY}; use enclave_core::{BusError, EnclaveErrorType, EnclaveEvent, EventBus}; use fhe::FHE_KEY; -use router::{E3Feature, E3RequestContext, E3RequestContextSnapshot, META_KEY}; use sortition::Sortition; pub struct PlaintextAggregatorFeature { @@ -30,7 +30,7 @@ const ERROR_PLAINTEXT_META_MISSING:&str = "Could not create PlaintextAggregator #[async_trait] impl E3Feature for PlaintextAggregatorFeature { - fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent) { + fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent) { // Save plaintext aggregator let EnclaveEvent::CiphertextOutputPublished { data, .. } = evt else { return; @@ -79,11 +79,7 @@ impl E3Feature for PlaintextAggregatorFeature { ); } - async fn hydrate( - &self, - ctx: &mut E3RequestContext, - snapshot: &E3RequestContextSnapshot, - ) -> Result<()> { + async fn hydrate(&self, ctx: &mut E3Context, snapshot: &E3ContextSnapshot) -> Result<()> { // No ID on the snapshot -> bail if !snapshot.contains("plaintext") { return Ok(()); @@ -153,7 +149,7 @@ const ERROR_PUBKEY_META_MISSING:&str = "Could not create PublicKeyAggregator bec #[async_trait] impl E3Feature for PublicKeyAggregatorFeature { - fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent) { + fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent) { // Saving the publickey aggregator with deps on E3Requested let EnclaveEvent::E3Requested { data, .. } = evt else { return; @@ -199,11 +195,7 @@ impl E3Feature for PublicKeyAggregatorFeature { ); } - async fn hydrate( - &self, - ctx: &mut E3RequestContext, - snapshot: &E3RequestContextSnapshot, - ) -> Result<()> { + async fn hydrate(&self, ctx: &mut E3Context, snapshot: &E3ContextSnapshot) -> Result<()> { // No ID on the snapshot -> bail if !snapshot.contains("publickey") { return Ok(()); diff --git a/packages/ciphernode/router/Cargo.toml b/packages/ciphernode/e3_request/Cargo.toml similarity index 94% rename from packages/ciphernode/router/Cargo.toml rename to packages/ciphernode/e3_request/Cargo.toml index f5e485c9..966e9b81 100644 --- a/packages/ciphernode/router/Cargo.toml +++ b/packages/ciphernode/e3_request/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "router" +name = "e3_request" version = "0.1.0" edition = "2021" diff --git a/packages/ciphernode/router/src/context.rs b/packages/ciphernode/e3_request/src/context.rs similarity index 86% rename from packages/ciphernode/router/src/context.rs rename to packages/ciphernode/e3_request/src/context.rs index e1912c30..96f03d38 100644 --- a/packages/ciphernode/router/src/context.rs +++ b/packages/ciphernode/e3_request/src/context.rs @@ -26,7 +26,7 @@ fn init_recipients() -> HashMap>> { // without circular deps // TODO: remove Arc import as we need to be able to move the Fhe feature out of the hooks // file without circular deps -pub struct E3RequestContext { +pub struct E3Context { /// The E3Request's ID pub e3_id: E3id, /// A way to store EnclaveEvent recipients on the context @@ -34,33 +34,33 @@ pub struct E3RequestContext { /// A way to store a feature's dependencies on the context pub dependencies: HetrogenousMap, /// A Repository for storing this context's data snapshot - pub store: Repository, + pub repository: Repository, } #[derive(Serialize, Deserialize)] -pub struct E3RequestContextSnapshot { +pub struct E3ContextSnapshot { pub e3_id: E3id, pub recipients: Vec, pub dependencies: Vec, } -impl E3RequestContextSnapshot { +impl E3ContextSnapshot { pub fn contains(&self, key: &str) -> bool { self.recipients.contains(&key.to_string()) || self.dependencies.contains(&key.to_string()) } } -pub struct E3RequestContextParams { - pub store: Repository, +pub struct E3ContextParams { + pub repository: Repository, pub e3_id: E3id, pub features: Arc>>, } -impl E3RequestContext { - pub fn from_params(params: E3RequestContextParams) -> Self { +impl E3Context { + pub fn from_params(params: E3ContextParams) -> Self { Self { e3_id: params.e3_id, - store: params.store, + repository: params.repository, recipients: init_recipients(), dependencies: HetrogenousMap::new(), } @@ -127,15 +127,15 @@ impl E3RequestContext { } } -impl RepositoriesFactory for E3RequestContext { +impl RepositoriesFactory for E3Context { fn repositories(&self) -> Repositories { self.repository().clone().into() } } #[async_trait] -impl Snapshot for E3RequestContext { - type Snapshot = E3RequestContextSnapshot; +impl Snapshot for E3Context { + type Snapshot = E3ContextSnapshot; fn snapshot(&self) -> Result { Ok(Self::Snapshot { @@ -147,12 +147,12 @@ impl Snapshot for E3RequestContext { } #[async_trait] -impl FromSnapshotWithParams for E3RequestContext { - type Params = E3RequestContextParams; +impl FromSnapshotWithParams for E3Context { + type Params = E3ContextParams; async fn from_snapshot(params: Self::Params, snapshot: Self::Snapshot) -> Result { let mut ctx = Self { e3_id: params.e3_id, - store: params.store, + repository: params.repository, recipients: init_recipients(), dependencies: HetrogenousMap::new(), }; @@ -165,8 +165,8 @@ impl FromSnapshotWithParams for E3RequestContext { } } -impl Checkpoint for E3RequestContext { - fn repository(&self) -> &Repository { - &self.store +impl Checkpoint for E3Context { + fn repository(&self) -> &Repository { + &self.repository } } diff --git a/packages/ciphernode/router/src/hetrogenous_map.rs b/packages/ciphernode/e3_request/src/hetrogenous_map.rs similarity index 100% rename from packages/ciphernode/router/src/hetrogenous_map.rs rename to packages/ciphernode/e3_request/src/hetrogenous_map.rs diff --git a/packages/ciphernode/router/src/lib.rs b/packages/ciphernode/e3_request/src/lib.rs similarity index 52% rename from packages/ciphernode/router/src/lib.rs rename to packages/ciphernode/e3_request/src/lib.rs index a51bcee3..f0c87315 100644 --- a/packages/ciphernode/router/src/lib.rs +++ b/packages/ciphernode/e3_request/src/lib.rs @@ -1,11 +1,11 @@ -mod committee_meta; mod context; -mod e3_request_router; mod hetrogenous_map; +mod meta; mod repo; +mod router; -pub use committee_meta::*; pub use context::*; -pub use e3_request_router::*; pub use hetrogenous_map::*; +pub use meta::*; pub use repo::*; +pub use router::*; diff --git a/packages/ciphernode/router/src/committee_meta.rs b/packages/ciphernode/e3_request/src/meta.rs similarity index 69% rename from packages/ciphernode/router/src/committee_meta.rs rename to packages/ciphernode/e3_request/src/meta.rs index 74c6cc2b..e17215eb 100644 --- a/packages/ciphernode/router/src/committee_meta.rs +++ b/packages/ciphernode/e3_request/src/meta.rs @@ -1,31 +1,29 @@ -use crate::{ - E3Feature, E3RequestContext, E3RequestContextSnapshot, MetaRepositoryFactory, TypedKey, -}; +use crate::{E3Context, E3ContextSnapshot, E3Feature, MetaRepositoryFactory, TypedKey}; use anyhow::*; use async_trait::async_trait; use data::RepositoriesFactory; use enclave_core::{E3Requested, EnclaveEvent, Seed}; -pub const META_KEY: TypedKey = TypedKey::new("meta"); +pub const META_KEY: TypedKey = TypedKey::new("meta"); #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct CommitteeMeta { +pub struct E3Meta { pub threshold_m: usize, pub seed: Seed, pub src_chain_id: u64, } -pub struct CommitteeMetaFeature; +pub struct E3MetaFeature; -impl CommitteeMetaFeature { +impl E3MetaFeature { pub fn create() -> Box { Box::new(Self {}) } } #[async_trait] -impl E3Feature for CommitteeMetaFeature { - fn on_event(&self, ctx: &mut crate::E3RequestContext, event: &EnclaveEvent) { +impl E3Feature for E3MetaFeature { + fn on_event(&self, ctx: &mut crate::E3Context, event: &EnclaveEvent) { let EnclaveEvent::E3Requested { data, .. } = event else { return; }; @@ -38,7 +36,7 @@ impl E3Feature for CommitteeMetaFeature { } = data.clone(); // Meta doesn't implement Checkpoint so we are going to store it manually - let meta = CommitteeMeta { + let meta = E3Meta { threshold_m, seed, src_chain_id, @@ -47,11 +45,7 @@ impl E3Feature for CommitteeMetaFeature { let _ = ctx.set_dependency(META_KEY, meta); } - async fn hydrate( - &self, - ctx: &mut E3RequestContext, - snapshot: &E3RequestContextSnapshot, - ) -> Result<()> { + async fn hydrate(&self, ctx: &mut E3Context, snapshot: &E3ContextSnapshot) -> Result<()> { // No ID on the snapshot -> bail if !snapshot.contains("meta") { return Ok(()); diff --git a/packages/ciphernode/router/src/repo.rs b/packages/ciphernode/e3_request/src/repo.rs similarity index 54% rename from packages/ciphernode/router/src/repo.rs rename to packages/ciphernode/e3_request/src/repo.rs index de6c11b2..d885d321 100644 --- a/packages/ciphernode/router/src/repo.rs +++ b/packages/ciphernode/e3_request/src/repo.rs @@ -2,34 +2,34 @@ use config::StoreKeys; use data::{Repositories, Repository}; use enclave_core::E3id; -use crate::{CommitteeMeta, E3RequestContextSnapshot, E3RequestRouterSnapshot}; +use crate::{E3ContextSnapshot, E3Meta, E3RouterSnapshot}; pub trait MetaRepositoryFactory { - fn meta(&self, e3_id: &E3id) -> Repository; + fn meta(&self, e3_id: &E3id) -> Repository; } impl MetaRepositoryFactory for Repositories { - fn meta(&self, e3_id: &E3id) -> Repository { + fn meta(&self, e3_id: &E3id) -> Repository { Repository::new(self.store.scope(StoreKeys::meta(e3_id))) } } pub trait ContextRepositoryFactory { - fn context(&self, e3_id: &E3id) -> Repository; + fn context(&self, e3_id: &E3id) -> Repository; } impl ContextRepositoryFactory for Repositories { - fn context(&self, e3_id: &E3id) -> Repository { + fn context(&self, e3_id: &E3id) -> Repository { Repository::new(self.store.scope(StoreKeys::context(e3_id))) } } pub trait RouterRepositoryFactory { - fn router(&self) -> Repository; + fn router(&self) -> Repository; } impl RouterRepositoryFactory for Repositories { - fn router(&self) -> Repository { + fn router(&self) -> Repository { Repository::new(self.store.scope(StoreKeys::router())) } } diff --git a/packages/ciphernode/router/src/e3_request_router.rs b/packages/ciphernode/e3_request/src/router.rs similarity index 77% rename from packages/ciphernode/router/src/e3_request_router.rs rename to packages/ciphernode/e3_request/src/router.rs index 9c29c81a..ce3a55bb 100644 --- a/packages/ciphernode/router/src/e3_request_router.rs +++ b/packages/ciphernode/e3_request/src/router.rs @@ -1,8 +1,8 @@ -use crate::CommitteeMetaFeature; use crate::ContextRepositoryFactory; -use crate::E3RequestContext; -use crate::E3RequestContextParams; -use crate::E3RequestContextSnapshot; +use crate::E3Context; +use crate::E3ContextParams; +use crate::E3ContextSnapshot; +use crate::E3MetaFeature; use crate::RouterRepositoryFactory; use actix::AsyncContext; use actix::{Actor, Addr, Context, Handler}; @@ -21,6 +21,7 @@ use serde::Deserialize; use serde::Serialize; use std::collections::HashSet; use std::{collections::HashMap, sync::Arc}; +use tracing::error; /// Helper class to buffer events for downstream instances incase events arrive in the wrong order #[derive(Default)] @@ -41,7 +42,7 @@ impl EventBuffer { } } -/// Format of a Feature that can be passed to E3RequestRouter. E3Features listen for EnclaveEvents +/// Format of a Feature that can be passed to E3Router. E3Features listen for EnclaveEvents /// that are braoadcast to know when to instantiate themselves. They define the events they respond /// to using the `on_event` handler. Within this handler they will typically use the request's /// context to construct a version of their requisite actors and save their addresses to the @@ -56,53 +57,50 @@ pub trait E3Feature: Send + Sync + 'static { /// initialize the receiver using `ctx.set_event_receiver(my_address.into())`. Typically this /// means filtering for specific e3_id enabled events that give rise to actors that have to /// handle certain behaviour. - fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent); + fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent); /// This function it triggered when the request context is being hydrated from snapshot. - async fn hydrate( - &self, - ctx: &mut E3RequestContext, - snapshot: &E3RequestContextSnapshot, - ) -> Result<()>; + async fn hydrate(&self, ctx: &mut E3Context, snapshot: &E3ContextSnapshot) -> Result<()>; } -/// E3RequestRouter will register features that receive an E3_id specific context. After features +/// E3Router will register features that receive an E3_id specific context. After features /// have run e3_id specific messages are forwarded to all instances on the context as they come in. /// This enables features to lazily register instances that have the correct dependencies available /// per e3_id request. // TODO: setup so that we have to place features within correct order of dependencies -pub struct E3RequestRouter { +pub struct E3Router { /// The context for every E3 request - contexts: HashMap, + contexts: HashMap, /// A list of completed requests completed: HashSet, /// The features this instance of the router is configured to listen for features: Arc>>, + /// A buffer for events to send to the buffer: EventBuffer, bus: Addr, - store: Repository, + store: Repository, } -pub struct E3RequestRouterParams { +pub struct E3RouterParams { features: Arc>>, bus: Addr, - store: Repository, + store: Repository, } -impl E3RequestRouter { - pub fn builder(bus: &Addr, store: DataStore) -> E3RequestRouterBuilder { +impl E3Router { + pub fn builder(bus: &Addr, store: DataStore) -> E3RouterBuilder { let repositories = store.repositories(); - let builder = E3RequestRouterBuilder { + let builder = E3RouterBuilder { bus: bus.clone(), features: vec![], store: repositories.router(), }; // Everything needs the committe meta factory so adding it here by default - builder.add_feature(CommitteeMetaFeature::create()) + builder.add_feature(E3MetaFeature::create()) } - pub fn from_params(params: E3RequestRouterParams) -> Self { + pub fn from_params(params: E3RouterParams) -> Self { Self { features: params.features, bus: params.bus.clone(), @@ -116,11 +114,11 @@ impl E3RequestRouter { } } -impl Actor for E3RequestRouter { +impl Actor for E3Router { type Context = Context; } -impl Handler for E3RequestRouter { +impl Handler for E3Router { type Result = (); fn handle(&mut self, msg: EnclaveEvent, ctx: &mut Self::Context) -> Self::Result { // If we are shuttomg down then bail on anything else @@ -129,20 +127,22 @@ impl Handler for E3RequestRouter { return; } + // Only process events with e3_ids let Some(e3_id) = msg.get_e3_id() else { return; }; + // If this e3_id has already been completed then we are not going to do anything here if self.completed.contains(&e3_id) { - // TODO: Log warning that e3 event was received for completed e3_id + error!("Received the following event to E3Id({}) despite already being completed:\n\n{:?}\n\n", e3_id, msg); return; } let repositories = self.repository().repositories(); let context = self.contexts.entry(e3_id.clone()).or_insert_with(|| { - E3RequestContext::from_params(E3RequestContextParams { + E3Context::from_params(E3ContextParams { e3_id: e3_id.clone(), - store: repositories.context(&e3_id), + repository: repositories.context(&e3_id), features: self.features.clone(), }) }); @@ -178,7 +178,7 @@ impl Handler for E3RequestRouter { } } -impl Handler for E3RequestRouter { +impl Handler for E3Router { type Result = (); fn handle(&mut self, msg: Shutdown, _ctx: &mut Self::Context) -> Self::Result { let shutdown_evt = EnclaveEvent::from(msg); @@ -189,13 +189,13 @@ impl Handler for E3RequestRouter { } #[derive(Serialize, Deserialize)] -pub struct E3RequestRouterSnapshot { +pub struct E3RouterSnapshot { contexts: Vec, completed: HashSet, } -impl Snapshot for E3RequestRouter { - type Snapshot = E3RequestRouterSnapshot; +impl Snapshot for E3Router { + type Snapshot = E3RouterSnapshot; fn snapshot(&self) -> Result { let contexts = self.contexts.keys().cloned().collect(); let completed = self.completed.clone(); @@ -207,15 +207,15 @@ impl Snapshot for E3RequestRouter { } } -impl Checkpoint for E3RequestRouter { - fn repository(&self) -> &Repository { +impl Checkpoint for E3Router { + fn repository(&self) -> &Repository { &self.store } } #[async_trait] -impl FromSnapshotWithParams for E3RequestRouter { - type Params = E3RequestRouterParams; +impl FromSnapshotWithParams for E3Router { + type Params = E3RouterParams; async fn from_snapshot(params: Self::Params, snapshot: Self::Snapshot) -> Result { let mut contexts = HashMap::new(); @@ -228,9 +228,9 @@ impl FromSnapshotWithParams for E3RequestRouter { contexts.insert( e3_id.clone(), - E3RequestContext::from_snapshot( - E3RequestContextParams { - store: repositories.context(&e3_id), + E3Context::from_snapshot( + E3ContextParams { + repository: repositories.context(&e3_id), e3_id: e3_id.clone(), features: params.features.clone(), }, @@ -240,7 +240,7 @@ impl FromSnapshotWithParams for E3RequestRouter { ); } - Ok(E3RequestRouter { + Ok(E3Router { contexts, completed: snapshot.completed, features: params.features.into(), @@ -251,24 +251,24 @@ impl FromSnapshotWithParams for E3RequestRouter { } } -/// Builder for E3RequestRouter -pub struct E3RequestRouterBuilder { +/// Builder for E3Router +pub struct E3RouterBuilder { pub bus: Addr, pub features: Vec>, - pub store: Repository, + pub store: Repository, } -impl E3RequestRouterBuilder { +impl E3RouterBuilder { pub fn add_feature(mut self, listener: Box) -> Self { self.features.push(listener); self } - pub async fn build(self) -> Result> { + pub async fn build(self) -> Result> { let repositories = self.store.repositories(); let router_repo = repositories.router(); - let snapshot: Option = router_repo.read().await?; - let params = E3RequestRouterParams { + let snapshot: Option = router_repo.read().await?; + let params = E3RouterParams { features: self.features.into(), bus: self.bus.clone(), @@ -276,8 +276,8 @@ impl E3RequestRouterBuilder { }; let e3r = match snapshot { - Some(snapshot) => E3RequestRouter::from_snapshot(params, snapshot).await?, - None => E3RequestRouter::from_params(params), + Some(snapshot) => E3Router::from_snapshot(params, snapshot).await?, + None => E3Router::from_params(params), }; let addr = e3r.start(); diff --git a/packages/ciphernode/enclave/Cargo.toml b/packages/ciphernode/enclave/Cargo.toml index fcf601d3..5060ab81 100644 --- a/packages/ciphernode/enclave/Cargo.toml +++ b/packages/ciphernode/enclave/Cargo.toml @@ -18,7 +18,7 @@ data = { path = "../data" } dialoguer = "0.11.0" dirs = { workspace = true } enclave-core = { path = "../core" } -enclave_node = { path = "../enclave_node" } +enclave_node = { workspace = true } evm = { workspace = true } hex = { workspace = true } libp2p = { workspace = true } @@ -27,7 +27,7 @@ once_cell = "1.20.2" petname = "2.0.2" phf = { version = "0.11", features = ["macros"] } rand = { workspace = true } -router = { path = "../router" } +e3_request = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } diff --git a/packages/ciphernode/enclave_node/Cargo.toml b/packages/ciphernode/enclave_node/Cargo.toml index ad03d445..92882e17 100644 --- a/packages/ciphernode/enclave_node/Cargo.toml +++ b/packages/ciphernode/enclave_node/Cargo.toml @@ -27,7 +27,7 @@ logger = { path = "../logger" } net = { path = "../net" } rand = { workspace = true } rand_chacha = { workspace = true } -router = { path = "../router" } +e3_request = { workspace = true } serde = { workspace = true } sortition = { path = "../sortition" } test-helpers = { path = "../test_helpers" } diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index 178432c0..5ce904d4 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -5,6 +5,7 @@ use anyhow::Result; use cipher::Cipher; use config::AppConfig; use data::RepositoriesFactory; +use e3_request::E3Router; use enclave_core::EventBus; use evm::{ helpers::{get_signer_from_repository, ProviderConfig}, @@ -16,7 +17,6 @@ use logger::SimpleLogger; use net::{NetRepositoryFactory, NetworkManager}; use rand::SeedableRng; use rand_chacha::{rand_core::OsRng, ChaCha20Rng}; -use router::E3RequestRouter; use sortition::Sortition; use sortition::SortitionRepositoryFactory; use std::sync::{Arc, Mutex}; @@ -71,7 +71,7 @@ pub async fn setup_aggregator( .await?; } - E3RequestRouter::builder(&bus, store) + E3Router::builder(&bus, store) .add_feature(FheFeature::create(&bus, &rng)) .add_feature(PublicKeyAggregatorFeature::create(&bus, &sortition)) .add_feature(PlaintextAggregatorFeature::create(&bus, &sortition)) diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/ciphernode.rs index 77bd8cc4..1917ac58 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/ciphernode.rs @@ -5,6 +5,7 @@ use anyhow::Result; use cipher::Cipher; use config::AppConfig; use data::RepositoriesFactory; +use e3_request::E3Router; use enclave_core::{get_tag, EventBus}; use evm::{ helpers::ProviderConfig, CiphernodeRegistryReaderRepositoryFactory, CiphernodeRegistrySol, @@ -16,7 +17,6 @@ use logger::SimpleLogger; use net::{NetRepositoryFactory, NetworkManager}; use rand::SeedableRng; use rand_chacha::rand_core::OsRng; -use router::E3RequestRouter; use sortition::CiphernodeSelector; use sortition::Sortition; use sortition::SortitionRepositoryFactory; @@ -65,7 +65,7 @@ pub async fn setup_ciphernode( .await?; } - E3RequestRouter::builder(&bus, store.clone()) + E3Router::builder(&bus, store.clone()) .add_feature(FheFeature::create(&bus, &rng)) .add_feature(KeyshareFeature::create(&bus, &address.to_string(), &cipher)) .build() diff --git a/packages/ciphernode/fhe/Cargo.toml b/packages/ciphernode/fhe/Cargo.toml index aeca4615..6909c463 100644 --- a/packages/ciphernode/fhe/Cargo.toml +++ b/packages/ciphernode/fhe/Cargo.toml @@ -16,5 +16,5 @@ fhe-util = { workspace = true } fhe_rs = { workspace = true } rand = { workspace = true } rand_chacha = { workspace = true } -router = { workspace = true } +e3_request = { workspace = true } serde = { workspace = true } diff --git a/packages/ciphernode/fhe/src/feature.rs b/packages/ciphernode/fhe/src/feature.rs index adba1897..0dbb017f 100644 --- a/packages/ciphernode/fhe/src/feature.rs +++ b/packages/ciphernode/fhe/src/feature.rs @@ -3,8 +3,8 @@ use actix::Addr; use anyhow::{anyhow, Result}; use async_trait::async_trait; use data::{FromSnapshotWithParams, RepositoriesFactory, Snapshot}; +use e3_request::{E3Context, E3ContextSnapshot, E3Feature, TypedKey}; use enclave_core::{BusError, E3Requested, EnclaveErrorType, EnclaveEvent, EventBus}; -use router::{E3Feature, E3RequestContext, E3RequestContextSnapshot, TypedKey}; use std::sync::Arc; pub const FHE_KEY: TypedKey> = TypedKey::new("fhe"); @@ -28,7 +28,7 @@ const ERROR_FHE_FAILED_TO_DECODE: &str = "Failed to decode encoded FHE params"; #[async_trait] impl E3Feature for FheFeature { - fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent) { + fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent) { // Saving the fhe on Committee Requested let EnclaveEvent::E3Requested { data, .. } = evt else { return; @@ -63,11 +63,7 @@ impl E3Feature for FheFeature { let _ = ctx.set_dependency(FHE_KEY, fhe); } - async fn hydrate( - &self, - ctx: &mut E3RequestContext, - snapshot: &E3RequestContextSnapshot, - ) -> Result<()> { + async fn hydrate(&self, ctx: &mut E3Context, snapshot: &E3ContextSnapshot) -> Result<()> { // No ID on the snapshot -> bail without reporting if !snapshot.contains("fhe") { return Ok(()); diff --git a/packages/ciphernode/keyshare/Cargo.toml b/packages/ciphernode/keyshare/Cargo.toml index b5a9c599..d6cb60b1 100644 --- a/packages/ciphernode/keyshare/Cargo.toml +++ b/packages/ciphernode/keyshare/Cargo.toml @@ -12,6 +12,6 @@ data = { path = "../data" } cipher = { path = "../cipher" } enclave-core = { path = "../core" } fhe = { path = "../fhe" } -router = { workspace = true } +e3_request = { workspace = true } serde = { workspace = true } tracing = { workspace = true } diff --git a/packages/ciphernode/keyshare/src/feature.rs b/packages/ciphernode/keyshare/src/feature.rs index e5db14ea..dc4bc098 100644 --- a/packages/ciphernode/keyshare/src/feature.rs +++ b/packages/ciphernode/keyshare/src/feature.rs @@ -4,9 +4,9 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use cipher::Cipher; use data::{AutoPersist, RepositoriesFactory}; +use e3_request::{E3Context, E3ContextSnapshot, E3Feature}; use enclave_core::{BusError, EnclaveErrorType, EnclaveEvent, EventBus}; use fhe::FHE_KEY; -use router::{E3Feature, E3RequestContext, E3RequestContextSnapshot}; use std::sync::Arc; pub struct KeyshareFeature { @@ -30,7 +30,7 @@ const ERROR_KEYSHARE_FHE_MISSING: &str = #[async_trait] impl E3Feature for KeyshareFeature { - fn on_event(&self, ctx: &mut E3RequestContext, evt: &EnclaveEvent) { + fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent) { // if this is NOT a CiphernodeSelected event then ignore let EnclaveEvent::CiphernodeSelected { data, .. } = evt else { return; @@ -65,11 +65,7 @@ impl E3Feature for KeyshareFeature { ); } - async fn hydrate( - &self, - ctx: &mut E3RequestContext, - snapshot: &E3RequestContextSnapshot, - ) -> Result<()> { + async fn hydrate(&self, ctx: &mut E3Context, snapshot: &E3ContextSnapshot) -> Result<()> { // No keyshare on the snapshot -> bail if !snapshot.contains("keyshare") { return Ok(()); diff --git a/packages/ciphernode/tests/Cargo.toml b/packages/ciphernode/tests/Cargo.toml index 9bc1d083..9bf651a7 100644 --- a/packages/ciphernode/tests/Cargo.toml +++ b/packages/ciphernode/tests/Cargo.toml @@ -15,7 +15,7 @@ cipher = { path = "../cipher" } data = { path = "../data" } keyshare = { path = "../keyshare" } aggregator = { path = "../aggregator" } -router = { path = "../router" } +e3_request = { workspace = true } test-helpers = { path = "../test_helpers" } fhe_rs = { workspace = true } fhe-traits = { workspace = true } diff --git a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs index 2a04720a..cdc8be5c 100644 --- a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs +++ b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs @@ -2,6 +2,7 @@ use aggregator::{PlaintextAggregatorFeature, PublicKeyAggregatorFeature}; use cipher::Cipher; use data::RepositoriesFactory; use data::{DataStore, InMemStore}; +use e3_request::E3Router; use enclave_core::{ CiphernodeAdded, CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated, E3RequestComplete, E3Requested, E3id, EnclaveEvent, EventBus, GetErrors, GetHistory, @@ -12,7 +13,6 @@ use fhe::{setup_crp_params, FheFeature, ParamsWithCrp, SharedRng}; use keyshare::KeyshareFeature; use logger::SimpleLogger; use net::{events::NetworkPeerEvent, NetworkManager}; -use router::E3RequestRouter; use sortition::SortitionRepositoryFactory; use sortition::{CiphernodeSelector, Sortition}; @@ -36,7 +36,7 @@ type LocalCiphernodeTuple = ( String, // Address Addr, Addr, - Addr, + Addr, Addr, ); @@ -56,7 +56,7 @@ async fn setup_local_ciphernode( let sortition = Sortition::attach(&bus, repositories.sortition()).await?; CiphernodeSelector::attach(&bus, &sortition, addr); - let router = E3RequestRouter::builder(&bus, store) + let router = E3Router::builder(&bus, store) .add_feature(FheFeature::create(&bus, &rng)) .add_feature(PublicKeyAggregatorFeature::create(&bus, &sortition)) .add_feature(PlaintextAggregatorFeature::create(&bus, &sortition)) From 2e8f19565ddf5062393c84f09a29c887555303b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Mon, 30 Dec 2024 13:40:54 +1100 Subject: [PATCH 06/15] Refactor CLI Structure (#224) * Flatten CLI * Flatten CLI * Refactor * Use datastore module * Extract main functions from password_delete --- packages/ciphernode/Cargo.lock | 16 +-- packages/ciphernode/Cargo.toml | 1 + packages/ciphernode/enclave/Cargo.toml | 14 -- .../aggregator/mod.rs => aggregator.rs} | 5 +- .../start.rs => aggregator_start.rs} | 4 +- packages/ciphernode/enclave/src/cli.rs | 126 ++++++++++++++++++ .../ciphernode/enclave/src/commands/mod.rs | 68 ---------- .../enclave/src/commands/net/generate.rs | 38 ------ .../enclave/src/commands/net/set.rs | 60 --------- .../enclave/src/commands/wallet/set.rs | 51 ------- .../enclave/src/{ => helpers}/compile_id.rs | 0 .../ciphernode/enclave/src/helpers/mod.rs | 2 + .../helpers.rs => helpers/prompt_password.rs} | 0 packages/ciphernode/enclave/src/init.rs | 75 +++++++++++ packages/ciphernode/enclave/src/main.rs | 93 +++---------- .../src/{commands/net/mod.rs => net.rs} | 11 +- .../ciphernode/enclave/src/net_generate.rs | 10 ++ packages/ciphernode/enclave/src/net_purge.rs | 9 ++ packages/ciphernode/enclave/src/net_set.rs | 23 ++++ .../{commands/password/mod.rs => password.rs} | 15 +-- .../password/create.rs => password_create.rs} | 22 +-- .../password/delete.rs => password_delete.rs} | 47 +++---- .../overwrite.rs => password_overwrite.rs} | 4 +- .../enclave/src/{commands => }/start.rs | 4 +- .../src/{commands/wallet/mod.rs => wallet.rs} | 5 +- packages/ciphernode/enclave/src/wallet_set.rs | 24 ++++ packages/ciphernode/enclave_node/Cargo.toml | 10 +- .../{enclave => enclave_node}/build.rs | 0 .../{aggregator.rs => aggregator_start.rs} | 5 +- .../src/{ => helpers}/datastore.rs | 0 .../enclave_node/src/helpers/mod.rs | 4 + .../src/{ => helpers}/shutdown.rs | 0 .../src/commands => enclave_node/src}/init.rs | 78 +---------- packages/ciphernode/enclave_node/src/lib.rs | 19 +-- .../enclave_node/src/net_generate.rs | 27 ++++ .../src/net_purge.rs} | 3 +- .../ciphernode/enclave_node/src/net_set.rs | 38 ++++++ .../enclave_node/src/password_create.rs | 28 ++++ .../enclave_node/src/password_delete.rs | 21 +++ .../src/{ciphernode.rs => start.rs} | 5 +- .../ciphernode/enclave_node/src/wallet_set.rs | 29 ++++ packages/ciphernode/evm/tests/evm_reader.rs | 2 +- 42 files changed, 526 insertions(+), 470 deletions(-) rename packages/ciphernode/enclave/src/{commands/aggregator/mod.rs => aggregator.rs} (93%) rename packages/ciphernode/enclave/src/{commands/aggregator/start.rs => aggregator_start.rs} (77%) create mode 100644 packages/ciphernode/enclave/src/cli.rs delete mode 100644 packages/ciphernode/enclave/src/commands/mod.rs delete mode 100644 packages/ciphernode/enclave/src/commands/net/generate.rs delete mode 100644 packages/ciphernode/enclave/src/commands/net/set.rs delete mode 100644 packages/ciphernode/enclave/src/commands/wallet/set.rs rename packages/ciphernode/enclave/src/{ => helpers}/compile_id.rs (100%) create mode 100644 packages/ciphernode/enclave/src/helpers/mod.rs rename packages/ciphernode/enclave/src/{commands/password/helpers.rs => helpers/prompt_password.rs} (100%) create mode 100644 packages/ciphernode/enclave/src/init.rs rename packages/ciphernode/enclave/src/{commands/net/mod.rs => net.rs} (63%) create mode 100644 packages/ciphernode/enclave/src/net_generate.rs create mode 100644 packages/ciphernode/enclave/src/net_purge.rs create mode 100644 packages/ciphernode/enclave/src/net_set.rs rename packages/ciphernode/enclave/src/{commands/password/mod.rs => password.rs} (67%) rename packages/ciphernode/enclave/src/{commands/password/create.rs => password_create.rs} (68%) rename packages/ciphernode/enclave/src/{commands/password/delete.rs => password_delete.rs} (52%) rename packages/ciphernode/enclave/src/{commands/password/overwrite.rs => password_overwrite.rs} (69%) rename packages/ciphernode/enclave/src/{commands => }/start.rs (80%) rename packages/ciphernode/enclave/src/{commands/wallet/mod.rs => wallet.rs} (85%) create mode 100644 packages/ciphernode/enclave/src/wallet_set.rs rename packages/ciphernode/{enclave => enclave_node}/build.rs (100%) rename packages/ciphernode/enclave_node/src/{aggregator.rs => aggregator_start.rs} (97%) rename packages/ciphernode/enclave_node/src/{ => helpers}/datastore.rs (100%) create mode 100644 packages/ciphernode/enclave_node/src/helpers/mod.rs rename packages/ciphernode/enclave_node/src/{ => helpers}/shutdown.rs (100%) rename packages/ciphernode/{enclave/src/commands => enclave_node/src}/init.rs (56%) create mode 100644 packages/ciphernode/enclave_node/src/net_generate.rs rename packages/ciphernode/{enclave/src/commands/net/purge.rs => enclave_node/src/net_purge.rs} (72%) create mode 100644 packages/ciphernode/enclave_node/src/net_set.rs create mode 100644 packages/ciphernode/enclave_node/src/password_create.rs create mode 100644 packages/ciphernode/enclave_node/src/password_delete.rs rename packages/ciphernode/enclave_node/src/{ciphernode.rs => start.rs} (97%) create mode 100644 packages/ciphernode/enclave_node/src/wallet_set.rs diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index d017cd95..8df8065e 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -2192,28 +2192,17 @@ name = "enclave" version = "0.1.0" dependencies = [ "actix", - "alloy", "anyhow", "cipher 0.1.0", "clap", "compile-time", "config", - "data", "dialoguer", - "dirs", - "e3_request", "enclave-core", "enclave_node", - "evm", "hex", - "libp2p", - "net", - "once_cell", "petname", - "phf", "rand", - "serde", - "serde_json", "tokio", "tracing", "tracing-subscriber", @@ -2252,20 +2241,25 @@ dependencies = [ "clap", "config", "data", + "dirs", "e3_request", "enclave-core", "evm", "fhe 0.1.0", "keyshare", + "libp2p", "logger", "net", + "phf", "rand", "rand_chacha", "serde", + "serde_json", "sortition", "test-helpers", "tokio", "tracing", + "zeroize", ] [[package]] diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index f5507284..1dddc596 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -55,6 +55,7 @@ hex = "0.4.3" lazy_static = "1.5.0" num = "0.4.3" net = { path = "./net" } +phf = { version = "0.11", features = ["macros"] } rand_chacha = "0.3.1" rand = "0.8.5" e3_request = { path = "./e3_request" } diff --git a/packages/ciphernode/enclave/Cargo.toml b/packages/ciphernode/enclave/Cargo.toml index 5060ab81..07dccf05 100644 --- a/packages/ciphernode/enclave/Cargo.toml +++ b/packages/ciphernode/enclave/Cargo.toml @@ -4,36 +4,22 @@ version = "0.1.0" edition = "2021" description = ": coordinates the encryption and decryption of enclave computations" repository = "https://github.com/gnosisguild/enclave/packages/ciphernode" -build = "build.rs" [dependencies] actix = { workspace = true } -alloy = { workspace = true } anyhow = { workspace = true } cipher = { path = "../cipher" } clap = { workspace = true } compile-time = { workspace = true } config = { path = "../config" } -data = { path = "../data" } dialoguer = "0.11.0" -dirs = { workspace = true } enclave-core = { path = "../core" } enclave_node = { workspace = true } -evm = { workspace = true } hex = { workspace = true } -libp2p = { workspace = true } -net = { workspace = true } -once_cell = "1.20.2" petname = "2.0.2" -phf = { version = "0.11", features = ["macros"] } rand = { workspace = true } -e3_request = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } zeroize = { workspace = true } -[build-dependencies] -serde_json = { workspace = true } diff --git a/packages/ciphernode/enclave/src/commands/aggregator/mod.rs b/packages/ciphernode/enclave/src/aggregator.rs similarity index 93% rename from packages/ciphernode/enclave/src/commands/aggregator/mod.rs rename to packages/ciphernode/enclave/src/aggregator.rs index d52c5f64..1320f7e1 100644 --- a/packages/ciphernode/enclave/src/commands/aggregator/mod.rs +++ b/packages/ciphernode/enclave/src/aggregator.rs @@ -1,8 +1,9 @@ -mod start; use anyhow::*; use clap::Subcommand; use config::AppConfig; +use crate::aggregator_start; + #[derive(Subcommand, Debug)] pub enum AggregatorCommands { /// Start the application as an aggregator @@ -23,7 +24,7 @@ pub async fn execute(command: AggregatorCommands, config: AppConfig) -> Result<( pubkey_write_path, plaintext_write_path, } => { - start::execute( + aggregator_start::execute( config, pubkey_write_path.as_deref(), plaintext_write_path.as_deref(), diff --git a/packages/ciphernode/enclave/src/commands/aggregator/start.rs b/packages/ciphernode/enclave/src/aggregator_start.rs similarity index 77% rename from packages/ciphernode/enclave/src/commands/aggregator/start.rs rename to packages/ciphernode/enclave/src/aggregator_start.rs index 4ae1e9ce..5750c8ec 100644 --- a/packages/ciphernode/enclave/src/commands/aggregator/start.rs +++ b/packages/ciphernode/enclave/src/aggregator_start.rs @@ -1,7 +1,7 @@ use anyhow::*; use config::AppConfig; use enclave_core::get_tag; -use enclave_node::{listen_for_shutdown, setup_aggregator}; +use enclave_node::{aggregator_start, listen_for_shutdown}; use tracing::{info, instrument}; use crate::owo; @@ -15,7 +15,7 @@ pub async fn execute( owo(); let (bus, handle, peer_id) = - setup_aggregator(config, pubkey_write_path, plaintext_write_path).await?; + aggregator_start::execute(config, pubkey_write_path, plaintext_write_path).await?; info!("LAUNCHING AGGREGATOR {}", peer_id); tokio::spawn(listen_for_shutdown(bus.into(), handle)); diff --git a/packages/ciphernode/enclave/src/cli.rs b/packages/ciphernode/enclave/src/cli.rs new file mode 100644 index 00000000..4e521115 --- /dev/null +++ b/packages/ciphernode/enclave/src/cli.rs @@ -0,0 +1,126 @@ +use crate::net; +use crate::net::NetCommands; +use crate::password::PasswordCommands; +use crate::wallet::WalletCommands; +use crate::{aggregator, init, password, wallet}; +use crate::{aggregator::AggregatorCommands, start}; +use anyhow::Result; +use clap::{command, Parser, Subcommand}; +use config::load_config; +use enclave_core::get_tag; +use tracing::instrument; + +#[derive(Parser, Debug)] +#[command(name = "enclave")] +#[command(about = "A CLI for interacting with Enclave the open-source protocol for Encrypted Execution Environments (E3)", long_about = None)] +pub struct Cli { + /// Path to config file + #[arg(long, global = true)] + config: Option, + + #[command(subcommand)] + command: Commands, + + #[arg(short, long, global = true)] + tag: Option, +} + +impl Cli { + #[instrument(skip(self),fields(id = get_tag()))] + pub async fn execute(self) -> Result<()> { + let config_path = self.config.as_deref(); + let config = load_config(config_path)?; + + match self.command { + Commands::Start => start::execute(config).await?, + Commands::Init { + rpc_url, + eth_address, + password, + skip_eth, + net_keypair, + generate_net_keypair, + } => { + init::execute( + rpc_url, + eth_address, + password, + skip_eth, + net_keypair, + generate_net_keypair, + ) + .await? + } + Commands::Password { command } => password::execute(command, &config).await?, + Commands::Aggregator { command } => aggregator::execute(command, config).await?, + Commands::Wallet { command } => wallet::execute(command, config).await?, + Commands::Net { command } => net::execute(command, &config).await?, + } + + Ok(()) + } + + pub fn get_tag(&self) -> String { + if let Some(tag) = self.tag.clone() { + tag + } else { + "default".to_string() + } + } +} + +#[derive(Subcommand, Debug)] +pub enum Commands { + /// Start the application + Start, + + /// Aggregator node management commands + Aggregator { + #[command(subcommand)] + command: AggregatorCommands, + }, + + /// Password management commands + Password { + #[command(subcommand)] + command: PasswordCommands, + }, + + /// Wallet management commands + Wallet { + #[command(subcommand)] + command: WalletCommands, + }, + + /// Networking related commands + Net { + #[command(subcommand)] + command: NetCommands, + }, + + Init { + /// An rpc url for enclave to connect to + #[arg(long = "rpc-url")] + rpc_url: Option, + + /// An Ethereum address that enclave should use to identify the node + #[arg(long = "eth-address")] + eth_address: Option, + + /// The password + #[arg(short, long)] + password: Option, + + /// Skip asking for eth + #[arg(long = "skip-eth")] + skip_eth: bool, + + /// The network private key (ed25519) + #[arg(long = "net-keypair")] + net_keypair: Option, + + /// Generate a new network keypair + #[arg(long = "generate-net-keypair")] + generate_net_keypair: bool, + }, +} diff --git a/packages/ciphernode/enclave/src/commands/mod.rs b/packages/ciphernode/enclave/src/commands/mod.rs deleted file mode 100644 index 92cc422b..00000000 --- a/packages/ciphernode/enclave/src/commands/mod.rs +++ /dev/null @@ -1,68 +0,0 @@ -pub mod aggregator; -pub mod init; -pub mod net; -pub mod password; -pub mod start; -pub mod wallet; - -use self::password::PasswordCommands; -use aggregator::AggregatorCommands; -use clap::Subcommand; -use net::NetCommands; -use wallet::WalletCommands; - -#[derive(Subcommand, Debug)] -pub enum Commands { - /// Start the application - Start, - - /// Aggregator node management commands - Aggregator { - #[command(subcommand)] - command: AggregatorCommands, - }, - - /// Password management commands - Password { - #[command(subcommand)] - command: PasswordCommands, - }, - - /// Wallet management commands - Wallet { - #[command(subcommand)] - command: WalletCommands, - }, - - /// Networking related commands - Net { - #[command(subcommand)] - command: NetCommands, - }, - - Init { - /// An rpc url for enclave to connect to - #[arg(long = "rpc-url")] - rpc_url: Option, - - /// An Ethereum address that enclave should use to identify the node - #[arg(long = "eth-address")] - eth_address: Option, - - /// The password - #[arg(short, long)] - password: Option, - - /// Skip asking for eth - #[arg(long = "skip-eth")] - skip_eth: bool, - - /// The network private key (ed25519) - #[arg(long = "net-keypair")] - net_keypair: Option, - - /// Generate a new network keypair - #[arg(long = "generate-net-keypair")] - generate_net_keypair: bool, - }, -} diff --git a/packages/ciphernode/enclave/src/commands/net/generate.rs b/packages/ciphernode/enclave/src/commands/net/generate.rs deleted file mode 100644 index d582daab..00000000 --- a/packages/ciphernode/enclave/src/commands/net/generate.rs +++ /dev/null @@ -1,38 +0,0 @@ -use actix::Actor; -use anyhow::{bail, Result}; -use cipher::Cipher; -use config::AppConfig; -use enclave_core::{EventBus, GetErrors}; -use enclave_node::get_repositories; -use libp2p::identity::Keypair; -use net::NetRepositoryFactory; -use zeroize::Zeroize; - -pub async fn execute(config: &AppConfig) -> Result<()> { - let kp = Keypair::generate_ed25519(); - println!( - "Generated new keypair with peer ID: {}", - kp.public().to_peer_id() - ); - let mut bytes = kp.try_into_ed25519()?.to_bytes().to_vec(); - let cipher = Cipher::from_config(config).await?; - let encrypted = cipher.encrypt_data(&mut bytes.clone())?; - let bus = EventBus::new(true).start(); - let repositories = get_repositories(&config, &bus)?; - bytes.zeroize(); - - // NOTE: We are writing an encrypted string here - repositories.libp2p_keypair().write(&encrypted); - - let errors = bus.send(GetErrors).await?; - if errors.len() > 0 { - for error in errors.iter() { - println!("{error}"); - } - bail!("There were errors generating the network keypair") - } - - println!("Network keypair has been successfully generated and encrypted."); - - Ok(()) -} diff --git a/packages/ciphernode/enclave/src/commands/net/set.rs b/packages/ciphernode/enclave/src/commands/net/set.rs deleted file mode 100644 index 3a6739ca..00000000 --- a/packages/ciphernode/enclave/src/commands/net/set.rs +++ /dev/null @@ -1,60 +0,0 @@ -use actix::Actor; -use alloy::primitives::hex; -use anyhow::{bail, Result}; -use cipher::Cipher; -use config::AppConfig; -use dialoguer::{theme::ColorfulTheme, Password}; -use enclave_core::{EventBus, GetErrors}; -use enclave_node::get_repositories; -use libp2p::identity::Keypair; -use net::NetRepositoryFactory; - -pub fn create_keypair(input: &String) -> Result { - match hex::check(input) { - Ok(()) => match Keypair::ed25519_from_bytes(hex::decode(input)?) { - Ok(kp) => Ok(kp), - Err(e) => bail!("Invalid network keypair: {}", e), - }, - Err(e) => bail!("Error decoding network keypair: {}", e), - } -} - -fn validate_keypair_input(input: &String) -> Result<()> { - create_keypair(input).map(|_| ()) -} - -pub async fn execute(config: &AppConfig, net_keypair: Option) -> Result<()> { - let input = if let Some(net_keypair) = net_keypair { - let kp = create_keypair(&net_keypair)?; - kp.try_into_ed25519()?.to_bytes().to_vec() - } else { - let kp = Password::with_theme(&ColorfulTheme::default()) - .with_prompt("Enter your network private key") - .validate_with(validate_keypair_input) - .interact()? - .trim() - .to_string(); - let kp = create_keypair(&kp)?; - kp.try_into_ed25519()?.to_bytes().to_vec() - }; - - let cipher = Cipher::from_config(config).await?; - let encrypted = cipher.encrypt_data(&mut input.clone())?; - let bus = EventBus::new(true).start(); - let repositories = get_repositories(&config, &bus)?; - - // NOTE: We are writing an encrypted string here - repositories.libp2p_keypair().write(&encrypted); - - let errors = bus.send(GetErrors).await?; - if errors.len() > 0 { - for error in errors.iter() { - println!("{error}"); - } - bail!("There were errors setting the network keypair") - } - - println!("Network keypair has been successfully encrypted."); - - Ok(()) -} diff --git a/packages/ciphernode/enclave/src/commands/wallet/set.rs b/packages/ciphernode/enclave/src/commands/wallet/set.rs deleted file mode 100644 index 4c322628..00000000 --- a/packages/ciphernode/enclave/src/commands/wallet/set.rs +++ /dev/null @@ -1,51 +0,0 @@ -use actix::Actor; -use alloy::{hex::FromHex, primitives::FixedBytes, signers::local::PrivateKeySigner}; -use anyhow::{anyhow, bail, Result}; -use cipher::Cipher; -use config::AppConfig; -use dialoguer::{theme::ColorfulTheme, Password}; -use enclave_core::{EventBus, GetErrors}; -use enclave_node::get_repositories; -use evm::EthPrivateKeyRepositoryFactory; - -pub fn validate_private_key(input: &String) -> Result<()> { - let bytes = - FixedBytes::<32>::from_hex(input).map_err(|e| anyhow!("Invalid private key: {}", e))?; - let _ = - PrivateKeySigner::from_bytes(&bytes).map_err(|e| anyhow!("Invalid private key: {}", e))?; - Ok(()) -} - -pub async fn execute(config: &AppConfig, private_key: Option) -> Result<()> { - let input = if let Some(private_key) = private_key { - validate_private_key(&private_key)?; - private_key - } else { - Password::with_theme(&ColorfulTheme::default()) - .with_prompt("Enter your Ethereum private key") - .validate_with(validate_private_key) - .interact()? - .trim() - .to_string() - }; - - let cipher = Cipher::from_config(config).await?; - let encrypted = cipher.encrypt_data(&mut input.as_bytes().to_vec())?; - let bus = EventBus::new(true).start(); - let repositories = get_repositories(&config, &bus)?; - - // NOTE: We are writing an encrypted string here - repositories.eth_private_key().write(&encrypted); - - let errors = bus.send(GetErrors).await?; - if errors.len() > 0 { - for error in errors.iter() { - println!("{error}"); - } - bail!("There were errors setting the wallet key") - } - - println!("WalletKey key has been successfully encrypted."); - - Ok(()) -} diff --git a/packages/ciphernode/enclave/src/compile_id.rs b/packages/ciphernode/enclave/src/helpers/compile_id.rs similarity index 100% rename from packages/ciphernode/enclave/src/compile_id.rs rename to packages/ciphernode/enclave/src/helpers/compile_id.rs diff --git a/packages/ciphernode/enclave/src/helpers/mod.rs b/packages/ciphernode/enclave/src/helpers/mod.rs new file mode 100644 index 00000000..1f4b246a --- /dev/null +++ b/packages/ciphernode/enclave/src/helpers/mod.rs @@ -0,0 +1,2 @@ +pub mod compile_id; +pub mod prompt_password; diff --git a/packages/ciphernode/enclave/src/commands/password/helpers.rs b/packages/ciphernode/enclave/src/helpers/prompt_password.rs similarity index 100% rename from packages/ciphernode/enclave/src/commands/password/helpers.rs rename to packages/ciphernode/enclave/src/helpers/prompt_password.rs diff --git a/packages/ciphernode/enclave/src/init.rs b/packages/ciphernode/enclave/src/init.rs new file mode 100644 index 00000000..8d4ea439 --- /dev/null +++ b/packages/ciphernode/enclave/src/init.rs @@ -0,0 +1,75 @@ +use anyhow::Result; +use dialoguer::{theme::ColorfulTheme, Input}; +use enclave_core::get_tag; +use enclave_node::init; +use tracing::instrument; + +use crate::net; +use crate::net::NetCommands; +use crate::password; +use crate::password::PasswordCommands; + +#[instrument(name = "app", skip_all, fields(id = get_tag()))] +pub async fn execute( + rpc_url: Option, + eth_address: Option, + password: Option, + skip_eth: bool, + net_keypair: Option, + generate_net_keypair: bool, +) -> Result<()> { + let rpc_url = match rpc_url { + Some(url) => { + init::validate_rpc_url(&url)?; + url + } + None => Input::::new() + .with_prompt("Enter WebSocket devnet RPC URL") + .default("wss://ethereum-sepolia-rpc.publicnode.com".to_string()) + .validate_with(init::validate_rpc_url) + .interact_text()?, + }; + + let eth_address: Option = match eth_address { + Some(address) => { + init::validate_eth_address(&address)?; + Some(address) + } + None => { + if skip_eth { + None + } else { + Input::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter your Ethereum address (press Enter to skip)") + .allow_empty(true) + .validate_with(init::validate_eth_address) + .interact() + .ok() + .map(|s| if s.is_empty() { None } else { Some(s) }) + .flatten() + } + } + }; + + let config = init::execute(rpc_url, eth_address).await?; + + password::execute( + PasswordCommands::Create { + password, + overwrite: true, + }, + &config, + ) + .await?; + + if generate_net_keypair { + net::execute(net::NetCommands::GenerateKey, &config).await?; + } else { + net::execute(NetCommands::SetKey { net_keypair }, &config).await?; + } + + println!("Enclave configuration successfully created!"); + println!("You can start your node using `enclave start`"); + + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index e655fcc6..c7caa613 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -1,13 +1,25 @@ -use anyhow::Result; use clap::Parser; -use commands::{aggregator, init, net, password, start, wallet, Commands}; -use config::load_config; -use enclave_core::{get_tag, set_tag}; -use tracing::{info, instrument}; +use cli::Cli; +use enclave_core::set_tag; +use tracing::info; use tracing_subscriber::EnvFilter; -pub mod commands; -mod compile_id; +mod aggregator; +mod aggregator_start; +mod cli; +pub mod helpers; +mod init; +pub mod net; +mod net_generate; +mod net_purge; +mod net_set; +mod password; +mod password_create; +mod password_delete; +mod password_overwrite; +mod start; +mod wallet; +mod wallet_set; const OWO: &str = r#" ___ ___ ___ ___ ___ @@ -29,78 +41,13 @@ pub fn owo() { println!("\n\n\n\n"); } -#[derive(Parser, Debug)] -#[command(name = "enclave")] -#[command(about = "A CLI for interacting with Enclave the open-source protocol for Encrypted Execution Environments (E3)", long_about = None)] -pub struct Cli { - /// Path to config file - #[arg(long, global = true)] - config: Option, - - #[command(subcommand)] - command: Commands, - - #[arg(short, long, global = true)] - tag: Option, -} - -impl Cli { - #[instrument(skip(self),fields(id = get_tag()))] - pub async fn execute(self) -> Result<()> { - let config_path = self.config.as_deref(); - let config = load_config(config_path)?; - - match self.command { - Commands::Start => start::execute(config).await?, - Commands::Init { - rpc_url, - eth_address, - password, - skip_eth, - net_keypair, - generate_net_keypair, - } => { - init::execute( - rpc_url, - eth_address, - password, - skip_eth, - net_keypair, - generate_net_keypair, - ) - .await? - } - Commands::Password { command } => password::execute(command, &config).await?, - Commands::Aggregator { command } => aggregator::execute(command, config).await?, - Commands::Wallet { command } => wallet::execute(command, config).await?, - Commands::Net { command } => net::execute(command, &config).await?, - } - - Ok(()) - } - - pub fn get_tag(&self) -> String { - if let Some(tag) = self.tag.clone() { - tag - } else { - "default".to_string() - } - } -} - #[actix::main] pub async fn main() { tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) - // .with_env_filter("error") - // .with_env_filter("[app{id=cn1}]=info") - // .with_env_filter("[app{id=cn2}]=info,libp2p_mdns::behaviour=error") - // .with_env_filter("[app{id=cn3}]=info") - // .with_env_filter("[app{id=cn4}]=info") - // .with_env_filter("[app{id=ag}]=info") .init(); - info!("COMPILATION ID: '{}'", compile_id::generate_id()); + info!("COMPILATION ID: '{}'", helpers::compile_id::generate_id()); let cli = Cli::parse(); diff --git a/packages/ciphernode/enclave/src/commands/net/mod.rs b/packages/ciphernode/enclave/src/net.rs similarity index 63% rename from packages/ciphernode/enclave/src/commands/net/mod.rs rename to packages/ciphernode/enclave/src/net.rs index 37b6b11e..4276282e 100644 --- a/packages/ciphernode/enclave/src/commands/net/mod.rs +++ b/packages/ciphernode/enclave/src/net.rs @@ -1,10 +1,9 @@ -mod generate; -mod purge; -mod set; use anyhow::*; use clap::Subcommand; use config::AppConfig; +use crate::{net_generate, net_purge, net_set}; + #[derive(Subcommand, Debug)] pub enum NetCommands { /// Purge the current peer ID from the database. @@ -22,9 +21,9 @@ pub enum NetCommands { pub async fn execute(command: NetCommands, config: &AppConfig) -> Result<()> { match command { - NetCommands::PurgeId => purge::execute(&config).await?, - NetCommands::GenerateKey => generate::execute(&config).await?, - NetCommands::SetKey { net_keypair } => set::execute(&config, net_keypair).await?, + NetCommands::PurgeId => net_purge::execute(&config).await?, + NetCommands::GenerateKey => net_generate::execute(&config).await?, + NetCommands::SetKey { net_keypair } => net_set::execute(&config, net_keypair).await?, }; Ok(()) diff --git a/packages/ciphernode/enclave/src/net_generate.rs b/packages/ciphernode/enclave/src/net_generate.rs new file mode 100644 index 00000000..35aaf84d --- /dev/null +++ b/packages/ciphernode/enclave/src/net_generate.rs @@ -0,0 +1,10 @@ +use anyhow::Result; +use config::AppConfig; +use enclave_node::net_generate; + +pub async fn execute(config: &AppConfig) -> Result<()> { + let peer_id = net_generate::execute(config).await?; + println!("Generated new keypair with peer ID: {}", peer_id); + println!("Network keypair has been successfully generated and encrypted."); + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/net_purge.rs b/packages/ciphernode/enclave/src/net_purge.rs new file mode 100644 index 00000000..9aedf2c8 --- /dev/null +++ b/packages/ciphernode/enclave/src/net_purge.rs @@ -0,0 +1,9 @@ +use anyhow::*; +use config::AppConfig; +use enclave_node::net_purge; + +pub async fn execute(config: &AppConfig) -> Result<()> { + net_purge::execute(config).await?; + println!("Peer ID has been purged. A new Peer ID will be generated upon restart."); + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/net_set.rs b/packages/ciphernode/enclave/src/net_set.rs new file mode 100644 index 00000000..a8f61439 --- /dev/null +++ b/packages/ciphernode/enclave/src/net_set.rs @@ -0,0 +1,23 @@ +use anyhow::Result; +use config::AppConfig; +use dialoguer::{theme::ColorfulTheme, Password}; +use enclave_node::net_set::{self, validate_keypair_input}; + +pub async fn execute(config: &AppConfig, net_keypair: Option) -> Result<()> { + let input = if let Some(nkp) = net_keypair { + nkp + } else { + Password::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter your network private key") + .validate_with(validate_keypair_input) + .interact()? + .trim() + .to_string() + }; + + net_set::execute(config, input).await?; + + println!("Network keypair has been successfully stored and encrypted."); + + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/commands/password/mod.rs b/packages/ciphernode/enclave/src/password.rs similarity index 67% rename from packages/ciphernode/enclave/src/commands/password/mod.rs rename to packages/ciphernode/enclave/src/password.rs index fe6259d1..e659171a 100644 --- a/packages/ciphernode/enclave/src/commands/password/mod.rs +++ b/packages/ciphernode/enclave/src/password.rs @@ -1,11 +1,8 @@ -mod create; -mod delete; -mod helpers; -mod overwrite; use anyhow::*; use clap::Subcommand; use config::AppConfig; -use helpers::*; + +use crate::{password_create, password_delete, password_overwrite}; #[derive(Subcommand, Debug)] pub enum PasswordCommands { @@ -35,9 +32,11 @@ pub async fn execute(command: PasswordCommands, config: &AppConfig) -> Result<() PasswordCommands::Create { password, overwrite, - } => create::execute(&config, password, overwrite).await?, - PasswordCommands::Delete => delete::execute(&config).await?, - PasswordCommands::Overwrite { password } => overwrite::execute(&config, password).await?, + } => password_create::execute(&config, password, overwrite).await?, + PasswordCommands::Delete => password_delete::execute(&config).await?, + PasswordCommands::Overwrite { password } => { + password_overwrite::execute(&config, password).await? + } }; Ok(()) diff --git a/packages/ciphernode/enclave/src/commands/password/create.rs b/packages/ciphernode/enclave/src/password_create.rs similarity index 68% rename from packages/ciphernode/enclave/src/commands/password/create.rs rename to packages/ciphernode/enclave/src/password_create.rs index 382d87e6..dda0b0be 100644 --- a/packages/ciphernode/enclave/src/commands/password/create.rs +++ b/packages/ciphernode/enclave/src/password_create.rs @@ -1,9 +1,9 @@ use anyhow::{bail, Result}; -use cipher::{FilePasswordManager, PasswordManager}; use config::AppConfig; +use enclave_node::password_create; use zeroize::{Zeroize, Zeroizing}; -use super::prompt_password; +use crate::helpers::prompt_password::prompt_password; fn get_zeroizing_pw_vec(input: Option) -> Result>> { if let Some(mut pw_str) = input { @@ -42,23 +42,13 @@ fn get_zeroizing_pw_vec(input: Option) -> Result>> { } pub async fn execute(config: &AppConfig, input: Option, overwrite: bool) -> Result<()> { - let key_file = config.key_file(); - let mut pm = FilePasswordManager::new(key_file); - - if overwrite && pm.is_set() { - pm.delete_key().await?; - } - - if pm.is_set() { - bail!("Keyfile already exists. Refusing to overwrite. Try using `enclave password overwrite` or `enclave password delete` in order to change or delete your password.") - } + password_create::preflight(config, overwrite).await?; let pw = get_zeroizing_pw_vec(input)?; - match pm.set_key(pw).await { - Ok(_) => println!("Password sucessfully set."), - Err(err) => println!("{}", err), - }; + password_create::execute(config, pw, overwrite).await?; + + println!("Password sucessfully set."); Ok(()) } diff --git a/packages/ciphernode/enclave/src/commands/password/delete.rs b/packages/ciphernode/enclave/src/password_delete.rs similarity index 52% rename from packages/ciphernode/enclave/src/commands/password/delete.rs rename to packages/ciphernode/enclave/src/password_delete.rs index 0bf07077..a5071bf2 100644 --- a/packages/ciphernode/enclave/src/commands/password/delete.rs +++ b/packages/ciphernode/enclave/src/password_delete.rs @@ -1,11 +1,10 @@ -use anyhow::*; -use cipher::{FilePasswordManager, PasswordManager}; +use crate::helpers::prompt_password::prompt_password; +use anyhow::Result; use config::AppConfig; use dialoguer::{theme::ColorfulTheme, Confirm}; +use enclave_node::password_delete; use zeroize::Zeroize; -use super::prompt_password; - pub enum DeleteMode { Delete, Overwrite, @@ -22,38 +21,36 @@ impl DeleteMode { pub async fn prompt_delete(config: &AppConfig, delete_mode: DeleteMode) -> Result { let mode = delete_mode.to_string(); - let proceed = Confirm::with_theme(&ColorfulTheme::default()) + + if !Confirm::with_theme(&ColorfulTheme::default()) .with_prompt(format!( "Are you sure you want to {mode} the key? This action cannot be undone." )) .default(false) - .interact()?; - - if proceed { - let key_file = config.key_file(); - let mut pm = FilePasswordManager::new(key_file); - if !pm.is_set() { - println!("Password is not set. Nothing to do."); - return Ok(false); - } - let mut pw_str = prompt_password("Please enter the current password")?; - let mut cur_pw = pm.get_key().await?; - - if pw_str != String::from_utf8_lossy(&cur_pw) { - // Clean up sensitive data - pw_str.zeroize(); - cur_pw.zeroize(); - return Err(anyhow::anyhow!("Incorrect password")); - } - pm.delete_key().await?; - } else { + .interact()? + { return Ok(false); } + + let Ok(mut cur_pw) = password_delete::get_current_password(config).await else { + println!("Password is not set. Nothing to do."); + return Ok(false); + }; + + let mut pw_str = prompt_password("Please enter the current password")?; + if pw_str != *cur_pw { + // Clean up sensitive data + pw_str.zeroize(); + cur_pw.zeroize(); + return Err(anyhow::anyhow!("Incorrect password")); + } + Ok(true) } pub async fn execute(config: &AppConfig) -> Result<()> { if prompt_delete(config, DeleteMode::Delete).await? { + password_delete::execute(config).await?; println!("Key successfully deleted."); } else { println!("Operation cancelled."); diff --git a/packages/ciphernode/enclave/src/commands/password/overwrite.rs b/packages/ciphernode/enclave/src/password_overwrite.rs similarity index 69% rename from packages/ciphernode/enclave/src/commands/password/overwrite.rs rename to packages/ciphernode/enclave/src/password_overwrite.rs index 1720bb36..dd14cc5e 100644 --- a/packages/ciphernode/enclave/src/commands/password/overwrite.rs +++ b/packages/ciphernode/enclave/src/password_overwrite.rs @@ -1,5 +1,5 @@ -use super::create::execute as set_password; -use super::delete::{prompt_delete, DeleteMode}; +use super::password_create::execute as set_password; +use super::password_delete::{prompt_delete, DeleteMode}; use anyhow::Result; use config::AppConfig; diff --git a/packages/ciphernode/enclave/src/commands/start.rs b/packages/ciphernode/enclave/src/start.rs similarity index 80% rename from packages/ciphernode/enclave/src/commands/start.rs rename to packages/ciphernode/enclave/src/start.rs index 3cea046d..9f73311b 100644 --- a/packages/ciphernode/enclave/src/commands/start.rs +++ b/packages/ciphernode/enclave/src/start.rs @@ -2,7 +2,7 @@ use crate::owo; use anyhow::{anyhow, Result}; use config::AppConfig; use enclave_core::get_tag; -use enclave_node::{listen_for_shutdown, setup_ciphernode}; +use enclave_node::{listen_for_shutdown, start}; use tracing::{info, instrument}; #[instrument(name="app", skip_all,fields(id = get_tag()))] @@ -12,7 +12,7 @@ pub async fn execute(config: AppConfig) -> Result<()> { return Err(anyhow!("You must provide an address")); }; - let (bus, handle, peer_id) = setup_ciphernode(config, address).await?; + let (bus, handle, peer_id) = start::execute(config, address).await?; info!("LAUNCHING CIPHERNODE: ({}/{})", address, peer_id); tokio::spawn(listen_for_shutdown(bus.into(), handle)); diff --git a/packages/ciphernode/enclave/src/commands/wallet/mod.rs b/packages/ciphernode/enclave/src/wallet.rs similarity index 85% rename from packages/ciphernode/enclave/src/commands/wallet/mod.rs rename to packages/ciphernode/enclave/src/wallet.rs index 6c41c82b..237a5ec0 100644 --- a/packages/ciphernode/enclave/src/commands/wallet/mod.rs +++ b/packages/ciphernode/enclave/src/wallet.rs @@ -1,8 +1,9 @@ -mod set; use anyhow::*; use clap::Subcommand; use config::AppConfig; +use crate::wallet_set; + #[derive(Subcommand, Debug)] pub enum WalletCommands { /// Set a new Wallet Private Key @@ -24,7 +25,7 @@ fn ensure_hex(s: &str) -> Result { pub async fn execute(command: WalletCommands, config: AppConfig) -> Result<()> { match command { - WalletCommands::Set { private_key } => set::execute(&config, private_key).await?, + WalletCommands::Set { private_key } => wallet_set::execute(&config, private_key).await?, }; Ok(()) diff --git a/packages/ciphernode/enclave/src/wallet_set.rs b/packages/ciphernode/enclave/src/wallet_set.rs new file mode 100644 index 00000000..e128787d --- /dev/null +++ b/packages/ciphernode/enclave/src/wallet_set.rs @@ -0,0 +1,24 @@ +use anyhow::Result; +use config::AppConfig; +use dialoguer::{theme::ColorfulTheme, Password}; +use enclave_node::wallet_set::{self, validate_private_key}; + +pub async fn execute(config: &AppConfig, private_key: Option) -> Result<()> { + let input = if let Some(private_key) = private_key { + validate_private_key(&private_key)?; + private_key + } else { + Password::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter your Ethereum private key") + .validate_with(validate_private_key) + .interact()? + .trim() + .to_string() + }; + + wallet_set::execute(config, input).await?; + + println!("WalletKey key has been successfully stored and encrypted."); + + Ok(()) +} diff --git a/packages/ciphernode/enclave_node/Cargo.toml b/packages/ciphernode/enclave_node/Cargo.toml index 92882e17..84917330 100644 --- a/packages/ciphernode/enclave_node/Cargo.toml +++ b/packages/ciphernode/enclave_node/Cargo.toml @@ -4,8 +4,7 @@ version = "0.1.0" edition = "2021" description = ": coordinates the encryption and decryption of enclave computations" repository = "https://github.com/gnosisguild/enclave/packages/ciphernode" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +build = "build.rs" [dependencies] actix = { workspace = true } @@ -19,12 +18,15 @@ config = { path = "../config" } clap = { workspace = true } cipher = { path = "../cipher" } data = { path = "../data" } +dirs = { workspace = true } enclave-core = { path = "../core" } evm = { path = "../evm" } fhe = { path = "../fhe" } keyshare = { path = "../keyshare" } logger = { path = "../logger" } +libp2p = { workspace = true } net = { path = "../net" } +phf = { workspace = true } rand = { workspace = true } rand_chacha = { workspace = true } e3_request = { workspace = true } @@ -33,3 +35,7 @@ sortition = { path = "../sortition" } test-helpers = { path = "../test_helpers" } tokio = { workspace = true } tracing = { workspace = true } +zeroize = { workspace = true } + +[build-dependencies] +serde_json = { workspace = true } diff --git a/packages/ciphernode/enclave/build.rs b/packages/ciphernode/enclave_node/build.rs similarity index 100% rename from packages/ciphernode/enclave/build.rs rename to packages/ciphernode/enclave_node/build.rs diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator_start.rs similarity index 97% rename from packages/ciphernode/enclave_node/src/aggregator.rs rename to packages/ciphernode/enclave_node/src/aggregator_start.rs index 5ce904d4..eda426a4 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator_start.rs @@ -1,4 +1,3 @@ -use crate::setup_datastore; use actix::{Actor, Addr}; use aggregator::{PlaintextAggregatorFeature, PublicKeyAggregatorFeature}; use anyhow::Result; @@ -23,7 +22,9 @@ use std::sync::{Arc, Mutex}; use test_helpers::{PlaintextWriter, PublicKeyWriter}; use tokio::task::JoinHandle; -pub async fn setup_aggregator( +use crate::helpers::datastore::setup_datastore; + +pub async fn execute( config: AppConfig, pubkey_write_path: Option<&str>, plaintext_write_path: Option<&str>, diff --git a/packages/ciphernode/enclave_node/src/datastore.rs b/packages/ciphernode/enclave_node/src/helpers/datastore.rs similarity index 100% rename from packages/ciphernode/enclave_node/src/datastore.rs rename to packages/ciphernode/enclave_node/src/helpers/datastore.rs diff --git a/packages/ciphernode/enclave_node/src/helpers/mod.rs b/packages/ciphernode/enclave_node/src/helpers/mod.rs new file mode 100644 index 00000000..06bb4462 --- /dev/null +++ b/packages/ciphernode/enclave_node/src/helpers/mod.rs @@ -0,0 +1,4 @@ +pub mod datastore; +pub mod shutdown; + +pub use shutdown::*; diff --git a/packages/ciphernode/enclave_node/src/shutdown.rs b/packages/ciphernode/enclave_node/src/helpers/shutdown.rs similarity index 100% rename from packages/ciphernode/enclave_node/src/shutdown.rs rename to packages/ciphernode/enclave_node/src/helpers/shutdown.rs diff --git a/packages/ciphernode/enclave/src/commands/init.rs b/packages/ciphernode/enclave_node/src/init.rs similarity index 56% rename from packages/ciphernode/enclave/src/commands/init.rs rename to packages/ciphernode/enclave_node/src/init.rs index dbfe9952..adae77f5 100644 --- a/packages/ciphernode/enclave/src/commands/init.rs +++ b/packages/ciphernode/enclave_node/src/init.rs @@ -1,12 +1,8 @@ -use crate::commands::{ - net, - password::{self, PasswordCommands}, -}; use alloy::primitives::Address; use anyhow::{anyhow, bail, Result}; use config::load_config; +use config::AppConfig; use config::RPC; -use dialoguer::{theme::ColorfulTheme, Input}; use enclave_core::get_tag; use std::fs; use tracing::instrument; @@ -23,12 +19,12 @@ fn get_contract_info(name: &str) -> Result<&ContractInfo> { .ok_or(anyhow!("Could not get contract info"))?) } -fn validate_rpc_url(url: &String) -> Result<()> { +pub fn validate_rpc_url(url: &String) -> Result<()> { RPC::from_url(url)?; Ok(()) } -fn validate_eth_address(address: &String) -> Result<()> { +pub fn validate_eth_address(address: &String) -> Result<()> { match Address::parse_checksummed(address, None) { Ok(_) => Ok(()), Err(e) => bail!("Invalid Ethereum address: {}", e), @@ -36,47 +32,7 @@ fn validate_eth_address(address: &String) -> Result<()> { } #[instrument(name = "app", skip_all, fields(id = get_tag()))] -pub async fn execute( - rpc_url: Option, - eth_address: Option, - password: Option, - skip_eth: bool, - net_keypair: Option, - generate_net_keypair: bool, -) -> Result<()> { - let rpc_url = match rpc_url { - Some(url) => { - validate_rpc_url(&url)?; - url - } - None => Input::::new() - .with_prompt("Enter WebSocket devnet RPC URL") - .default("wss://ethereum-sepolia-rpc.publicnode.com".to_string()) - .validate_with(validate_rpc_url) - .interact_text()?, - }; - - let eth_address: Option = match eth_address { - Some(address) => { - validate_eth_address(&address)?; - Some(address) - } - None => { - if skip_eth { - None - } else { - Input::with_theme(&ColorfulTheme::default()) - .with_prompt("Enter your Ethereum address (press Enter to skip)") - .allow_empty(true) - .validate_with(validate_eth_address) - .interact() - .ok() - .map(|s| if s.is_empty() { None } else { Some(s) }) - .flatten() - } - } - }; - +pub async fn execute(rpc_url: String, eth_address: Option) -> Result { let config_dir = dirs::home_dir() .ok_or_else(|| anyhow!("Could not determine home directory"))? .join(".config") @@ -121,31 +77,7 @@ chains: // Load with default location let config = load_config(Some(&config_path.display().to_string()))?; - password::execute( - PasswordCommands::Create { - password, - overwrite: true, - }, - &config, - ) - .await?; - - if generate_net_keypair { - net::execute(net::NetCommands::GenerateKey, &config).await?; - } else { - net::execute( - net::NetCommands::SetKey { - net_keypair: net_keypair, - }, - &config, - ) - .await?; - } - - println!("Enclave configuration successfully created!"); - println!("You can start your node using `enclave start`"); - - Ok(()) + Ok(config) } #[cfg(test)] diff --git a/packages/ciphernode/enclave_node/src/lib.rs b/packages/ciphernode/enclave_node/src/lib.rs index d66e66b1..aebd83f4 100644 --- a/packages/ciphernode/enclave_node/src/lib.rs +++ b/packages/ciphernode/enclave_node/src/lib.rs @@ -1,9 +1,12 @@ -mod aggregator; -mod ciphernode; -mod datastore; -mod shutdown; +pub mod aggregator_start; +mod helpers; +pub mod init; +pub mod net_generate; +pub mod net_purge; +pub mod net_set; +pub mod password_create; +pub mod password_delete; +pub mod start; +pub mod wallet_set; -pub use aggregator::*; -pub use ciphernode::*; -pub use datastore::*; -pub use shutdown::*; +pub use helpers::*; diff --git a/packages/ciphernode/enclave_node/src/net_generate.rs b/packages/ciphernode/enclave_node/src/net_generate.rs new file mode 100644 index 00000000..32ce3d28 --- /dev/null +++ b/packages/ciphernode/enclave_node/src/net_generate.rs @@ -0,0 +1,27 @@ +use actix::Actor; +use anyhow::{anyhow, Result}; +use cipher::Cipher; +use config::AppConfig; +use enclave_core::{EventBus, GetErrors}; +use libp2p::{identity::Keypair, PeerId}; +use net::NetRepositoryFactory; +use zeroize::Zeroize; + +use crate::datastore::get_repositories; + +pub async fn execute(config: &AppConfig) -> Result { + let kp = Keypair::generate_ed25519(); + let peer_id = kp.public().to_peer_id(); + let mut bytes = kp.try_into_ed25519()?.to_bytes().to_vec(); + let cipher = Cipher::from_config(config).await?; + let encrypted = cipher.encrypt_data(&mut bytes.clone())?; + let bus = EventBus::new(true).start(); + let repositories = get_repositories(&config, &bus)?; + bytes.zeroize(); + repositories.libp2p_keypair().write(&encrypted); + if let Some(error) = bus.send(GetErrors).await?.first() { + return Err(anyhow!(error.clone())); + } + + Ok(peer_id) +} diff --git a/packages/ciphernode/enclave/src/commands/net/purge.rs b/packages/ciphernode/enclave_node/src/net_purge.rs similarity index 72% rename from packages/ciphernode/enclave/src/commands/net/purge.rs rename to packages/ciphernode/enclave_node/src/net_purge.rs index a83cc46e..704f0f5d 100644 --- a/packages/ciphernode/enclave/src/commands/net/purge.rs +++ b/packages/ciphernode/enclave_node/src/net_purge.rs @@ -1,14 +1,13 @@ +use crate::datastore::get_repositories; use actix::Actor; use anyhow::*; use config::AppConfig; use enclave_core::EventBus; -use enclave_node::get_repositories; use net::NetRepositoryFactory; pub async fn execute(config: &AppConfig) -> Result<()> { let bus = EventBus::new(true).start(); let repositories = get_repositories(&config, &bus)?; repositories.libp2p_keypair().clear(); - println!("Peer ID has been purged. A new Peer ID will be generated upon restart."); Ok(()) } diff --git a/packages/ciphernode/enclave_node/src/net_set.rs b/packages/ciphernode/enclave_node/src/net_set.rs new file mode 100644 index 00000000..0f9f4dc7 --- /dev/null +++ b/packages/ciphernode/enclave_node/src/net_set.rs @@ -0,0 +1,38 @@ +use actix::Actor; +use alloy::primitives::hex; +use anyhow::{anyhow, Result}; +use cipher::Cipher; +use config::AppConfig; +use enclave_core::{EventBus, GetErrors}; +use libp2p::identity::Keypair; +use net::NetRepositoryFactory; + +use crate::datastore::get_repositories; + +fn create_keypair(input: &String) -> Result { + hex::check(&input)?; + let kp = Keypair::ed25519_from_bytes(hex::decode(&input)?)?; + Ok(kp) +} + +pub fn validate_keypair_input(input: &String) -> Result<()> { + create_keypair(input).map(|_| ()) +} + +pub async fn execute(config: &AppConfig, value: String) -> Result<()> { + let kp = create_keypair(&value)?; + let mut secret = kp.try_into_ed25519()?.to_bytes().to_vec(); + let cipher = Cipher::from_config(config).await?; + let encrypted = cipher.encrypt_data(&mut secret)?; + + // TODO: Tighten this up by removing external use of bus as it is confusing + let bus = EventBus::new(true).start(); + let repositories = get_repositories(&config, &bus)?; + repositories.libp2p_keypair().write(&encrypted); + + if let Some(error) = bus.send(GetErrors).await?.first() { + return Err(anyhow!(error.clone())); + } + + Ok(()) +} diff --git a/packages/ciphernode/enclave_node/src/password_create.rs b/packages/ciphernode/enclave_node/src/password_create.rs new file mode 100644 index 00000000..aee00f7d --- /dev/null +++ b/packages/ciphernode/enclave_node/src/password_create.rs @@ -0,0 +1,28 @@ +use anyhow::{bail, Result}; +use cipher::{FilePasswordManager, PasswordManager}; +use config::AppConfig; +use zeroize::Zeroizing; + +pub async fn preflight(config: &AppConfig, overwrite: bool) -> Result<()> { + let key_file = config.key_file(); + let pm = FilePasswordManager::new(key_file); + + if !overwrite && pm.is_set() { + bail!("Keyfile already exists. Refusing to overwrite. Try using `enclave password overwrite` or `enclave password delete` in order to change or delete your password.") + } + + Ok(()) +} + +pub async fn execute(config: &AppConfig, pw: Zeroizing>, overwrite: bool) -> Result<()> { + let key_file = config.key_file(); + let mut pm = FilePasswordManager::new(key_file); + + if overwrite && pm.is_set() { + pm.delete_key().await?; + } + + pm.set_key(pw).await?; + + Ok(()) +} diff --git a/packages/ciphernode/enclave_node/src/password_delete.rs b/packages/ciphernode/enclave_node/src/password_delete.rs new file mode 100644 index 00000000..34e92565 --- /dev/null +++ b/packages/ciphernode/enclave_node/src/password_delete.rs @@ -0,0 +1,21 @@ +use anyhow::*; +use cipher::{FilePasswordManager, PasswordManager}; +use config::AppConfig; +use zeroize::Zeroizing; + +pub async fn get_current_password(config: &AppConfig) -> Result> { + let key_file = config.key_file(); + let pm = FilePasswordManager::new(key_file); + if !pm.is_set() { + bail!("Password is not set. Nothing to do.") + } + let pw = pm.get_key().await?; + Ok(Zeroizing::new(String::from_utf8_lossy(&pw).to_string())) +} + +pub async fn execute(config: &AppConfig) -> Result<()> { + let key_file = config.key_file(); + let mut pm = FilePasswordManager::new(key_file); + pm.delete_key().await?; + Ok(()) +} diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/start.rs similarity index 97% rename from packages/ciphernode/enclave_node/src/ciphernode.rs rename to packages/ciphernode/enclave_node/src/start.rs index 1917ac58..4a6ad045 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/start.rs @@ -1,4 +1,3 @@ -use crate::setup_datastore; use actix::{Actor, Addr}; use alloy::primitives::Address; use anyhow::Result; @@ -24,8 +23,10 @@ use std::sync::{Arc, Mutex}; use tokio::task::JoinHandle; use tracing::instrument; +use crate::helpers::datastore::setup_datastore; + #[instrument(name="app", skip_all,fields(id = get_tag()))] -pub async fn setup_ciphernode( +pub async fn execute( config: AppConfig, address: Address, ) -> Result<(Addr, JoinHandle>, String)> { diff --git a/packages/ciphernode/enclave_node/src/wallet_set.rs b/packages/ciphernode/enclave_node/src/wallet_set.rs new file mode 100644 index 00000000..81003469 --- /dev/null +++ b/packages/ciphernode/enclave_node/src/wallet_set.rs @@ -0,0 +1,29 @@ +use actix::Actor; +use alloy::{hex::FromHex, primitives::FixedBytes, signers::local::PrivateKeySigner}; +use anyhow::{anyhow, Result}; +use cipher::Cipher; +use config::AppConfig; +use enclave_core::{EventBus, GetErrors}; +use evm::EthPrivateKeyRepositoryFactory; + +use crate::datastore::get_repositories; + +pub fn validate_private_key(input: &String) -> Result<()> { + let bytes = + FixedBytes::<32>::from_hex(input).map_err(|e| anyhow!("Invalid private key: {}", e))?; + let _ = + PrivateKeySigner::from_bytes(&bytes).map_err(|e| anyhow!("Invalid private key: {}", e))?; + Ok(()) +} + +pub async fn execute(config: &AppConfig, input: String) -> Result<()> { + let cipher = Cipher::from_config(config).await?; + let encrypted = cipher.encrypt_data(&mut input.as_bytes().to_vec())?; + let bus = EventBus::new(true).start(); + let repositories = get_repositories(&config, &bus)?; + repositories.eth_private_key().write(&encrypted); + if let Some(error) = bus.send(GetErrors).await?.first() { + return Err(anyhow!(error.clone())); + } + Ok(()) +} diff --git a/packages/ciphernode/evm/tests/evm_reader.rs b/packages/ciphernode/evm/tests/evm_reader.rs index 0fdacf07..9505bd2e 100644 --- a/packages/ciphernode/evm/tests/evm_reader.rs +++ b/packages/ciphernode/evm/tests/evm_reader.rs @@ -9,7 +9,7 @@ use alloy::{ use anyhow::Result; use data::Repository; use enclave_core::{EnclaveEvent, EventBus, GetHistory, Shutdown, TestEvent}; -use enclave_node::get_in_mem_store; +use enclave_node::datastore::get_in_mem_store; use evm::{helpers::WithChainId, EvmEventReader}; use std::time::Duration; use tokio::time::sleep; From 4c1e755cc8912a44231a0ec05018ed38ab077ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Mon, 30 Dec 2024 15:39:08 +1100 Subject: [PATCH 07/15] Rename things (#226) * Rename `enclave_node` -> `runtime` * Rename `runtime` -> `enclave_core` * Revert poor replace * Use workspace paths --- packages/ciphernode/Cargo.lock | 114 +++++++++--------- packages/ciphernode/Cargo.toml | 12 +- packages/ciphernode/aggregator/Cargo.toml | 2 +- packages/ciphernode/aggregator/src/feature.rs | 2 +- .../aggregator/src/plaintext_aggregator.rs | 2 +- .../aggregator/src/publickey_aggregator.rs | 2 +- packages/ciphernode/aggregator/src/repo.rs | 2 +- packages/ciphernode/config/Cargo.toml | 2 +- packages/ciphernode/config/src/store_keys.rs | 2 +- .../ciphernode/{cipher => crypto}/Cargo.toml | 2 +- .../{cipher => crypto}/src/cipher.rs | 0 .../ciphernode/{cipher => crypto}/src/lib.rs | 0 .../src/password_manager.rs | 0 packages/ciphernode/data/Cargo.toml | 2 +- packages/ciphernode/data/src/sled_store.rs | 2 +- packages/ciphernode/e3_request/Cargo.toml | 2 +- packages/ciphernode/e3_request/src/context.rs | 2 +- packages/ciphernode/e3_request/src/meta.rs | 2 +- packages/ciphernode/e3_request/src/repo.rs | 2 +- packages/ciphernode/e3_request/src/router.rs | 6 +- packages/ciphernode/enclave/Cargo.toml | 6 +- .../enclave/src/aggregator_start.rs | 4 +- packages/ciphernode/enclave/src/cli.rs | 2 +- packages/ciphernode/enclave/src/init.rs | 4 +- packages/ciphernode/enclave/src/main.rs | 2 +- .../ciphernode/enclave/src/net_generate.rs | 2 +- packages/ciphernode/enclave/src/net_purge.rs | 2 +- packages/ciphernode/enclave/src/net_set.rs | 2 +- .../ciphernode/enclave/src/password_create.rs | 2 +- .../ciphernode/enclave/src/password_delete.rs | 2 +- packages/ciphernode/enclave/src/start.rs | 4 +- packages/ciphernode/enclave/src/wallet_set.rs | 2 +- .../{enclave_node => enclave_core}/Cargo.toml | 6 +- .../{enclave_node => enclave_core}/build.rs | 0 .../src/aggregator_start.rs | 4 +- .../src/helpers/datastore.rs | 2 +- .../src/helpers/mod.rs | 0 .../src/helpers/shutdown.rs | 2 +- .../src/init.rs | 2 +- .../{enclave_node => enclave_core}/src/lib.rs | 0 .../src/net_generate.rs | 4 +- .../src/net_purge.rs | 2 +- .../src/net_set.rs | 4 +- .../src/password_create.rs | 2 +- .../src/password_delete.rs | 2 +- .../src/start.rs | 4 +- .../src/wallet_set.rs | 4 +- .../ciphernode/{core => events}/Cargo.toml | 2 +- .../{core => events}/src/eventbus.rs | 0 .../ciphernode/{core => events}/src/events.rs | 0 .../ciphernode/{core => events}/src/lib.rs | 4 - .../{core => events}/src/ordered_set.rs | 0 .../ciphernode/{core => events}/src/tag.rs | 0 packages/ciphernode/evm/Cargo.toml | 6 +- .../evm/src/ciphernode_registry_sol.rs | 14 +-- packages/ciphernode/evm/src/enclave_sol.rs | 2 +- .../ciphernode/evm/src/enclave_sol_reader.rs | 14 +-- .../ciphernode/evm/src/enclave_sol_writer.rs | 6 +- packages/ciphernode/evm/src/event_reader.rs | 4 +- packages/ciphernode/evm/src/helpers.rs | 2 +- .../ciphernode/evm/src/registry_filter_sol.rs | 2 +- packages/ciphernode/evm/tests/evm_reader.rs | 4 +- packages/ciphernode/fhe/Cargo.toml | 2 +- packages/ciphernode/fhe/src/feature.rs | 2 +- packages/ciphernode/fhe/src/fhe.rs | 2 +- packages/ciphernode/fhe/src/repo.rs | 2 +- packages/ciphernode/keyshare/Cargo.toml | 4 +- packages/ciphernode/keyshare/src/feature.rs | 4 +- packages/ciphernode/keyshare/src/keyshare.rs | 4 +- packages/ciphernode/keyshare/src/repo.rs | 2 +- packages/ciphernode/logger/Cargo.toml | 2 +- packages/ciphernode/logger/src/logger.rs | 2 +- packages/ciphernode/net/Cargo.toml | 4 +- .../ciphernode/net/src/network_manager.rs | 4 +- packages/ciphernode/sortition/Cargo.toml | 2 +- .../sortition/src/ciphernode_selector.rs | 2 +- .../ciphernode/sortition/src/sortition.rs | 2 +- packages/ciphernode/test_helpers/Cargo.toml | 2 +- .../test_helpers/src/plaintext_writer.rs | 2 +- .../test_helpers/src/public_key_writer.rs | 2 +- packages/ciphernode/tests/Cargo.toml | 4 +- .../tests/test_aggregation_and_decryption.rs | 4 +- 82 files changed, 170 insertions(+), 176 deletions(-) rename packages/ciphernode/{cipher => crypto}/Cargo.toml (96%) rename packages/ciphernode/{cipher => crypto}/src/cipher.rs (100%) rename packages/ciphernode/{cipher => crypto}/src/lib.rs (100%) rename packages/ciphernode/{cipher => crypto}/src/password_manager.rs (100%) rename packages/ciphernode/{enclave_node => enclave_core}/Cargo.toml (92%) rename packages/ciphernode/{enclave_node => enclave_core}/build.rs (100%) rename packages/ciphernode/{enclave_node => enclave_core}/src/aggregator_start.rs (98%) rename packages/ciphernode/{enclave_node => enclave_core}/src/helpers/datastore.rs (96%) rename packages/ciphernode/{enclave_node => enclave_core}/src/helpers/mod.rs (100%) rename packages/ciphernode/{enclave_node => enclave_core}/src/helpers/shutdown.rs (94%) rename packages/ciphernode/{enclave_node => enclave_core}/src/init.rs (99%) rename packages/ciphernode/{enclave_node => enclave_core}/src/lib.rs (100%) rename packages/ciphernode/{enclave_node => enclave_core}/src/net_generate.rs (93%) rename packages/ciphernode/{enclave_node => enclave_core}/src/net_purge.rs (92%) rename packages/ciphernode/{enclave_node => enclave_core}/src/net_set.rs (94%) rename packages/ciphernode/{enclave_node => enclave_core}/src/password_create.rs (93%) rename packages/ciphernode/{enclave_node => enclave_core}/src/password_delete.rs (92%) rename packages/ciphernode/{enclave_node => enclave_core}/src/start.rs (98%) rename packages/ciphernode/{enclave_node => enclave_core}/src/wallet_set.rs (94%) rename packages/ciphernode/{core => events}/Cargo.toml (97%) rename packages/ciphernode/{core => events}/src/eventbus.rs (100%) rename packages/ciphernode/{core => events}/src/events.rs (100%) rename packages/ciphernode/{core => events}/src/lib.rs (57%) rename packages/ciphernode/{core => events}/src/ordered_set.rs (100%) rename packages/ciphernode/{core => events}/src/tag.rs (100%) diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 8df8065e..606d89d1 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -90,7 +90,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher 0.4.4", + "cipher", "cpufeatures", ] @@ -102,7 +102,7 @@ checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", - "cipher 0.4.4", + "cipher", "ctr", "ghash", "subtle", @@ -119,7 +119,7 @@ dependencies = [ "config", "data", "e3_request", - "enclave-core", + "events", "fhe 0.1.0", "serde", "sortition", @@ -1608,20 +1608,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cipher" -version = "0.1.0" -dependencies = [ - "aes-gcm", - "anyhow", - "argon2", - "async-trait", - "config", - "rand", - "tokio", - "zeroize", -] - [[package]] name = "cipher" version = "0.4.4" @@ -1708,7 +1694,7 @@ dependencies = [ "alloy", "anyhow", "dirs", - "enclave-core", + "events", "figment", "serde", "shellexpand", @@ -1827,6 +1813,20 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto" +version = "0.1.0" +dependencies = [ + "aes-gcm", + "anyhow", + "argon2", + "async-trait", + "config", + "rand", + "tokio", + "zeroize", +] + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -1856,7 +1856,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.4.4", + "cipher", ] [[package]] @@ -1908,7 +1908,7 @@ dependencies = [ "anyhow", "async-trait", "bincode", - "enclave-core", + "events", "serde", "sled", "tracing", @@ -2118,7 +2118,7 @@ dependencies = [ "bincode", "config", "data", - "enclave-core", + "events", "serde", "tracing", ] @@ -2193,13 +2193,13 @@ version = "0.1.0" dependencies = [ "actix", "anyhow", - "cipher 0.1.0", "clap", "compile-time", "config", + "crypto", "dialoguer", - "enclave-core", - "enclave_node", + "enclave_core", + "events", "hex", "petname", "rand", @@ -2210,24 +2210,7 @@ dependencies = [ ] [[package]] -name = "enclave-core" -version = "0.1.0" -dependencies = [ - "actix", - "alloy", - "alloy-primitives 0.6.4", - "alloy-sol-types 0.6.4", - "anyhow", - "bincode", - "bs58", - "futures-util", - "lazy_static", - "serde", - "sha2", -] - -[[package]] -name = "enclave_node" +name = "enclave_core" version = "0.1.0" dependencies = [ "actix", @@ -2237,13 +2220,13 @@ dependencies = [ "anyhow", "bfv", "bincode", - "cipher 0.1.0", "clap", "config", + "crypto", "data", "dirs", "e3_request", - "enclave-core", + "events", "evm", "fhe 0.1.0", "keyshare", @@ -2340,6 +2323,23 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "events" +version = "0.1.0" +dependencies = [ + "actix", + "alloy", + "alloy-primitives 0.6.4", + "alloy-sol-types 0.6.4", + "anyhow", + "bincode", + "bs58", + "futures-util", + "lazy_static", + "serde", + "sha2", +] + [[package]] name = "evm" version = "0.1.0" @@ -2350,11 +2350,11 @@ dependencies = [ "anyhow", "async-trait", "base64", - "cipher 0.1.0", "config", + "crypto", "data", - "enclave-core", - "enclave_node", + "enclave_core", + "events", "futures-util", "serde", "sortition", @@ -2434,7 +2434,7 @@ dependencies = [ "config", "data", "e3_request", - "enclave-core", + "events", "fhe 0.1.0-beta.7", "fhe-traits", "fhe-util", @@ -3504,11 +3504,11 @@ dependencies = [ "actix", "anyhow", "async-trait", - "cipher 0.1.0", "config", + "crypto", "data", "e3_request", - "enclave-core", + "events", "fhe 0.1.0", "serde", "tracing", @@ -3980,7 +3980,7 @@ version = "0.1.0" dependencies = [ "actix", "base64", - "enclave-core", + "events", "tracing", ] @@ -4164,10 +4164,10 @@ dependencies = [ "anyhow", "async-std", "async-trait", - "cipher 0.1.0", "config", + "crypto", "data", - "enclave-core", + "events", "futures", "libp2p", "tokio", @@ -5838,7 +5838,7 @@ dependencies = [ "async-trait", "config", "data", - "enclave-core", + "events", "num", "rand", "serde", @@ -6032,7 +6032,7 @@ dependencies = [ "actix", "bincode", "clap", - "enclave-core", + "events", "fhe 0.1.0", "fhe 0.1.0-beta.7", "fhe-traits", @@ -6054,11 +6054,11 @@ dependencies = [ "base64", "bfv", "bincode", - "cipher 0.1.0", "clap", + "crypto", "data", "e3_request", - "enclave-core", + "events", "evm", "fhe 0.1.0", "fhe 0.1.0-beta.7", diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index 1dddc596..d9569d48 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -1,8 +1,8 @@ [workspace] members = [ - "core", + "events", "enclave", - "enclave_node", + "enclave_core", "net", "bfv", "data", @@ -14,7 +14,7 @@ members = [ "test_helpers", "logger", "tests", - "cipher", + "crypto", "config", ] @@ -36,13 +36,13 @@ bincode = "1.3.3" bs58 = "0.5.1" base64 = "0.22.1" clap = { version = "4.5.17", features = ["derive"] } -cipher = { path = "./cipher" } +crypto = { path = "./crypto" } compile-time = "0.2.0" config = { path = "./config" } dirs = "5.0.1" data = { path = "./data" } -enclave-core = { path = "./core" } -enclave_node = { path = "./enclave_node" } +events = { path = "./events" } +enclave_core = { path = "./enclave_core" } evm = { path = "./evm" } shellexpand = "3.1.0" figment = { version = "0.10.19", features = ["yaml", "test"] } diff --git a/packages/ciphernode/aggregator/Cargo.toml b/packages/ciphernode/aggregator/Cargo.toml index 491f51bd..8bb971fc 100644 --- a/packages/ciphernode/aggregator/Cargo.toml +++ b/packages/ciphernode/aggregator/Cargo.toml @@ -10,7 +10,7 @@ serde = { workspace = true } bincode = { workspace = true } config = { workspace = true } async-trait = { workspace = true } -enclave-core = { path = "../core" } +events = { workspace = true } fhe = { path = "../fhe" } sortition = { path = "../sortition" } e3_request = { workspace = true } diff --git a/packages/ciphernode/aggregator/src/feature.rs b/packages/ciphernode/aggregator/src/feature.rs index e93de3d2..bd37838f 100644 --- a/packages/ciphernode/aggregator/src/feature.rs +++ b/packages/ciphernode/aggregator/src/feature.rs @@ -8,7 +8,7 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use data::{AutoPersist, RepositoriesFactory}; use e3_request::{E3Context, E3ContextSnapshot, E3Feature, META_KEY}; -use enclave_core::{BusError, EnclaveErrorType, EnclaveEvent, EventBus}; +use events::{BusError, EnclaveErrorType, EnclaveEvent, EventBus}; use fhe::FHE_KEY; use sortition::Sortition; diff --git a/packages/ciphernode/aggregator/src/plaintext_aggregator.rs b/packages/ciphernode/aggregator/src/plaintext_aggregator.rs index c2db077f..04914ef3 100644 --- a/packages/ciphernode/aggregator/src/plaintext_aggregator.rs +++ b/packages/ciphernode/aggregator/src/plaintext_aggregator.rs @@ -1,7 +1,7 @@ use actix::prelude::*; use anyhow::Result; use data::Persistable; -use enclave_core::{ +use events::{ DecryptionshareCreated, Die, E3id, EnclaveEvent, EventBus, OrderedSet, PlaintextAggregated, Seed, }; diff --git a/packages/ciphernode/aggregator/src/publickey_aggregator.rs b/packages/ciphernode/aggregator/src/publickey_aggregator.rs index f64d148f..4cd8cf28 100644 --- a/packages/ciphernode/aggregator/src/publickey_aggregator.rs +++ b/packages/ciphernode/aggregator/src/publickey_aggregator.rs @@ -1,7 +1,7 @@ use actix::prelude::*; use anyhow::Result; use data::Persistable; -use enclave_core::{ +use events::{ Die, E3id, EnclaveEvent, EventBus, KeyshareCreated, OrderedSet, PublicKeyAggregated, Seed, }; use fhe::{Fhe, GetAggregatePublicKey}; diff --git a/packages/ciphernode/aggregator/src/repo.rs b/packages/ciphernode/aggregator/src/repo.rs index a010b142..e12ad6e8 100644 --- a/packages/ciphernode/aggregator/src/repo.rs +++ b/packages/ciphernode/aggregator/src/repo.rs @@ -1,6 +1,6 @@ use config::StoreKeys; use data::{Repositories, Repository}; -use enclave_core::E3id; +use events::E3id; use crate::{PlaintextAggregatorState, PublicKeyAggregatorState}; diff --git a/packages/ciphernode/config/Cargo.toml b/packages/ciphernode/config/Cargo.toml index 0ce89132..3bec1b3b 100644 --- a/packages/ciphernode/config/Cargo.toml +++ b/packages/ciphernode/config/Cargo.toml @@ -11,7 +11,7 @@ figment = { workspace = true } alloy = { workspace = true } shellexpand = { workspace = true } url = { workspace = true } -enclave-core = { workspace = true } +events = { workspace = true } [dev-dependencies] tempfile = { workspace = true } diff --git a/packages/ciphernode/config/src/store_keys.rs b/packages/ciphernode/config/src/store_keys.rs index 3cdb653f..812d0bdc 100644 --- a/packages/ciphernode/config/src/store_keys.rs +++ b/packages/ciphernode/config/src/store_keys.rs @@ -1,4 +1,4 @@ -use enclave_core::E3id; +use events::E3id; pub struct StoreKeys; diff --git a/packages/ciphernode/cipher/Cargo.toml b/packages/ciphernode/crypto/Cargo.toml similarity index 96% rename from packages/ciphernode/cipher/Cargo.toml rename to packages/ciphernode/crypto/Cargo.toml index 4bbf0f0f..98da1ae0 100644 --- a/packages/ciphernode/cipher/Cargo.toml +++ b/packages/ciphernode/crypto/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cipher" +name = "crypto" version = "0.1.0" edition = "2021" diff --git a/packages/ciphernode/cipher/src/cipher.rs b/packages/ciphernode/crypto/src/cipher.rs similarity index 100% rename from packages/ciphernode/cipher/src/cipher.rs rename to packages/ciphernode/crypto/src/cipher.rs diff --git a/packages/ciphernode/cipher/src/lib.rs b/packages/ciphernode/crypto/src/lib.rs similarity index 100% rename from packages/ciphernode/cipher/src/lib.rs rename to packages/ciphernode/crypto/src/lib.rs diff --git a/packages/ciphernode/cipher/src/password_manager.rs b/packages/ciphernode/crypto/src/password_manager.rs similarity index 100% rename from packages/ciphernode/cipher/src/password_manager.rs rename to packages/ciphernode/crypto/src/password_manager.rs diff --git a/packages/ciphernode/data/Cargo.toml b/packages/ciphernode/data/Cargo.toml index eb9990ef..145d6b92 100644 --- a/packages/ciphernode/data/Cargo.toml +++ b/packages/ciphernode/data/Cargo.toml @@ -7,7 +7,7 @@ repository = "https://github.com/gnosisguild/enclave/packages/ciphernode" [dependencies] actix = { workspace = true } -enclave-core = { path = "../core" } +events = { workspace = true } anyhow = { workspace = true } serde = { workspace = true } sled = { workspace = true } diff --git a/packages/ciphernode/data/src/sled_store.rs b/packages/ciphernode/data/src/sled_store.rs index 0c79b71c..f4075cc4 100644 --- a/packages/ciphernode/data/src/sled_store.rs +++ b/packages/ciphernode/data/src/sled_store.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use crate::{Get, Insert, Remove}; use actix::{Actor, ActorContext, Addr, Handler}; use anyhow::{Context, Result}; -use enclave_core::{BusError, EnclaveErrorType, EnclaveEvent, EventBus, Subscribe}; +use events::{BusError, EnclaveErrorType, EnclaveEvent, EventBus, Subscribe}; use sled::Db; use tracing::{error, info}; diff --git a/packages/ciphernode/e3_request/Cargo.toml b/packages/ciphernode/e3_request/Cargo.toml index 966e9b81..0c26c63e 100644 --- a/packages/ciphernode/e3_request/Cargo.toml +++ b/packages/ciphernode/e3_request/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] actix = { workspace = true } -enclave-core = { path = "../core" } +events = { workspace = true } data = { path = "../data" } anyhow = { workspace = true } serde = { workspace = true } diff --git a/packages/ciphernode/e3_request/src/context.rs b/packages/ciphernode/e3_request/src/context.rs index 96f03d38..b338861b 100644 --- a/packages/ciphernode/e3_request/src/context.rs +++ b/packages/ciphernode/e3_request/src/context.rs @@ -5,7 +5,7 @@ use async_trait::async_trait; use data::{ Checkpoint, FromSnapshotWithParams, Repositories, RepositoriesFactory, Repository, Snapshot, }; -use enclave_core::{E3id, EnclaveEvent}; +use events::{E3id, EnclaveEvent}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, sync::Arc}; diff --git a/packages/ciphernode/e3_request/src/meta.rs b/packages/ciphernode/e3_request/src/meta.rs index e17215eb..257bac07 100644 --- a/packages/ciphernode/e3_request/src/meta.rs +++ b/packages/ciphernode/e3_request/src/meta.rs @@ -2,7 +2,7 @@ use crate::{E3Context, E3ContextSnapshot, E3Feature, MetaRepositoryFactory, Type use anyhow::*; use async_trait::async_trait; use data::RepositoriesFactory; -use enclave_core::{E3Requested, EnclaveEvent, Seed}; +use events::{E3Requested, EnclaveEvent, Seed}; pub const META_KEY: TypedKey = TypedKey::new("meta"); diff --git a/packages/ciphernode/e3_request/src/repo.rs b/packages/ciphernode/e3_request/src/repo.rs index d885d321..21f781f4 100644 --- a/packages/ciphernode/e3_request/src/repo.rs +++ b/packages/ciphernode/e3_request/src/repo.rs @@ -1,6 +1,6 @@ use config::StoreKeys; use data::{Repositories, Repository}; -use enclave_core::E3id; +use events::E3id; use crate::{E3ContextSnapshot, E3Meta, E3RouterSnapshot}; diff --git a/packages/ciphernode/e3_request/src/router.rs b/packages/ciphernode/e3_request/src/router.rs index ce3a55bb..d6e40335 100644 --- a/packages/ciphernode/e3_request/src/router.rs +++ b/packages/ciphernode/e3_request/src/router.rs @@ -14,9 +14,9 @@ use data::FromSnapshotWithParams; use data::RepositoriesFactory; use data::Repository; use data::Snapshot; -use enclave_core::E3RequestComplete; -use enclave_core::Shutdown; -use enclave_core::{E3id, EnclaveEvent, EventBus, Subscribe}; +use events::E3RequestComplete; +use events::Shutdown; +use events::{E3id, EnclaveEvent, EventBus, Subscribe}; use serde::Deserialize; use serde::Serialize; use std::collections::HashSet; diff --git a/packages/ciphernode/enclave/Cargo.toml b/packages/ciphernode/enclave/Cargo.toml index 07dccf05..0cac8145 100644 --- a/packages/ciphernode/enclave/Cargo.toml +++ b/packages/ciphernode/enclave/Cargo.toml @@ -8,13 +8,13 @@ repository = "https://github.com/gnosisguild/enclave/packages/ciphernode" [dependencies] actix = { workspace = true } anyhow = { workspace = true } -cipher = { path = "../cipher" } +crypto = { workspace = true } clap = { workspace = true } compile-time = { workspace = true } config = { path = "../config" } dialoguer = "0.11.0" -enclave-core = { path = "../core" } -enclave_node = { workspace = true } +events = { workspace = true } +enclave_core = { workspace = true } hex = { workspace = true } petname = "2.0.2" rand = { workspace = true } diff --git a/packages/ciphernode/enclave/src/aggregator_start.rs b/packages/ciphernode/enclave/src/aggregator_start.rs index 5750c8ec..326dc8cb 100644 --- a/packages/ciphernode/enclave/src/aggregator_start.rs +++ b/packages/ciphernode/enclave/src/aggregator_start.rs @@ -1,7 +1,7 @@ use anyhow::*; use config::AppConfig; -use enclave_core::get_tag; -use enclave_node::{aggregator_start, listen_for_shutdown}; +use enclave_core::{aggregator_start, listen_for_shutdown}; +use events::get_tag; use tracing::{info, instrument}; use crate::owo; diff --git a/packages/ciphernode/enclave/src/cli.rs b/packages/ciphernode/enclave/src/cli.rs index 4e521115..72674d19 100644 --- a/packages/ciphernode/enclave/src/cli.rs +++ b/packages/ciphernode/enclave/src/cli.rs @@ -7,7 +7,7 @@ use crate::{aggregator::AggregatorCommands, start}; use anyhow::Result; use clap::{command, Parser, Subcommand}; use config::load_config; -use enclave_core::get_tag; +use events::get_tag; use tracing::instrument; #[derive(Parser, Debug)] diff --git a/packages/ciphernode/enclave/src/init.rs b/packages/ciphernode/enclave/src/init.rs index 8d4ea439..0c36edd5 100644 --- a/packages/ciphernode/enclave/src/init.rs +++ b/packages/ciphernode/enclave/src/init.rs @@ -1,7 +1,7 @@ use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Input}; -use enclave_core::get_tag; -use enclave_node::init; +use enclave_core::init; +use events::get_tag; use tracing::instrument; use crate::net; diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index c7caa613..8e344196 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -1,6 +1,6 @@ use clap::Parser; use cli::Cli; -use enclave_core::set_tag; +use events::set_tag; use tracing::info; use tracing_subscriber::EnvFilter; diff --git a/packages/ciphernode/enclave/src/net_generate.rs b/packages/ciphernode/enclave/src/net_generate.rs index 35aaf84d..d97785e8 100644 --- a/packages/ciphernode/enclave/src/net_generate.rs +++ b/packages/ciphernode/enclave/src/net_generate.rs @@ -1,6 +1,6 @@ use anyhow::Result; use config::AppConfig; -use enclave_node::net_generate; +use enclave_core::net_generate; pub async fn execute(config: &AppConfig) -> Result<()> { let peer_id = net_generate::execute(config).await?; diff --git a/packages/ciphernode/enclave/src/net_purge.rs b/packages/ciphernode/enclave/src/net_purge.rs index 9aedf2c8..bf0be1a5 100644 --- a/packages/ciphernode/enclave/src/net_purge.rs +++ b/packages/ciphernode/enclave/src/net_purge.rs @@ -1,6 +1,6 @@ use anyhow::*; use config::AppConfig; -use enclave_node::net_purge; +use enclave_core::net_purge; pub async fn execute(config: &AppConfig) -> Result<()> { net_purge::execute(config).await?; diff --git a/packages/ciphernode/enclave/src/net_set.rs b/packages/ciphernode/enclave/src/net_set.rs index a8f61439..7a5a22dc 100644 --- a/packages/ciphernode/enclave/src/net_set.rs +++ b/packages/ciphernode/enclave/src/net_set.rs @@ -1,7 +1,7 @@ use anyhow::Result; use config::AppConfig; use dialoguer::{theme::ColorfulTheme, Password}; -use enclave_node::net_set::{self, validate_keypair_input}; +use enclave_core::net_set::{self, validate_keypair_input}; pub async fn execute(config: &AppConfig, net_keypair: Option) -> Result<()> { let input = if let Some(nkp) = net_keypair { diff --git a/packages/ciphernode/enclave/src/password_create.rs b/packages/ciphernode/enclave/src/password_create.rs index dda0b0be..46d7ec94 100644 --- a/packages/ciphernode/enclave/src/password_create.rs +++ b/packages/ciphernode/enclave/src/password_create.rs @@ -1,6 +1,6 @@ use anyhow::{bail, Result}; use config::AppConfig; -use enclave_node::password_create; +use enclave_core::password_create; use zeroize::{Zeroize, Zeroizing}; use crate::helpers::prompt_password::prompt_password; diff --git a/packages/ciphernode/enclave/src/password_delete.rs b/packages/ciphernode/enclave/src/password_delete.rs index a5071bf2..6914053f 100644 --- a/packages/ciphernode/enclave/src/password_delete.rs +++ b/packages/ciphernode/enclave/src/password_delete.rs @@ -2,7 +2,7 @@ use crate::helpers::prompt_password::prompt_password; use anyhow::Result; use config::AppConfig; use dialoguer::{theme::ColorfulTheme, Confirm}; -use enclave_node::password_delete; +use enclave_core::password_delete; use zeroize::Zeroize; pub enum DeleteMode { diff --git a/packages/ciphernode/enclave/src/start.rs b/packages/ciphernode/enclave/src/start.rs index 9f73311b..4a2abb03 100644 --- a/packages/ciphernode/enclave/src/start.rs +++ b/packages/ciphernode/enclave/src/start.rs @@ -1,8 +1,8 @@ use crate::owo; use anyhow::{anyhow, Result}; use config::AppConfig; -use enclave_core::get_tag; -use enclave_node::{listen_for_shutdown, start}; +use enclave_core::{listen_for_shutdown, start}; +use events::get_tag; use tracing::{info, instrument}; #[instrument(name="app", skip_all,fields(id = get_tag()))] diff --git a/packages/ciphernode/enclave/src/wallet_set.rs b/packages/ciphernode/enclave/src/wallet_set.rs index e128787d..ba2a5f5e 100644 --- a/packages/ciphernode/enclave/src/wallet_set.rs +++ b/packages/ciphernode/enclave/src/wallet_set.rs @@ -1,7 +1,7 @@ use anyhow::Result; use config::AppConfig; use dialoguer::{theme::ColorfulTheme, Password}; -use enclave_node::wallet_set::{self, validate_private_key}; +use enclave_core::wallet_set::{self, validate_private_key}; pub async fn execute(config: &AppConfig, private_key: Option) -> Result<()> { let input = if let Some(private_key) = private_key { diff --git a/packages/ciphernode/enclave_node/Cargo.toml b/packages/ciphernode/enclave_core/Cargo.toml similarity index 92% rename from packages/ciphernode/enclave_node/Cargo.toml rename to packages/ciphernode/enclave_core/Cargo.toml index 84917330..8e972443 100644 --- a/packages/ciphernode/enclave_node/Cargo.toml +++ b/packages/ciphernode/enclave_core/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "enclave_node" +name = "enclave_core" version = "0.1.0" edition = "2021" description = ": coordinates the encryption and decryption of enclave computations" @@ -16,10 +16,10 @@ bfv = { path = "../bfv" } bincode = { workspace = true } config = { path = "../config" } clap = { workspace = true } -cipher = { path = "../cipher" } +crypto = { workspace = true } data = { path = "../data" } dirs = { workspace = true } -enclave-core = { path = "../core" } +events = { workspace = true } evm = { path = "../evm" } fhe = { path = "../fhe" } keyshare = { path = "../keyshare" } diff --git a/packages/ciphernode/enclave_node/build.rs b/packages/ciphernode/enclave_core/build.rs similarity index 100% rename from packages/ciphernode/enclave_node/build.rs rename to packages/ciphernode/enclave_core/build.rs diff --git a/packages/ciphernode/enclave_node/src/aggregator_start.rs b/packages/ciphernode/enclave_core/src/aggregator_start.rs similarity index 98% rename from packages/ciphernode/enclave_node/src/aggregator_start.rs rename to packages/ciphernode/enclave_core/src/aggregator_start.rs index eda426a4..2ddff355 100644 --- a/packages/ciphernode/enclave_node/src/aggregator_start.rs +++ b/packages/ciphernode/enclave_core/src/aggregator_start.rs @@ -1,11 +1,11 @@ use actix::{Actor, Addr}; use aggregator::{PlaintextAggregatorFeature, PublicKeyAggregatorFeature}; use anyhow::Result; -use cipher::Cipher; use config::AppConfig; +use crypto::Cipher; use data::RepositoriesFactory; use e3_request::E3Router; -use enclave_core::EventBus; +use events::EventBus; use evm::{ helpers::{get_signer_from_repository, ProviderConfig}, CiphernodeRegistryReaderRepositoryFactory, CiphernodeRegistrySol, EnclaveSol, diff --git a/packages/ciphernode/enclave_node/src/helpers/datastore.rs b/packages/ciphernode/enclave_core/src/helpers/datastore.rs similarity index 96% rename from packages/ciphernode/enclave_node/src/helpers/datastore.rs rename to packages/ciphernode/enclave_core/src/helpers/datastore.rs index 43ff182f..cfa6c49d 100644 --- a/packages/ciphernode/enclave_node/src/helpers/datastore.rs +++ b/packages/ciphernode/enclave_core/src/helpers/datastore.rs @@ -5,7 +5,7 @@ use anyhow::Result; use config::AppConfig; use data::{DataStore, InMemStore, SledStore}; use data::{Repositories, RepositoriesFactory}; -use enclave_core::EventBus; +use events::EventBus; pub fn get_sled_store(bus: &Addr, db_file: &PathBuf) -> Result { Ok((&SledStore::new(bus, db_file)?).into()) diff --git a/packages/ciphernode/enclave_node/src/helpers/mod.rs b/packages/ciphernode/enclave_core/src/helpers/mod.rs similarity index 100% rename from packages/ciphernode/enclave_node/src/helpers/mod.rs rename to packages/ciphernode/enclave_core/src/helpers/mod.rs diff --git a/packages/ciphernode/enclave_node/src/helpers/shutdown.rs b/packages/ciphernode/enclave_core/src/helpers/shutdown.rs similarity index 94% rename from packages/ciphernode/enclave_node/src/helpers/shutdown.rs rename to packages/ciphernode/enclave_core/src/helpers/shutdown.rs index 58771dc5..1736b648 100644 --- a/packages/ciphernode/enclave_node/src/helpers/shutdown.rs +++ b/packages/ciphernode/enclave_core/src/helpers/shutdown.rs @@ -1,6 +1,6 @@ use actix::Recipient; use anyhow::Result; -use enclave_core::{EnclaveEvent, Shutdown}; +use events::{EnclaveEvent, Shutdown}; use std::time::Duration; use tokio::{ signal::unix::{signal, SignalKind}, diff --git a/packages/ciphernode/enclave_node/src/init.rs b/packages/ciphernode/enclave_core/src/init.rs similarity index 99% rename from packages/ciphernode/enclave_node/src/init.rs rename to packages/ciphernode/enclave_core/src/init.rs index adae77f5..7b949ec1 100644 --- a/packages/ciphernode/enclave_node/src/init.rs +++ b/packages/ciphernode/enclave_core/src/init.rs @@ -3,7 +3,7 @@ use anyhow::{anyhow, bail, Result}; use config::load_config; use config::AppConfig; use config::RPC; -use enclave_core::get_tag; +use events::get_tag; use std::fs; use tracing::instrument; diff --git a/packages/ciphernode/enclave_node/src/lib.rs b/packages/ciphernode/enclave_core/src/lib.rs similarity index 100% rename from packages/ciphernode/enclave_node/src/lib.rs rename to packages/ciphernode/enclave_core/src/lib.rs diff --git a/packages/ciphernode/enclave_node/src/net_generate.rs b/packages/ciphernode/enclave_core/src/net_generate.rs similarity index 93% rename from packages/ciphernode/enclave_node/src/net_generate.rs rename to packages/ciphernode/enclave_core/src/net_generate.rs index 32ce3d28..f040937e 100644 --- a/packages/ciphernode/enclave_node/src/net_generate.rs +++ b/packages/ciphernode/enclave_core/src/net_generate.rs @@ -1,8 +1,8 @@ use actix::Actor; use anyhow::{anyhow, Result}; -use cipher::Cipher; use config::AppConfig; -use enclave_core::{EventBus, GetErrors}; +use crypto::Cipher; +use events::{EventBus, GetErrors}; use libp2p::{identity::Keypair, PeerId}; use net::NetRepositoryFactory; use zeroize::Zeroize; diff --git a/packages/ciphernode/enclave_node/src/net_purge.rs b/packages/ciphernode/enclave_core/src/net_purge.rs similarity index 92% rename from packages/ciphernode/enclave_node/src/net_purge.rs rename to packages/ciphernode/enclave_core/src/net_purge.rs index 704f0f5d..fc337f09 100644 --- a/packages/ciphernode/enclave_node/src/net_purge.rs +++ b/packages/ciphernode/enclave_core/src/net_purge.rs @@ -2,7 +2,7 @@ use crate::datastore::get_repositories; use actix::Actor; use anyhow::*; use config::AppConfig; -use enclave_core::EventBus; +use events::EventBus; use net::NetRepositoryFactory; pub async fn execute(config: &AppConfig) -> Result<()> { diff --git a/packages/ciphernode/enclave_node/src/net_set.rs b/packages/ciphernode/enclave_core/src/net_set.rs similarity index 94% rename from packages/ciphernode/enclave_node/src/net_set.rs rename to packages/ciphernode/enclave_core/src/net_set.rs index 0f9f4dc7..14163a9a 100644 --- a/packages/ciphernode/enclave_node/src/net_set.rs +++ b/packages/ciphernode/enclave_core/src/net_set.rs @@ -1,9 +1,9 @@ use actix::Actor; use alloy::primitives::hex; use anyhow::{anyhow, Result}; -use cipher::Cipher; use config::AppConfig; -use enclave_core::{EventBus, GetErrors}; +use crypto::Cipher; +use events::{EventBus, GetErrors}; use libp2p::identity::Keypair; use net::NetRepositoryFactory; diff --git a/packages/ciphernode/enclave_node/src/password_create.rs b/packages/ciphernode/enclave_core/src/password_create.rs similarity index 93% rename from packages/ciphernode/enclave_node/src/password_create.rs rename to packages/ciphernode/enclave_core/src/password_create.rs index aee00f7d..29e218ea 100644 --- a/packages/ciphernode/enclave_node/src/password_create.rs +++ b/packages/ciphernode/enclave_core/src/password_create.rs @@ -1,6 +1,6 @@ use anyhow::{bail, Result}; -use cipher::{FilePasswordManager, PasswordManager}; use config::AppConfig; +use crypto::{FilePasswordManager, PasswordManager}; use zeroize::Zeroizing; pub async fn preflight(config: &AppConfig, overwrite: bool) -> Result<()> { diff --git a/packages/ciphernode/enclave_node/src/password_delete.rs b/packages/ciphernode/enclave_core/src/password_delete.rs similarity index 92% rename from packages/ciphernode/enclave_node/src/password_delete.rs rename to packages/ciphernode/enclave_core/src/password_delete.rs index 34e92565..15e47d6a 100644 --- a/packages/ciphernode/enclave_node/src/password_delete.rs +++ b/packages/ciphernode/enclave_core/src/password_delete.rs @@ -1,6 +1,6 @@ use anyhow::*; -use cipher::{FilePasswordManager, PasswordManager}; use config::AppConfig; +use crypto::{FilePasswordManager, PasswordManager}; use zeroize::Zeroizing; pub async fn get_current_password(config: &AppConfig) -> Result> { diff --git a/packages/ciphernode/enclave_node/src/start.rs b/packages/ciphernode/enclave_core/src/start.rs similarity index 98% rename from packages/ciphernode/enclave_node/src/start.rs rename to packages/ciphernode/enclave_core/src/start.rs index 4a6ad045..cc94db08 100644 --- a/packages/ciphernode/enclave_node/src/start.rs +++ b/packages/ciphernode/enclave_core/src/start.rs @@ -1,11 +1,11 @@ use actix::{Actor, Addr}; use alloy::primitives::Address; use anyhow::Result; -use cipher::Cipher; use config::AppConfig; +use crypto::Cipher; use data::RepositoriesFactory; use e3_request::E3Router; -use enclave_core::{get_tag, EventBus}; +use events::{get_tag, EventBus}; use evm::{ helpers::ProviderConfig, CiphernodeRegistryReaderRepositoryFactory, CiphernodeRegistrySol, EnclaveSolReader, EnclaveSolReaderRepositoryFactory, diff --git a/packages/ciphernode/enclave_node/src/wallet_set.rs b/packages/ciphernode/enclave_core/src/wallet_set.rs similarity index 94% rename from packages/ciphernode/enclave_node/src/wallet_set.rs rename to packages/ciphernode/enclave_core/src/wallet_set.rs index 81003469..97d7ce60 100644 --- a/packages/ciphernode/enclave_node/src/wallet_set.rs +++ b/packages/ciphernode/enclave_core/src/wallet_set.rs @@ -1,9 +1,9 @@ use actix::Actor; use alloy::{hex::FromHex, primitives::FixedBytes, signers::local::PrivateKeySigner}; use anyhow::{anyhow, Result}; -use cipher::Cipher; use config::AppConfig; -use enclave_core::{EventBus, GetErrors}; +use crypto::Cipher; +use events::{EventBus, GetErrors}; use evm::EthPrivateKeyRepositoryFactory; use crate::datastore::get_repositories; diff --git a/packages/ciphernode/core/Cargo.toml b/packages/ciphernode/events/Cargo.toml similarity index 97% rename from packages/ciphernode/core/Cargo.toml rename to packages/ciphernode/events/Cargo.toml index b77f4045..f257b27f 100644 --- a/packages/ciphernode/core/Cargo.toml +++ b/packages/ciphernode/events/Cargo.toml @@ -1,6 +1,6 @@ [package] # we have to name this other than core for disambiguation -name = "enclave-core" +name = "events" version = "0.1.0" edition = "2021" description = ": coordinates the encryption and decryption of enclave computations" diff --git a/packages/ciphernode/core/src/eventbus.rs b/packages/ciphernode/events/src/eventbus.rs similarity index 100% rename from packages/ciphernode/core/src/eventbus.rs rename to packages/ciphernode/events/src/eventbus.rs diff --git a/packages/ciphernode/core/src/events.rs b/packages/ciphernode/events/src/events.rs similarity index 100% rename from packages/ciphernode/core/src/events.rs rename to packages/ciphernode/events/src/events.rs diff --git a/packages/ciphernode/core/src/lib.rs b/packages/ciphernode/events/src/lib.rs similarity index 57% rename from packages/ciphernode/core/src/lib.rs rename to packages/ciphernode/events/src/lib.rs index 0084e902..a4432629 100644 --- a/packages/ciphernode/core/src/lib.rs +++ b/packages/ciphernode/events/src/lib.rs @@ -1,7 +1,3 @@ -#![crate_name = "enclave_core"] -#![crate_type = "lib"] -// #![warn(missing_docs, unused_imports)] - mod eventbus; mod events; mod ordered_set; diff --git a/packages/ciphernode/core/src/ordered_set.rs b/packages/ciphernode/events/src/ordered_set.rs similarity index 100% rename from packages/ciphernode/core/src/ordered_set.rs rename to packages/ciphernode/events/src/ordered_set.rs diff --git a/packages/ciphernode/core/src/tag.rs b/packages/ciphernode/events/src/tag.rs similarity index 100% rename from packages/ciphernode/core/src/tag.rs rename to packages/ciphernode/events/src/tag.rs diff --git a/packages/ciphernode/evm/Cargo.toml b/packages/ciphernode/evm/Cargo.toml index 0a50c22a..826a134e 100644 --- a/packages/ciphernode/evm/Cargo.toml +++ b/packages/ciphernode/evm/Cargo.toml @@ -10,10 +10,10 @@ alloy-primitives = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } base64 = { workspace = true } -cipher = { path = "../cipher" } +crypto = { workspace = true } config = { path = "../config" } data = { path = "../data" } -enclave-core = { path = "../core" } +events = { workspace = true } futures-util = { workspace = true } sortition = { path = "../sortition" } serde = { workspace = true } @@ -23,5 +23,5 @@ url = { workspace = true } zeroize = { workspace = true } [dev-dependencies] -enclave_node = { path = "../enclave_node" } +enclave_core = { workspace = true } diff --git a/packages/ciphernode/evm/src/ciphernode_registry_sol.rs b/packages/ciphernode/evm/src/ciphernode_registry_sol.rs index d4846a50..14821333 100644 --- a/packages/ciphernode/evm/src/ciphernode_registry_sol.rs +++ b/packages/ciphernode/evm/src/ciphernode_registry_sol.rs @@ -12,7 +12,7 @@ use alloy::{ }; use anyhow::Result; use data::Repository; -use enclave_core::{EnclaveEvent, EventBus}; +use events::{EnclaveEvent, EventBus}; use tracing::{error, info, trace}; sol!( @@ -22,9 +22,9 @@ sol!( "../../evm/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json" ); -impl From for enclave_core::CiphernodeAdded { +impl From for events::CiphernodeAdded { fn from(value: ICiphernodeRegistry::CiphernodeAdded) -> Self { - enclave_core::CiphernodeAdded { + events::CiphernodeAdded { address: value.node.to_string(), // TODO: limit index and numNodes to uint32 at the solidity level index: value @@ -41,14 +41,14 @@ impl From for enclave_core::CiphernodeAdde impl From for EnclaveEvent { fn from(value: ICiphernodeRegistry::CiphernodeAdded) -> Self { - let payload: enclave_core::CiphernodeAdded = value.into(); + let payload: events::CiphernodeAdded = value.into(); EnclaveEvent::from(payload) } } -impl From for enclave_core::CiphernodeRemoved { +impl From for events::CiphernodeRemoved { fn from(value: ICiphernodeRegistry::CiphernodeRemoved) -> Self { - enclave_core::CiphernodeRemoved { + events::CiphernodeRemoved { address: value.node.to_string(), index: value .index @@ -64,7 +64,7 @@ impl From for enclave_core::CiphernodeRe impl From for EnclaveEvent { fn from(value: ICiphernodeRegistry::CiphernodeRemoved) -> Self { - let payload: enclave_core::CiphernodeRemoved = value.into(); + let payload: events::CiphernodeRemoved = value.into(); EnclaveEvent::from(payload) } } diff --git a/packages/ciphernode/evm/src/enclave_sol.rs b/packages/ciphernode/evm/src/enclave_sol.rs index bcdcab98..03db5817 100644 --- a/packages/ciphernode/evm/src/enclave_sol.rs +++ b/packages/ciphernode/evm/src/enclave_sol.rs @@ -8,7 +8,7 @@ use actix::Addr; use alloy::transports::BoxTransport; use anyhow::Result; use data::Repository; -use enclave_core::EventBus; +use events::EventBus; pub struct EnclaveSol; impl EnclaveSol { diff --git a/packages/ciphernode/evm/src/enclave_sol_reader.rs b/packages/ciphernode/evm/src/enclave_sol_reader.rs index c71c39de..63f42013 100644 --- a/packages/ciphernode/evm/src/enclave_sol_reader.rs +++ b/packages/ciphernode/evm/src/enclave_sol_reader.rs @@ -7,7 +7,7 @@ use alloy::transports::BoxTransport; use alloy::{sol, sol_types::SolEvent}; use anyhow::Result; use data::Repository; -use enclave_core::{EnclaveEvent, EventBus}; +use events::{EnclaveEvent, EventBus}; use tracing::{error, info, trace}; sol!( @@ -18,9 +18,9 @@ sol!( struct E3RequestedWithChainId(pub IEnclave::E3Requested, pub u64); -impl From for enclave_core::E3Requested { +impl From for events::E3Requested { fn from(value: E3RequestedWithChainId) -> Self { - enclave_core::E3Requested { + events::E3Requested { params: value.0.e3.e3ProgramParams.to_vec(), threshold_m: value.0.e3.threshold[0] as usize, seed: value.0.e3.seed.into(), @@ -32,14 +32,14 @@ impl From for enclave_core::E3Requested { impl From for EnclaveEvent { fn from(value: E3RequestedWithChainId) -> Self { - let payload: enclave_core::E3Requested = value.into(); + let payload: events::E3Requested = value.into(); EnclaveEvent::from(payload) } } -impl From for enclave_core::CiphertextOutputPublished { +impl From for events::CiphertextOutputPublished { fn from(value: IEnclave::CiphertextOutputPublished) -> Self { - enclave_core::CiphertextOutputPublished { + events::CiphertextOutputPublished { e3_id: value.e3Id.to_string().into(), ciphertext_output: value.ciphertextOutput.to_vec(), } @@ -48,7 +48,7 @@ impl From for enclave_core::CiphertextOutpu impl From for EnclaveEvent { fn from(value: IEnclave::CiphertextOutputPublished) -> Self { - let payload: enclave_core::CiphertextOutputPublished = value.into(); + let payload: events::CiphertextOutputPublished = value.into(); EnclaveEvent::from(payload) } } diff --git a/packages/ciphernode/evm/src/enclave_sol_writer.rs b/packages/ciphernode/evm/src/enclave_sol_writer.rs index 36b519b0..8eb3105c 100644 --- a/packages/ciphernode/evm/src/enclave_sol_writer.rs +++ b/packages/ciphernode/evm/src/enclave_sol_writer.rs @@ -7,9 +7,9 @@ use alloy::{ rpc::types::TransactionReceipt, }; use anyhow::Result; -use enclave_core::Shutdown; -use enclave_core::{BusError, E3id, EnclaveErrorType, PlaintextAggregated, Subscribe}; -use enclave_core::{EnclaveEvent, EventBus}; +use events::Shutdown; +use events::{BusError, E3id, EnclaveErrorType, PlaintextAggregated, Subscribe}; +use events::{EnclaveEvent, EventBus}; use tracing::info; sol!( diff --git a/packages/ciphernode/evm/src/event_reader.rs b/packages/ciphernode/evm/src/event_reader.rs index 79c90029..b279e126 100644 --- a/packages/ciphernode/evm/src/event_reader.rs +++ b/packages/ciphernode/evm/src/event_reader.rs @@ -9,9 +9,7 @@ use alloy::rpc::types::Filter; use alloy::transports::{BoxTransport, Transport}; use anyhow::{anyhow, Result}; use data::{AutoPersist, Persistable, Repository}; -use enclave_core::{ - get_tag, BusError, EnclaveErrorType, EnclaveEvent, EventBus, EventId, Subscribe, -}; +use events::{get_tag, BusError, EnclaveErrorType, EnclaveEvent, EventBus, EventId, Subscribe}; use futures_util::stream::StreamExt; use std::collections::HashSet; use tokio::select; diff --git a/packages/ciphernode/evm/src/helpers.rs b/packages/ciphernode/evm/src/helpers.rs index 08a5921f..10a36b5e 100644 --- a/packages/ciphernode/evm/src/helpers.rs +++ b/packages/ciphernode/evm/src/helpers.rs @@ -24,8 +24,8 @@ use alloy::{ }; use anyhow::{bail, Context, Result}; use base64::{engine::general_purpose::STANDARD, Engine}; -use cipher::Cipher; use config::{RpcAuth, RPC}; +use crypto::Cipher; use data::Repository; use std::{env, marker::PhantomData, sync::Arc}; use zeroize::Zeroizing; diff --git a/packages/ciphernode/evm/src/registry_filter_sol.rs b/packages/ciphernode/evm/src/registry_filter_sol.rs index b4b8f770..f9a4731f 100644 --- a/packages/ciphernode/evm/src/registry_filter_sol.rs +++ b/packages/ciphernode/evm/src/registry_filter_sol.rs @@ -6,7 +6,7 @@ use alloy::{ sol, }; use anyhow::Result; -use enclave_core::{ +use events::{ BusError, E3id, EnclaveErrorType, EnclaveEvent, EventBus, OrderedSet, PublicKeyAggregated, Shutdown, Subscribe, }; diff --git a/packages/ciphernode/evm/tests/evm_reader.rs b/packages/ciphernode/evm/tests/evm_reader.rs index 9505bd2e..376d859d 100644 --- a/packages/ciphernode/evm/tests/evm_reader.rs +++ b/packages/ciphernode/evm/tests/evm_reader.rs @@ -8,8 +8,8 @@ use alloy::{ }; use anyhow::Result; use data::Repository; -use enclave_core::{EnclaveEvent, EventBus, GetHistory, Shutdown, TestEvent}; -use enclave_node::datastore::get_in_mem_store; +use enclave_core::datastore::get_in_mem_store; +use events::{EnclaveEvent, EventBus, GetHistory, Shutdown, TestEvent}; use evm::{helpers::WithChainId, EvmEventReader}; use std::time::Duration; use tokio::time::sleep; diff --git a/packages/ciphernode/fhe/Cargo.toml b/packages/ciphernode/fhe/Cargo.toml index 6909c463..1a6a3f72 100644 --- a/packages/ciphernode/fhe/Cargo.toml +++ b/packages/ciphernode/fhe/Cargo.toml @@ -10,7 +10,7 @@ async-trait = { workspace = true } bincode = { workspace = true } config = { workspace = true } data = { path = "../data" } -enclave-core = { path = "../core" } +events = { workspace = true } fhe-traits = { workspace = true } fhe-util = { workspace = true } fhe_rs = { workspace = true } diff --git a/packages/ciphernode/fhe/src/feature.rs b/packages/ciphernode/fhe/src/feature.rs index 0dbb017f..bae9ef3a 100644 --- a/packages/ciphernode/fhe/src/feature.rs +++ b/packages/ciphernode/fhe/src/feature.rs @@ -4,7 +4,7 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use data::{FromSnapshotWithParams, RepositoriesFactory, Snapshot}; use e3_request::{E3Context, E3ContextSnapshot, E3Feature, TypedKey}; -use enclave_core::{BusError, E3Requested, EnclaveErrorType, EnclaveEvent, EventBus}; +use events::{BusError, E3Requested, EnclaveErrorType, EnclaveEvent, EventBus}; use std::sync::Arc; pub const FHE_KEY: TypedKey> = TypedKey::new("fhe"); diff --git a/packages/ciphernode/fhe/src/fhe.rs b/packages/ciphernode/fhe/src/fhe.rs index 84333de4..8d72575b 100644 --- a/packages/ciphernode/fhe/src/fhe.rs +++ b/packages/ciphernode/fhe/src/fhe.rs @@ -2,7 +2,7 @@ use super::set_up_crp; use anyhow::*; use async_trait::async_trait; use data::{FromSnapshotWithParams, Snapshot}; -use enclave_core::{OrderedSet, Seed}; +use events::{OrderedSet, Seed}; use fhe_rs::{ bfv::{ BfvParameters, BfvParametersBuilder, Ciphertext, Encoding, Plaintext, PublicKey, SecretKey, diff --git a/packages/ciphernode/fhe/src/repo.rs b/packages/ciphernode/fhe/src/repo.rs index 7e035b73..b7ef37e4 100644 --- a/packages/ciphernode/fhe/src/repo.rs +++ b/packages/ciphernode/fhe/src/repo.rs @@ -1,6 +1,6 @@ use config::StoreKeys; use data::{Repositories, Repository}; -use enclave_core::E3id; +use events::E3id; use crate::FheSnapshot; diff --git a/packages/ciphernode/keyshare/Cargo.toml b/packages/ciphernode/keyshare/Cargo.toml index d6cb60b1..f7551f98 100644 --- a/packages/ciphernode/keyshare/Cargo.toml +++ b/packages/ciphernode/keyshare/Cargo.toml @@ -9,8 +9,8 @@ anyhow = { workspace = true } async-trait = { workspace = true } config = { workspace = true } data = { path = "../data" } -cipher = { path = "../cipher" } -enclave-core = { path = "../core" } +crypto = { workspace = true } +events = { workspace = true } fhe = { path = "../fhe" } e3_request = { workspace = true } serde = { workspace = true } diff --git a/packages/ciphernode/keyshare/src/feature.rs b/packages/ciphernode/keyshare/src/feature.rs index dc4bc098..0fbc5a10 100644 --- a/packages/ciphernode/keyshare/src/feature.rs +++ b/packages/ciphernode/keyshare/src/feature.rs @@ -2,10 +2,10 @@ use crate::{Keyshare, KeyshareParams, KeyshareRepositoryFactory}; use actix::{Actor, Addr}; use anyhow::{anyhow, Result}; use async_trait::async_trait; -use cipher::Cipher; +use crypto::Cipher; use data::{AutoPersist, RepositoriesFactory}; use e3_request::{E3Context, E3ContextSnapshot, E3Feature}; -use enclave_core::{BusError, EnclaveErrorType, EnclaveEvent, EventBus}; +use events::{BusError, EnclaveErrorType, EnclaveEvent, EventBus}; use fhe::FHE_KEY; use std::sync::Arc; diff --git a/packages/ciphernode/keyshare/src/keyshare.rs b/packages/ciphernode/keyshare/src/keyshare.rs index dab8a0c8..35acebdd 100644 --- a/packages/ciphernode/keyshare/src/keyshare.rs +++ b/packages/ciphernode/keyshare/src/keyshare.rs @@ -1,8 +1,8 @@ use actix::prelude::*; use anyhow::{anyhow, Result}; -use cipher::Cipher; +use crypto::Cipher; use data::Persistable; -use enclave_core::{ +use events::{ BusError, CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated, Die, E3RequestComplete, EnclaveErrorType, EnclaveEvent, EventBus, FromError, KeyshareCreated, }; diff --git a/packages/ciphernode/keyshare/src/repo.rs b/packages/ciphernode/keyshare/src/repo.rs index 772c5335..504c2fa7 100644 --- a/packages/ciphernode/keyshare/src/repo.rs +++ b/packages/ciphernode/keyshare/src/repo.rs @@ -1,6 +1,6 @@ use config::StoreKeys; use data::{Repositories, Repository}; -use enclave_core::E3id; +use events::E3id; pub trait KeyshareRepositoryFactory { fn keyshare(&self, e3_id: &E3id) -> Repository>; diff --git a/packages/ciphernode/logger/Cargo.toml b/packages/ciphernode/logger/Cargo.toml index 9f87116f..e13453a8 100644 --- a/packages/ciphernode/logger/Cargo.toml +++ b/packages/ciphernode/logger/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -enclave-core = { path = "../core" } +events = { workspace = true } actix = { workspace = true } base64 = { workspace = true } tracing = { workspace = true } diff --git a/packages/ciphernode/logger/src/logger.rs b/packages/ciphernode/logger/src/logger.rs index 8fe3a3cb..0586a0c4 100644 --- a/packages/ciphernode/logger/src/logger.rs +++ b/packages/ciphernode/logger/src/logger.rs @@ -1,5 +1,5 @@ use actix::{Actor, Addr, Context, Handler}; -use enclave_core::{EnclaveEvent, EventBus, Subscribe}; +use events::{EnclaveEvent, EventBus, Subscribe}; use tracing::{error, info}; pub struct SimpleLogger { diff --git a/packages/ciphernode/net/Cargo.toml b/packages/ciphernode/net/Cargo.toml index dba2c1d5..d2794b3b 100644 --- a/packages/ciphernode/net/Cargo.toml +++ b/packages/ciphernode/net/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/gnosisguild/enclave/packages/ciphernode" async-std = { workspace = true, features = ["attributes"] } async-trait = { workspace = true } futures = { workspace = true } -cipher = { workspace = true } +crypto = { workspace = true } config = { workspace = true } data = { workspace = true } libp2p = { workspace = true, features = [ @@ -28,7 +28,7 @@ libp2p = { workspace = true, features = [ tokio = { workspace = true, features = ["full"] } tracing = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter"] } -enclave-core = { path = "../core" } +events = { workspace = true } anyhow = { workspace = true } actix = { workspace = true } zeroize = { workspace = true } diff --git a/packages/ciphernode/net/src/network_manager.rs b/packages/ciphernode/net/src/network_manager.rs index 7c26f1e1..5bf23cbb 100644 --- a/packages/ciphernode/net/src/network_manager.rs +++ b/packages/ciphernode/net/src/network_manager.rs @@ -6,9 +6,9 @@ use crate::NetworkPeer; /// This Actor should be responsible for use actix::prelude::*; use anyhow::{bail, Result}; -use cipher::Cipher; +use crypto::Cipher; use data::Repository; -use enclave_core::{EnclaveEvent, EventBus, EventId, Subscribe}; +use events::{EnclaveEvent, EventBus, EventId, Subscribe}; use libp2p::identity::ed25519; use std::collections::HashSet; use std::sync::Arc; diff --git a/packages/ciphernode/sortition/Cargo.toml b/packages/ciphernode/sortition/Cargo.toml index bed3b0a7..3e72f855 100644 --- a/packages/ciphernode/sortition/Cargo.toml +++ b/packages/ciphernode/sortition/Cargo.toml @@ -15,7 +15,7 @@ anyhow = { workspace = true } async-trait = { workspace = true } config = { workspace = true } data = { path = "../data" } -enclave-core = { path = "../core" } +events = { workspace = true } num = { workspace = true } rand = { workspace = true } serde = { workspace = true } diff --git a/packages/ciphernode/sortition/src/ciphernode_selector.rs b/packages/ciphernode/sortition/src/ciphernode_selector.rs index 72ca1d50..97b71e2b 100644 --- a/packages/ciphernode/sortition/src/ciphernode_selector.rs +++ b/packages/ciphernode/sortition/src/ciphernode_selector.rs @@ -2,7 +2,7 @@ use crate::{GetHasNode, Sortition}; /// CiphernodeSelector is an actor that determines if a ciphernode is part of a committee and if so /// forwards a CiphernodeSelected event to the event bus use actix::prelude::*; -use enclave_core::{CiphernodeSelected, E3Requested, EnclaveEvent, EventBus, Shutdown, Subscribe}; +use events::{CiphernodeSelected, E3Requested, EnclaveEvent, EventBus, Shutdown, Subscribe}; use tracing::info; pub struct CiphernodeSelector { diff --git a/packages/ciphernode/sortition/src/sortition.rs b/packages/ciphernode/sortition/src/sortition.rs index 07ff6704..c0b0f569 100644 --- a/packages/ciphernode/sortition/src/sortition.rs +++ b/packages/ciphernode/sortition/src/sortition.rs @@ -3,7 +3,7 @@ use actix::prelude::*; use alloy::primitives::Address; use anyhow::{anyhow, Result}; use data::{AutoPersist, Persistable, Repository}; -use enclave_core::{ +use events::{ get_tag, BusError, CiphernodeAdded, CiphernodeRemoved, EnclaveErrorType, EnclaveEvent, EventBus, Seed, Subscribe, }; diff --git a/packages/ciphernode/test_helpers/Cargo.toml b/packages/ciphernode/test_helpers/Cargo.toml index e91ac8dd..6ae81a66 100644 --- a/packages/ciphernode/test_helpers/Cargo.toml +++ b/packages/ciphernode/test_helpers/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] actix = { workspace = true } -enclave-core = { path = "../core" } +events = { workspace = true } fhe = { path = "../fhe" } bincode = { workspace = true } clap = { workspace = true, features = ["derive"] } diff --git a/packages/ciphernode/test_helpers/src/plaintext_writer.rs b/packages/ciphernode/test_helpers/src/plaintext_writer.rs index 851d2f1f..d5d14209 100644 --- a/packages/ciphernode/test_helpers/src/plaintext_writer.rs +++ b/packages/ciphernode/test_helpers/src/plaintext_writer.rs @@ -1,6 +1,6 @@ use super::write_file_with_dirs; use actix::{Actor, Addr, Context, Handler}; -use enclave_core::{EnclaveEvent, EventBus, Subscribe}; +use events::{EnclaveEvent, EventBus, Subscribe}; use tracing::info; pub struct PlaintextWriter { diff --git a/packages/ciphernode/test_helpers/src/public_key_writer.rs b/packages/ciphernode/test_helpers/src/public_key_writer.rs index 7310ee2e..cda81557 100644 --- a/packages/ciphernode/test_helpers/src/public_key_writer.rs +++ b/packages/ciphernode/test_helpers/src/public_key_writer.rs @@ -1,6 +1,6 @@ use super::write_file_with_dirs; use actix::{Actor, Addr, Context, Handler}; -use enclave_core::{EnclaveEvent, EventBus, Subscribe}; +use events::{EnclaveEvent, EventBus, Subscribe}; use tracing::info; pub struct PublicKeyWriter { diff --git a/packages/ciphernode/tests/Cargo.toml b/packages/ciphernode/tests/Cargo.toml index 9bf651a7..ac203690 100644 --- a/packages/ciphernode/tests/Cargo.toml +++ b/packages/ciphernode/tests/Cargo.toml @@ -7,11 +7,11 @@ edition = "2021" net = { path = "../net" } bfv = { path = "../bfv" } sortition = { path = "../sortition" } -enclave-core = { path = "../core" } +events = { workspace = true } evm = { path = "../evm" } logger = { path = "../logger" } fhe = { path = "../fhe" } -cipher = { path = "../cipher" } +crypto = { workspace = true } data = { path = "../data" } keyshare = { path = "../keyshare" } aggregator = { path = "../aggregator" } diff --git a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs index cdc8be5c..f81286d9 100644 --- a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs +++ b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs @@ -1,9 +1,9 @@ use aggregator::{PlaintextAggregatorFeature, PublicKeyAggregatorFeature}; -use cipher::Cipher; +use crypto::Cipher; use data::RepositoriesFactory; use data::{DataStore, InMemStore}; use e3_request::E3Router; -use enclave_core::{ +use events::{ CiphernodeAdded, CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated, E3RequestComplete, E3Requested, E3id, EnclaveEvent, EventBus, GetErrors, GetHistory, KeyshareCreated, OrderedSet, PlaintextAggregated, PublicKeyAggregated, ResetHistory, Seed, From 5ffb688c30fb85592796f4bea31c157a598e250c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Mon, 30 Dec 2024 16:00:50 +1100 Subject: [PATCH 08/15] Remove redundant packages (#227) --- packages/ciphernode/Cargo.lock | 13 -- packages/ciphernode/Cargo.toml | 1 - packages/ciphernode/bfv/Cargo.toml | 15 --- packages/ciphernode/bfv/src/lib.rs | 84 ------------- packages/ciphernode/bfv/src/util.rs | 129 -------------------- packages/ciphernode/enclave_core/Cargo.toml | 1 - packages/ciphernode/tests/Cargo.toml | 1 - packages/ciphernode/zkp/Cargo.toml | 14 --- packages/ciphernode/zkp/src/main.rs | 3 - 9 files changed, 261 deletions(-) delete mode 100644 packages/ciphernode/bfv/Cargo.toml delete mode 100644 packages/ciphernode/bfv/src/lib.rs delete mode 100644 packages/ciphernode/bfv/src/util.rs delete mode 100644 packages/ciphernode/zkp/Cargo.toml delete mode 100644 packages/ciphernode/zkp/src/main.rs diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 606d89d1..0798534f 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -1431,17 +1431,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bfv" -version = "0.1.0" -dependencies = [ - "async-std", - "fhe 0.1.0-beta.7", - "fhe-traits", - "fhe-util", - "rand", -] - [[package]] name = "bimap" version = "0.6.3" @@ -2218,7 +2207,6 @@ dependencies = [ "alloy", "alloy-primitives 0.6.4", "anyhow", - "bfv", "bincode", "clap", "config", @@ -6052,7 +6040,6 @@ dependencies = [ "anyhow", "async-std", "base64", - "bfv", "bincode", "clap", "crypto", diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index d9569d48..67f7dce7 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -4,7 +4,6 @@ members = [ "enclave", "enclave_core", "net", - "bfv", "data", "evm", "fhe", diff --git a/packages/ciphernode/bfv/Cargo.toml b/packages/ciphernode/bfv/Cargo.toml deleted file mode 100644 index 2426bd01..00000000 --- a/packages/ciphernode/bfv/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "bfv" -version = "0.1.0" -edition = "2021" -description = ": coordinates the encryption and decryption of enclave computations" -repository = "https://github.com/gnosisguild/enclave/packages/ciphernode" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -async-std = "1.12.0" -fhe = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } -fhe-traits = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } -fhe-util = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } -rand = "0.8.5" diff --git a/packages/ciphernode/bfv/src/lib.rs b/packages/ciphernode/bfv/src/lib.rs deleted file mode 100644 index b3c76b19..00000000 --- a/packages/ciphernode/bfv/src/lib.rs +++ /dev/null @@ -1,84 +0,0 @@ -#![crate_name = "bfv"] -#![crate_type = "lib"] -#![warn(missing_docs, unused_imports)] - -mod util; - -use fhe::{ - bfv::{BfvParameters, BfvParametersBuilder, Ciphertext, Encoding, Plaintext, SecretKey}, - mbfv::{AggregateIter, CommonRandomPoly, DecryptionShare, PublicKeyShare}, -}; -use fhe_traits::{Deserialize, DeserializeParametrized, FheDecoder, Serialize as FheSerialize}; -use rand::{rngs::OsRng, thread_rng, Rng}; -use std::sync::Arc; -use util::timeit::timeit; - -pub struct EnclaveBFV { - pub pk_share: PublicKeyShare, - sk_share: SecretKey, - pub params: Arc, - pub crp: CommonRandomPoly, -} - -impl EnclaveBFV { - pub fn new(degree: usize, plaintext_modulus: u64, moduli: Vec) -> Self { - // let degree = 4096; - // let plaintext_modulus: u64 = 4096; - // let moduli = vec![0xffffee001, 0xffffc4001, 0x1ffffe0001]; - - // Generate the BFV parameters structure. - let params = timeit!( - "Parameters generation", - BfvParametersBuilder::new() - .set_degree(degree) - .set_plaintext_modulus(plaintext_modulus) - .set_moduli(&moduli) - .build_arc() - .unwrap() - ); - - let crp = CommonRandomPoly::new(¶ms, &mut thread_rng()).unwrap(); - //TODO: save encrypted sk_share to disk? - let sk_share = SecretKey::random(¶ms, &mut OsRng); - let pk_share = PublicKeyShare::new(&sk_share, crp.clone(), &mut thread_rng()).unwrap(); - - Self { - pk_share, - sk_share, - params, - crp, - } - } - - pub fn serialize_pk(&mut self) -> Vec { - self.pk_share.to_bytes() - } - - pub fn deserialize_pk( - &mut self, - bytes: Vec, - par_bytes: Vec, - crp_bytes: Vec, - ) -> PublicKeyShare { - let params = Arc::new(BfvParameters::try_deserialize(&par_bytes).unwrap()); - let crp = CommonRandomPoly::deserialize(&crp_bytes, ¶ms).unwrap(); - PublicKeyShare::deserialize(&bytes, ¶ms, crp.clone()).unwrap() - } - - pub fn serialize_crp(&mut self) -> Vec { - self.crp.to_bytes() - } - - pub fn deserialize_crp(&mut self, bytes: Vec, par_bytes: Vec) -> CommonRandomPoly { - let params = Arc::new(BfvParameters::try_deserialize(&par_bytes).unwrap()); - CommonRandomPoly::deserialize(&bytes, ¶ms).unwrap() - } - - pub fn serialize_params(&mut self) -> Vec { - self.params.to_bytes() - } - - pub fn deserialize_params(&mut self, par_bytes: Vec) -> Arc { - Arc::new(BfvParameters::try_deserialize(&par_bytes).unwrap()) - } -} diff --git a/packages/ciphernode/bfv/src/util.rs b/packages/ciphernode/bfv/src/util.rs deleted file mode 100644 index 2b5c2e48..00000000 --- a/packages/ciphernode/bfv/src/util.rs +++ /dev/null @@ -1,129 +0,0 @@ -//! Utility functions - -use fhe::bfv; -use fhe_traits::FheEncoder; -use fhe_util::transcode_from_bytes; -use std::{cmp::min, fmt, sync::Arc, time::Duration}; - -/// Macros to time code and display a human-readable duration. -pub mod timeit { - #[allow(unused_macros)] - macro_rules! timeit_n { - ($name:expr, $loops:expr, $code:expr) => {{ - use util::DisplayDuration; - let start = std::time::Instant::now(); - let r = $code; - for _ in 1..$loops { - let _ = $code; - } - println!( - "⏱ {}: {}", - $name, - DisplayDuration(start.elapsed() / $loops) - ); - r - }}; - } - - #[allow(unused_macros)] - macro_rules! timeit { - ($name:expr, $code:expr) => {{ - use util::DisplayDuration; - let start = std::time::Instant::now(); - let r = $code; - println!("⏱ {}: {}", $name, DisplayDuration(start.elapsed())); - r - }}; - } - - #[allow(unused_imports)] - pub(crate) use timeit; - #[allow(unused_imports)] - pub(crate) use timeit_n; -} - -/// Utility struct for displaying human-readable duration of the form "10.5 ms", -/// "350 μs", or "27 ns". -pub struct DisplayDuration(pub Duration); - -impl fmt::Display for DisplayDuration { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let duration_ns = self.0.as_nanos(); - if duration_ns < 1_000_u128 { - write!(f, "{duration_ns} ns") - } else if duration_ns < 1_000_000_u128 { - write!(f, "{} μs", (duration_ns + 500) / 1_000) - } else { - let duration_ms_times_10 = (duration_ns + 50_000) / (100_000); - write!(f, "{} ms", (duration_ms_times_10 as f64) / 10.0) - } - } -} - -// Utility functions for Private Information Retrieval. - -/// Generate a database of elements of the form [i || 0...0] where i is the 4B -/// little endian encoding of the index. When the element size is less than 4B, -/// the encoding is truncated. -#[allow(dead_code)] -pub fn generate_database(database_size: usize, elements_size: usize) -> Vec> { - assert!(database_size > 0 && elements_size > 0); - let mut database = vec![vec![0u8; elements_size]; database_size]; - for (i, element) in database.iter_mut().enumerate() { - element[..min(4, elements_size)] - .copy_from_slice(&(i as u32).to_le_bytes()[..min(4, elements_size)]); - } - database -} - -#[allow(dead_code)] -pub fn number_elements_per_plaintext( - degree: usize, - plaintext_nbits: usize, - elements_size: usize, -) -> usize { - (plaintext_nbits * degree) / (elements_size * 8) -} - -#[allow(dead_code)] -pub fn encode_database( - database: &Vec>, - par: Arc, - level: usize, -) -> (Vec, (usize, usize)) { - assert!(!database.is_empty()); - - let elements_size = database[0].len(); - let plaintext_nbits = par.plaintext().ilog2() as usize; - let number_elements_per_plaintext = - number_elements_per_plaintext(par.degree(), plaintext_nbits, elements_size); - let number_rows = - (database.len() + number_elements_per_plaintext - 1) / number_elements_per_plaintext; - println!("number_rows = {number_rows}"); - println!("number_elements_per_plaintext = {number_elements_per_plaintext}"); - let dimension_1 = (number_rows as f64).sqrt().ceil() as usize; - let dimension_2 = (number_rows + dimension_1 - 1) / dimension_1; - println!("dimensions = {dimension_1} {dimension_2}"); - println!("dimension = {}", dimension_1 * dimension_2); - let mut preprocessed_database = - vec![ - bfv::Plaintext::zero(bfv::Encoding::poly_at_level(level), &par).unwrap(); - dimension_1 * dimension_2 - ]; - (0..number_rows).for_each(|i| { - let mut serialized_plaintext = vec![0u8; number_elements_per_plaintext * elements_size]; - for j in 0..number_elements_per_plaintext { - if let Some(pt) = database.get(j + i * number_elements_per_plaintext) { - serialized_plaintext[j * elements_size..(j + 1) * elements_size].copy_from_slice(pt) - } - } - let pt_values = transcode_from_bytes(&serialized_plaintext, plaintext_nbits); - preprocessed_database[i] = - bfv::Plaintext::try_encode(&pt_values, bfv::Encoding::poly_at_level(level), &par) - .unwrap(); - }); - (preprocessed_database, (dimension_1, dimension_2)) -} - -#[allow(dead_code)] -fn main() {} diff --git a/packages/ciphernode/enclave_core/Cargo.toml b/packages/ciphernode/enclave_core/Cargo.toml index 8e972443..338fba74 100644 --- a/packages/ciphernode/enclave_core/Cargo.toml +++ b/packages/ciphernode/enclave_core/Cargo.toml @@ -12,7 +12,6 @@ aggregator = { path = "../aggregator" } alloy = { workspace = true } alloy-primitives = { workspace = true } anyhow = { workspace = true } -bfv = { path = "../bfv" } bincode = { workspace = true } config = { path = "../config" } clap = { workspace = true } diff --git a/packages/ciphernode/tests/Cargo.toml b/packages/ciphernode/tests/Cargo.toml index ac203690..3e883a35 100644 --- a/packages/ciphernode/tests/Cargo.toml +++ b/packages/ciphernode/tests/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] net = { path = "../net" } -bfv = { path = "../bfv" } sortition = { path = "../sortition" } events = { workspace = true } evm = { path = "../evm" } diff --git a/packages/ciphernode/zkp/Cargo.toml b/packages/ciphernode/zkp/Cargo.toml deleted file mode 100644 index 06f73543..00000000 --- a/packages/ciphernode/zkp/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "zkp" -version = "0.1.0" -edition = "2021" -description = ": coordinates the encryption and decryption of enclave computations" -repository = "https://github.com/gnosisguild/enclave/packages/ciphernode" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -async-std = "1.12.0" -fhe = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } -fhe-traits = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } -fhe-util = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } diff --git a/packages/ciphernode/zkp/src/main.rs b/packages/ciphernode/zkp/src/main.rs deleted file mode 100644 index a0d9916c..00000000 --- a/packages/ciphernode/zkp/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, cipher world!"); -} From 7e2b08cf8c373c75238e7b7441e0e183dbac547f Mon Sep 17 00:00:00 2001 From: ryardley Date: Mon, 30 Dec 2024 19:58:54 +1100 Subject: [PATCH 09/15] Explicitly COPY every Cargo.toml --- packages/ciphernode/Dockerfile | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/ciphernode/Dockerfile b/packages/ciphernode/Dockerfile index cb100076..b8132444 100644 --- a/packages/ciphernode/Dockerfile +++ b/packages/ciphernode/Dockerfile @@ -18,9 +18,23 @@ COPY --from=evm-builder /build/packages/evm/artifacts ../evm/artifacts COPY --from=evm-builder /build/packages/evm/deployments ../evm/deployments # Copy workpace Cargo.toml +COPY ./packages/ciphernode/rust-toolchain.toml . COPY ./packages/ciphernode/Cargo.toml ./Cargo.toml COPY ./packages/ciphernode/Cargo.lock ./Cargo.lock -COPY ./packages/ciphernode/*/Cargo.toml ./ +COPY ./packages/ciphernode/aggregator/Cargo.toml ./aggregator/Cargo.toml +COPY ./packages/ciphernode/config/Cargo.toml ./config/Cargo.toml +COPY ./packages/ciphernode/crypto/Cargo.toml ./crypto/Cargo.toml +COPY ./packages/ciphernode/data/Cargo.toml ./data/Cargo.toml +COPY ./packages/ciphernode/e3_request/Cargo.toml ./e3_request/Cargo.toml +COPY ./packages/ciphernode/enclave/Cargo.toml ./enclave/Cargo.toml +COPY ./packages/ciphernode/enclave_core/Cargo.toml ./enclave_core/Cargo.toml +COPY ./packages/ciphernode/events/Cargo.toml ./events/Cargo.toml +COPY ./packages/ciphernode/evm/Cargo.toml ./evm/Cargo.toml +COPY ./packages/ciphernode/fhe/Cargo.toml ./fhe/Cargo.toml +COPY ./packages/ciphernode/keyshare/Cargo.toml ./keyshare/Cargo.toml +COPY ./packages/ciphernode/logger/Cargo.toml ./logger/Cargo.toml +COPY ./packages/ciphernode/net/Cargo.toml ./net/Cargo.toml +COPY ./packages/ciphernode/sortition/Cargo.toml ./sortition/Cargo.toml # Build all dependencies and add them to the build cache RUN mkdir -p src && \ From ed322f3e75434b32489ee4b8b198b99487f78345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Mon, 30 Dec 2024 22:18:03 +1100 Subject: [PATCH 10/15] Workspace dependencies (#229) * Fix dockerfile issues and set packages to inherit from workspace * Add toolchain file to ensure we dont redownload tools --- packages/ciphernode/.dockerignore | 1 + packages/ciphernode/Cargo.toml | 29 ++++++++++++++------- packages/ciphernode/Dockerfile | 11 ++++---- packages/ciphernode/aggregator/Cargo.toml | 6 ++--- packages/ciphernode/crypto/Cargo.toml | 2 +- packages/ciphernode/e3_request/Cargo.toml | 2 +- packages/ciphernode/enclave/Cargo.toml | 6 ++--- packages/ciphernode/enclave_core/Cargo.toml | 20 +++++++------- packages/ciphernode/evm/Cargo.toml | 6 ++--- packages/ciphernode/fhe/Cargo.toml | 2 +- packages/ciphernode/keyshare/Cargo.toml | 4 +-- packages/ciphernode/sortition/Cargo.toml | 2 +- packages/ciphernode/test_helpers/Cargo.toml | 2 +- packages/ciphernode/tests/Cargo.toml | 18 ++++++------- 14 files changed, 61 insertions(+), 50 deletions(-) diff --git a/packages/ciphernode/.dockerignore b/packages/ciphernode/.dockerignore index 521131cf..3eea9b3e 100644 --- a/packages/ciphernode/.dockerignore +++ b/packages/ciphernode/.dockerignore @@ -11,6 +11,7 @@ !*/src/**/*.rs # Explicitly ignore build artifacts everywhere +target/ **/target/ **/.git/ **/.gitignore diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index 67f7dce7..90702fe7 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -1,25 +1,27 @@ [workspace] members = [ - "events", + "aggregator", + "config", + "crypto", + "data", + "e3_request", "enclave", "enclave_core", - "net", - "data", + "events", "evm", "fhe", - "e3_request", "keyshare", - "aggregator", - "test_helpers", "logger", + "net", + "sortition", + "test_helpers", "tests", - "crypto", - "config", ] [workspace.dependencies] actix = "0.13.5" aes-gcm = "0.10.3" +aggregator = { path = "./aggregator" } alloy = { version = "0.5.2", features = ["full", "node-bindings"] } alloy-primitives = { version = "0.6", default-features = false, features = [ "rlp", @@ -40,20 +42,24 @@ compile-time = "0.2.0" config = { path = "./config" } dirs = "5.0.1" data = { path = "./data" } +dialoguer = "0.11.0" events = { path = "./events" } enclave_core = { path = "./enclave_core" } evm = { path = "./evm" } -shellexpand = "3.1.0" -figment = { version = "0.10.19", features = ["yaml", "test"] } +fhe = { path = "./fhe" } fhe_rs = { package = "fhe", git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } fhe-traits = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } fhe-util = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } +figment = { version = "0.10.19", features = ["yaml", "test"] } futures = "0.3.30" futures-util = "0.3" hex = "0.4.3" +keyshare = { path = "./keyshare" } lazy_static = "1.5.0" +logger = { path = "./logger" } num = "0.4.3" net = { path = "./net" } +petname = "2.0.2" phf = { version = "0.11", features = ["macros"] } rand_chacha = "0.3.1" rand = "0.8.5" @@ -62,6 +68,9 @@ serde = { version = "1.0.208", features = ["derive"] } serde_json = { version = "1.0.133" } sled = "0.34.7" sha2 = "0.10.8" +shellexpand = "3.1.0" +sortition = { path = "./sortition" } +test-helpers = { path = "./test_helpers" } tempfile = "3.14.0" tokio = { version = "1.38", features = ["full"] } tracing = "0.1.37" diff --git a/packages/ciphernode/Dockerfile b/packages/ciphernode/Dockerfile index b8132444..ab6b043b 100644 --- a/packages/ciphernode/Dockerfile +++ b/packages/ciphernode/Dockerfile @@ -18,7 +18,6 @@ COPY --from=evm-builder /build/packages/evm/artifacts ../evm/artifacts COPY --from=evm-builder /build/packages/evm/deployments ../evm/deployments # Copy workpace Cargo.toml -COPY ./packages/ciphernode/rust-toolchain.toml . COPY ./packages/ciphernode/Cargo.toml ./Cargo.toml COPY ./packages/ciphernode/Cargo.lock ./Cargo.lock COPY ./packages/ciphernode/aggregator/Cargo.toml ./aggregator/Cargo.toml @@ -28,6 +27,7 @@ COPY ./packages/ciphernode/data/Cargo.toml ./data/Cargo.toml COPY ./packages/ciphernode/e3_request/Cargo.toml ./e3_request/Cargo.toml COPY ./packages/ciphernode/enclave/Cargo.toml ./enclave/Cargo.toml COPY ./packages/ciphernode/enclave_core/Cargo.toml ./enclave_core/Cargo.toml +COPY ./packages/ciphernode/enclave_core/build.rs ./enclave_core/build.rs COPY ./packages/ciphernode/events/Cargo.toml ./events/Cargo.toml COPY ./packages/ciphernode/evm/Cargo.toml ./evm/Cargo.toml COPY ./packages/ciphernode/fhe/Cargo.toml ./fhe/Cargo.toml @@ -35,11 +35,11 @@ COPY ./packages/ciphernode/keyshare/Cargo.toml ./keyshare/Cargo.toml COPY ./packages/ciphernode/logger/Cargo.toml ./logger/Cargo.toml COPY ./packages/ciphernode/net/Cargo.toml ./net/Cargo.toml COPY ./packages/ciphernode/sortition/Cargo.toml ./sortition/Cargo.toml +COPY ./packages/ciphernode/test_helpers/Cargo.toml ./test_helpers/Cargo.toml +COPY ./packages/ciphernode/tests/Cargo.toml ./tests/Cargo.toml +COPY ./packages/ciphernode/rust-toolchain.toml . -# Build all dependencies and add them to the build cache -RUN mkdir -p src && \ - echo "fn main() {}" > src/main.rs && \ - for d in ./*/ ; do \ +RUN for d in ./*/ ; do \ if [ -f "$d/Cargo.toml" ]; then \ mkdir -p "$d/src" && \ echo "fn main() {}" > "$d/src/lib.rs"; \ @@ -50,6 +50,7 @@ RUN cargo build --release COPY ./packages/ciphernode . +RUN find . -name "*.rs" -exec touch {} + RUN cargo build --release # Runtime stage diff --git a/packages/ciphernode/aggregator/Cargo.toml b/packages/ciphernode/aggregator/Cargo.toml index 8bb971fc..384c0a8a 100644 --- a/packages/ciphernode/aggregator/Cargo.toml +++ b/packages/ciphernode/aggregator/Cargo.toml @@ -11,8 +11,8 @@ bincode = { workspace = true } config = { workspace = true } async-trait = { workspace = true } events = { workspace = true } -fhe = { path = "../fhe" } -sortition = { path = "../sortition" } +fhe = { workspace = true } +sortition = { workspace = true } e3_request = { workspace = true } -data = { path = "../data" } +data = { workspace = true } tracing = { workspace = true } diff --git a/packages/ciphernode/crypto/Cargo.toml b/packages/ciphernode/crypto/Cargo.toml index 98da1ae0..dc587a58 100644 --- a/packages/ciphernode/crypto/Cargo.toml +++ b/packages/ciphernode/crypto/Cargo.toml @@ -10,5 +10,5 @@ rand = { workspace = true, version = "=0.8.5" } zeroize = { workspace = true, version = "=1.6.0" } anyhow = { workspace = true } tokio = { workspace = true } -config = { path = "../config" } +config = { workspace = true } async-trait = { workspace = true } diff --git a/packages/ciphernode/e3_request/Cargo.toml b/packages/ciphernode/e3_request/Cargo.toml index 0c26c63e..94102949 100644 --- a/packages/ciphernode/e3_request/Cargo.toml +++ b/packages/ciphernode/e3_request/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] actix = { workspace = true } events = { workspace = true } -data = { path = "../data" } +data = { workspace = true } anyhow = { workspace = true } serde = { workspace = true } config = { workspace = true } diff --git a/packages/ciphernode/enclave/Cargo.toml b/packages/ciphernode/enclave/Cargo.toml index 0cac8145..82bef21b 100644 --- a/packages/ciphernode/enclave/Cargo.toml +++ b/packages/ciphernode/enclave/Cargo.toml @@ -11,13 +11,13 @@ anyhow = { workspace = true } crypto = { workspace = true } clap = { workspace = true } compile-time = { workspace = true } -config = { path = "../config" } -dialoguer = "0.11.0" +config = { workspace = true } +dialoguer = { workspace = true } events = { workspace = true } enclave_core = { workspace = true } hex = { workspace = true } -petname = "2.0.2" rand = { workspace = true } +petname = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/packages/ciphernode/enclave_core/Cargo.toml b/packages/ciphernode/enclave_core/Cargo.toml index 338fba74..b9e8cbf9 100644 --- a/packages/ciphernode/enclave_core/Cargo.toml +++ b/packages/ciphernode/enclave_core/Cargo.toml @@ -8,30 +8,30 @@ build = "build.rs" [dependencies] actix = { workspace = true } -aggregator = { path = "../aggregator" } +aggregator = { workspace = true } alloy = { workspace = true } alloy-primitives = { workspace = true } anyhow = { workspace = true } bincode = { workspace = true } -config = { path = "../config" } +config = { workspace = true } clap = { workspace = true } crypto = { workspace = true } -data = { path = "../data" } +data = { workspace = true } dirs = { workspace = true } events = { workspace = true } -evm = { path = "../evm" } -fhe = { path = "../fhe" } -keyshare = { path = "../keyshare" } -logger = { path = "../logger" } +evm = { workspace = true } +fhe = { workspace = true } +keyshare = { workspace = true } +logger = { workspace = true } libp2p = { workspace = true } -net = { path = "../net" } +net = { workspace = true } phf = { workspace = true } rand = { workspace = true } rand_chacha = { workspace = true } e3_request = { workspace = true } serde = { workspace = true } -sortition = { path = "../sortition" } -test-helpers = { path = "../test_helpers" } +sortition = { workspace = true } +test-helpers = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } zeroize = { workspace = true } diff --git a/packages/ciphernode/evm/Cargo.toml b/packages/ciphernode/evm/Cargo.toml index 826a134e..3739c5e9 100644 --- a/packages/ciphernode/evm/Cargo.toml +++ b/packages/ciphernode/evm/Cargo.toml @@ -11,11 +11,11 @@ anyhow = { workspace = true } async-trait = { workspace = true } base64 = { workspace = true } crypto = { workspace = true } -config = { path = "../config" } -data = { path = "../data" } +config = { workspace = true } +data = { workspace = true } events = { workspace = true } futures-util = { workspace = true } -sortition = { path = "../sortition" } +sortition = { workspace = true } serde = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } diff --git a/packages/ciphernode/fhe/Cargo.toml b/packages/ciphernode/fhe/Cargo.toml index 1a6a3f72..4d05402f 100644 --- a/packages/ciphernode/fhe/Cargo.toml +++ b/packages/ciphernode/fhe/Cargo.toml @@ -9,7 +9,7 @@ anyhow = { workspace = true } async-trait = { workspace = true } bincode = { workspace = true } config = { workspace = true } -data = { path = "../data" } +data = { workspace = true } events = { workspace = true } fhe-traits = { workspace = true } fhe-util = { workspace = true } diff --git a/packages/ciphernode/keyshare/Cargo.toml b/packages/ciphernode/keyshare/Cargo.toml index f7551f98..7efe9b82 100644 --- a/packages/ciphernode/keyshare/Cargo.toml +++ b/packages/ciphernode/keyshare/Cargo.toml @@ -8,10 +8,10 @@ actix = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } config = { workspace = true } -data = { path = "../data" } +data = { workspace = true } crypto = { workspace = true } events = { workspace = true } -fhe = { path = "../fhe" } +fhe = { workspace = true } e3_request = { workspace = true } serde = { workspace = true } tracing = { workspace = true } diff --git a/packages/ciphernode/sortition/Cargo.toml b/packages/ciphernode/sortition/Cargo.toml index 3e72f855..5e808a97 100644 --- a/packages/ciphernode/sortition/Cargo.toml +++ b/packages/ciphernode/sortition/Cargo.toml @@ -14,7 +14,7 @@ alloy = { workspace = true, features = ["full"] } anyhow = { workspace = true } async-trait = { workspace = true } config = { workspace = true } -data = { path = "../data" } +data = { workspace = true } events = { workspace = true } num = { workspace = true } rand = { workspace = true } diff --git a/packages/ciphernode/test_helpers/Cargo.toml b/packages/ciphernode/test_helpers/Cargo.toml index 6ae81a66..7abd96f7 100644 --- a/packages/ciphernode/test_helpers/Cargo.toml +++ b/packages/ciphernode/test_helpers/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] actix = { workspace = true } events = { workspace = true } -fhe = { path = "../fhe" } +fhe = { workspace = true } bincode = { workspace = true } clap = { workspace = true, features = ["derive"] } fhe_rs = { workspace = true } diff --git a/packages/ciphernode/tests/Cargo.toml b/packages/ciphernode/tests/Cargo.toml index 3e883a35..7ce99632 100644 --- a/packages/ciphernode/tests/Cargo.toml +++ b/packages/ciphernode/tests/Cargo.toml @@ -4,18 +4,18 @@ version = "0.1.0" edition = "2021" [dependencies] -net = { path = "../net" } -sortition = { path = "../sortition" } +net = { workspace = true } +sortition = { workspace = true } events = { workspace = true } -evm = { path = "../evm" } -logger = { path = "../logger" } -fhe = { path = "../fhe" } +evm = { workspace = true } +logger = { workspace = true } +fhe = { workspace = true } crypto = { workspace = true } -data = { path = "../data" } -keyshare = { path = "../keyshare" } -aggregator = { path = "../aggregator" } +data = { workspace = true } +keyshare = { workspace = true } +aggregator = { workspace = true } e3_request = { workspace = true } -test-helpers = { path = "../test_helpers" } +test-helpers = { workspace = true } fhe_rs = { workspace = true } fhe-traits = { workspace = true } fhe-util = { workspace = true } From 76b76e195c6d8ce13af13ea2c0fb37d767f96aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Tue, 31 Dec 2024 10:56:36 +1100 Subject: [PATCH 11/15] Extract events (#230) * Extract events * Use a macro to add From traits * Expose TestEvent --- packages/ciphernode/events/src/e3id.rs | 43 ++ .../src/enclave_event/ciphernode_added.rs | 21 + .../src/enclave_event/ciphernode_removed.rs | 21 + .../src/enclave_event/ciphernode_selected.rs | 21 + .../ciphertext_output_published.rs | 17 + .../enclave_event/decryptionshare_created.rs | 18 + .../events/src/enclave_event/die.rs | 12 + .../src/enclave_event/e3_request_complete.rs | 17 + .../events/src/enclave_event/e3_requested.rs | 24 + .../events/src/enclave_event/enclave_error.rs | 52 ++ .../src/enclave_event/keyshare_created.rs | 19 + .../events/src/enclave_event/mod.rs | 242 +++++++ .../src/enclave_event/plaintext_aggregated.rs | 22 + .../src/enclave_event/publickey_aggregated.rs | 23 + .../events/src/enclave_event/shutdown.rs | 13 + .../events/src/enclave_event/test_event.rs | 17 + packages/ciphernode/events/src/event_id.rs | 27 + packages/ciphernode/events/src/eventbus.rs | 4 +- packages/ciphernode/events/src/events.rs | 637 ------------------ packages/ciphernode/events/src/lib.rs | 10 +- packages/ciphernode/events/src/seed.rs | 30 + 21 files changed, 649 insertions(+), 641 deletions(-) create mode 100644 packages/ciphernode/events/src/e3id.rs create mode 100644 packages/ciphernode/events/src/enclave_event/ciphernode_added.rs create mode 100644 packages/ciphernode/events/src/enclave_event/ciphernode_removed.rs create mode 100644 packages/ciphernode/events/src/enclave_event/ciphernode_selected.rs create mode 100644 packages/ciphernode/events/src/enclave_event/ciphertext_output_published.rs create mode 100644 packages/ciphernode/events/src/enclave_event/decryptionshare_created.rs create mode 100644 packages/ciphernode/events/src/enclave_event/die.rs create mode 100644 packages/ciphernode/events/src/enclave_event/e3_request_complete.rs create mode 100644 packages/ciphernode/events/src/enclave_event/e3_requested.rs create mode 100644 packages/ciphernode/events/src/enclave_event/enclave_error.rs create mode 100644 packages/ciphernode/events/src/enclave_event/keyshare_created.rs create mode 100644 packages/ciphernode/events/src/enclave_event/mod.rs create mode 100644 packages/ciphernode/events/src/enclave_event/plaintext_aggregated.rs create mode 100644 packages/ciphernode/events/src/enclave_event/publickey_aggregated.rs create mode 100644 packages/ciphernode/events/src/enclave_event/shutdown.rs create mode 100644 packages/ciphernode/events/src/enclave_event/test_event.rs create mode 100644 packages/ciphernode/events/src/event_id.rs delete mode 100644 packages/ciphernode/events/src/events.rs create mode 100644 packages/ciphernode/events/src/seed.rs diff --git a/packages/ciphernode/events/src/e3id.rs b/packages/ciphernode/events/src/e3id.rs new file mode 100644 index 00000000..20c6362b --- /dev/null +++ b/packages/ciphernode/events/src/e3id.rs @@ -0,0 +1,43 @@ +use alloy::primitives::U256; +use alloy_primitives::ruint::ParseError; +use core::fmt; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct E3id(pub String); +impl fmt::Display for E3id { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +impl E3id { + pub fn new(id: impl Into) -> Self { + Self(id.into()) + } +} + +impl From for E3id { + fn from(value: u32) -> Self { + E3id::new(value.to_string()) + } +} + +impl From for E3id { + fn from(value: String) -> Self { + E3id::new(value) + } +} + +impl From<&str> for E3id { + fn from(value: &str) -> Self { + E3id::new(value) + } +} + +impl TryFrom for U256 { + type Error = ParseError; + fn try_from(value: E3id) -> Result { + U256::from_str_radix(&value.0, 10) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/ciphernode_added.rs b/packages/ciphernode/events/src/enclave_event/ciphernode_added.rs new file mode 100644 index 00000000..33c8af41 --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/ciphernode_added.rs @@ -0,0 +1,21 @@ +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct CiphernodeAdded { + pub address: String, + pub index: usize, + pub num_nodes: usize, +} + +impl Display for CiphernodeAdded { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "address: {}, index: {}, num_nodes: {}", + self.address, self.index, self.num_nodes + ) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/ciphernode_removed.rs b/packages/ciphernode/events/src/enclave_event/ciphernode_removed.rs new file mode 100644 index 00000000..e43e3828 --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/ciphernode_removed.rs @@ -0,0 +1,21 @@ +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct CiphernodeRemoved { + pub address: String, + pub index: usize, + pub num_nodes: usize, +} + +impl Display for CiphernodeRemoved { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "address: {}, index: {}, num_nodes: {}", + self.address, self.index, self.num_nodes + ) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/ciphernode_selected.rs b/packages/ciphernode/events/src/enclave_event/ciphernode_selected.rs new file mode 100644 index 00000000..3cfc2791 --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/ciphernode_selected.rs @@ -0,0 +1,21 @@ +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct CiphernodeSelected { + pub e3_id: E3id, + pub threshold_m: usize, +} + +impl Display for CiphernodeSelected { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "e3_id: {}, threshold_m: {}", + self.e3_id, self.threshold_m, + ) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/ciphertext_output_published.rs b/packages/ciphernode/events/src/enclave_event/ciphertext_output_published.rs new file mode 100644 index 00000000..30171412 --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/ciphertext_output_published.rs @@ -0,0 +1,17 @@ +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct CiphertextOutputPublished { + pub e3_id: E3id, + pub ciphertext_output: Vec, +} + +impl Display for CiphertextOutputPublished { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "e3_id: {}", self.e3_id,) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/decryptionshare_created.rs b/packages/ciphernode/events/src/enclave_event/decryptionshare_created.rs new file mode 100644 index 00000000..870620ac --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/decryptionshare_created.rs @@ -0,0 +1,18 @@ +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "anyhow::Result<()>")] +pub struct DecryptionshareCreated { + pub decryption_share: Vec, + pub e3_id: E3id, + pub node: String, +} + +impl Display for DecryptionshareCreated { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "e3_id: {}, node: {}", self.e3_id, self.node,) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/die.rs b/packages/ciphernode/events/src/enclave_event/die.rs new file mode 100644 index 00000000..80f417f9 --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/die.rs @@ -0,0 +1,12 @@ +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct Die; +impl Display for Die { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Die",) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/e3_request_complete.rs b/packages/ciphernode/events/src/enclave_event/e3_request_complete.rs new file mode 100644 index 00000000..e4677a66 --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/e3_request_complete.rs @@ -0,0 +1,17 @@ +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +/// E3RequestComplete event is a local only event +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct E3RequestComplete { + pub e3_id: E3id, +} + +impl Display for E3RequestComplete { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "e3_id: {}", self.e3_id) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/e3_requested.rs b/packages/ciphernode/events/src/enclave_event/e3_requested.rs new file mode 100644 index 00000000..4526a2a0 --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/e3_requested.rs @@ -0,0 +1,24 @@ +use crate::{E3id, Seed}; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct E3Requested { + pub e3_id: E3id, + pub threshold_m: usize, + pub seed: Seed, + pub params: Vec, + pub src_chain_id: u64, +} + +impl Display for E3Requested { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "e3_id: {}, threshold_m: {}, src_chain_id: {}, seed: {}, params: ", + self.e3_id, self.threshold_m, self.src_chain_id, self.seed + ) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/enclave_error.rs b/packages/ciphernode/events/src/enclave_event/enclave_error.rs new file mode 100644 index 00000000..f75db1ea --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/enclave_error.rs @@ -0,0 +1,52 @@ +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +pub trait FromError { + type Error; + fn from_error(err_type: EnclaveErrorType, error: Self::Error) -> Self; +} + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct EnclaveError { + pub err_type: EnclaveErrorType, + pub message: String, +} + +impl Display for EnclaveError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum EnclaveErrorType { + Evm, + KeyGeneration, + PublickeyAggregation, + IO, + PlaintextAggregation, + Decryption, + Sortition, + Data, +} + +impl EnclaveError { + pub fn new(err_type: EnclaveErrorType, message: &str) -> Self { + Self { + err_type, + message: message.to_string(), + } + } +} + +impl FromError for EnclaveError { + type Error = anyhow::Error; + fn from_error(err_type: EnclaveErrorType, error: Self::Error) -> Self { + Self { + err_type, + message: error.to_string(), + } + } +} diff --git a/packages/ciphernode/events/src/enclave_event/keyshare_created.rs b/packages/ciphernode/events/src/enclave_event/keyshare_created.rs new file mode 100644 index 00000000..d6c174ad --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/keyshare_created.rs @@ -0,0 +1,19 @@ +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::Display; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "anyhow::Result<()>")] +pub struct KeyshareCreated { + pub pubkey: Vec, + pub e3_id: E3id, + pub node: String, +} + +impl Display for KeyshareCreated { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "e3_id: {}, node: {}", self.e3_id, self.node,) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/mod.rs b/packages/ciphernode/events/src/enclave_event/mod.rs new file mode 100644 index 00000000..7f1a561f --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/mod.rs @@ -0,0 +1,242 @@ +mod ciphernode_added; +mod ciphernode_removed; +mod ciphernode_selected; +mod ciphertext_output_published; +mod decryptionshare_created; +mod die; +mod e3_request_complete; +mod e3_requested; +mod enclave_error; +mod keyshare_created; +mod plaintext_aggregated; +mod publickey_aggregated; +mod shutdown; +mod test_event; + +pub use ciphernode_added::*; +pub use ciphernode_removed::*; +pub use ciphernode_selected::*; +pub use ciphertext_output_published::*; +pub use decryptionshare_created::*; +pub use die::*; +pub use e3_request_complete::*; +pub use e3_requested::*; +pub use enclave_error::*; +pub use keyshare_created::*; +pub use plaintext_aggregated::*; +pub use publickey_aggregated::*; +pub use shutdown::*; +pub use test_event::*; + +use crate::{E3id, EventId}; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::{ + fmt::{self}, + hash::Hash, +}; + +/// Macro to help define From traits for EnclaveEvent +macro_rules! impl_from_event { + ($($variant:ident),*) => { + $( + impl From<$variant> for EnclaveEvent { + fn from(data: $variant) -> Self { + EnclaveEvent::$variant { + id: EventId::hash(data.clone()), + data: data.clone(), + } + } + } + )* + }; +} + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub enum EnclaveEvent { + KeyshareCreated { + id: EventId, + data: KeyshareCreated, + }, + E3Requested { + id: EventId, + data: E3Requested, + }, + PublicKeyAggregated { + id: EventId, + data: PublicKeyAggregated, + }, + CiphertextOutputPublished { + id: EventId, + data: CiphertextOutputPublished, + }, + DecryptionshareCreated { + id: EventId, + data: DecryptionshareCreated, + }, + PlaintextAggregated { + id: EventId, + data: PlaintextAggregated, + }, + CiphernodeSelected { + id: EventId, + data: CiphernodeSelected, + }, + CiphernodeAdded { + id: EventId, + data: CiphernodeAdded, + }, + CiphernodeRemoved { + id: EventId, + data: CiphernodeRemoved, + }, + EnclaveError { + id: EventId, + data: EnclaveError, + }, + E3RequestComplete { + id: EventId, + data: E3RequestComplete, + }, + Shutdown { + id: EventId, + data: Shutdown, + }, + /// This is a test event to use in testing + TestEvent { + id: EventId, + data: TestEvent, + }, + // CommitteeSelected, + // OutputDecrypted, + // CiphernodeRegistered, + // CiphernodeDeregistered, +} + +impl EnclaveEvent { + pub fn to_bytes(&self) -> Result, bincode::Error> { + bincode::serialize(self) + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + bincode::deserialize(bytes) + } + + pub fn get_id(&self) -> EventId { + self.clone().into() + } + + pub fn is_local_only(&self) -> bool { + // Add a list of local events + match self { + EnclaveEvent::CiphernodeSelected { .. } => true, + EnclaveEvent::E3Requested { .. } => true, + EnclaveEvent::CiphernodeAdded { .. } => true, + EnclaveEvent::CiphernodeRemoved { .. } => true, + EnclaveEvent::E3RequestComplete { .. } => true, + EnclaveEvent::Shutdown { .. } => true, + _ => false, + } + } +} + +impl From for EventId { + fn from(value: EnclaveEvent) -> Self { + match value { + EnclaveEvent::KeyshareCreated { id, .. } => id, + EnclaveEvent::E3Requested { id, .. } => id, + EnclaveEvent::PublicKeyAggregated { id, .. } => id, + EnclaveEvent::CiphertextOutputPublished { id, .. } => id, + EnclaveEvent::DecryptionshareCreated { id, .. } => id, + EnclaveEvent::PlaintextAggregated { id, .. } => id, + EnclaveEvent::CiphernodeSelected { id, .. } => id, + EnclaveEvent::CiphernodeAdded { id, .. } => id, + EnclaveEvent::CiphernodeRemoved { id, .. } => id, + EnclaveEvent::EnclaveError { id, .. } => id, + EnclaveEvent::E3RequestComplete { id, .. } => id, + EnclaveEvent::Shutdown { id, .. } => id, + EnclaveEvent::TestEvent { id, .. } => id, + } + } +} + +impl EnclaveEvent { + pub fn get_e3_id(&self) -> Option { + match self.clone() { + EnclaveEvent::KeyshareCreated { data, .. } => Some(data.e3_id), + EnclaveEvent::E3Requested { data, .. } => Some(data.e3_id), + EnclaveEvent::PublicKeyAggregated { data, .. } => Some(data.e3_id), + EnclaveEvent::CiphertextOutputPublished { data, .. } => Some(data.e3_id), + EnclaveEvent::DecryptionshareCreated { data, .. } => Some(data.e3_id), + EnclaveEvent::PlaintextAggregated { data, .. } => Some(data.e3_id), + EnclaveEvent::CiphernodeSelected { data, .. } => Some(data.e3_id), + _ => None, + } + } + pub fn get_data(&self) -> String { + match self.clone() { + EnclaveEvent::KeyshareCreated { data, .. } => format!("{}", data), + EnclaveEvent::E3Requested { data, .. } => format!("{}", data), + EnclaveEvent::PublicKeyAggregated { data, .. } => format!("{}", data), + EnclaveEvent::CiphertextOutputPublished { data, .. } => format!("{}", data), + EnclaveEvent::DecryptionshareCreated { data, .. } => format!("{}", data), + EnclaveEvent::PlaintextAggregated { data, .. } => format!("{}", data), + EnclaveEvent::CiphernodeSelected { data, .. } => format!("{}", data), + EnclaveEvent::CiphernodeAdded { data, .. } => format!("{}", data), + EnclaveEvent::CiphernodeRemoved { data, .. } => format!("{}", data), + EnclaveEvent::E3RequestComplete { data, .. } => format!("{}", data), + EnclaveEvent::EnclaveError { data, .. } => format!("{:?}", data), + EnclaveEvent::Shutdown { data, .. } => format!("{:?}", data), + EnclaveEvent::TestEvent { data, .. } => format!("{:?}", data), + // _ => "".to_string(), + } + } +} + +impl_from_event!( + KeyshareCreated, + E3Requested, + PublicKeyAggregated, + CiphertextOutputPublished, + DecryptionshareCreated, + PlaintextAggregated, + E3RequestComplete, + CiphernodeSelected, + CiphernodeAdded, + CiphernodeRemoved, + EnclaveError, + Shutdown, + TestEvent +); + +impl FromError for EnclaveEvent { + type Error = anyhow::Error; + fn from_error(err_type: EnclaveErrorType, error: Self::Error) -> Self { + let error_event = EnclaveError::from_error(err_type, error); + EnclaveEvent::from(error_event) + } +} + +impl fmt::Display for EnclaveEvent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&format!("{}({})", self.event_type(), self.get_data())) + } +} + +fn extract_enclave_event_name(s: &str) -> &str { + let bytes = s.as_bytes(); + for (i, &item) in bytes.iter().enumerate() { + if item == b' ' || item == b'(' { + return &s[..i]; + } + } + s +} + +impl EnclaveEvent { + pub fn event_type(&self) -> String { + let s = format!("{:?}", self); + extract_enclave_event_name(&s).to_string() + } +} diff --git a/packages/ciphernode/events/src/enclave_event/plaintext_aggregated.rs b/packages/ciphernode/events/src/enclave_event/plaintext_aggregated.rs new file mode 100644 index 00000000..7e24d5a7 --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/plaintext_aggregated.rs @@ -0,0 +1,22 @@ +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct PlaintextAggregated { + pub e3_id: E3id, + pub decrypted_output: Vec, + pub src_chain_id: u64, +} + +impl Display for PlaintextAggregated { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "e3_id: {}, src_chain_id: {}", + self.e3_id, self.src_chain_id + ) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/publickey_aggregated.rs b/packages/ciphernode/events/src/enclave_event/publickey_aggregated.rs new file mode 100644 index 00000000..0a71cbc3 --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/publickey_aggregated.rs @@ -0,0 +1,23 @@ +use crate::{E3id, OrderedSet}; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct PublicKeyAggregated { + pub pubkey: Vec, + pub e3_id: E3id, + pub nodes: OrderedSet, + pub src_chain_id: u64, +} + +impl Display for PublicKeyAggregated { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "e3_id: {}, src_chain_id: {}, nodes: , pubkey: ", + self.e3_id, self.src_chain_id, + ) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/shutdown.rs b/packages/ciphernode/events/src/enclave_event/shutdown.rs new file mode 100644 index 00000000..a56e7b69 --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/shutdown.rs @@ -0,0 +1,13 @@ +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +/// Represents a shutdown event triggered by SIG TERM +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct Shutdown; +impl Display for Shutdown { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Shutdown",) + } +} diff --git a/packages/ciphernode/events/src/enclave_event/test_event.rs b/packages/ciphernode/events/src/enclave_event/test_event.rs new file mode 100644 index 00000000..2f5f3b25 --- /dev/null +++ b/packages/ciphernode/events/src/enclave_event/test_event.rs @@ -0,0 +1,17 @@ +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct TestEvent { + pub msg: String, + pub entropy: u64, +} + +#[cfg(test)] +impl Display for TestEvent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TestEvent(msg: {})", self.msg) + } +} diff --git a/packages/ciphernode/events/src/event_id.rs b/packages/ciphernode/events/src/event_id.rs new file mode 100644 index 00000000..2f372179 --- /dev/null +++ b/packages/ciphernode/events/src/event_id.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::{ + fmt, + hash::{DefaultHasher, Hash, Hasher}, +}; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct EventId(pub [u8; 32]); + +impl EventId { + pub fn hash(value: T) -> Self { + let mut hasher = Sha256::new(); + let mut std_hasher = DefaultHasher::new(); + value.hash(&mut std_hasher); + hasher.update(std_hasher.finish().to_le_bytes()); + let result = hasher.finalize(); + EventId(result.into()) + } +} + +impl fmt::Display for EventId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let base58_string = bs58::encode(&self.0).into_string(); + write!(f, "evt:{}", &base58_string[0..8]) + } +} diff --git a/packages/ciphernode/events/src/eventbus.rs b/packages/ciphernode/events/src/eventbus.rs index f7e63f04..0b99b0d9 100644 --- a/packages/ciphernode/events/src/eventbus.rs +++ b/packages/ciphernode/events/src/eventbus.rs @@ -1,9 +1,9 @@ use actix::prelude::*; use std::collections::{HashMap, HashSet}; -use crate::{EnclaveError, EnclaveErrorType}; +use crate::{EnclaveError, EnclaveErrorType, EventId}; -use super::events::{EnclaveEvent, EventId, FromError}; +use super::enclave_event::{EnclaveEvent, FromError}; #[derive(Message, Debug)] #[rtype(result = "()")] diff --git a/packages/ciphernode/events/src/events.rs b/packages/ciphernode/events/src/events.rs deleted file mode 100644 index 276cd80c..00000000 --- a/packages/ciphernode/events/src/events.rs +++ /dev/null @@ -1,637 +0,0 @@ -use actix::Message; -use alloy::{ - hex, - primitives::{Uint, U256}, -}; -use alloy_primitives::ruint::ParseError; -use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; -use std::{ - fmt::{self, Display}, - hash::{DefaultHasher, Hash, Hasher}, -}; - -use crate::OrderedSet; - -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct E3id(pub String); -impl fmt::Display for E3id { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.0) - } -} - -impl E3id { - pub fn new(id: impl Into) -> Self { - Self(id.into()) - } -} - -impl From for E3id { - fn from(value: u32) -> Self { - E3id::new(value.to_string()) - } -} - -impl From for E3id { - fn from(value: String) -> Self { - E3id::new(value) - } -} - -impl From<&str> for E3id { - fn from(value: &str) -> Self { - E3id::new(value) - } -} - -impl TryFrom for U256 { - type Error = ParseError; - fn try_from(value: E3id) -> Result { - U256::from_str_radix(&value.0, 10) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct EventId(pub [u8; 32]); - -impl EventId { - pub fn hash(value: T) -> Self { - let mut hasher = Sha256::new(); - let mut std_hasher = DefaultHasher::new(); - value.hash(&mut std_hasher); - hasher.update(std_hasher.finish().to_le_bytes()); - let result = hasher.finalize(); - EventId(result.into()) - } -} - -impl fmt::Display for EventId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let base58_string = bs58::encode(&self.0).into_string(); - write!(f, "evt:{}", &base58_string[0..8]) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub enum EnclaveEvent { - KeyshareCreated { - id: EventId, - data: KeyshareCreated, - }, - E3Requested { - id: EventId, - data: E3Requested, - }, - PublicKeyAggregated { - id: EventId, - data: PublicKeyAggregated, - }, - CiphertextOutputPublished { - id: EventId, - data: CiphertextOutputPublished, - }, - DecryptionshareCreated { - id: EventId, - data: DecryptionshareCreated, - }, - PlaintextAggregated { - id: EventId, - data: PlaintextAggregated, - }, - CiphernodeSelected { - id: EventId, - data: CiphernodeSelected, - }, - CiphernodeAdded { - id: EventId, - data: CiphernodeAdded, - }, - CiphernodeRemoved { - id: EventId, - data: CiphernodeRemoved, - }, - EnclaveError { - id: EventId, - data: EnclaveError, - }, - E3RequestComplete { - id: EventId, - data: E3RequestComplete, - }, - Shutdown { - id: EventId, - data: Shutdown, - }, - /// This is a test event to use in testing - TestEvent { - id: EventId, - data: TestEvent, - }, - // CommitteeSelected, - // OutputDecrypted, - // CiphernodeRegistered, - // CiphernodeDeregistered, -} - -impl EnclaveEvent { - pub fn to_bytes(&self) -> Result, bincode::Error> { - bincode::serialize(self) - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - bincode::deserialize(bytes) - } - - pub fn get_id(&self) -> EventId { - self.clone().into() - } - - pub fn is_local_only(&self) -> bool { - // Add a list of local events - match self { - EnclaveEvent::CiphernodeSelected { .. } => true, - EnclaveEvent::E3Requested { .. } => true, - EnclaveEvent::CiphernodeAdded { .. } => true, - EnclaveEvent::CiphernodeRemoved { .. } => true, - EnclaveEvent::E3RequestComplete { .. } => true, - EnclaveEvent::Shutdown { .. } => true, - _ => false, - } - } -} - -impl From for EventId { - fn from(value: EnclaveEvent) -> Self { - match value { - EnclaveEvent::KeyshareCreated { id, .. } => id, - EnclaveEvent::E3Requested { id, .. } => id, - EnclaveEvent::PublicKeyAggregated { id, .. } => id, - EnclaveEvent::CiphertextOutputPublished { id, .. } => id, - EnclaveEvent::DecryptionshareCreated { id, .. } => id, - EnclaveEvent::PlaintextAggregated { id, .. } => id, - EnclaveEvent::CiphernodeSelected { id, .. } => id, - EnclaveEvent::CiphernodeAdded { id, .. } => id, - EnclaveEvent::CiphernodeRemoved { id, .. } => id, - EnclaveEvent::EnclaveError { id, .. } => id, - EnclaveEvent::E3RequestComplete { id, .. } => id, - EnclaveEvent::Shutdown { id, .. } => id, - EnclaveEvent::TestEvent { id, .. } => id, - } - } -} - -impl EnclaveEvent { - pub fn get_e3_id(&self) -> Option { - match self.clone() { - EnclaveEvent::KeyshareCreated { data, .. } => Some(data.e3_id), - EnclaveEvent::E3Requested { data, .. } => Some(data.e3_id), - EnclaveEvent::PublicKeyAggregated { data, .. } => Some(data.e3_id), - EnclaveEvent::CiphertextOutputPublished { data, .. } => Some(data.e3_id), - EnclaveEvent::DecryptionshareCreated { data, .. } => Some(data.e3_id), - EnclaveEvent::PlaintextAggregated { data, .. } => Some(data.e3_id), - EnclaveEvent::CiphernodeSelected { data, .. } => Some(data.e3_id), - _ => None, - } - } - pub fn get_data(&self) -> String { - match self.clone() { - EnclaveEvent::KeyshareCreated { data, .. } => format!("{}", data), - EnclaveEvent::E3Requested { data, .. } => format!("{}", data), - EnclaveEvent::PublicKeyAggregated { data, .. } => format!("{}", data), - EnclaveEvent::CiphertextOutputPublished { data, .. } => format!("{}", data), - EnclaveEvent::DecryptionshareCreated { data, .. } => format!("{}", data), - EnclaveEvent::PlaintextAggregated { data, .. } => format!("{}", data), - EnclaveEvent::CiphernodeSelected { data, .. } => format!("{}", data), - EnclaveEvent::CiphernodeAdded { data, .. } => format!("{}", data), - EnclaveEvent::CiphernodeRemoved { data, .. } => format!("{}", data), - EnclaveEvent::E3RequestComplete { data, .. } => format!("{}", data), - EnclaveEvent::EnclaveError { data, .. } => format!("{:?}", data), - EnclaveEvent::Shutdown { data, .. } => format!("{:?}", data), - EnclaveEvent::TestEvent { data, .. } => format!("{:?}", data), - // _ => "".to_string(), - } - } -} - -pub trait FromError { - type Error; - fn from_error(err_type: EnclaveErrorType, error: Self::Error) -> Self; -} - -// TODO: These From traits should be handled by a macro -impl From for EnclaveEvent { - fn from(data: KeyshareCreated) -> Self { - EnclaveEvent::KeyshareCreated { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(data: E3Requested) -> Self { - EnclaveEvent::E3Requested { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(data: PublicKeyAggregated) -> Self { - EnclaveEvent::PublicKeyAggregated { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(data: CiphertextOutputPublished) -> Self { - EnclaveEvent::CiphertextOutputPublished { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(data: DecryptionshareCreated) -> Self { - EnclaveEvent::DecryptionshareCreated { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(data: PlaintextAggregated) -> Self { - EnclaveEvent::PlaintextAggregated { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(data: E3RequestComplete) -> Self { - EnclaveEvent::E3RequestComplete { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(data: CiphernodeSelected) -> Self { - EnclaveEvent::CiphernodeSelected { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(data: CiphernodeAdded) -> Self { - EnclaveEvent::CiphernodeAdded { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(data: CiphernodeRemoved) -> Self { - EnclaveEvent::CiphernodeRemoved { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(data: EnclaveError) -> Self { - EnclaveEvent::EnclaveError { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(data: Shutdown) -> Self { - EnclaveEvent::Shutdown { - id: EventId::hash(data.clone()), - data: data.clone(), - } - } -} - -impl From for EnclaveEvent { - fn from(value: TestEvent) -> Self { - EnclaveEvent::TestEvent { - id: EventId::hash(value.clone()), - data: value.clone(), - } - } -} - -impl FromError for EnclaveEvent { - type Error = anyhow::Error; - fn from_error(err_type: EnclaveErrorType, error: Self::Error) -> Self { - let error_event = EnclaveError::from_error(err_type, error); - EnclaveEvent::from(error_event) - } -} - -impl fmt::Display for EnclaveEvent { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&format!("{}({})", self.event_type(), self.get_data())) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "anyhow::Result<()>")] -pub struct KeyshareCreated { - pub pubkey: Vec, - pub e3_id: E3id, - pub node: String, -} - -impl Display for KeyshareCreated { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "e3_id: {}, node: {}", self.e3_id, self.node,) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "anyhow::Result<()>")] -pub struct DecryptionshareCreated { - pub decryption_share: Vec, - pub e3_id: E3id, - pub node: String, -} - -impl Display for DecryptionshareCreated { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "e3_id: {}, node: {}", self.e3_id, self.node,) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct PublicKeyAggregated { - pub pubkey: Vec, - pub e3_id: E3id, - pub nodes: OrderedSet, - pub src_chain_id: u64, -} - -impl Display for PublicKeyAggregated { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "e3_id: {}, src_chain_id: {}, nodes: , pubkey: ", - self.e3_id, self.src_chain_id, - ) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct E3Requested { - pub e3_id: E3id, - pub threshold_m: usize, - pub seed: Seed, - pub params: Vec, - pub src_chain_id: u64, -} - -impl Display for E3Requested { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "e3_id: {}, threshold_m: {}, src_chain_id: {}, seed: {}, params: ", - self.e3_id, self.threshold_m, self.src_chain_id, self.seed - ) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct CiphernodeSelected { - pub e3_id: E3id, - pub threshold_m: usize, -} - -impl Display for CiphernodeSelected { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "e3_id: {}, threshold_m: {}", - self.e3_id, self.threshold_m, - ) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct CiphertextOutputPublished { - pub e3_id: E3id, - pub ciphertext_output: Vec, -} - -impl Display for CiphertextOutputPublished { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "e3_id: {}", self.e3_id,) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct PlaintextAggregated { - pub e3_id: E3id, - pub decrypted_output: Vec, - pub src_chain_id: u64, -} - -impl Display for PlaintextAggregated { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "e3_id: {}, src_chain_id: {}", - self.e3_id, self.src_chain_id - ) - } -} - -/// E3RequestComplete event is a local only event -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct E3RequestComplete { - pub e3_id: E3id, -} - -impl Display for E3RequestComplete { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "e3_id: {}", self.e3_id) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct CiphernodeAdded { - pub address: String, - pub index: usize, - pub num_nodes: usize, -} - -impl Display for CiphernodeAdded { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "address: {}, index: {}, num_nodes: {}", - self.address, self.index, self.num_nodes - ) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct CiphernodeRemoved { - pub address: String, - pub index: usize, - pub num_nodes: usize, -} - -impl Display for CiphernodeRemoved { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "address: {}, index: {}, num_nodes: {}", - self.address, self.index, self.num_nodes - ) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct EnclaveError { - pub err_type: EnclaveErrorType, - pub message: String, -} - -impl Display for EnclaveError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct Die; -impl Display for Die { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Die",) - } -} - -/// Represents a shutdown event triggered by SIG TERM -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct Shutdown; -impl Display for Shutdown { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Shutdown",) - } -} - -#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[rtype(result = "()")] -pub struct TestEvent { - pub msg: String, - pub entropy: u64, -} - -#[cfg(test)] -impl Display for TestEvent { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TestEvent(msg: {})", self.msg) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct Seed(pub [u8; 32]); -impl From for u64 { - fn from(value: Seed) -> Self { - u64::from_le_bytes(value.0[..8].try_into().unwrap()) - } -} - -impl From for [u8; 32] { - fn from(value: Seed) -> Self { - value.0 - } -} - -impl From> for Seed { - fn from(value: Uint<256, 4>) -> Self { - Seed(value.to_le_bytes()) - } -} - -impl Display for Seed { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Seed(0x{})", hex::encode(self.0)) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum EnclaveErrorType { - Evm, - KeyGeneration, - PublickeyAggregation, - IO, - PlaintextAggregation, - Decryption, - Sortition, - Data, -} - -impl EnclaveError { - pub fn new(err_type: EnclaveErrorType, message: &str) -> Self { - Self { - err_type, - message: message.to_string(), - } - } -} - -impl FromError for EnclaveError { - type Error = anyhow::Error; - fn from_error(err_type: EnclaveErrorType, error: Self::Error) -> Self { - Self { - err_type, - message: error.to_string(), - } - } -} - -fn extract_enclave_event_name(s: &str) -> &str { - let bytes = s.as_bytes(); - for (i, &item) in bytes.iter().enumerate() { - if item == b' ' || item == b'(' { - return &s[..i]; - } - } - s -} - -impl EnclaveEvent { - pub fn event_type(&self) -> String { - let s = format!("{:?}", self); - extract_enclave_event_name(&s).to_string() - } -} diff --git a/packages/ciphernode/events/src/lib.rs b/packages/ciphernode/events/src/lib.rs index a4432629..fa787cb2 100644 --- a/packages/ciphernode/events/src/lib.rs +++ b/packages/ciphernode/events/src/lib.rs @@ -1,9 +1,15 @@ +mod e3id; +mod enclave_event; +mod event_id; mod eventbus; -mod events; mod ordered_set; +mod seed; mod tag; +pub use e3id::*; +pub use enclave_event::*; +pub use event_id::*; pub use eventbus::*; -pub use events::*; pub use ordered_set::*; +pub use seed::*; pub use tag::*; diff --git a/packages/ciphernode/events/src/seed.rs b/packages/ciphernode/events/src/seed.rs new file mode 100644 index 00000000..bcd624f4 --- /dev/null +++ b/packages/ciphernode/events/src/seed.rs @@ -0,0 +1,30 @@ +use alloy::hex; +use alloy_primitives::Uint; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Seed(pub [u8; 32]); +impl From for u64 { + fn from(value: Seed) -> Self { + u64::from_le_bytes(value.0[..8].try_into().unwrap()) + } +} + +impl From for [u8; 32] { + fn from(value: Seed) -> Self { + value.0 + } +} + +impl From> for Seed { + fn from(value: Uint<256, 4>) -> Self { + Seed(value.to_le_bytes()) + } +} + +impl Display for Seed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Seed(0x{})", hex::encode(self.0)) + } +} From 72e4a57a11630a8f04d06f50e91222f07d4a8c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Tue, 31 Dec 2024 14:12:52 +1100 Subject: [PATCH 12/15] Remove tag and set info as default (#232) * Remove tag * Update docs * Update comment * Use binaries instead of running through cargo * Extract logs to separate files * Update warnings --- deploy/docker-compose.yml | 4 -- packages/ciphernode/Dockerfile | 1 - packages/ciphernode/README.md | 30 ++++------ packages/ciphernode/ciphernode-entrypoint.sh | 4 +- .../enclave/src/aggregator_start.rs | 3 +- packages/ciphernode/enclave/src/cli.rs | 55 ++++++++++++++----- packages/ciphernode/enclave/src/init.rs | 3 +- packages/ciphernode/enclave/src/main.rs | 15 ++--- .../ciphernode/enclave/src/password_create.rs | 1 + packages/ciphernode/enclave/src/start.rs | 3 +- packages/ciphernode/enclave_core/src/init.rs | 3 +- packages/ciphernode/enclave_core/src/start.rs | 4 +- packages/ciphernode/events/src/lib.rs | 2 - packages/ciphernode/events/src/tag.rs | 21 ------- packages/ciphernode/evm/src/event_reader.rs | 11 +--- .../ciphernode/sortition/src/sortition.rs | 12 ++-- tests/basic_integration/fns.sh | 44 +++++++++------ tests/basic_integration/lib/fake_encrypt.sh | 2 +- tests/basic_integration/lib/prebuild.sh | 2 +- tests/basic_integration/logs/.gitignore | 1 + tests/basic_integration/persist.sh | 2 +- 21 files changed, 105 insertions(+), 118 deletions(-) delete mode 100644 packages/ciphernode/events/src/tag.rs create mode 100644 tests/basic_integration/logs/.gitignore diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index ef076133..8f647333 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -9,7 +9,6 @@ services: target: secrets.json env_file: .env environment: - RUST_LOG: "info" AGGREGATOR: "false" ADDRESS: "0xbDA5747bFD65F08deb54cb465eB87D40e51B197E" QUIC_PORT: 9091 @@ -29,7 +28,6 @@ services: target: secrets.json env_file: .env environment: - RUST_LOG: "info" AGGREGATOR: "false" ADDRESS: "0xdD2FD4581271e230360230F9337D5c0430Bf44C0" QUIC_PORT: 9092 @@ -49,7 +47,6 @@ services: target: secrets.json env_file: .env environment: - RUST_LOG: "info" AGGREGATOR: "false" ADDRESS: "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" QUIC_PORT: 9093 @@ -71,7 +68,6 @@ services: target: secrets.json env_file: .env environment: - RUST_LOG: "info" AGGREGATOR: "true" ADDRESS: "0x8626a6940E2eb28930eFb4CeF49B2d1F2C9C1199" QUIC_PORT: 9094 diff --git a/packages/ciphernode/Dockerfile b/packages/ciphernode/Dockerfile index ab6b043b..d4782f46 100644 --- a/packages/ciphernode/Dockerfile +++ b/packages/ciphernode/Dockerfile @@ -80,7 +80,6 @@ COPY --from=ciphernode-builder --chmod=755 --chown=ciphernode:ciphernode /build/ # Environment variables for configuration ENV CONFIG_DIR=/home/ciphernode/.config/enclave ENV DATA_DIR=/home/ciphernode/.local/share/enclave -ENV RUST_LOG=info # Add entrypoint script ENTRYPOINT ["ciphernode-entrypoint.sh"] diff --git a/packages/ciphernode/README.md b/packages/ciphernode/README.md index 9e998890..e41c9a97 100644 --- a/packages/ciphernode/README.md +++ b/packages/ciphernode/README.md @@ -83,33 +83,27 @@ sequenceDiagram # Debugging -You can debug using the `RUST_LOG` environment var to alter what output is produced by the node +You can debug using the `-vvv` cli arguments to alter what output is produced by the node ``` -RUST_LOG=info enclave start +# Show INFO logging +enclave start ``` -if you supply a tag as an argument you can filter for that tag +``` +# Show DEBUG logging +enclave start -v +``` ``` -RUST_LOG="[sortition{id=cn1}]" enclave start --tag cn1 +# Show TRACE logging +enclave start -vv ``` -This helps filter noise during tests where you might have multiple instances running and you need to see the output of a specific one. +if you want to remove all output aside from errors you can use the `--quiet` argument. -In order to add tracing to a method or function it is recommended to use the `instrument` macro. -```rust -impl Sorition { - // ... - #[instrument(name="sortition", skip_all, fields(id = get_tag()))] - pub async fn attach( - bus: &Addr, - store: Repository, - ) -> Result> { - // ... - } -} ``` - +enclave --quiet +``` diff --git a/packages/ciphernode/ciphernode-entrypoint.sh b/packages/ciphernode/ciphernode-entrypoint.sh index 917b8411..9ed6f087 100644 --- a/packages/ciphernode/ciphernode-entrypoint.sh +++ b/packages/ciphernode/ciphernode-entrypoint.sh @@ -40,10 +40,10 @@ if [ "$AGGREGATOR" = "true" ]; then enclave wallet set --config "$CONFIG_FILE" --private-key "$PRIVATE_KEY" echo "Starting aggregator" - exec enclave aggregator start --config "$CONFIG_FILE" + exec enclave aggregator start -v --config "$CONFIG_FILE" else echo "Starting Ciphernode" - exec enclave start --config "$CONFIG_FILE" + exec enclave start -v --config "$CONFIG_FILE" fi diff --git a/packages/ciphernode/enclave/src/aggregator_start.rs b/packages/ciphernode/enclave/src/aggregator_start.rs index 326dc8cb..56d34107 100644 --- a/packages/ciphernode/enclave/src/aggregator_start.rs +++ b/packages/ciphernode/enclave/src/aggregator_start.rs @@ -1,12 +1,11 @@ use anyhow::*; use config::AppConfig; use enclave_core::{aggregator_start, listen_for_shutdown}; -use events::get_tag; use tracing::{info, instrument}; use crate::owo; -#[instrument(name="app", skip_all,fields(id = get_tag()))] +#[instrument(name = "app", skip_all)] pub async fn execute( config: AppConfig, pubkey_write_path: Option<&str>, diff --git a/packages/ciphernode/enclave/src/cli.rs b/packages/ciphernode/enclave/src/cli.rs index 72674d19..0f7abc00 100644 --- a/packages/ciphernode/enclave/src/cli.rs +++ b/packages/ciphernode/enclave/src/cli.rs @@ -5,10 +5,9 @@ use crate::wallet::WalletCommands; use crate::{aggregator, init, password, wallet}; use crate::{aggregator::AggregatorCommands, start}; use anyhow::Result; -use clap::{command, Parser, Subcommand}; +use clap::{command, ArgAction, Parser, Subcommand}; use config::load_config; -use events::get_tag; -use tracing::instrument; +use tracing::{instrument, Level}; #[derive(Parser, Debug)] #[command(name = "enclave")] @@ -21,12 +20,48 @@ pub struct Cli { #[command(subcommand)] command: Commands, - #[arg(short, long, global = true)] - tag: Option, + /// Indicate error levels by adding additional `-v` arguments. Eg. `enclave -vvv` will give you + /// trace level output + #[arg( + short, + long, + action = ArgAction::Count, + global = true + )] + pub verbose: u8, + + /// Silence all output. This argument cannot be used alongside `-v` + #[arg( + short, + long, + action = ArgAction::SetTrue, + conflicts_with = "verbose", + global = true + )] + quiet: bool, + + // NOTE: The --tag argument is being left here deliberately so that we can target the aggregator in our tests + // to be killed. We may wish to extend it later to other logging. + /// Tag is not currently used but may be used in the future. + #[arg(long, global = true)] + pub tag: Option, } impl Cli { - #[instrument(skip(self),fields(id = get_tag()))] + pub fn log_level(&self) -> Level { + if self.quiet { + Level::ERROR + } else { + match self.verbose { + 0 => Level::WARN, // + 1 => Level::INFO, // -v + 2 => Level::DEBUG, // -vv + _ => Level::TRACE, // -vvv + } + } + } + + #[instrument(skip(self))] pub async fn execute(self) -> Result<()> { let config_path = self.config.as_deref(); let config = load_config(config_path)?; @@ -59,14 +94,6 @@ impl Cli { Ok(()) } - - pub fn get_tag(&self) -> String { - if let Some(tag) = self.tag.clone() { - tag - } else { - "default".to_string() - } - } } #[derive(Subcommand, Debug)] diff --git a/packages/ciphernode/enclave/src/init.rs b/packages/ciphernode/enclave/src/init.rs index 0c36edd5..81c99cc9 100644 --- a/packages/ciphernode/enclave/src/init.rs +++ b/packages/ciphernode/enclave/src/init.rs @@ -1,7 +1,6 @@ use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Input}; use enclave_core::init; -use events::get_tag; use tracing::instrument; use crate::net; @@ -9,7 +8,7 @@ use crate::net::NetCommands; use crate::password; use crate::password::PasswordCommands; -#[instrument(name = "app", skip_all, fields(id = get_tag()))] +#[instrument(name = "app", skip_all)] pub async fn execute( rpc_url: Option, eth_address: Option, diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index 8e344196..6e65ade9 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -1,7 +1,6 @@ use clap::Parser; use cli::Cli; -use events::set_tag; -use tracing::info; +use tracing::{info, Level}; use tracing_subscriber::EnvFilter; mod aggregator; @@ -43,18 +42,12 @@ pub fn owo() { #[actix::main] pub async fn main() { - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .init(); - info!("COMPILATION ID: '{}'", helpers::compile_id::generate_id()); let cli = Cli::parse(); - - // Set the tag for all future traces - if let Err(err) = set_tag(cli.get_tag()) { - eprintln!("{}", err); - } + tracing_subscriber::fmt() + .with_max_level(cli.log_level()) + .init(); // Execute the cli if let Err(err) = cli.execute().await { diff --git a/packages/ciphernode/enclave/src/password_create.rs b/packages/ciphernode/enclave/src/password_create.rs index 46d7ec94..43feb525 100644 --- a/packages/ciphernode/enclave/src/password_create.rs +++ b/packages/ciphernode/enclave/src/password_create.rs @@ -42,6 +42,7 @@ fn get_zeroizing_pw_vec(input: Option) -> Result>> { } pub async fn execute(config: &AppConfig, input: Option, overwrite: bool) -> Result<()> { + println!("Setting password..."); password_create::preflight(config, overwrite).await?; let pw = get_zeroizing_pw_vec(input)?; diff --git a/packages/ciphernode/enclave/src/start.rs b/packages/ciphernode/enclave/src/start.rs index 4a2abb03..79a89f79 100644 --- a/packages/ciphernode/enclave/src/start.rs +++ b/packages/ciphernode/enclave/src/start.rs @@ -2,10 +2,9 @@ use crate::owo; use anyhow::{anyhow, Result}; use config::AppConfig; use enclave_core::{listen_for_shutdown, start}; -use events::get_tag; use tracing::{info, instrument}; -#[instrument(name="app", skip_all,fields(id = get_tag()))] +#[instrument(name = "app", skip_all)] pub async fn execute(config: AppConfig) -> Result<()> { owo(); let Some(address) = config.address() else { diff --git a/packages/ciphernode/enclave_core/src/init.rs b/packages/ciphernode/enclave_core/src/init.rs index 7b949ec1..d4531cfe 100644 --- a/packages/ciphernode/enclave_core/src/init.rs +++ b/packages/ciphernode/enclave_core/src/init.rs @@ -3,7 +3,6 @@ use anyhow::{anyhow, bail, Result}; use config::load_config; use config::AppConfig; use config::RPC; -use events::get_tag; use std::fs; use tracing::instrument; @@ -31,7 +30,7 @@ pub fn validate_eth_address(address: &String) -> Result<()> { } } -#[instrument(name = "app", skip_all, fields(id = get_tag()))] +#[instrument(name = "app", skip_all)] pub async fn execute(rpc_url: String, eth_address: Option) -> Result { let config_dir = dirs::home_dir() .ok_or_else(|| anyhow!("Could not determine home directory"))? diff --git a/packages/ciphernode/enclave_core/src/start.rs b/packages/ciphernode/enclave_core/src/start.rs index cc94db08..2a76bd24 100644 --- a/packages/ciphernode/enclave_core/src/start.rs +++ b/packages/ciphernode/enclave_core/src/start.rs @@ -5,7 +5,7 @@ use config::AppConfig; use crypto::Cipher; use data::RepositoriesFactory; use e3_request::E3Router; -use events::{get_tag, EventBus}; +use events::EventBus; use evm::{ helpers::ProviderConfig, CiphernodeRegistryReaderRepositoryFactory, CiphernodeRegistrySol, EnclaveSolReader, EnclaveSolReaderRepositoryFactory, @@ -25,7 +25,7 @@ use tracing::instrument; use crate::helpers::datastore::setup_datastore; -#[instrument(name="app", skip_all,fields(id = get_tag()))] +#[instrument(name = "app", skip_all)] pub async fn execute( config: AppConfig, address: Address, diff --git a/packages/ciphernode/events/src/lib.rs b/packages/ciphernode/events/src/lib.rs index fa787cb2..13cf2c47 100644 --- a/packages/ciphernode/events/src/lib.rs +++ b/packages/ciphernode/events/src/lib.rs @@ -4,7 +4,6 @@ mod event_id; mod eventbus; mod ordered_set; mod seed; -mod tag; pub use e3id::*; pub use enclave_event::*; @@ -12,4 +11,3 @@ pub use event_id::*; pub use eventbus::*; pub use ordered_set::*; pub use seed::*; -pub use tag::*; diff --git a/packages/ciphernode/events/src/tag.rs b/packages/ciphernode/events/src/tag.rs deleted file mode 100644 index 7b67ebba..00000000 --- a/packages/ciphernode/events/src/tag.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Tag management for EVM event processing. -//! -//! This module provides thread-safe access to a global string tag that's used to -//! differentiate between different EVM contract instances during event processing. -//! The tag helps track and manage historical and live events for specific contracts. - -use std::sync::OnceLock; - -/// Global tag for contract event tracking with a default value of "_". -/// This tag is initialized once and remains constant throughout the lifecycle -/// of event processing to ensure consistent event tracking across restarts. -static TAG: OnceLock = OnceLock::new(); - -pub fn get_tag() -> String { - TAG.get().cloned().unwrap_or_else(|| String::from("_")) -} - -pub fn set_tag(new_tag: impl Into) -> Result<(), &'static str> { - TAG.set(new_tag.into()) - .map_err(|_| "Tag has already been initialized") -} diff --git a/packages/ciphernode/evm/src/event_reader.rs b/packages/ciphernode/evm/src/event_reader.rs index b279e126..1b873892 100644 --- a/packages/ciphernode/evm/src/event_reader.rs +++ b/packages/ciphernode/evm/src/event_reader.rs @@ -9,7 +9,7 @@ use alloy::rpc::types::Filter; use alloy::transports::{BoxTransport, Transport}; use anyhow::{anyhow, Result}; use data::{AutoPersist, Persistable, Repository}; -use events::{get_tag, BusError, EnclaveErrorType, EnclaveEvent, EventBus, EventId, Subscribe}; +use events::{BusError, EnclaveErrorType, EnclaveEvent, EventBus, EventId, Subscribe}; use futures_util::stream::StreamExt; use std::collections::HashSet; use tokio::select; @@ -35,8 +35,6 @@ impl EnclaveEvmEvent { pub type ExtractorFn = fn(&LogData, Option<&B256>, u64) -> Option; -pub type EventReader = EvmEventReader; - pub struct EvmEventReaderParams where P: Provider + Clone + 'static, @@ -151,7 +149,6 @@ where let contract_address = self.contract_address; let start_block = self.start_block; - let tag = get_tag(); ctx.spawn( async move { stream_from_evm( @@ -162,7 +159,6 @@ where shutdown, start_block, &bus, - &tag, ) .await } @@ -171,7 +167,7 @@ where } } -#[instrument(name = "evm_event_reader", skip_all, fields(id=id))] +#[instrument(name = "evm_event_reader", skip_all)] async fn stream_from_evm, T: Transport + Clone>( provider: WithChainId, contract_address: &Address, @@ -180,7 +176,6 @@ async fn stream_from_evm, T: Transport + Clone>( mut shutdown: oneshot::Receiver<()>, start_block: Option, bus: &Addr, - id: &str, ) { let chain_id = provider.get_chain_id(); let provider = provider.get_provider(); @@ -270,7 +265,7 @@ where { type Result = (); - #[instrument(name="evm_event_reader", skip_all, fields(id = get_tag()))] + #[instrument(name = "evm_event_reader", skip_all)] fn handle(&mut self, wrapped: EnclaveEvmEvent, _: &mut Self::Context) -> Self::Result { match self.state.try_mutate(|mut state| { let event_id = wrapped.get_id(); diff --git a/packages/ciphernode/sortition/src/sortition.rs b/packages/ciphernode/sortition/src/sortition.rs index c0b0f569..6dab3ff1 100644 --- a/packages/ciphernode/sortition/src/sortition.rs +++ b/packages/ciphernode/sortition/src/sortition.rs @@ -4,8 +4,8 @@ use alloy::primitives::Address; use anyhow::{anyhow, Result}; use data::{AutoPersist, Persistable, Repository}; use events::{ - get_tag, BusError, CiphernodeAdded, CiphernodeRemoved, EnclaveErrorType, EnclaveEvent, - EventBus, Seed, Subscribe, + BusError, CiphernodeAdded, CiphernodeRemoved, EnclaveErrorType, EnclaveEvent, EventBus, Seed, + Subscribe, }; use std::collections::HashSet; use tracing::{info, instrument}; @@ -104,7 +104,7 @@ impl Sortition { } } - #[instrument(name="sortition", skip_all, fields(id = get_tag()))] + #[instrument(name = "sortition", skip_all)] pub async fn attach( bus: &Addr, store: Repository, @@ -142,7 +142,7 @@ impl Handler for Sortition { impl Handler for Sortition { type Result = (); - #[instrument(name="sortition", skip_all, fields(id = get_tag()))] + #[instrument(name = "sortition", skip_all)] fn handle(&mut self, msg: CiphernodeAdded, _ctx: &mut Self::Context) -> Self::Result { info!("Adding node: {}", msg.address); match self.list.try_mutate(|mut list| { @@ -158,7 +158,7 @@ impl Handler for Sortition { impl Handler for Sortition { type Result = (); - #[instrument(name="sortition", skip_all, fields(id = get_tag()))] + #[instrument(name = "sortition", skip_all)] fn handle(&mut self, msg: CiphernodeRemoved, _ctx: &mut Self::Context) -> Self::Result { info!("Removing node: {}", msg.address); match self.list.try_mutate(|mut list| { @@ -174,7 +174,7 @@ impl Handler for Sortition { impl Handler for Sortition { type Result = bool; - #[instrument(name="sortition", skip_all, fields(id = get_tag()))] + #[instrument(name = "sortition", skip_all)] fn handle(&mut self, msg: GetHasNode, _ctx: &mut Self::Context) -> Self::Result { self.list .try_with(|list| list.contains(msg.seed, msg.size, msg.address)) diff --git a/tests/basic_integration/fns.sh b/tests/basic_integration/fns.sh index be24e90a..f44fee0b 100644 --- a/tests/basic_integration/fns.sh +++ b/tests/basic_integration/fns.sh @@ -38,6 +38,7 @@ NETWORK_PRIVATE_KEY_2="0x21a1e500a548b70d88184a1e042900c0ed6c57f8710bcc35dc8c85f NETWORK_PRIVATE_KEY_3="0x31a1e500a548b70d88184a1e042900c0ed6c57f8710bcc35dc8c85fa33d3f580" NETWORK_PRIVATE_KEY_4="0x41a1e500a548b70d88184a1e042900c0ed6c57f8710bcc35dc8c85fa33d3f580" +ENCLAVE_BIN=./packages/ciphernode/target/debug/enclave # Function to clean up background processes cleanup() { @@ -87,24 +88,28 @@ waiton-files() { set_password() { local name="$1" local password="$2" - yarn enclave password create \ + $ENCLAVE_BIN password create \ --config "$SCRIPT_DIR/lib/$name/config.yaml" \ --password "$password" } launch_ciphernode() { - local name="$1" - heading "Launch ciphernode $name" - yarn enclave start \ - --tag "$name" \ - --config "$SCRIPT_DIR/lib/$name/config.yaml" & echo $! > "/tmp/enclave.${ID}_${name}.pid" + local name="$1" + local log_file="${SCRIPT_DIR}/logs/${name}.log" + local log_dir="$(dirname "$log_file")" + heading "Launch ciphernode $name" + # Make sure the logs directory exists + mkdir -p "$log_dir" + $ENCLAVE_BIN start -v \ + --tag "$name" \ + --config "$SCRIPT_DIR/lib/$name/config.yaml" 2>&1 | tee "$log_file" & echo $! > "/tmp/enclave.${ID}_${name}.pid" } set_private_key() { local name="$1" local private_key="$2" - yarn enclave wallet set \ + $ENCLAVE_BIN wallet set \ --config "$SCRIPT_DIR/lib/$name/config.yaml" \ --private-key "$private_key" } @@ -113,22 +118,25 @@ set_network_private_key() { local name="$1" local private_key="$2" - yarn enclave net set-key \ + $ENCLAVE_BIN net set-key \ --config "$SCRIPT_DIR/lib/$name/config.yaml" \ --net-keypair "$private_key" } launch_aggregator() { - local name="$1" - heading "Launch aggregator $name" - - yarn enclave aggregator start \ - --tag "$name" \ - --config "$SCRIPT_DIR/lib/$name/config.yaml" \ - --pubkey-write-path "$SCRIPT_DIR/output/pubkey.bin" \ - --plaintext-write-path "$SCRIPT_DIR/output/plaintext.txt" & echo $! > "/tmp/enclave.${ID}_${name}.pid" - - ps aux | grep aggregator + local name="$1" + local suffix="${2:-}" # Optional suffix with empty default + local log_name="${name}${suffix:+_$suffix}" # Add suffix with underscore if provided + local log_file="${SCRIPT_DIR}/logs/${log_name}.log" + local log_dir="$(dirname "$log_file")" + heading "Launch aggregator $name" + # Make sure the logs directory exists + mkdir -p "$log_dir" + $ENCLAVE_BIN aggregator start -v \ + --tag "$name" \ + --config "$SCRIPT_DIR/lib/$name/config.yaml" \ + --pubkey-write-path "$SCRIPT_DIR/output/pubkey.bin" \ + --plaintext-write-path "$SCRIPT_DIR/output/plaintext.txt" 2>&1 | tee "$log_file" & echo $! > "/tmp/enclave.${ID}_${name}.pid" } kill_proc() { diff --git a/tests/basic_integration/lib/fake_encrypt.sh b/tests/basic_integration/lib/fake_encrypt.sh index 512a99ad..f8e8c6a1 100755 --- a/tests/basic_integration/lib/fake_encrypt.sh +++ b/tests/basic_integration/lib/fake_encrypt.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd packages/ciphernode && RUSTFLAGS="-A warnings" cargo run --bin fake_encrypt -- "$@" +./packages/ciphernode/target/debug/fake_encrypt "$@" diff --git a/tests/basic_integration/lib/prebuild.sh b/tests/basic_integration/lib/prebuild.sh index fb9bfe7c..33c7ce39 100755 --- a/tests/basic_integration/lib/prebuild.sh +++ b/tests/basic_integration/lib/prebuild.sh @@ -1,3 +1,3 @@ #!/usr/bin/env sh -cd packages/ciphernode && RUSTFLAGS="-A warnings" cargo build --bin fake_encrypt --bin enclave --bin pack_e3_params; +cd packages/ciphernode && cargo build --bin fake_encrypt --bin enclave --bin pack_e3_params; diff --git a/tests/basic_integration/logs/.gitignore b/tests/basic_integration/logs/.gitignore new file mode 100644 index 00000000..397b4a76 --- /dev/null +++ b/tests/basic_integration/logs/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/tests/basic_integration/persist.sh b/tests/basic_integration/persist.sh index 6372e6ee..6f795e20 100755 --- a/tests/basic_integration/persist.sh +++ b/tests/basic_integration/persist.sh @@ -68,7 +68,7 @@ kill_proc ag sleep 2 # relaunch the aggregator -launch_aggregator ag +launch_aggregator ag relaunch sleep 2 From 55c4f66adf33d1f3d69cb10a555e23cc5b939e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Tue, 31 Dec 2024 15:32:27 +1100 Subject: [PATCH 13/15] Parallel tests (#234) * Tidy up tests and logfiles * Split off prebuild from tests * Try running tests in parallel * Add typechain bindings * Dont run all tests in prebuild --- .github/workflows/integration.yml | 84 ++++++++++++++----- package.json | 2 +- tests/basic_integration/lib/pack_e3_params.sh | 3 - .../README.md | 0 .../base.sh | 0 .../{basic_integration => integration}/fns.sh | 9 +- .../lib/ag/.gitignore | 0 .../lib/ag/config.yaml | 0 .../lib/clean_folders.sh | 0 .../lib/cn1/.gitignore | 0 .../lib/cn1/config.yaml | 0 .../lib/cn2/.gitignore | 0 .../lib/cn2/config.yaml | 0 .../lib/cn3/.gitignore | 0 .../lib/cn3/config.yaml | 0 .../lib/cn4/.gitignore | 0 .../lib/cn4/config.yaml | 0 .../lib/fake_encrypt.sh | 0 tests/integration/lib/pack_e3_params.sh | 3 + .../lib/prebuild.sh | 7 +- .../logs/.gitignore | 0 .../{basic_integration => integration}/net.sh | 0 .../output/.gitignore | 0 .../persist.sh | 0 tests/integration/prebuild.sh | 9 ++ .../test.sh | 4 + 26 files changed, 93 insertions(+), 28 deletions(-) delete mode 100755 tests/basic_integration/lib/pack_e3_params.sh rename tests/{basic_integration => integration}/README.md (100%) rename tests/{basic_integration => integration}/base.sh (100%) rename tests/{basic_integration => integration}/fns.sh (94%) rename tests/{basic_integration => integration}/lib/ag/.gitignore (100%) rename tests/{basic_integration => integration}/lib/ag/config.yaml (100%) rename tests/{basic_integration => integration}/lib/clean_folders.sh (100%) rename tests/{basic_integration => integration}/lib/cn1/.gitignore (100%) rename tests/{basic_integration => integration}/lib/cn1/config.yaml (100%) rename tests/{basic_integration => integration}/lib/cn2/.gitignore (100%) rename tests/{basic_integration => integration}/lib/cn2/config.yaml (100%) rename tests/{basic_integration => integration}/lib/cn3/.gitignore (100%) rename tests/{basic_integration => integration}/lib/cn3/config.yaml (100%) rename tests/{basic_integration => integration}/lib/cn4/.gitignore (100%) rename tests/{basic_integration => integration}/lib/cn4/config.yaml (100%) rename tests/{basic_integration => integration}/lib/fake_encrypt.sh (100%) create mode 100755 tests/integration/lib/pack_e3_params.sh rename tests/{basic_integration => integration}/lib/prebuild.sh (52%) rename tests/{basic_integration => integration}/logs/.gitignore (100%) rename tests/{basic_integration => integration}/net.sh (100%) rename tests/{basic_integration => integration}/output/.gitignore (100%) rename tests/{basic_integration => integration}/persist.sh (100%) create mode 100755 tests/integration/prebuild.sh rename tests/{basic_integration => integration}/test.sh (78%) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 0861cc5c..9e4f72ec 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -1,20 +1,8 @@ name: "INTEGRATION" - env: HARDHAT_VAR_MNEMONIC: "test test test test test test test test test test test junk" HARDHAT_VAR_INFURA_API_KEY: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" PRIVATE_KEY: "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" - # Uncomment the following lines to set your configuration variables using - # GitHub secrets (https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions) - # - # HARDHAT_VAR_MNEMONIC: ${{ secrets.Mnemonic }} - # HARDHAT_VAR_INFURA_API_KEY: ${{ secrets.InfuraApiKey }} - # HARDHAT_VAR_ARBISCAN_API_KEY: ${{ secrets.ArbiscanApiKey }} - # HARDHAT_VAR_BSCSCAN_API_KEY: ${{ secrets.BscscanApiKey }} - # HARDHAT_VAR_ETHERSCAN_API_KEY: ${{ secrets.EtherscanApiKey }} - # HARDHAT_VAR_OPTIMISM_API_KEY: ${{ secrets.OptimismApiKey }} - # HARDHAT_VAR_POLYGONSCAN_API_KEY: ${{ secrets.PolygonscanApiKey }} - # HARDHAT_VAR_SNOWTRACE_API_KEY: ${{ secrets.SnowtraceApiKey }} on: workflow_dispatch: @@ -24,17 +12,17 @@ on: - main jobs: - ci: + prebuild: runs-on: "ubuntu-latest" steps: - name: "Check out the repo" uses: "actions/checkout@v4" - + - name: "Setup node" uses: actions/setup-node@v2 with: node-version: 20 - + - name: Install Rust 1.81.0 uses: actions-rs/toolchain@v1 with: @@ -46,10 +34,10 @@ jobs: with: path: "**/node_modules" key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} - + - name: "Install the dependencies" run: "yarn install" - + - name: "Lint the code" run: "yarn lint" @@ -61,10 +49,66 @@ jobs: - name: "Compile the contracts and generate the TypeChain bindings" run: "yarn typechain" - - name: "Test the contracts and generate the coverage report" - run: "yarn test:integration" + - name: "Run prebuild" + run: "yarn test:integration prebuild" + + - name: "Upload build artifacts" + uses: actions/upload-artifact@v3 + with: + name: build-artifacts + path: | + packages/ciphernode/target/debug/enclave + packages/ciphernode/target/debug/fake_encrypt + packages/ciphernode/target/debug/pack_e3_params + test: + needs: prebuild + runs-on: "ubuntu-latest" + strategy: + matrix: + test-suite: [base, persist, net] + fail-fast: false + + steps: + - name: "Check out the repo" + uses: "actions/checkout@v4" + + - name: "Setup node" + uses: actions/setup-node@v2 + with: + node-version: 20 + + - name: Install Rust 1.81.0 + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.81.0 + override: true + + - name: Cache node modules + uses: actions/cache@v2 + with: + path: "**/node_modules" + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} + + - name: "Install the dependencies" + run: "yarn install" + + - name: "Download build artifacts" + uses: actions/download-artifact@v3 + with: + name: build-artifacts + path: packages/ciphernode/target/debug/ + + - name: "Set executable permissions" + run: | + chmod +x packages/ciphernode/target/debug/enclave + chmod +x packages/ciphernode/target/debug/fake_encrypt + chmod +x packages/ciphernode/target/debug/pack_e3_params + + - name: "Run ${{ matrix.test-suite }} tests" + run: "yarn test:integration ${{ matrix.test-suite }} --no-prebuild" + - name: "Add test summary" run: | - echo "## Test results" >> $GITHUB_STEP_SUMMARY + echo "## Test results for ${{ matrix.test-suite }}" >> $GITHUB_STEP_SUMMARY echo "✅ Passed" >> $GITHUB_STEP_SUMMARY diff --git a/package.json b/package.json index 1e3167ff..196ff0ad 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "lint": "yarn evm:lint", "typechain": "yarn evm:typechain", "test": "yarn evm:test && yarn ciphernode:test", - "test:integration": "./tests/basic_integration/test.sh", + "test:integration": "./tests/integration/test.sh", "coverage": "yarn evm:coverage", "enclave": "cd packages/ciphernode && ./scripts/launch.sh", "ciphernode:lint": "cd packages/ciphernode && cargo fmt -- --check", diff --git a/tests/basic_integration/lib/pack_e3_params.sh b/tests/basic_integration/lib/pack_e3_params.sh deleted file mode 100755 index 78502e2e..00000000 --- a/tests/basic_integration/lib/pack_e3_params.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -cd packages/ciphernode && RUSTFLAGS="-A warnings" cargo run --bin pack_e3_params -- "$@" diff --git a/tests/basic_integration/README.md b/tests/integration/README.md similarity index 100% rename from tests/basic_integration/README.md rename to tests/integration/README.md diff --git a/tests/basic_integration/base.sh b/tests/integration/base.sh similarity index 100% rename from tests/basic_integration/base.sh rename to tests/integration/base.sh diff --git a/tests/basic_integration/fns.sh b/tests/integration/fns.sh similarity index 94% rename from tests/basic_integration/fns.sh rename to tests/integration/fns.sh index f44fee0b..c239e302 100644 --- a/tests/basic_integration/fns.sh +++ b/tests/integration/fns.sh @@ -56,6 +56,10 @@ heading() { echo "" } +strip_ansi() { + sed 's/\x1b\[[0-9;]*m//g' +} + waiton() { local file_path="$1" until [ -f "$file_path" ]; do @@ -102,7 +106,7 @@ launch_ciphernode() { mkdir -p "$log_dir" $ENCLAVE_BIN start -v \ --tag "$name" \ - --config "$SCRIPT_DIR/lib/$name/config.yaml" 2>&1 | tee "$log_file" & echo $! > "/tmp/enclave.${ID}_${name}.pid" + --config "$SCRIPT_DIR/lib/$name/config.yaml" 2>&1 | tee >(strip_ansi > "$log_file") & echo $! > "/tmp/enclave.${ID}_${name}.pid" } set_private_key() { @@ -136,7 +140,7 @@ launch_aggregator() { --tag "$name" \ --config "$SCRIPT_DIR/lib/$name/config.yaml" \ --pubkey-write-path "$SCRIPT_DIR/output/pubkey.bin" \ - --plaintext-write-path "$SCRIPT_DIR/output/plaintext.txt" 2>&1 | tee "$log_file" & echo $! > "/tmp/enclave.${ID}_${name}.pid" + --plaintext-write-path "$SCRIPT_DIR/output/plaintext.txt" 2>&1 | tee >(strip_ansi > "$log_file") & echo $! > "/tmp/enclave.${ID}_${name}.pid" } kill_proc() { @@ -165,6 +169,5 @@ metallica trap 'cleanup $?' ERR INT TERM $SCRIPT_DIR/lib/clean_folders.sh "$SCRIPT_DIR" -$SCRIPT_DIR/lib/prebuild.sh diff --git a/tests/basic_integration/lib/ag/.gitignore b/tests/integration/lib/ag/.gitignore similarity index 100% rename from tests/basic_integration/lib/ag/.gitignore rename to tests/integration/lib/ag/.gitignore diff --git a/tests/basic_integration/lib/ag/config.yaml b/tests/integration/lib/ag/config.yaml similarity index 100% rename from tests/basic_integration/lib/ag/config.yaml rename to tests/integration/lib/ag/config.yaml diff --git a/tests/basic_integration/lib/clean_folders.sh b/tests/integration/lib/clean_folders.sh similarity index 100% rename from tests/basic_integration/lib/clean_folders.sh rename to tests/integration/lib/clean_folders.sh diff --git a/tests/basic_integration/lib/cn1/.gitignore b/tests/integration/lib/cn1/.gitignore similarity index 100% rename from tests/basic_integration/lib/cn1/.gitignore rename to tests/integration/lib/cn1/.gitignore diff --git a/tests/basic_integration/lib/cn1/config.yaml b/tests/integration/lib/cn1/config.yaml similarity index 100% rename from tests/basic_integration/lib/cn1/config.yaml rename to tests/integration/lib/cn1/config.yaml diff --git a/tests/basic_integration/lib/cn2/.gitignore b/tests/integration/lib/cn2/.gitignore similarity index 100% rename from tests/basic_integration/lib/cn2/.gitignore rename to tests/integration/lib/cn2/.gitignore diff --git a/tests/basic_integration/lib/cn2/config.yaml b/tests/integration/lib/cn2/config.yaml similarity index 100% rename from tests/basic_integration/lib/cn2/config.yaml rename to tests/integration/lib/cn2/config.yaml diff --git a/tests/basic_integration/lib/cn3/.gitignore b/tests/integration/lib/cn3/.gitignore similarity index 100% rename from tests/basic_integration/lib/cn3/.gitignore rename to tests/integration/lib/cn3/.gitignore diff --git a/tests/basic_integration/lib/cn3/config.yaml b/tests/integration/lib/cn3/config.yaml similarity index 100% rename from tests/basic_integration/lib/cn3/config.yaml rename to tests/integration/lib/cn3/config.yaml diff --git a/tests/basic_integration/lib/cn4/.gitignore b/tests/integration/lib/cn4/.gitignore similarity index 100% rename from tests/basic_integration/lib/cn4/.gitignore rename to tests/integration/lib/cn4/.gitignore diff --git a/tests/basic_integration/lib/cn4/config.yaml b/tests/integration/lib/cn4/config.yaml similarity index 100% rename from tests/basic_integration/lib/cn4/config.yaml rename to tests/integration/lib/cn4/config.yaml diff --git a/tests/basic_integration/lib/fake_encrypt.sh b/tests/integration/lib/fake_encrypt.sh similarity index 100% rename from tests/basic_integration/lib/fake_encrypt.sh rename to tests/integration/lib/fake_encrypt.sh diff --git a/tests/integration/lib/pack_e3_params.sh b/tests/integration/lib/pack_e3_params.sh new file mode 100755 index 00000000..48ed6dd6 --- /dev/null +++ b/tests/integration/lib/pack_e3_params.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +./packages/ciphernode/target/debug/pack_e3_params "$@" diff --git a/tests/basic_integration/lib/prebuild.sh b/tests/integration/lib/prebuild.sh similarity index 52% rename from tests/basic_integration/lib/prebuild.sh rename to tests/integration/lib/prebuild.sh index 33c7ce39..8b8a957e 100755 --- a/tests/basic_integration/lib/prebuild.sh +++ b/tests/integration/lib/prebuild.sh @@ -1,3 +1,8 @@ #!/usr/bin/env sh - +echo "" +echo "PREBUILDING BINARIES..." +echo "" cd packages/ciphernode && cargo build --bin fake_encrypt --bin enclave --bin pack_e3_params; +echo "" +echo "FINISHED PREBUILDING BINARIES" +echo "" diff --git a/tests/basic_integration/logs/.gitignore b/tests/integration/logs/.gitignore similarity index 100% rename from tests/basic_integration/logs/.gitignore rename to tests/integration/logs/.gitignore diff --git a/tests/basic_integration/net.sh b/tests/integration/net.sh similarity index 100% rename from tests/basic_integration/net.sh rename to tests/integration/net.sh diff --git a/tests/basic_integration/output/.gitignore b/tests/integration/output/.gitignore similarity index 100% rename from tests/basic_integration/output/.gitignore rename to tests/integration/output/.gitignore diff --git a/tests/basic_integration/persist.sh b/tests/integration/persist.sh similarity index 100% rename from tests/basic_integration/persist.sh rename to tests/integration/persist.sh diff --git a/tests/integration/prebuild.sh b/tests/integration/prebuild.sh new file mode 100755 index 00000000..ada361cb --- /dev/null +++ b/tests/integration/prebuild.sh @@ -0,0 +1,9 @@ + +#!/usr/bin/env bash + +set -eu # Exit immediately if a command exits with a non-zero status + +# This script deliberately does nothing as prebuild has been managed by the outer process +# See test.sh +# +echo "Prebuild finished" diff --git a/tests/basic_integration/test.sh b/tests/integration/test.sh similarity index 78% rename from tests/basic_integration/test.sh rename to tests/integration/test.sh index 7e7ad3fd..992d7143 100755 --- a/tests/basic_integration/test.sh +++ b/tests/integration/test.sh @@ -4,6 +4,10 @@ set -eu # Exit immediately if a command exits with a non-zero status THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +if [[ "$*" != *"--no-prebuild"* ]]; then + "$THIS_DIR/lib/prebuild.sh" +fi + if [ $# -eq 0 ]; then "$THIS_DIR/persist.sh" "$THIS_DIR/base.sh" From 2067dc8e551d2d5d7a5243839a5a035f3eeec13b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Tue, 31 Dec 2024 15:48:10 +1100 Subject: [PATCH 14/15] Try and run the net test in parallel (#235) --- .github/workflows/integration.yml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 9e4f72ec..3ee30eb9 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -12,6 +12,25 @@ on: - main jobs: + test-net: + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v4" + + - name: "Setup node" + uses: actions/setup-node@v2 + with: + node-version: 20 + + - name: "Run network tests" + run: "yarn test:integration net --no-prebuild" + + - name: "Add test summary" + run: | + echo "## Network test results" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + prebuild: runs-on: "ubuntu-latest" steps: @@ -66,7 +85,7 @@ jobs: runs-on: "ubuntu-latest" strategy: matrix: - test-suite: [base, persist, net] + test-suite: [base, persist] fail-fast: false steps: From 921aaa5d8cefd4740439807fd2b5e05df6c0e9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Thu, 2 Jan 2025 15:49:16 +1100 Subject: [PATCH 15/15] Rename Feature -> Extension (#238) * Feature -> Extension * feature.rs -> ext.rs * Grammatical error * Update docs * Grammer --- .../aggregator/src/{feature.rs => ext.rs} | 16 ++-- packages/ciphernode/aggregator/src/lib.rs | 3 +- packages/ciphernode/e3_request/src/context.rs | 14 ++-- packages/ciphernode/e3_request/src/meta.rs | 8 +- packages/ciphernode/e3_request/src/router.rs | 84 ++++++++++++------- .../enclave_core/src/aggregator_start.rs | 10 +-- packages/ciphernode/enclave_core/src/start.rs | 12 ++- .../ciphernode/fhe/src/{feature.rs => ext.rs} | 8 +- packages/ciphernode/fhe/src/lib.rs | 3 +- .../keyshare/src/{feature.rs => ext.rs} | 10 +-- packages/ciphernode/keyshare/src/lib.rs | 3 +- .../tests/test_aggregation_and_decryption.rs | 15 ++-- 12 files changed, 102 insertions(+), 84 deletions(-) rename packages/ciphernode/aggregator/src/{feature.rs => ext.rs} (95%) rename packages/ciphernode/fhe/src/{feature.rs => ext.rs} (93%) rename packages/ciphernode/keyshare/src/{feature.rs => ext.rs} (94%) diff --git a/packages/ciphernode/aggregator/src/feature.rs b/packages/ciphernode/aggregator/src/ext.rs similarity index 95% rename from packages/ciphernode/aggregator/src/feature.rs rename to packages/ciphernode/aggregator/src/ext.rs index bd37838f..55321df1 100644 --- a/packages/ciphernode/aggregator/src/feature.rs +++ b/packages/ciphernode/aggregator/src/ext.rs @@ -7,16 +7,16 @@ use actix::{Actor, Addr}; use anyhow::{anyhow, Result}; use async_trait::async_trait; use data::{AutoPersist, RepositoriesFactory}; -use e3_request::{E3Context, E3ContextSnapshot, E3Feature, META_KEY}; +use e3_request::{E3Context, E3ContextSnapshot, E3Extension, META_KEY}; use events::{BusError, EnclaveErrorType, EnclaveEvent, EventBus}; -use fhe::FHE_KEY; +use fhe::ext::FHE_KEY; use sortition::Sortition; -pub struct PlaintextAggregatorFeature { +pub struct PlaintextAggregatorExtension { bus: Addr, sortition: Addr, } -impl PlaintextAggregatorFeature { +impl PlaintextAggregatorExtension { pub fn create(bus: &Addr, sortition: &Addr) -> Box { Box::new(Self { bus: bus.clone(), @@ -29,7 +29,7 @@ const ERROR_PLAINTEXT_FHE_MISSING:&str = "Could not create PlaintextAggregator b const ERROR_PLAINTEXT_META_MISSING:&str = "Could not create PlaintextAggregator because the meta instance it depends on was not set on the context."; #[async_trait] -impl E3Feature for PlaintextAggregatorFeature { +impl E3Extension for PlaintextAggregatorExtension { fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent) { // Save plaintext aggregator let EnclaveEvent::CiphertextOutputPublished { data, .. } = evt else { @@ -130,12 +130,12 @@ impl E3Feature for PlaintextAggregatorFeature { } } -pub struct PublicKeyAggregatorFeature { +pub struct PublicKeyAggregatorExtension { bus: Addr, sortition: Addr, } -impl PublicKeyAggregatorFeature { +impl PublicKeyAggregatorExtension { pub fn create(bus: &Addr, sortition: &Addr) -> Box { Box::new(Self { bus: bus.clone(), @@ -148,7 +148,7 @@ const ERROR_PUBKEY_FHE_MISSING:&str = "Could not create PublicKeyAggregator beca const ERROR_PUBKEY_META_MISSING:&str = "Could not create PublicKeyAggregator because the meta instance it depends on was not set on the context."; #[async_trait] -impl E3Feature for PublicKeyAggregatorFeature { +impl E3Extension for PublicKeyAggregatorExtension { fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent) { // Saving the publickey aggregator with deps on E3Requested let EnclaveEvent::E3Requested { data, .. } = evt else { diff --git a/packages/ciphernode/aggregator/src/lib.rs b/packages/ciphernode/aggregator/src/lib.rs index 8a7415a5..dae44c58 100644 --- a/packages/ciphernode/aggregator/src/lib.rs +++ b/packages/ciphernode/aggregator/src/lib.rs @@ -1,4 +1,4 @@ -mod feature; +pub mod ext; mod plaintext_aggregator; mod publickey_aggregator; mod repo; @@ -10,5 +10,4 @@ pub use publickey_aggregator::{ PublicKeyAggregator, PublicKeyAggregatorParams, PublicKeyAggregatorState, }; -pub use feature::*; pub use repo::*; diff --git a/packages/ciphernode/e3_request/src/context.rs b/packages/ciphernode/e3_request/src/context.rs index b338861b..cfd5832c 100644 --- a/packages/ciphernode/e3_request/src/context.rs +++ b/packages/ciphernode/e3_request/src/context.rs @@ -1,4 +1,4 @@ -use crate::{E3Feature, EventBuffer, HetrogenousMap, TypedKey}; +use crate::{E3Extension, EventBuffer, HetrogenousMap, TypedKey}; use actix::Recipient; use anyhow::Result; use async_trait::async_trait; @@ -22,16 +22,12 @@ fn init_recipients() -> HashMap>> { /// Context that is set to each event hook. Hooks can use this context to gather dependencies if /// they need to instantiate struct instances or actors. -// TODO: remove Addr imports as we need to be able to move the features out of the hooks file -// without circular deps -// TODO: remove Arc import as we need to be able to move the Fhe feature out of the hooks -// file without circular deps pub struct E3Context { /// The E3Request's ID pub e3_id: E3id, /// A way to store EnclaveEvent recipients on the context pub recipients: HashMap>>, // NOTE: can be a None value - /// A way to store a feature's dependencies on the context + /// A way to store an extension's dependencies on the context pub dependencies: HetrogenousMap, /// A Repository for storing this context's data snapshot pub repository: Repository, @@ -53,7 +49,7 @@ impl E3ContextSnapshot { pub struct E3ContextParams { pub repository: Repository, pub e3_id: E3id, - pub features: Arc>>, + pub extensions: Arc>>, } impl E3Context { @@ -157,8 +153,8 @@ impl FromSnapshotWithParams for E3Context { dependencies: HetrogenousMap::new(), }; - for feature in params.features.iter() { - feature.hydrate(&mut ctx, &snapshot).await?; + for extension in params.extensions.iter() { + extension.hydrate(&mut ctx, &snapshot).await?; } Ok(ctx) diff --git a/packages/ciphernode/e3_request/src/meta.rs b/packages/ciphernode/e3_request/src/meta.rs index 257bac07..48d53c5c 100644 --- a/packages/ciphernode/e3_request/src/meta.rs +++ b/packages/ciphernode/e3_request/src/meta.rs @@ -1,4 +1,4 @@ -use crate::{E3Context, E3ContextSnapshot, E3Feature, MetaRepositoryFactory, TypedKey}; +use crate::{E3Context, E3ContextSnapshot, E3Extension, MetaRepositoryFactory, TypedKey}; use anyhow::*; use async_trait::async_trait; use data::RepositoriesFactory; @@ -13,16 +13,16 @@ pub struct E3Meta { pub src_chain_id: u64, } -pub struct E3MetaFeature; +pub struct E3MetaExtension; -impl E3MetaFeature { +impl E3MetaExtension { pub fn create() -> Box { Box::new(Self {}) } } #[async_trait] -impl E3Feature for E3MetaFeature { +impl E3Extension for E3MetaExtension { fn on_event(&self, ctx: &mut crate::E3Context, event: &EnclaveEvent) { let EnclaveEvent::E3Requested { data, .. } = event else { return; diff --git a/packages/ciphernode/e3_request/src/router.rs b/packages/ciphernode/e3_request/src/router.rs index d6e40335..14545e6d 100644 --- a/packages/ciphernode/e3_request/src/router.rs +++ b/packages/ciphernode/e3_request/src/router.rs @@ -2,7 +2,7 @@ use crate::ContextRepositoryFactory; use crate::E3Context; use crate::E3ContextParams; use crate::E3ContextSnapshot; -use crate::E3MetaFeature; +use crate::E3MetaExtension; use crate::RouterRepositoryFactory; use actix::AsyncContext; use actix::{Actor, Addr, Context, Handler}; @@ -23,7 +23,9 @@ use std::collections::HashSet; use std::{collections::HashMap, sync::Arc}; use tracing::error; -/// Helper class to buffer events for downstream instances incase events arrive in the wrong order +/// Buffers events for downstream instances to handle out-of-order event delivery. +/// Events are stored in a HashMap keyed by string identifiers until they are ready +/// to be processed. #[derive(Default)] pub struct EventBuffer { buffer: HashMap>, @@ -42,17 +44,26 @@ impl EventBuffer { } } -/// Format of a Feature that can be passed to E3Router. E3Features listen for EnclaveEvents -/// that are braoadcast to know when to instantiate themselves. They define the events they respond -/// to using the `on_event` handler. Within this handler they will typically use the request's -/// context to construct a version of their requisite actors and save their addresses to the -/// context using the `set_event_recipient` method on the context. Event recipients once set will -/// then have all their events streamed to them from their buffer. Features can also reconstruct -/// Actors based on their persisted state using the context snapshot and relevant repositories. -/// Generally Features can ask the context to see if a dependency has already been set to know if -/// it has everything it needs to construct the Feature +/// An Extension interface for the E3Router system that listens and responds to EnclaveEvents. +/// +/// # Responsibilities +/// - Listens for broadcast EnclaveEvents +/// - Instantiates appropriate actors based on received events +/// - Manages actor state persistence and reconstruction +/// - Handles event streaming to registered recipients +/// +/// # Usage +/// Extensions implement the `on_event` handler to define which events they respond to. +/// When an event is received, the extension typically: +/// 1. Uses the request's context to construct required actors +/// 2. Saves actor addresses to the context using `set_event_recipient` +/// 3. Manages event streaming from buffers to registered recipients +/// +/// Extensions can also reconstruct actors from persisted state using context +/// snapshots and repositories. They can check for dependencies in the context +/// before constructing new extensions. #[async_trait] -pub trait E3Feature: Send + Sync + 'static { +pub trait E3Extension: Send + Sync + 'static { /// This function is triggered when an EnclaveEvent is sent to the router. Use this to /// initialize the receiver using `ctx.set_event_receiver(my_address.into())`. Typically this /// means filtering for specific e3_id enabled events that give rise to actors that have to @@ -63,26 +74,35 @@ pub trait E3Feature: Send + Sync + 'static { async fn hydrate(&self, ctx: &mut E3Context, snapshot: &E3ContextSnapshot) -> Result<()>; } -/// E3Router will register features that receive an E3_id specific context. After features -/// have run e3_id specific messages are forwarded to all instances on the context as they come in. -/// This enables features to lazily register instances that have the correct dependencies available -/// per e3_id request. -// TODO: setup so that we have to place features within correct order of dependencies +/// Routes E3_id-specific contexts to registered extensions and manages message forwarding. +/// +/// # Core Functions +/// - Maintains contexts for each E3 request +/// - Lazily registers extension instances with appropriate dependencies per E3_id +/// - Forwards incoming messages to registered instances +/// - Manages request lifecycle and completion +/// +/// Extensions receive an E3_id-specific context and can handle specific +/// message types. The router ensures proper message delivery and context management +/// throughout the request lifecycle. +// TODO: setup so that we have to place extensions within correct order of dependencies pub struct E3Router { /// The context for every E3 request contexts: HashMap, /// A list of completed requests completed: HashSet, - /// The features this instance of the router is configured to listen for - features: Arc>>, + /// The extensions this instance of the router is configured to listen for + extensions: Arc>>, /// A buffer for events to send to the buffer: EventBuffer, + /// The EventBus bus: Addr, + /// A repository for storing snapshots store: Repository, } pub struct E3RouterParams { - features: Arc>>, + extensions: Arc>>, bus: Addr, store: Repository, } @@ -92,17 +112,17 @@ impl E3Router { let repositories = store.repositories(); let builder = E3RouterBuilder { bus: bus.clone(), - features: vec![], + extensions: vec![], store: repositories.router(), }; // Everything needs the committe meta factory so adding it here by default - builder.add_feature(E3MetaFeature::create()) + builder.with(E3MetaExtension::create()) } pub fn from_params(params: E3RouterParams) -> Self { Self { - features: params.features, + extensions: params.extensions, bus: params.bus.clone(), store: params.store.clone(), completed: HashSet::new(), @@ -143,12 +163,12 @@ impl Handler for E3Router { E3Context::from_params(E3ContextParams { e3_id: e3_id.clone(), repository: repositories.context(&e3_id), - features: self.features.clone(), + extensions: self.extensions.clone(), }) }); - for feature in self.features.iter() { - feature.on_event(context, &msg); + for extension in self.extensions.iter() { + extension.on_event(context, &msg); } context.forward_message(&msg, &mut self.buffer); @@ -232,7 +252,7 @@ impl FromSnapshotWithParams for E3Router { E3ContextParams { repository: repositories.context(&e3_id), e3_id: e3_id.clone(), - features: params.features.clone(), + extensions: params.extensions.clone(), }, ctx_snapshot, ) @@ -243,7 +263,7 @@ impl FromSnapshotWithParams for E3Router { Ok(E3Router { contexts, completed: snapshot.completed, - features: params.features.into(), + extensions: params.extensions.into(), buffer: EventBuffer::default(), bus: params.bus, store: repositories.router(), @@ -254,13 +274,13 @@ impl FromSnapshotWithParams for E3Router { /// Builder for E3Router pub struct E3RouterBuilder { pub bus: Addr, - pub features: Vec>, + pub extensions: Vec>, pub store: Repository, } impl E3RouterBuilder { - pub fn add_feature(mut self, listener: Box) -> Self { - self.features.push(listener); + pub fn with(mut self, listener: Box) -> Self { + self.extensions.push(listener); self } @@ -269,7 +289,7 @@ impl E3RouterBuilder { let router_repo = repositories.router(); let snapshot: Option = router_repo.read().await?; let params = E3RouterParams { - features: self.features.into(), + extensions: self.extensions.into(), bus: self.bus.clone(), store: router_repo, diff --git a/packages/ciphernode/enclave_core/src/aggregator_start.rs b/packages/ciphernode/enclave_core/src/aggregator_start.rs index 2ddff355..c90909fb 100644 --- a/packages/ciphernode/enclave_core/src/aggregator_start.rs +++ b/packages/ciphernode/enclave_core/src/aggregator_start.rs @@ -1,5 +1,5 @@ use actix::{Actor, Addr}; -use aggregator::{PlaintextAggregatorFeature, PublicKeyAggregatorFeature}; +use aggregator::ext::{PlaintextAggregatorExtension, PublicKeyAggregatorExtension}; use anyhow::Result; use config::AppConfig; use crypto::Cipher; @@ -11,7 +11,7 @@ use evm::{ CiphernodeRegistryReaderRepositoryFactory, CiphernodeRegistrySol, EnclaveSol, EnclaveSolReaderRepositoryFactory, EthPrivateKeyRepositoryFactory, RegistryFilterSol, }; -use fhe::FheFeature; +use fhe::ext::FheExtension; use logger::SimpleLogger; use net::{NetRepositoryFactory, NetworkManager}; use rand::SeedableRng; @@ -73,9 +73,9 @@ pub async fn execute( } E3Router::builder(&bus, store) - .add_feature(FheFeature::create(&bus, &rng)) - .add_feature(PublicKeyAggregatorFeature::create(&bus, &sortition)) - .add_feature(PlaintextAggregatorFeature::create(&bus, &sortition)) + .with(FheExtension::create(&bus, &rng)) + .with(PublicKeyAggregatorExtension::create(&bus, &sortition)) + .with(PlaintextAggregatorExtension::create(&bus, &sortition)) .build() .await?; diff --git a/packages/ciphernode/enclave_core/src/start.rs b/packages/ciphernode/enclave_core/src/start.rs index 2a76bd24..02afa8bf 100644 --- a/packages/ciphernode/enclave_core/src/start.rs +++ b/packages/ciphernode/enclave_core/src/start.rs @@ -10,8 +10,8 @@ use evm::{ helpers::ProviderConfig, CiphernodeRegistryReaderRepositoryFactory, CiphernodeRegistrySol, EnclaveSolReader, EnclaveSolReaderRepositoryFactory, }; -use fhe::FheFeature; -use keyshare::KeyshareFeature; +use fhe::ext::FheExtension; +use keyshare::ext::KeyshareExtension; use logger::SimpleLogger; use net::{NetRepositoryFactory, NetworkManager}; use rand::SeedableRng; @@ -67,8 +67,12 @@ pub async fn execute( } E3Router::builder(&bus, store.clone()) - .add_feature(FheFeature::create(&bus, &rng)) - .add_feature(KeyshareFeature::create(&bus, &address.to_string(), &cipher)) + .with(FheExtension::create(&bus, &rng)) + .with(KeyshareExtension::create( + &bus, + &address.to_string(), + &cipher, + )) .build() .await?; diff --git a/packages/ciphernode/fhe/src/feature.rs b/packages/ciphernode/fhe/src/ext.rs similarity index 93% rename from packages/ciphernode/fhe/src/feature.rs rename to packages/ciphernode/fhe/src/ext.rs index bae9ef3a..c73b829f 100644 --- a/packages/ciphernode/fhe/src/feature.rs +++ b/packages/ciphernode/fhe/src/ext.rs @@ -3,19 +3,19 @@ use actix::Addr; use anyhow::{anyhow, Result}; use async_trait::async_trait; use data::{FromSnapshotWithParams, RepositoriesFactory, Snapshot}; -use e3_request::{E3Context, E3ContextSnapshot, E3Feature, TypedKey}; +use e3_request::{E3Context, E3ContextSnapshot, E3Extension, TypedKey}; use events::{BusError, E3Requested, EnclaveErrorType, EnclaveEvent, EventBus}; use std::sync::Arc; pub const FHE_KEY: TypedKey> = TypedKey::new("fhe"); /// TODO: move these to each package with access on MyStruct::launcher() -pub struct FheFeature { +pub struct FheExtension { rng: SharedRng, bus: Addr, } -impl FheFeature { +impl FheExtension { pub fn create(bus: &Addr, rng: &SharedRng) -> Box { Box::new(Self { rng: rng.clone(), @@ -27,7 +27,7 @@ impl FheFeature { const ERROR_FHE_FAILED_TO_DECODE: &str = "Failed to decode encoded FHE params"; #[async_trait] -impl E3Feature for FheFeature { +impl E3Extension for FheExtension { fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent) { // Saving the fhe on Committee Requested let EnclaveEvent::E3Requested { data, .. } = evt else { diff --git a/packages/ciphernode/fhe/src/lib.rs b/packages/ciphernode/fhe/src/lib.rs index f6a4203d..5dff411c 100644 --- a/packages/ciphernode/fhe/src/lib.rs +++ b/packages/ciphernode/fhe/src/lib.rs @@ -1,9 +1,8 @@ -mod feature; +pub mod ext; mod fhe; mod repo; mod utils; -pub use feature::*; pub use fhe::*; pub use repo::*; pub use utils::*; diff --git a/packages/ciphernode/keyshare/src/feature.rs b/packages/ciphernode/keyshare/src/ext.rs similarity index 94% rename from packages/ciphernode/keyshare/src/feature.rs rename to packages/ciphernode/keyshare/src/ext.rs index 0fbc5a10..035dd720 100644 --- a/packages/ciphernode/keyshare/src/feature.rs +++ b/packages/ciphernode/keyshare/src/ext.rs @@ -4,18 +4,18 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use crypto::Cipher; use data::{AutoPersist, RepositoriesFactory}; -use e3_request::{E3Context, E3ContextSnapshot, E3Feature}; +use e3_request::{E3Context, E3ContextSnapshot, E3Extension}; use events::{BusError, EnclaveErrorType, EnclaveEvent, EventBus}; -use fhe::FHE_KEY; +use fhe::ext::FHE_KEY; use std::sync::Arc; -pub struct KeyshareFeature { +pub struct KeyshareExtension { bus: Addr, address: String, cipher: Arc, } -impl KeyshareFeature { +impl KeyshareExtension { pub fn create(bus: &Addr, address: &str, cipher: &Arc) -> Box { Box::new(Self { bus: bus.clone(), @@ -29,7 +29,7 @@ const ERROR_KEYSHARE_FHE_MISSING: &str = "Could not create Keyshare because the fhe instance it depends on was not set on the context."; #[async_trait] -impl E3Feature for KeyshareFeature { +impl E3Extension for KeyshareExtension { fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent) { // if this is NOT a CiphernodeSelected event then ignore let EnclaveEvent::CiphernodeSelected { data, .. } = evt else { diff --git a/packages/ciphernode/keyshare/src/lib.rs b/packages/ciphernode/keyshare/src/lib.rs index a41ebf72..ca617633 100644 --- a/packages/ciphernode/keyshare/src/lib.rs +++ b/packages/ciphernode/keyshare/src/lib.rs @@ -1,6 +1,5 @@ -mod feature; +pub mod ext; mod keyshare; mod repo; -pub use feature::*; pub use keyshare::*; pub use repo::*; diff --git a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs index f81286d9..cd6240ca 100644 --- a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs +++ b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs @@ -1,4 +1,4 @@ -use aggregator::{PlaintextAggregatorFeature, PublicKeyAggregatorFeature}; +use aggregator::ext::{PlaintextAggregatorExtension, PublicKeyAggregatorExtension}; use crypto::Cipher; use data::RepositoriesFactory; use data::{DataStore, InMemStore}; @@ -9,8 +9,9 @@ use events::{ KeyshareCreated, OrderedSet, PlaintextAggregated, PublicKeyAggregated, ResetHistory, Seed, Shutdown, }; -use fhe::{setup_crp_params, FheFeature, ParamsWithCrp, SharedRng}; -use keyshare::KeyshareFeature; +use fhe::ext::FheExtension; +use fhe::{setup_crp_params, ParamsWithCrp, SharedRng}; +use keyshare::ext::KeyshareExtension; use logger::SimpleLogger; use net::{events::NetworkPeerEvent, NetworkManager}; use sortition::SortitionRepositoryFactory; @@ -57,10 +58,10 @@ async fn setup_local_ciphernode( CiphernodeSelector::attach(&bus, &sortition, addr); let router = E3Router::builder(&bus, store) - .add_feature(FheFeature::create(&bus, &rng)) - .add_feature(PublicKeyAggregatorFeature::create(&bus, &sortition)) - .add_feature(PlaintextAggregatorFeature::create(&bus, &sortition)) - .add_feature(KeyshareFeature::create(&bus, addr, &cipher)) + .with(FheExtension::create(&bus, &rng)) + .with(PublicKeyAggregatorExtension::create(&bus, &sortition)) + .with(PlaintextAggregatorExtension::create(&bus, &sortition)) + .with(KeyshareExtension::create(&bus, addr, &cipher)) .build() .await?;