divviup_client/
task.rs

1use prio::vdaf::prio3::optimal_chunk_length;
2use serde::{Deserialize, Serialize};
3use time::OffsetDateTime;
4use uuid::Uuid;
5
6use crate::dp_strategy;
7
8#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
9pub struct Task {
10    pub id: String,
11    pub account_id: Uuid,
12    pub name: String,
13    pub vdaf: Vdaf,
14    pub min_batch_size: u64,
15    pub max_batch_size: Option<u64>,
16    pub batch_time_window_size_seconds: Option<u64>,
17    #[serde(with = "time::serde::rfc3339")]
18    pub created_at: OffsetDateTime,
19    #[serde(with = "time::serde::rfc3339")]
20    pub updated_at: OffsetDateTime,
21    #[serde(with = "time::serde::rfc3339::option")]
22    pub deleted_at: Option<OffsetDateTime>,
23    pub time_precision_seconds: u32,
24    #[deprecated = "Not populated. Will be removed in a future release."]
25    pub report_count: u32,
26    #[deprecated = "Not populated. Will be removed in a future release."]
27    pub aggregate_collection_count: u32,
28    #[serde(default, with = "time::serde::rfc3339::option")]
29    pub expiration: Option<OffsetDateTime>,
30    pub leader_aggregator_id: Uuid,
31    pub helper_aggregator_id: Uuid,
32    pub collector_credential_id: Uuid,
33    pub report_counter_interval_collected: i64,
34    pub report_counter_decode_failure: i64,
35    pub report_counter_decrypt_failure: i64,
36    pub report_counter_expired: i64,
37    pub report_counter_outdated_key: i64,
38    pub report_counter_success: i64,
39    pub report_counter_too_early: i64,
40    pub report_counter_task_expired: i64,
41
42    #[serde(default)]
43    pub aggregation_job_counter_success: i64,
44    #[serde(default)]
45    pub aggregation_job_counter_helper_batch_collected: i64,
46    #[serde(default)]
47    pub aggregation_job_counter_helper_report_replayed: i64,
48    #[serde(default)]
49    pub aggregation_job_counter_helper_report_dropped: i64,
50    #[serde(default)]
51    pub aggregation_job_counter_helper_hpke_unknown_config_id: i64,
52    #[serde(default)]
53    pub aggregation_job_counter_helper_hpke_decrypt_failure: i64,
54    #[serde(default)]
55    pub aggregation_job_counter_helper_vdaf_prep_error: i64,
56    #[serde(default)]
57    pub aggregation_job_counter_helper_task_expired: i64,
58    #[serde(default)]
59    pub aggregation_job_counter_helper_invalid_message: i64,
60    #[serde(default)]
61    pub aggregation_job_counter_helper_report_too_early: i64,
62}
63
64#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
65pub struct NewTask {
66    pub name: String,
67    pub leader_aggregator_id: Uuid,
68    pub helper_aggregator_id: Uuid,
69    pub vdaf: Vdaf,
70    pub min_batch_size: u64,
71    pub max_batch_size: Option<u64>,
72    pub batch_time_window_size_seconds: Option<u64>,
73    pub time_precision_seconds: u64,
74    pub collector_credential_id: Uuid,
75}
76
77#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
78#[serde(rename_all = "snake_case", tag = "type")]
79pub enum Vdaf {
80    #[serde(rename = "count")]
81    Count,
82
83    #[serde(rename = "histogram")]
84    Histogram(Histogram),
85
86    #[serde(rename = "sum")]
87    Sum { bits: u8 },
88
89    #[serde(rename = "count_vec")]
90    CountVec {
91        length: u64,
92        chunk_length: Option<u64>,
93    },
94
95    #[serde(rename = "sum_vec")]
96    SumVec(SumVec),
97}
98
99#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
100#[serde(untagged)]
101pub enum Histogram {
102    Categorical {
103        buckets: Vec<String>,
104        chunk_length: Option<u64>,
105        #[serde(default)]
106        dp_strategy: dp_strategy::Prio3Histogram,
107    },
108    Continuous {
109        buckets: Vec<u64>,
110        chunk_length: Option<u64>,
111        #[serde(default)]
112        dp_strategy: dp_strategy::Prio3Histogram,
113    },
114    Length {
115        length: u64,
116        chunk_length: Option<u64>,
117        #[serde(default)]
118        dp_strategy: dp_strategy::Prio3Histogram,
119    },
120}
121
122impl Histogram {
123    /// The length of this histogram, i.e. the number of buckets.
124    pub fn length(&self) -> usize {
125        match self {
126            Histogram::Categorical { buckets, .. } => buckets.len(),
127            Histogram::Continuous { buckets, .. } => buckets.len() + 1,
128            Histogram::Length { length, .. } => *length as usize,
129        }
130    }
131
132    /// The chunk length used in the VDAF.
133    pub fn chunk_length(&self) -> usize {
134        match self {
135            Histogram::Categorical { chunk_length, .. } => chunk_length,
136            Histogram::Continuous { chunk_length, .. } => chunk_length,
137            Histogram::Length { chunk_length, .. } => chunk_length,
138        }
139        .map(|c| c as usize)
140        .unwrap_or_else(|| optimal_chunk_length(self.length()))
141    }
142
143    /// The differential privacy strategy.
144    pub fn dp_strategy(&self) -> &dp_strategy::Prio3Histogram {
145        match self {
146            Histogram::Categorical { dp_strategy, .. }
147            | Histogram::Continuous { dp_strategy, .. }
148            | Histogram::Length { dp_strategy, .. } => dp_strategy,
149        }
150    }
151}
152
153#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
154pub struct SumVec {
155    pub bits: u8,
156    pub length: u64,
157    chunk_length: Option<u64>,
158    #[serde(default)]
159    pub dp_strategy: dp_strategy::Prio3SumVec,
160}
161
162impl SumVec {
163    /// Create a new SumVec
164    pub fn new(
165        bits: u8,
166        length: u64,
167        chunk_length: Option<u64>,
168        dp_strategy: dp_strategy::Prio3SumVec,
169    ) -> Self {
170        Self {
171            bits,
172            length,
173            chunk_length,
174            dp_strategy,
175        }
176    }
177
178    /// The chunk length used in the VDAF.
179    pub fn chunk_length(&self) -> usize {
180        self.chunk_length
181            .map(|c| c as usize)
182            .unwrap_or_else(|| optimal_chunk_length(self.bits as usize * self.length as usize))
183    }
184}