prio/
vdaf.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Verifiable Distributed Aggregation Functions (VDAFs) as described in
4//! [[draft-irtf-cfrg-vdaf-08]].
5//!
6//! [draft-irtf-cfrg-vdaf-08]: https://datatracker.ietf.org/doc/draft-irtf-cfrg-vdaf/08/
7
8#[cfg(feature = "experimental")]
9use crate::dp::DifferentialPrivacyStrategy;
10#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
11use crate::flp::szk::SzkError;
12#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
13use crate::idpf::IdpfError;
14#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
15use crate::vidpf::VidpfError;
16use crate::{
17    codec::{CodecError, Decode, Encode, ParameterizedDecode},
18    field::{encode_fieldvec, merge_vector, FieldElement, FieldError},
19    flp::FlpError,
20    prng::PrngError,
21    vdaf::xof::Seed,
22};
23use serde::{Deserialize, Serialize};
24use std::{error::Error, fmt::Debug, io::Cursor};
25use subtle::{Choice, ConstantTimeEq};
26
27/// A component of the domain-separation tag, used to bind the VDAF operations to the document
28/// version. This will be revised with each draft with breaking changes.
29pub(crate) const VERSION: u8 = 8;
30
31/// Errors emitted by this module.
32#[derive(Debug, thiserror::Error)]
33#[non_exhaustive]
34pub enum VdafError {
35    /// An error occurred.
36    #[error("vdaf error: {0}")]
37    Uncategorized(String),
38
39    /// Field error.
40    #[error("field error: {0}")]
41    Field(#[from] FieldError),
42
43    /// An error occured while parsing a message.
44    #[error("io error: {0}")]
45    IoError(#[from] std::io::Error),
46
47    /// FLP error.
48    #[error("flp error: {0}")]
49    Flp(#[from] FlpError),
50
51    /// SZK error.
52    #[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
53    #[error("Szk error: {0}")]
54    Szk(#[from] SzkError),
55
56    /// PRNG error.
57    #[error("prng error: {0}")]
58    Prng(#[from] PrngError),
59
60    /// Failure when calling getrandom().
61    #[error("getrandom: {0}")]
62    GetRandom(#[from] getrandom::Error),
63
64    /// IDPF error.
65    #[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
66    #[error("idpf error: {0}")]
67    Idpf(#[from] IdpfError),
68
69    /// VIDPF error.
70    #[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
71    #[error("vidpf error: {0}")]
72    Vidpf(#[from] VidpfError),
73
74    /// Errors from other VDAFs.
75    #[error(transparent)]
76    Other(Box<dyn Error + 'static + Send + Sync>),
77}
78
79/// An additive share of a vector of field elements.
80#[derive(Clone, Debug)]
81pub enum Share<F, const SEED_SIZE: usize> {
82    /// An uncompressed share, typically sent to the leader.
83    Leader(Vec<F>),
84
85    /// A compressed share, typically sent to the helper.
86    Helper(Seed<SEED_SIZE>),
87}
88
89impl<F: ConstantTimeEq, const SEED_SIZE: usize> PartialEq for Share<F, SEED_SIZE> {
90    fn eq(&self, other: &Self) -> bool {
91        self.ct_eq(other).into()
92    }
93}
94
95impl<F: ConstantTimeEq, const SEED_SIZE: usize> Eq for Share<F, SEED_SIZE> {}
96
97impl<F: ConstantTimeEq, const SEED_SIZE: usize> ConstantTimeEq for Share<F, SEED_SIZE> {
98    fn ct_eq(&self, other: &Self) -> subtle::Choice {
99        // We allow short-circuiting on the type (Leader vs Helper) of the value, but not the types'
100        // contents.
101        match (self, other) {
102            (Share::Leader(self_val), Share::Leader(other_val)) => self_val.ct_eq(other_val),
103            (Share::Helper(self_val), Share::Helper(other_val)) => self_val.ct_eq(other_val),
104            _ => Choice::from(0),
105        }
106    }
107}
108
109/// Parameters needed to decode a [`Share`]
110#[derive(Clone, Debug, PartialEq, Eq)]
111pub(crate) enum ShareDecodingParameter<const SEED_SIZE: usize> {
112    Leader(usize),
113    Helper,
114}
115
116impl<F: FieldElement, const SEED_SIZE: usize> ParameterizedDecode<ShareDecodingParameter<SEED_SIZE>>
117    for Share<F, SEED_SIZE>
118{
119    fn decode_with_param(
120        decoding_parameter: &ShareDecodingParameter<SEED_SIZE>,
121        bytes: &mut Cursor<&[u8]>,
122    ) -> Result<Self, CodecError> {
123        match decoding_parameter {
124            ShareDecodingParameter::Leader(share_length) => {
125                let mut data = Vec::with_capacity(*share_length);
126                for _ in 0..*share_length {
127                    data.push(F::decode(bytes)?)
128                }
129                Ok(Self::Leader(data))
130            }
131            ShareDecodingParameter::Helper => {
132                let seed = Seed::decode(bytes)?;
133                Ok(Self::Helper(seed))
134            }
135        }
136    }
137}
138
139impl<F: FieldElement, const SEED_SIZE: usize> Encode for Share<F, SEED_SIZE> {
140    fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> {
141        match self {
142            Share::Leader(share_data) => {
143                for x in share_data {
144                    x.encode(bytes)?;
145                }
146                Ok(())
147            }
148            Share::Helper(share_seed) => share_seed.encode(bytes),
149        }
150    }
151
152    fn encoded_len(&self) -> Option<usize> {
153        match self {
154            Share::Leader(share_data) => {
155                // Each element of the data vector has the same size.
156                Some(share_data.len() * F::ENCODED_SIZE)
157            }
158            Share::Helper(share_seed) => share_seed.encoded_len(),
159        }
160    }
161}
162
163/// The base trait for VDAF schemes. This trait is inherited by traits [`Client`], [`Aggregator`],
164/// and [`Collector`], which define the roles of the various parties involved in the execution of
165/// the VDAF.
166pub trait Vdaf: Clone + Debug {
167    /// The type of Client measurement to be aggregated.
168    type Measurement: Clone + Debug;
169
170    /// The aggregate result of the VDAF execution.
171    type AggregateResult: Clone + Debug;
172
173    /// The aggregation parameter, used by the Aggregators to map their input shares to output
174    /// shares.
175    type AggregationParam: Clone + Debug + Decode + Encode;
176
177    /// A public share sent by a Client.
178    type PublicShare: Clone + Debug + ParameterizedDecode<Self> + Encode;
179
180    /// An input share sent by a Client.
181    type InputShare: Clone + Debug + for<'a> ParameterizedDecode<(&'a Self, usize)> + Encode;
182
183    /// An output share recovered from an input share by an Aggregator.
184    type OutputShare: Clone
185        + Debug
186        + for<'a> ParameterizedDecode<(&'a Self, &'a Self::AggregationParam)>
187        + Encode;
188
189    /// An Aggregator's share of the aggregate result.
190    type AggregateShare: Aggregatable<OutputShare = Self::OutputShare>
191        + for<'a> ParameterizedDecode<(&'a Self, &'a Self::AggregationParam)>
192        + Encode;
193
194    /// Return the VDAF's algorithm ID.
195    fn algorithm_id(&self) -> u32;
196
197    /// The number of Aggregators. The Client generates as many input shares as there are
198    /// Aggregators.
199    fn num_aggregators(&self) -> usize;
200
201    /// Generate the domain separation tag for this VDAF. The output is used for domain separation
202    /// by the XOF.
203    fn domain_separation_tag(&self, usage: u16) -> [u8; 8] {
204        let mut dst = [0_u8; 8];
205        dst[0] = VERSION;
206        dst[1] = 0; // algorithm class
207        dst[2..6].copy_from_slice(&(self.algorithm_id()).to_be_bytes());
208        dst[6..8].copy_from_slice(&usage.to_be_bytes());
209        dst
210    }
211}
212
213/// The Client's role in the execution of a VDAF.
214pub trait Client<const NONCE_SIZE: usize>: Vdaf {
215    /// Shards a measurement into a public share and a sequence of input shares, one for each
216    /// Aggregator.
217    ///
218    /// Implements `Vdaf::shard` from [VDAF].
219    ///
220    /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.1
221    fn shard(
222        &self,
223        measurement: &Self::Measurement,
224        nonce: &[u8; NONCE_SIZE],
225    ) -> Result<(Self::PublicShare, Vec<Self::InputShare>), VdafError>;
226}
227
228/// The Aggregator's role in the execution of a VDAF.
229pub trait Aggregator<const VERIFY_KEY_SIZE: usize, const NONCE_SIZE: usize>: Vdaf {
230    /// State of the Aggregator during the Prepare process.
231    type PrepareState: Clone + Debug + PartialEq + Eq;
232
233    /// The type of messages sent by each aggregator at each round of the Prepare Process.
234    ///
235    /// Decoding takes a [`Self::PrepareState`] as a parameter; this [`Self::PrepareState`] may be
236    /// associated with any aggregator involved in the execution of the VDAF.
237    type PrepareShare: Clone + Debug + ParameterizedDecode<Self::PrepareState> + Encode;
238
239    /// Result of preprocessing a round of preparation shares. This is used by all aggregators as an
240    /// input to the next round of the Prepare Process.
241    ///
242    /// Decoding takes a [`Self::PrepareState`] as a parameter; this [`Self::PrepareState`] may be
243    /// associated with any aggregator involved in the execution of the VDAF.
244    type PrepareMessage: Clone
245        + Debug
246        + PartialEq
247        + Eq
248        + ParameterizedDecode<Self::PrepareState>
249        + Encode;
250
251    /// Begins the Prepare process with the other Aggregators. The [`Self::PrepareState`] returned
252    /// is passed to [`Self::prepare_next`] to get this aggregator's first-round prepare message.
253    ///
254    /// Implements `Vdaf.prep_init` from [VDAF].
255    ///
256    /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.2
257    fn prepare_init(
258        &self,
259        verify_key: &[u8; VERIFY_KEY_SIZE],
260        agg_id: usize,
261        agg_param: &Self::AggregationParam,
262        nonce: &[u8; NONCE_SIZE],
263        public_share: &Self::PublicShare,
264        input_share: &Self::InputShare,
265    ) -> Result<(Self::PrepareState, Self::PrepareShare), VdafError>;
266
267    /// Preprocess a round of preparation shares into a single input to [`Self::prepare_next`].
268    ///
269    /// Implements `Vdaf.prep_shares_to_prep` from [VDAF].
270    ///
271    /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.2
272    fn prepare_shares_to_prepare_message<M: IntoIterator<Item = Self::PrepareShare>>(
273        &self,
274        agg_param: &Self::AggregationParam,
275        inputs: M,
276    ) -> Result<Self::PrepareMessage, VdafError>;
277
278    /// Compute the next state transition from the current state and the previous round of input
279    /// messages. If this returns [`PrepareTransition::Continue`], then the returned
280    /// [`Self::PrepareShare`] should be combined with the other Aggregators' `PrepareShare`s from
281    /// this round and passed into another call to this method. This continues until this method
282    /// returns [`PrepareTransition::Finish`], at which point the returned output share may be
283    /// aggregated. If the method returns an error, the aggregator should consider its input share
284    /// invalid and not attempt to process it any further.
285    ///
286    /// Implements `Vdaf.prep_next` from [VDAF].
287    ///
288    /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.2
289    fn prepare_next(
290        &self,
291        state: Self::PrepareState,
292        input: Self::PrepareMessage,
293    ) -> Result<PrepareTransition<Self, VERIFY_KEY_SIZE, NONCE_SIZE>, VdafError>;
294
295    /// Aggregates a sequence of output shares into an aggregate share.
296    fn aggregate<M: IntoIterator<Item = Self::OutputShare>>(
297        &self,
298        agg_param: &Self::AggregationParam,
299        output_shares: M,
300    ) -> Result<Self::AggregateShare, VdafError>;
301}
302
303/// Aggregator that implements differential privacy with Aggregator-side noise addition.
304#[cfg(feature = "experimental")]
305#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
306pub trait AggregatorWithNoise<
307    const VERIFY_KEY_SIZE: usize,
308    const NONCE_SIZE: usize,
309    DPStrategy: DifferentialPrivacyStrategy,
310>: Aggregator<VERIFY_KEY_SIZE, NONCE_SIZE>
311{
312    /// Adds noise to an aggregate share such that the aggregate result is differentially private
313    /// as long as one Aggregator is honest.
314    fn add_noise_to_agg_share(
315        &self,
316        dp_strategy: &DPStrategy,
317        agg_param: &Self::AggregationParam,
318        agg_share: &mut Self::AggregateShare,
319        num_measurements: usize,
320    ) -> Result<(), VdafError>;
321}
322
323/// The Collector's role in the execution of a VDAF.
324pub trait Collector: Vdaf {
325    /// Combines aggregate shares into the aggregate result.
326    fn unshard<M: IntoIterator<Item = Self::AggregateShare>>(
327        &self,
328        agg_param: &Self::AggregationParam,
329        agg_shares: M,
330        num_measurements: usize,
331    ) -> Result<Self::AggregateResult, VdafError>;
332}
333
334/// A state transition of an Aggregator during the Prepare process.
335#[derive(Clone, Debug)]
336pub enum PrepareTransition<
337    V: Aggregator<VERIFY_KEY_SIZE, NONCE_SIZE>,
338    const VERIFY_KEY_SIZE: usize,
339    const NONCE_SIZE: usize,
340> {
341    /// Continue processing.
342    Continue(V::PrepareState, V::PrepareShare),
343
344    /// Finish processing and return the output share.
345    Finish(V::OutputShare),
346}
347
348/// An aggregate share resulting from aggregating output shares together that
349/// can merged with aggregate shares of the same type.
350pub trait Aggregatable: Clone + Debug + From<Self::OutputShare> {
351    /// Type of output shares that can be accumulated into an aggregate share.
352    type OutputShare;
353
354    /// Update an aggregate share by merging it with another (`agg_share`).
355    fn merge(&mut self, agg_share: &Self) -> Result<(), VdafError>;
356
357    /// Update an aggregate share by adding `output_share`.
358    fn accumulate(&mut self, output_share: &Self::OutputShare) -> Result<(), VdafError>;
359}
360
361/// An output share comprised of a vector of field elements.
362#[derive(Clone)]
363pub struct OutputShare<F>(Vec<F>);
364
365impl<F: ConstantTimeEq> PartialEq for OutputShare<F> {
366    fn eq(&self, other: &Self) -> bool {
367        self.ct_eq(other).into()
368    }
369}
370
371impl<F: ConstantTimeEq> Eq for OutputShare<F> {}
372
373impl<F: ConstantTimeEq> ConstantTimeEq for OutputShare<F> {
374    fn ct_eq(&self, other: &Self) -> Choice {
375        self.0.ct_eq(&other.0)
376    }
377}
378
379impl<F> AsRef<[F]> for OutputShare<F> {
380    fn as_ref(&self) -> &[F] {
381        &self.0
382    }
383}
384
385impl<F> From<Vec<F>> for OutputShare<F> {
386    fn from(other: Vec<F>) -> Self {
387        Self(other)
388    }
389}
390
391impl<F: FieldElement> Encode for OutputShare<F> {
392    fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> {
393        encode_fieldvec(&self.0, bytes)
394    }
395
396    fn encoded_len(&self) -> Option<usize> {
397        Some(F::ENCODED_SIZE * self.0.len())
398    }
399}
400
401impl<F> Debug for OutputShare<F> {
402    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
403        f.debug_tuple("OutputShare").finish()
404    }
405}
406
407/// An aggregate share comprised of a vector of field elements.
408///
409/// This is suitable for VDAFs where both output shares and aggregate shares are vectors of field
410/// elements, and output shares need no special transformation to be merged into an aggregate share.
411#[derive(Clone, Debug, Serialize, Deserialize)]
412
413pub struct AggregateShare<F>(Vec<F>);
414
415impl<F> From<Vec<F>> for AggregateShare<F> {
416    fn from(other: Vec<F>) -> Self {
417        Self(other)
418    }
419}
420
421impl<F: ConstantTimeEq> PartialEq for AggregateShare<F> {
422    fn eq(&self, other: &Self) -> bool {
423        self.ct_eq(other).into()
424    }
425}
426
427impl<F: ConstantTimeEq> Eq for AggregateShare<F> {}
428
429impl<F: ConstantTimeEq> ConstantTimeEq for AggregateShare<F> {
430    fn ct_eq(&self, other: &Self) -> subtle::Choice {
431        self.0.ct_eq(&other.0)
432    }
433}
434
435impl<F: FieldElement> AsRef<[F]> for AggregateShare<F> {
436    fn as_ref(&self) -> &[F] {
437        &self.0
438    }
439}
440
441impl<F> From<OutputShare<F>> for AggregateShare<F> {
442    fn from(other: OutputShare<F>) -> Self {
443        Self(other.0)
444    }
445}
446
447impl<F: FieldElement> Aggregatable for AggregateShare<F> {
448    type OutputShare = OutputShare<F>;
449
450    fn merge(&mut self, agg_share: &Self) -> Result<(), VdafError> {
451        self.sum(agg_share.as_ref())
452    }
453
454    fn accumulate(&mut self, output_share: &Self::OutputShare) -> Result<(), VdafError> {
455        // For Poplar1, Prio2, and Prio3, no conversion is needed between output shares and
456        // aggregate shares.
457        self.sum(output_share.as_ref())
458    }
459}
460
461impl<F: FieldElement> AggregateShare<F> {
462    fn sum(&mut self, other: &[F]) -> Result<(), VdafError> {
463        merge_vector(&mut self.0, other).map_err(Into::into)
464    }
465}
466
467impl<F: FieldElement> Encode for AggregateShare<F> {
468    fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> {
469        encode_fieldvec(&self.0, bytes)
470    }
471
472    fn encoded_len(&self) -> Option<usize> {
473        Some(F::ENCODED_SIZE * self.0.len())
474    }
475}
476
477/// Utilities for testing VDAFs.
478#[cfg(feature = "test-util")]
479#[cfg_attr(docsrs, doc(cfg(feature = "test-util")))]
480pub mod test_utils {
481    use super::{Aggregatable, Aggregator, Client, Collector, PrepareTransition, VdafError};
482    use crate::codec::{Encode, ParameterizedDecode};
483    use rand::prelude::*;
484
485    /// Execute the VDAF end-to-end and return the aggregate result.
486    pub fn run_vdaf<V, M, const SEED_SIZE: usize>(
487        vdaf: &V,
488        agg_param: &V::AggregationParam,
489        measurements: M,
490    ) -> Result<V::AggregateResult, VdafError>
491    where
492        V: Client<16> + Aggregator<SEED_SIZE, 16> + Collector,
493        M: IntoIterator<Item = V::Measurement>,
494    {
495        let mut sharded_measurements = Vec::new();
496        for measurement in measurements.into_iter() {
497            let nonce = random();
498            let (public_share, input_shares) = vdaf.shard(&measurement, &nonce)?;
499
500            sharded_measurements.push((public_share, nonce, input_shares));
501        }
502
503        run_vdaf_sharded(vdaf, agg_param, sharded_measurements)
504    }
505
506    /// Execute the VDAF on sharded measurements and return the aggregate result.
507    pub fn run_vdaf_sharded<V, M, I, const SEED_SIZE: usize>(
508        vdaf: &V,
509        agg_param: &V::AggregationParam,
510        sharded_measurements: M,
511    ) -> Result<V::AggregateResult, VdafError>
512    where
513        V: Client<16> + Aggregator<SEED_SIZE, 16> + Collector,
514        M: IntoIterator<Item = (V::PublicShare, [u8; 16], I)>,
515        I: IntoIterator<Item = V::InputShare>,
516    {
517        let mut rng = thread_rng();
518        let mut verify_key = [0; SEED_SIZE];
519        rng.fill(&mut verify_key[..]);
520
521        let mut agg_shares: Vec<Option<V::AggregateShare>> = vec![None; vdaf.num_aggregators()];
522        let mut num_measurements: usize = 0;
523        for (public_share, nonce, input_shares) in sharded_measurements.into_iter() {
524            num_measurements += 1;
525            let out_shares = run_vdaf_prepare(
526                vdaf,
527                &verify_key,
528                agg_param,
529                &nonce,
530                public_share,
531                input_shares,
532            )?;
533            for (out_share, agg_share) in out_shares.into_iter().zip(agg_shares.iter_mut()) {
534                // Check serialization of output shares
535                let encoded_out_share = out_share.get_encoded().unwrap();
536                let round_trip_out_share =
537                    V::OutputShare::get_decoded_with_param(&(vdaf, agg_param), &encoded_out_share)
538                        .unwrap();
539                assert_eq!(
540                    round_trip_out_share.get_encoded().unwrap(),
541                    encoded_out_share
542                );
543
544                let this_agg_share = V::AggregateShare::from(out_share);
545                if let Some(ref mut inner) = agg_share {
546                    inner.merge(&this_agg_share)?;
547                } else {
548                    *agg_share = Some(this_agg_share);
549                }
550            }
551        }
552
553        for agg_share in agg_shares.iter() {
554            // Check serialization of aggregate shares
555            let encoded_agg_share = agg_share.as_ref().unwrap().get_encoded().unwrap();
556            let round_trip_agg_share =
557                V::AggregateShare::get_decoded_with_param(&(vdaf, agg_param), &encoded_agg_share)
558                    .unwrap();
559            assert_eq!(
560                round_trip_agg_share.get_encoded().unwrap(),
561                encoded_agg_share
562            );
563        }
564
565        let res = vdaf.unshard(
566            agg_param,
567            agg_shares.into_iter().map(|option| option.unwrap()),
568            num_measurements,
569        )?;
570        Ok(res)
571    }
572
573    /// Execute VDAF preparation for a single report and return the recovered output shares.
574    pub fn run_vdaf_prepare<V, M, const SEED_SIZE: usize>(
575        vdaf: &V,
576        verify_key: &[u8; SEED_SIZE],
577        agg_param: &V::AggregationParam,
578        nonce: &[u8; 16],
579        public_share: V::PublicShare,
580        input_shares: M,
581    ) -> Result<Vec<V::OutputShare>, VdafError>
582    where
583        V: Client<16> + Aggregator<SEED_SIZE, 16> + Collector,
584        M: IntoIterator<Item = V::InputShare>,
585    {
586        let public_share =
587            V::PublicShare::get_decoded_with_param(vdaf, &public_share.get_encoded().unwrap())
588                .unwrap();
589        let input_shares = input_shares
590            .into_iter()
591            .map(|input_share| input_share.get_encoded().unwrap());
592
593        let mut states = Vec::new();
594        let mut outbound = Vec::new();
595        for (agg_id, input_share) in input_shares.enumerate() {
596            let (state, msg) = vdaf.prepare_init(
597                verify_key,
598                agg_id,
599                agg_param,
600                nonce,
601                &public_share,
602                &V::InputShare::get_decoded_with_param(&(vdaf, agg_id), &input_share)
603                    .expect("failed to decode input share"),
604            )?;
605            states.push(state);
606            outbound.push(msg.get_encoded().unwrap());
607        }
608
609        let mut inbound = vdaf
610            .prepare_shares_to_prepare_message(
611                agg_param,
612                outbound.iter().map(|encoded| {
613                    V::PrepareShare::get_decoded_with_param(&states[0], encoded)
614                        .expect("failed to decode prep share")
615                }),
616            )?
617            .get_encoded()
618            .unwrap();
619
620        let mut out_shares = Vec::new();
621        loop {
622            let mut outbound = Vec::new();
623            for state in states.iter_mut() {
624                match vdaf.prepare_next(
625                    state.clone(),
626                    V::PrepareMessage::get_decoded_with_param(state, &inbound)
627                        .expect("failed to decode prep message"),
628                )? {
629                    PrepareTransition::Continue(new_state, msg) => {
630                        outbound.push(msg.get_encoded().unwrap());
631                        *state = new_state
632                    }
633                    PrepareTransition::Finish(out_share) => {
634                        out_shares.push(out_share);
635                    }
636                }
637            }
638
639            if outbound.len() == vdaf.num_aggregators() {
640                // Another round is required before output shares are computed.
641                inbound = vdaf
642                    .prepare_shares_to_prepare_message(
643                        agg_param,
644                        outbound.iter().map(|encoded| {
645                            V::PrepareShare::get_decoded_with_param(&states[0], encoded)
646                                .expect("failed to decode prep share")
647                        }),
648                    )?
649                    .get_encoded()
650                    .unwrap();
651            } else if outbound.is_empty() {
652                // Each Aggregator recovered an output share.
653                break;
654            } else {
655                panic!("Aggregators did not finish the prepare phase at the same time");
656            }
657        }
658
659        Ok(out_shares)
660    }
661}
662
663#[cfg(test)]
664fn fieldvec_roundtrip_test<F, V, T>(vdaf: &V, agg_param: &V::AggregationParam, length: usize)
665where
666    F: FieldElement,
667    V: Vdaf,
668    T: Encode,
669    for<'a> T: ParameterizedDecode<(&'a V, &'a V::AggregationParam)>,
670{
671    // Generate an arbitrary vector of field elements.
672    let vec: Vec<F> = crate::field::random_vector(length).unwrap();
673
674    // Serialize the field element vector into a vector of bytes.
675    let mut bytes = Vec::with_capacity(vec.len() * F::ENCODED_SIZE);
676    encode_fieldvec(&vec, &mut bytes).unwrap();
677
678    // Deserialize the type of interest from those bytes.
679    let value = T::get_decoded_with_param(&(vdaf, agg_param), &bytes).unwrap();
680
681    // Round-trip the value back to a vector of bytes.
682    let encoded = value.get_encoded().unwrap();
683
684    assert_eq!(encoded, bytes);
685}
686
687#[cfg(test)]
688fn equality_comparison_test<T>(values: &[T])
689where
690    T: Debug + PartialEq,
691{
692    use std::ptr;
693
694    // This function expects that every value passed in `values` is distinct, i.e. should not
695    // compare as equal to any other element. We test both (i, j) and (j, i) to gain confidence that
696    // equality implementations are symmetric.
697    for (i, i_val) in values.iter().enumerate() {
698        for (j, j_val) in values.iter().enumerate() {
699            if i == j {
700                assert!(ptr::eq(i_val, j_val)); // sanity
701                assert_eq!(
702                    i_val, j_val,
703                    "Expected element at index {i} to be equal to itself, but it was not"
704                );
705            } else {
706                assert_ne!(
707                    i_val, j_val,
708                    "Expected elements at indices {i} & {j} to not be equal, but they were"
709                )
710            }
711        }
712    }
713}
714
715#[cfg(test)]
716mod tests {
717    use crate::vdaf::{equality_comparison_test, xof::Seed, AggregateShare, OutputShare, Share};
718
719    #[test]
720    fn share_equality_test() {
721        equality_comparison_test(&[
722            Share::Leader(Vec::from([1, 2, 3])),
723            Share::Leader(Vec::from([3, 2, 1])),
724            Share::Helper(Seed([1, 2, 3])),
725            Share::Helper(Seed([3, 2, 1])),
726        ])
727    }
728
729    #[test]
730    fn output_share_equality_test() {
731        equality_comparison_test(&[
732            OutputShare(Vec::from([1, 2, 3])),
733            OutputShare(Vec::from([3, 2, 1])),
734        ])
735    }
736
737    #[test]
738    fn aggregate_share_equality_test() {
739        equality_comparison_test(&[
740            AggregateShare(Vec::from([1, 2, 3])),
741            AggregateShare(Vec::from([3, 2, 1])),
742        ])
743    }
744}
745
746#[cfg(feature = "test-util")]
747#[cfg_attr(docsrs, doc(cfg(feature = "test-util")))]
748pub mod dummy;
749#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
750pub mod mastic;
751#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
752#[cfg_attr(
753    docsrs,
754    doc(cfg(all(feature = "crypto-dependencies", feature = "experimental")))
755)]
756pub mod poplar1;
757#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
758#[cfg_attr(
759    docsrs,
760    doc(cfg(all(feature = "crypto-dependencies", feature = "experimental")))
761)]
762pub mod prio2;
763pub mod prio3;
764#[cfg(any(test, feature = "test-util"))]
765#[cfg_attr(docsrs, doc(cfg(feature = "test-util")))]
766pub mod prio3_test;
767pub mod xof;