From 4e4b51f81e06e024923cfba22fc45597e8326521 Mon Sep 17 00:00:00 2001 From: Taiki Yamaguchi Date: Tue, 26 Apr 2022 19:46:32 +0800 Subject: [PATCH 1/4] Events and query structure definitions --- src/helpers/mod.rs | 1 + src/helpers/models.rs | 143 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 src/helpers/models.rs diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs index 01f18ed9f..2700df14d 100644 --- a/src/helpers/mod.rs +++ b/src/helpers/mod.rs @@ -1,5 +1,6 @@ pub mod aggregation; pub mod event; +pub mod models; pub mod privacy_budget; pub use aggregation::{ diff --git a/src/helpers/models.rs b/src/helpers/models.rs new file mode 100644 index 000000000..f4942fcba --- /dev/null +++ b/src/helpers/models.rs @@ -0,0 +1,143 @@ +use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter}; +use std::ops::Range; + +// Type aliases to indicate whether the parameter should be encrypted, secret shared, etc. +// Underlying types are temporalily assigned for PoC. +type CipherText = [u8; 32]; +type PlainText = String; +type SecretShare = [CipherText; 3]; + +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +struct Event { + /// Name of the business entity that generated this event. For source-fanout, + /// it's the business who promotes ads. For trigger-fanout, it's the business + /// where a user has made a conversion. + entity_name: PlainText, + + /// Secret shared and then encrypted match keys. + matchkey: SecretShare, + + /// Date and time of the event occurence. Secret shared and encrypted. + timestamp: SecretShare, +} + +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +struct SourceEvent { + event: Event, + + /// Campaign ID of the ad served to the user. + campaign_id: PlainText, +} + +#[cfg(feature = "debug")] +impl Debug for SourceEvent { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!( + f, + "SourceEvent:\n matchkey={:?}\n campaign_id={}", + &self.event.matchkey, &self.campaign_id + ) + } +} + +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +struct TriggerEvent { + event: Event, + + /// Conversion value. + value: SecretShare, + + /// Zero knowledge proom that the trigger value lies within a specific range + /// of values. The range is specified in [TriggerFanoutQuery]. + zkp: PlainText, +} + +#[cfg(feature = "debug")] +impl Debug for TriggerEvent { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!( + f, + "TriggerEvent:\n matchkey={:?}\n value={:?}", + &self.event.matchkey, &self.value + ) + } +} + +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +enum QueryType { + Source, + Trigger, +} + +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +enum Node { + Helper1, + Helper2, + Helper3, +} + +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +struct Query { + /// Caller authentication token. + auth_token: PlainText, + + /// Initial MPC helper node to send the data to. + leader_node: Node, + + /// List of match key providers to be used by the source and trigger events during an epoch. + mk_providers: Vec, + + /// Source-fanout or Trigger-fanout. + query_type: QueryType, + + /// Percentage of epoch-level privacy budget this query should consume. + privacy_budget: f32, + + /// A collection of source events. At least 100 (TBD) unique source events must be provided. + source_events: Vec, + + /// A collection of trigger events. At least 10 (TBD) unique trigger events must be provided. + trigger_events: Vec, +} + +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +struct SourceFanoutQuery { + query: Query, + + /// The maximum number of attributed conversion events that a single person can contribute + /// towards the final output. + cap: u32, +} + +#[cfg(feature = "debug")] +impl Debug for SourceFanoutQuery { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!( + f, + "SourceFanoutQuery:\n {} source events\n {} trigger events", + &self.query.source_events.len(), + &self.query.trigger_events.len() + ) + } +} + +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +struct TriggerFanoutQuery { + query: Query, + + /// The range which all trigger event's conversion values must lie within. + value_range: Range, +} + +#[cfg(feature = "debug")] +impl Debug for TriggerFanoutQuery { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!( + f, + "TriggerFanoutQuery:\n {} source events\n {} trigger events", + &self.query.source_events.len(), + &self.query.trigger_events.len() + ) + } +} From e5e64d1e995a4759fdc8fe4c380806fa1bb7dfcb Mon Sep 17 00:00:00 2001 From: Taiki Yamaguchi Date: Tue, 10 May 2022 16:41:36 +0800 Subject: [PATCH 2/4] Code review comments addressed --- Cargo.toml | 3 ++- src/helpers/models.rs | 41 +++++++++++++++++++---------------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cfbe01f78..57c102f66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" default = ["debug", "cli"] cli = ["enable-serde", "stderrlog", "structopt"] debug = ["hex"] -enable-serde = ["serde", "serde_json", "rust-elgamal/enable-serde"] +enable-serde = ["serde", "serde_json", "rust-elgamal/enable-serde", "num-bigint/serde"] [dependencies] # rust-elgamal (via curve25519-dalek-ng) only works with digest 0.9, not 0.10 @@ -27,6 +27,7 @@ serde_json = {version = "1.0", optional = true} sha2 = "0.9" stderrlog = {version = "0.5", optional = true} structopt = {version = "0.3", optional = true} +num-bigint = {version = "0.4.3", optional = true} [dev-dependencies] hex = "0.4" diff --git a/src/helpers/models.rs b/src/helpers/models.rs index f4942fcba..ef07528fe 100644 --- a/src/helpers/models.rs +++ b/src/helpers/models.rs @@ -1,22 +1,19 @@ +use num_bigint::BigInt; use serde::{Deserialize, Serialize}; use std::fmt::{Debug, Formatter}; use std::ops::Range; // Type aliases to indicate whether the parameter should be encrypted, secret shared, etc. // Underlying types are temporalily assigned for PoC. -type CipherText = [u8; 32]; +type CipherText = BigInt; type PlainText = String; type SecretShare = [CipherText; 3]; +type PBRange = Range; #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] struct Event { - /// Name of the business entity that generated this event. For source-fanout, - /// it's the business who promotes ads. For trigger-fanout, it's the business - /// where a user has made a conversion. - entity_name: PlainText, - /// Secret shared and then encrypted match keys. - matchkey: SecretShare, + matchkeys: Vec, /// Date and time of the event occurence. Secret shared and encrypted. timestamp: SecretShare, @@ -26,8 +23,8 @@ struct Event { struct SourceEvent { event: Event, - /// Campaign ID of the ad served to the user. - campaign_id: PlainText, + /// A key to group sets of the events into. + breakdown_key: PlainText, } #[cfg(feature = "debug")] @@ -35,8 +32,8 @@ impl Debug for SourceEvent { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!( f, - "SourceEvent:\n matchkey={:?}\n campaign_id={}", - &self.event.matchkey, &self.campaign_id + "SourceEvent:\n matchkeys={:?}\n breakdown_key={}", + &self.event.matchkeys, &self.breakdown_key ) } } @@ -48,7 +45,7 @@ struct TriggerEvent { /// Conversion value. value: SecretShare, - /// Zero knowledge proom that the trigger value lies within a specific range + /// Zero knowledge proof that the trigger value lies within a specific range /// of values. The range is specified in [TriggerFanoutQuery]. zkp: PlainText, } @@ -58,16 +55,16 @@ impl Debug for TriggerEvent { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!( f, - "TriggerEvent:\n matchkey={:?}\n value={:?}", - &self.event.matchkey, &self.value + "TriggerEvent:\n matchkeys={:?}\n value={:?}", + &self.event.matchkeys, &self.value ) } } #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] enum QueryType { - Source, - Trigger, + SourceFanout, + TriggerFanout, } #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -78,7 +75,7 @@ enum Node { } #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -struct Query { +struct IPAQuery { /// Caller authentication token. auth_token: PlainText, @@ -91,8 +88,8 @@ struct Query { /// Source-fanout or Trigger-fanout. query_type: QueryType, - /// Percentage of epoch-level privacy budget this query should consume. - privacy_budget: f32, + /// Percentage of epoch-level privacy budget this query should consume. Likely 1-100. + privacy_budget: PBRange, /// A collection of source events. At least 100 (TBD) unique source events must be provided. source_events: Vec, @@ -103,11 +100,11 @@ struct Query { #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] struct SourceFanoutQuery { - query: Query, + query: IPAQuery, /// The maximum number of attributed conversion events that a single person can contribute /// towards the final output. - cap: u32, + cap: u8, } #[cfg(feature = "debug")] @@ -124,7 +121,7 @@ impl Debug for SourceFanoutQuery { #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] struct TriggerFanoutQuery { - query: Query, + query: IPAQuery, /// The range which all trigger event's conversion values must lie within. value_range: Range, From 92d36d3ea0af38cb19214dd39cceabe367158152 Mon Sep 17 00:00:00 2001 From: Taiki Yamaguchi Date: Tue, 24 May 2022 11:34:25 +0800 Subject: [PATCH 3/4] Add an epoch field and fix a few other fields' types --- Cargo.toml | 3 +-- src/helpers/models.rs | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 57c102f66..cfbe01f78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" default = ["debug", "cli"] cli = ["enable-serde", "stderrlog", "structopt"] debug = ["hex"] -enable-serde = ["serde", "serde_json", "rust-elgamal/enable-serde", "num-bigint/serde"] +enable-serde = ["serde", "serde_json", "rust-elgamal/enable-serde"] [dependencies] # rust-elgamal (via curve25519-dalek-ng) only works with digest 0.9, not 0.10 @@ -27,7 +27,6 @@ serde_json = {version = "1.0", optional = true} sha2 = "0.9" stderrlog = {version = "0.5", optional = true} structopt = {version = "0.3", optional = true} -num-bigint = {version = "0.4.3", optional = true} [dev-dependencies] hex = "0.4" diff --git a/src/helpers/models.rs b/src/helpers/models.rs index ef07528fe..d533c399a 100644 --- a/src/helpers/models.rs +++ b/src/helpers/models.rs @@ -1,21 +1,23 @@ -use num_bigint::BigInt; use serde::{Deserialize, Serialize}; use std::fmt::{Debug, Formatter}; use std::ops::Range; // Type aliases to indicate whether the parameter should be encrypted, secret shared, etc. // Underlying types are temporalily assigned for PoC. -type CipherText = BigInt; +type CipherText = Vec; type PlainText = String; type SecretShare = [CipherText; 3]; -type PBRange = Range; #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] struct Event { /// Secret shared and then encrypted match keys. matchkeys: Vec, - /// Date and time of the event occurence. Secret shared and encrypted. + /// The epoch which this event is generated. Using an 8-bit value = 256 epochs > 4 years (assuming 1 epoch = 1 week). + /// This field is in the clear. + epoch: u8, + + /// An offset in seconds into a given offset. The clear is u32 (< 2^20 seconds), then encrypted and secret shared. timestamp: SecretShare, } @@ -23,7 +25,7 @@ struct Event { struct SourceEvent { event: Event, - /// A key to group sets of the events into. + /// A key to group sets of the events. breakdown_key: PlainText, } @@ -89,7 +91,7 @@ struct IPAQuery { query_type: QueryType, /// Percentage of epoch-level privacy budget this query should consume. Likely 1-100. - privacy_budget: PBRange, + privacy_budget: u8, /// A collection of source events. At least 100 (TBD) unique source events must be provided. source_events: Vec, @@ -103,7 +105,8 @@ struct SourceFanoutQuery { query: IPAQuery, /// The maximum number of attributed conversion events that a single person can contribute - /// towards the final output. + /// towards the final output. We could also express this using sum of trigger values. + /// We'll leave it for the future spec to decide. cap: u8, } @@ -123,7 +126,7 @@ impl Debug for SourceFanoutQuery { struct TriggerFanoutQuery { query: IPAQuery, - /// The range which all trigger event's conversion values must lie within. + /// The range within which all the trigger event values must lie. value_range: Range, } From 3f374341547ff210e7b060ae68c5b5e136da22ee Mon Sep 17 00:00:00 2001 From: Taiki Yamaguchi Date: Thu, 26 May 2022 13:19:43 +0800 Subject: [PATCH 4/4] Fixed a typo in the comment --- src/helpers/models.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/models.rs b/src/helpers/models.rs index d533c399a..2951fe890 100644 --- a/src/helpers/models.rs +++ b/src/helpers/models.rs @@ -17,7 +17,7 @@ struct Event { /// This field is in the clear. epoch: u8, - /// An offset in seconds into a given offset. The clear is u32 (< 2^20 seconds), then encrypted and secret shared. + /// An offset in seconds into a given epoch. The clear is u32 (< 2^20 seconds), then encrypted and secret shared. timestamp: SecretShare, }