Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sortition actor #58

Merged
merged 2 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
669 changes: 579 additions & 90 deletions packages/ciphernode/Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions packages/ciphernode/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ repository = "https://github.com/gnosisguild/enclave/packages/ciphernode"

[dependencies]
p2p = { path = "../p2p" }
sortition = { path = "../sortition" }
async-std = "1.12.0"
libp2p = "0.53.2"
fhe = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" }
Expand All @@ -26,4 +27,6 @@ sha2 = "0.10.8"
bs58 = "0.5.1"
serde = { version = "1.0.208", features = ["derive"] }
bincode = "1.3.3"
alloy = "0.3.3"
alloy-primitives = { version = "0.6", default-features = false, features = ["rlp", "serde", "std"] }

57 changes: 43 additions & 14 deletions packages/ciphernode/core/src/ciphernode_selector.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
use actix::prelude::*;
use alloy_primitives::Address;

use crate::{CiphernodeSelected, CommitteeRequested, EnclaveEvent, EventBus, Subscribe};
use crate::{
CiphernodeSelected, EnclaveEvent, EventBus, GetHasNode, Sortition, Subscribe,
};

pub struct CiphernodeSelector {
bus: Addr<EventBus>,
sortition: Addr<Sortition>,
address: Address,
}

impl Actor for CiphernodeSelector {
type Context = Context<Self>;
}

impl CiphernodeSelector {
pub fn new(bus: Addr<EventBus>) -> Self {
Self { bus }
pub fn new(bus: Addr<EventBus>, sortition: Addr<Sortition>, address: Address) -> Self {
Self {
bus,
sortition,
address,
}
}

pub fn attach(bus: Addr<EventBus>) -> Addr<Self> {
let addr = CiphernodeSelector::new(bus.clone()).start();
pub fn attach(bus: Addr<EventBus>, sortition: Addr<Sortition>, address: Address) -> Addr<Self> {
let addr = CiphernodeSelector::new(bus.clone(), sortition, address).start();

bus.do_send(Subscribe::new(
"CommitteeRequested",
Expand All @@ -28,19 +37,39 @@ impl CiphernodeSelector {
}

impl Handler<EnclaveEvent> for CiphernodeSelector {
type Result = ();
type Result = ResponseFuture<()>;

fn handle(&mut self, event: EnclaveEvent, _ctx: &mut Self::Context) -> Self::Result {
let address = self.address;
let sortition = self.sortition.clone();
let bus = self.bus.clone();

Box::pin(async move {
let EnclaveEvent::CommitteeRequested { data, .. } = event else {
return;
};

fn handle(&mut self, event: EnclaveEvent, ctx: &mut Self::Context) -> Self::Result {
match event {
EnclaveEvent::CommitteeRequested { data, .. } => {
// TODO: ask Sortition module whether registered node has been selected
self.bus.do_send(EnclaveEvent::from(CiphernodeSelected {
let seed = data.sortition_seed;
let size = data.nodecount;

if let Ok(is_selected) = sortition
.send(GetHasNode {
seed,
address,
size,
})
.await
{
if !is_selected {
return;
}

bus.do_send(EnclaveEvent::from(CiphernodeSelected {
e3_id: data.e3_id,
nodecount: data.nodecount,
threshold: data.threshold,
}))
}));
}
_ => (),
}
})
}
}
81 changes: 68 additions & 13 deletions packages/ciphernode/core/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
use actix::Message;
use bincode;
use alloy_primitives::Address;
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 EthAddr(pub Vec<u8>);

impl From<Address> for EthAddr {
fn from(value: Address) -> Self {
Self(value.to_vec())
}
}

#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct E3id(pub String);
impl fmt::Display for E3id {
Expand Down Expand Up @@ -79,6 +88,14 @@ pub enum EnclaveEvent {
id: EventId,
data: CiphernodeSelected,
},
CiphernodeAdded {
id: EventId,
data: CiphernodeAdded,
},
CiphernodeRemoved {
id: EventId,
data: CiphernodeRemoved,
},
// CommitteeSelected,
// OutputDecrypted,
// CiphernodeRegistered,
Expand All @@ -99,6 +116,7 @@ impl EnclaveEvent {
}

pub fn is_local_only(&self) -> bool {
// Add a list of local events
match self {
EnclaveEvent::CiphernodeSelected { .. } => true,
_ => false,
Expand All @@ -116,20 +134,23 @@ impl From<EnclaveEvent> for EventId {
EnclaveEvent::DecryptionshareCreated { id, .. } => id,
EnclaveEvent::PlaintextAggregated { id, .. } => id,
EnclaveEvent::CiphernodeSelected { id, .. } => id,
EnclaveEvent::CiphernodeAdded { id, .. } => id,
EnclaveEvent::CiphernodeRemoved { id, .. } => id,
}
}
}

impl From<EnclaveEvent> for E3id {
fn from(value: EnclaveEvent) -> Self {
match value {
EnclaveEvent::KeyshareCreated { data, .. } => data.e3_id,
EnclaveEvent::CommitteeRequested { data, .. } => data.e3_id,
EnclaveEvent::PublicKeyAggregated { data, .. } => data.e3_id,
EnclaveEvent::CiphertextOutputPublished { data, .. } => data.e3_id,
EnclaveEvent::DecryptionshareCreated { data, .. } => data.e3_id,
EnclaveEvent::PlaintextAggregated { data, .. } => data.e3_id,
EnclaveEvent::CiphernodeSelected { data, .. } => data.e3_id,
impl EnclaveEvent {
pub fn get_e3_id(&self) -> Option<E3id> {
match self.clone() {
EnclaveEvent::KeyshareCreated { data, .. } => Some(data.e3_id),
EnclaveEvent::CommitteeRequested { 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,
}
}
}
Expand Down Expand Up @@ -197,6 +218,23 @@ impl From<CiphernodeSelected> for EnclaveEvent {
}
}

impl From<CiphernodeAdded> for EnclaveEvent {
fn from(data: CiphernodeAdded) -> Self {
EnclaveEvent::CiphernodeAdded {
id: EventId::from(data.clone()),
data: data.clone(),
}
}
}

impl From<CiphernodeRemoved> for EnclaveEvent {
fn from(data: CiphernodeRemoved) -> Self {
EnclaveEvent::CiphernodeRemoved {
id: EventId::from(data.clone()),
data: data.clone(),
}
}
}
impl fmt::Display for EnclaveEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&format!("{}({})", self.event_type(), self.get_id()))
Expand Down Expand Up @@ -230,12 +268,13 @@ pub struct CommitteeRequested {
pub e3_id: E3id,
pub nodecount: usize,
pub threshold: usize,
pub sortition_seed: u32,
pub sortition_seed: u64, // Should actually be much larger eg [u8;32]

// fhe params
pub moduli: Vec<u64>,
pub degree: usize,
pub plaintext_modulus: u64,
pub crp: Vec<u8>
pub crp: Vec<u8>,
// computation_type: ??, // TODO:
// execution_model_type: ??, // TODO:
// input_deadline: ??, // TODO:
Expand Down Expand Up @@ -264,6 +303,22 @@ pub struct PlaintextAggregated {
pub decrypted_output: Vec<u8>,
}

#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[rtype(result = "()")]
pub struct CiphernodeAdded {
pub address: Address,
pub index: usize,
pub num_nodes: usize,
}

#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[rtype(result = "()")]
pub struct CiphernodeRemoved {
pub address: Address,
pub index: usize,
pub num_nodes: usize,
}

fn extract_enclave_event_name(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
Expand Down
69 changes: 58 additions & 11 deletions packages/ciphernode/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod publickey_aggregator;
mod publickey_sequencer;
mod registry;
mod serializers;
mod sortition;

// TODO: this is too permissive
pub use actix::prelude::*;
Expand All @@ -36,6 +37,7 @@ pub use plaintext_sequencer::*;
pub use publickey_aggregator::*;
pub use publickey_sequencer::*;
pub use registry::*;
pub use sortition::*;

// TODO: move these out to a test folder
#[cfg(test)]
Expand All @@ -50,29 +52,37 @@ mod tests {
CiphertextSerializer, DecryptionShareSerializer, PublicKeySerializer,
PublicKeyShareSerializer,
},
CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated, PlaintextAggregated,
Registry, ResetHistory, SharedRng,
CiphernodeAdded, CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated,
PlaintextAggregated, Registry, ResetHistory, SharedRng, Sortition,
};
use actix::prelude::*;
use alloy_primitives::Address;
use anyhow::*;
use fhe::{
bfv::{BfvParameters, BfvParametersBuilder, Encoding, Plaintext, PublicKey, SecretKey},
mbfv::{AggregateIter, CommonRandomPoly, DecryptionShare, PublicKeyShare},
};
use fhe_traits::{FheEncoder, FheEncrypter, Serialize};
use rand::Rng;
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
use std::{sync::Arc, time::Duration};
use tokio::sync::Mutex;
use tokio::{sync::mpsc::channel, time::sleep};

// Simulating a local node
async fn setup_local_ciphernode(bus: Addr<EventBus>, rng: SharedRng, logging: bool) {
async fn setup_local_ciphernode(
bus: Addr<EventBus>,
rng: SharedRng,
logging: bool,
addr: Address,
) {
// create data actor for saving data
let data = Data::new(logging).start(); // TODO: Use a sled backed Data Actor

// create ciphernode actor for managing ciphernode flow
CiphernodeSelector::attach(bus.clone());
let sortition = Sortition::attach(bus.clone());
CiphernodeSelector::attach(bus.clone(), sortition, addr);
Registry::attach(bus.clone(), data.clone(), rng).await;
}

Expand All @@ -84,7 +94,7 @@ mod tests {
BfvParametersBuilder::new()
.set_degree(degree)
.set_plaintext_modulus(plaintext_modulus)
.set_moduli(&moduli)
.set_moduli(moduli)
.build_arc()
.unwrap()
}
Expand Down Expand Up @@ -137,9 +147,14 @@ mod tests {
let bus = EventBus::new(true).start();

let rng = Arc::new(std::sync::Mutex::new(ChaCha20Rng::seed_from_u64(42)));
setup_local_ciphernode(bus.clone(), rng.clone(), true).await;
setup_local_ciphernode(bus.clone(), rng.clone(), true).await;
setup_local_ciphernode(bus.clone(), rng.clone(), true).await;

let eth_addrs: Vec<Address> = (0..3)
.map(|_| Address::from_slice(&rand::thread_rng().gen::<[u8; 20]>()))
.collect();

setup_local_ciphernode(bus.clone(), rng.clone(), true, eth_addrs[0]).await;
setup_local_ciphernode(bus.clone(), rng.clone(), true, eth_addrs[1]).await;
setup_local_ciphernode(bus.clone(), rng.clone(), true, eth_addrs[2]).await;

let e3_id = E3id::new("1234");

Expand All @@ -149,7 +164,36 @@ mod tests {
plaintext_modulus,
crp_bytes,
params,
} = setup_crp_params(&vec![0x3FFFFFFF000001], 2048, 1032193, Arc::new(std::sync::Mutex::new(ChaCha20Rng::seed_from_u64(42))));
} = setup_crp_params(
&[0x3FFFFFFF000001],
2048,
1032193,
Arc::new(std::sync::Mutex::new(ChaCha20Rng::seed_from_u64(42))),
);

let regevt_1 = EnclaveEvent::from(CiphernodeAdded {
address: eth_addrs[0],
index: 0,
num_nodes: 1,
});

bus.send(regevt_1.clone()).await?;

let regevt_2 = EnclaveEvent::from(CiphernodeAdded {
address: eth_addrs[1],
index: 1,
num_nodes: 2,
});

bus.send(regevt_2.clone()).await?;

let regevt_3 = EnclaveEvent::from(CiphernodeAdded {
address: eth_addrs[2],
index: 2,
num_nodes: 3,
});

bus.send(regevt_3.clone()).await?;

let event = EnclaveEvent::from(CommitteeRequested {
e3_id: e3_id.clone(),
Expand Down Expand Up @@ -179,15 +223,18 @@ mod tests {
let (p2, sk2) = generate_pk_share(params.clone(), crpoly.clone(), rng_test.clone())?;
let (p3, sk3) = generate_pk_share(params.clone(), crpoly.clone(), rng_test.clone())?;

let pubkey: PublicKey = vec![p1.clone(), p2.clone(), p3.clone()]
let pubkey: PublicKey = [p1.clone(), p2.clone(), p3.clone()]
.iter()
.map(|k| PublicKeyShareSerializer::from_bytes(k).unwrap())
.aggregate()?;

assert_eq!(history.len(), 6);
assert_eq!(history.len(), 9);
assert_eq!(
history,
vec![
regevt_1,
regevt_2,
regevt_3,
EnclaveEvent::from(CommitteeRequested {
e3_id: e3_id.clone(),
nodecount: 3,
Expand Down
4 changes: 3 additions & 1 deletion packages/ciphernode/core/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ impl Handler<EnclaveEvent> for Registry {
type Result = ();

fn handle(&mut self, msg: EnclaveEvent, _ctx: &mut Self::Context) -> Self::Result {
let e3_id = E3id::from(msg.clone());
let Some(e3_id) = msg.get_e3_id() else {
return;
};

match msg.clone() {
EnclaveEvent::CommitteeRequested { data, .. } => {
Expand Down
Loading
Loading