From e681e35054e84bb39426a253392cf0f5fba0bc5d Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Wed, 3 Feb 2021 05:05:59 -0800 Subject: [PATCH] Add Oracle and graphviz output --- Cargo.toml | 5 ++-- src/main.rs | 17 ++++++++--- src/oracle.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/parties.rs | 61 +++++++++++++++++++++++----------------- src/server.rs | 18 +++++++----- 5 files changed, 138 insertions(+), 39 deletions(-) create mode 100644 src/oracle.rs diff --git a/Cargo.toml b/Cargo.toml index 6731349..5a9330a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,5 +13,6 @@ rand_distr = "0.4.0" hashbrown = "0.9.1" termcolor = "1.1.2" clap = "3.0.0-beta.2" -tracing = "0.1.0" -tracing-subscriber = "0.2" \ No newline at end of file +tracing = "0.1.22" +tracing-subscriber = "0.2.15" +hex = "0.4.2" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 96d36ff..a397ed2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,11 +3,14 @@ use crate::server::SimulatedServer; use rand_distr::Pareto; use std::io::Write; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; +mod oracle; mod parties; mod server; + use clap::Clap; use tracing::event; +use crate::oracle::Oracle; use tracing::Level; use tracing_subscriber; use tracing_subscriber::FmtSubscriber; @@ -47,7 +50,10 @@ fn main() { true => Level::TRACE, _ => Level::INFO, }; - let subscriber = FmtSubscriber::builder().with_max_level(level).finish(); + + let mut oracle = Oracle::new(); + + let subscriber = FmtSubscriber::default(); tracing::subscriber::with_default(subscriber, || { let mut rng = rand::thread_rng(); let mut server = SimulatedServer::new(); @@ -55,14 +61,14 @@ fn main() { let simulated_parties = SimulatedParties::new_simulation(opts.num_parties, opts.gamma); { event!(Level::INFO, "Generating {} Parties and registering them with the server", opts.num_parties); - simulated_parties.register_with_server(&mut server, &mut rng, opts.min_p, opts.max_p); + simulated_parties.register_with_server(&mut server, &mut rng, opts.min_p, opts.max_p, &mut oracle); } let pareto = Pareto::new(1.0, 1.0).unwrap(); { event!(Level::INFO, "Simulating message sends using {} samples from a pareto distribution...", opts.samples_per_round); - (0..opts.samples_per_round).for_each(|_i| simulated_parties.sample_traffic(&mut server, &mut rng, pareto, opts.prob_entangled)); + (0..opts.samples_per_round).for_each(|_i| simulated_parties.sample_traffic(&mut server, &mut rng, pareto, opts.prob_entangled, &mut oracle)); } { @@ -71,7 +77,7 @@ fn main() { } { - let (round_stats, party_stats) = server.statistics(); + let (server_oracle, round_stats, party_stats) = server.statistics(); let if_uniform = (round_stats.num_messages as f64) / (round_stats.num_registered_parties as f64); event!( Level::INFO, @@ -101,6 +107,9 @@ fn main() { ) .unwrap(); } + server_oracle.compile_to_dot("server_event.dot", true); } }); + + oracle.compile_to_dot("actual_events.dot", false); } diff --git a/src/oracle.rs b/src/oracle.rs new file mode 100644 index 0000000..cebe4e3 --- /dev/null +++ b/src/oracle.rs @@ -0,0 +1,76 @@ +// todo would be neat to just make this a subscriber of the tracing log, but there doesn't +// seem to be a nice way for that API to do what we want.. so until then... + +use rand::{thread_rng, Rng}; +use std::fs::File; +use std::io::Write; + +#[derive(Clone)] +pub struct Event { + sender: String, + intended_receiver: String, + entangled_receiver: Option, +} + +#[derive(Clone)] +pub struct Oracle { + parties: Vec, + actual_events: Vec, +} + +impl Oracle { + pub fn new() -> Oracle { + Oracle { + parties: vec![], + actual_events: vec![], + } + } + + pub fn register_party(&mut self, party: String) { + self.parties.push(party); + } + + pub fn add_event(&mut self, sender: String, intended_receiver: String, entangled_receiver: Option) { + self.actual_events.push(Event { + sender, + intended_receiver, + entangled_receiver, + }); + } + + pub fn compile_to_dot(&self, filename: &str, strict: bool) { + let mut output = File::create(filename).unwrap(); + if strict { + write!(output, "strict "); + } + write!(output, "digraph {{\n"); + write!( + output, + r#"K=2.5; + repulsiveforce=0.1; + overlap=true; + splines = true; + dpi=400; + penwidth = 0.8; + "# + ); + + for party in self.parties.iter() { + let r = hex::decode(party).unwrap()[0]; + let g = hex::decode(party).unwrap()[1]; + let b = hex::decode(party).unwrap()[2]; + writeln!(output, "\"{}\" [shape=point, color=\"#{:x}{:x}{:x}\"]", party, r, g, b); + } + + for event in self.actual_events.iter() { + writeln!(output, "\"{}\" -> \"{}\" [arrowhead=none]", event.sender, event.intended_receiver); + match &event.entangled_receiver { + Some(entangled_receiver) => { + writeln!(output, "\"{}\" -> \"{}\" [arrowhead=none,style=dashed]", event.sender, entangled_receiver); + } + _ => {} + }; + } + write!(output, "}}"); + } +} diff --git a/src/parties.rs b/src/parties.rs index ff83a33..6600387 100644 --- a/src/parties.rs +++ b/src/parties.rs @@ -1,3 +1,4 @@ +use crate::oracle::Oracle; use crate::server::SimulatedServer; use fuzzytags::{FuzzyPublicKey, FuzzySecretKey}; use rand::distributions::Distribution; @@ -22,24 +23,23 @@ impl SimulatedParties { SimulatedParties { gamma, parties } } - pub fn register_with_server(&self, server: &mut SimulatedServer, rng: &mut R, min_p: usize, max_p: usize) + pub fn register_with_server(&self, server: &mut SimulatedServer, rng: &mut R, min_p: usize, max_p: usize, oracle: &mut Oracle) where R: Rng, { - let span = span!(Level::TRACE, "register_with_server"); - let _enter = span.enter(); for party in self.parties.iter() { let n = rng.gen_range(min_p..max_p); - let span = span!(Level::TRACE, "{register}", party = party.public_key().id().as_str()); + let span = span!(Level::INFO, "register", party = party.public_key().id().as_str()); let _enter = span.enter(); let detection_key = party.extract(n); event!(Level::TRACE, "create detection key {detection_key}", detection_key = detection_key.id().as_str()); event!(Level::TRACE, "register with server"); server.register_key(&detection_key, &party.public_key()); + oracle.register_party(party.public_key().id()); } } - pub fn sample_traffic(&self, server: &mut SimulatedServer, rng: &mut R, distribution: D, probs_entangled: f64) + pub fn sample_traffic(&self, server: &mut SimulatedServer, rng: &mut R, distribution: D, probs_entangled: f64, oracle: &mut Oracle) where D: Distribution, R: Rng, @@ -47,31 +47,40 @@ impl SimulatedParties { let span = span!(Level::INFO, "sample_traffic"); let _enter = span.enter(); let v = distribution.sample(rng).to_u16().unwrap(); + + let sender = rng.gen_range(0..self.parties.len()); + let sender_public_key = self.parties.get(sender).unwrap().public_key(); + let receiver = rng.gen_range(0..self.parties.len()); let receiver_public_key = self.parties.get(receiver).unwrap().public_key(); - let entangle = rng.gen_bool(probs_entangled); - if entangle { - let receiver_2 = rng.gen_range(0..self.parties.len()); - let receiver_public_key_2 = self.parties.get(receiver_2).unwrap().public_key(); - event!( - Level::INFO, - "entangled send {party_1} {party_2}", - party_1 = receiver_public_key.id().as_str(), - party_2 = receiver_public_key_2.id().as_str() - ); + if sender != receiver { + let entangle = rng.gen_bool(probs_entangled); + if entangle { + let receiver_2 = rng.gen_range(0..self.parties.len()); + let receiver_public_key_2 = self.parties.get(receiver_2).unwrap().public_key(); + event!( + Level::INFO, + "entangled send {party_1} {party_2}", + party_1 = receiver_public_key.id().as_str(), + party_2 = receiver_public_key_2.id().as_str() + ); - for _i in 0..v { - let tag = FuzzyPublicKey::generate_entangled_tag(vec![receiver_public_key.clone(), receiver_public_key_2.clone()], self.gamma); - event!(Level::TRACE, "message sent to server {tag}", tag = tag.to_string()); - server.add_message(tag); - } - } else { - event!(Level::INFO, "regular send {party}", party = receiver_public_key.id().as_str()); - for _i in 0..v { - let tag = receiver_public_key.generate_tag(); - event!(Level::INFO, "message sent server {tag}", tag = tag.to_string()); - server.add_message(tag); + for _i in 0..v { + let tag = FuzzyPublicKey::generate_entangled_tag(vec![receiver_public_key.clone(), receiver_public_key_2.clone()], self.gamma); + event!(Level::TRACE, "message sent to server {tag}", tag = tag.to_string()); + server.add_message(tag, &sender_public_key); + } + + oracle.add_event(sender_public_key.id().clone(), receiver_public_key.id(), Some(receiver_public_key_2.id())); + } else { + event!(Level::INFO, "regular send {party}", party = receiver_public_key.id().as_str()); + for _i in 0..v { + let tag = receiver_public_key.generate_tag(); + event!(Level::INFO, "message sent server {tag}", tag = tag.to_string()); + server.add_message(tag, &sender_public_key); + } + oracle.add_event(sender_public_key.id().clone(), receiver_public_key.id(), None); } } } diff --git a/src/server.rs b/src/server.rs index 343975d..4a47588 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,3 +1,4 @@ +use crate::oracle::Oracle; use fuzzytags::{FuzzyDetectionKey, FuzzyPublicKey, FuzzyTag}; use hashbrown::HashMap; use tracing::event; @@ -6,9 +7,10 @@ use tracing::Level; pub struct SimulatedServer { keybase: Vec<(FuzzyDetectionKey, FuzzyPublicKey)>, - messages: Vec, + messages: Vec<(FuzzyTag, FuzzyPublicKey)>, tags_to_keys_cache: HashMap>, keys_to_tags_cache: HashMap>, + oracle: Oracle, } pub struct RoundStatistics { @@ -34,35 +36,37 @@ impl SimulatedServer { messages: vec![], tags_to_keys_cache: HashMap::new(), keys_to_tags_cache: HashMap::new(), + oracle: Oracle::new(), } } pub fn register_key(&mut self, detection_key: &FuzzyDetectionKey, public_key: &FuzzyPublicKey) { self.keybase.push((detection_key.clone(), public_key.clone())); self.keys_to_tags_cache.insert(public_key.id(), vec![]); + self.oracle.register_party(public_key.id()); } - pub fn add_message(&mut self, tag: FuzzyTag) { - self.messages.push(tag.clone()); + pub fn add_message(&mut self, tag: FuzzyTag, sender_public_key: &FuzzyPublicKey) { + self.messages.push((tag.clone(), sender_public_key.clone())); self.tags_to_keys_cache.insert(tag.to_string(), vec![]); } pub fn test_messages(&mut self) { - for message in self.messages.iter() { + for (message, sender) in self.messages.iter() { for (detection_key, public_key) in self.keybase.iter() { let span = span!(Level::TRACE, "{detection}", party = public_key.id().as_str()); let _enter = span.enter(); if detection_key.test_tag(message) { event!(Level::TRACE, "Matched detection key for {key} to tag {tag} ", key = public_key.id(), tag = message.to_string()); self.tags_to_keys_cache.get_mut(message.to_string().as_str()).unwrap().push((*public_key).clone()); - self.keys_to_tags_cache.get_mut(public_key.id().as_str()).unwrap().push((*message).clone()); + self.oracle.add_event(sender.id(), public_key.id(), None); } } } } - pub fn statistics(&self) -> (RoundStatistics, HashMap) { + pub fn statistics(&self) -> (Oracle, RoundStatistics, HashMap) { let mut party_stats = HashMap::new(); let round_stats = RoundStatistics { num_messages: self.messages.len(), @@ -98,6 +102,6 @@ impl SimulatedServer { party_stats.insert(pub_key.id(), p_stats); } - (round_stats, party_stats) + (self.oracle.clone(), round_stats, party_stats) } }