Skip to content

Commit

Permalink
Sortition actor (#58)
Browse files Browse the repository at this point in the history
* Sortition actor implemented

* Fix up comments
  • Loading branch information
ryardley authored Sep 13, 2024
1 parent 3924477 commit b1dba82
Show file tree
Hide file tree
Showing 8 changed files with 875 additions and 137 deletions.
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

0 comments on commit b1dba82

Please sign in to comment.