use crate::{
vdaf::VdafError, DapAggregateResult, DapMeasurement, Prio3Config, VdafAggregateShare,
VdafMessage, VdafState,
};
use prio::{
codec::{CodecError, Encode, ParameterizedDecode},
vdaf::{
prio3::{
Prio3, Prio3InputShare, Prio3PrepareMessage, Prio3PrepareShare, Prio3PrepareState,
},
AggregateShare, Aggregator, Client, Collector, PrepareTransition, Vdaf,
},
};
use std::{convert::TryFrom, io::Cursor};
const ERR_EXPECT_FINISH: &str = "unexpected transition (continued)";
const ERR_FIELD_TYPE: &str = "unexpected field type for step or message";
macro_rules! shard {
(
$vdaf:ident,
$measurement:expr
) => {{
let (_public_share, input_shares) = $vdaf.shard($measurement)?;
input_shares
.iter()
.map(|input_share| input_share.get_encoded())
.collect()
}};
}
pub(crate) fn prio3_shard(
config: &Prio3Config,
measurement: DapMeasurement,
) -> Result<Vec<Vec<u8>>, VdafError> {
match (&config, measurement) {
(Prio3Config::Count, DapMeasurement::U64(measurement)) => {
let vdaf = Prio3::new_aes128_count(2)?;
Ok(shard!(vdaf, &measurement))
}
(Prio3Config::Histogram { buckets }, DapMeasurement::U64(measurement)) => {
let vdaf = Prio3::new_aes128_histogram(2, buckets)?;
Ok(shard!(vdaf, &(measurement as u128)))
}
(Prio3Config::Sum { bits }, DapMeasurement::U64(measurement)) => {
let vdaf = Prio3::new_aes128_sum(2, *bits)?;
Ok(shard!(vdaf, &(measurement as u128)))
}
_ => panic!("prio3_shard: unexpected VDAF config"),
}
}
macro_rules! prep_init {
(
$vdaf:ident,
$verify_key:expr,
$agg_id:expr,
$nonce_data:expr,
$input_share_data:expr
) => {{
let input_share =
Prio3InputShare::get_decoded_with_param(&(&$vdaf, $agg_id), $input_share_data)?;
$vdaf.prepare_init($verify_key, $agg_id, &(), $nonce_data, &(), &input_share)?
}};
}
pub(crate) fn prio3_prepare_init(
config: &Prio3Config,
verify_key: &[u8; 16],
agg_id: usize,
nonce_data: &[u8],
input_share_data: &[u8],
) -> Result<(VdafState, VdafMessage), VdafError> {
match &config {
Prio3Config::Count => {
let vdaf = Prio3::new_aes128_count(2)?;
let (state, share) = prep_init!(vdaf, verify_key, agg_id, nonce_data, input_share_data);
Ok((
VdafState::Prio3Field64(state),
VdafMessage::Prio3ShareField64(share),
))
}
Prio3Config::Histogram { buckets } => {
let vdaf = Prio3::new_aes128_histogram(2, buckets)?;
let (state, share) = prep_init!(vdaf, verify_key, agg_id, nonce_data, input_share_data);
Ok((
VdafState::Prio3Field128(state),
VdafMessage::Prio3ShareField128(share),
))
}
Prio3Config::Sum { bits } => {
let vdaf = Prio3::new_aes128_sum(2, *bits)?;
let (state, share) = prep_init!(vdaf, verify_key, agg_id, nonce_data, input_share_data);
Ok((
VdafState::Prio3Field128(state),
VdafMessage::Prio3ShareField128(share),
))
}
}
}
macro_rules! leader_prep_fin {
(
$vdaf:ident,
$leader_state:expr,
$leader_share:expr,
$helper_share_data:expr
) => {{
let helper_share =
Prio3PrepareShare::get_decoded_with_param(&$leader_state, $helper_share_data)?;
let message = $vdaf.prepare_preprocess([$leader_share, helper_share])?;
let message_data = message.get_encoded();
match $vdaf.prepare_step($leader_state, message)? {
PrepareTransition::Continue(..) => {
panic!("prio3_leader_prepare_finish: {}", ERR_EXPECT_FINISH)
}
PrepareTransition::Finish(out_share) => (out_share, message_data),
}
}};
}
pub(crate) fn prio3_leader_prepare_finish(
config: &Prio3Config,
leader_state: VdafState,
leader_share: VdafMessage,
helper_share_data: &[u8],
) -> Result<(VdafAggregateShare, Vec<u8>), VdafError> {
let (agg_share, outbound) = match (&config, leader_state, leader_share) {
(
Prio3Config::Count,
VdafState::Prio3Field64(state),
VdafMessage::Prio3ShareField64(share),
) => {
let vdaf = Prio3::new_aes128_count(2)?;
let (out_share, outbound) = leader_prep_fin!(vdaf, state, share, helper_share_data);
let agg_share = VdafAggregateShare::Field64(vdaf.aggregate(&(), [out_share])?);
(agg_share, outbound)
}
(
Prio3Config::Histogram { buckets },
VdafState::Prio3Field128(state),
VdafMessage::Prio3ShareField128(share),
) => {
let vdaf = Prio3::new_aes128_histogram(2, buckets)?;
let (out_share, outbound) = leader_prep_fin!(vdaf, state, share, helper_share_data);
let agg_share = VdafAggregateShare::Field128(vdaf.aggregate(&(), [out_share])?);
(agg_share, outbound)
}
(
Prio3Config::Sum { bits },
VdafState::Prio3Field128(state),
VdafMessage::Prio3ShareField128(share),
) => {
let vdaf = Prio3::new_aes128_sum(2, *bits)?;
let (out_share, outbound) = leader_prep_fin!(vdaf, state, share, helper_share_data);
let agg_share = VdafAggregateShare::Field128(vdaf.aggregate(&(), [out_share])?);
(agg_share, outbound)
}
_ => panic!("prio3_leader_prepare_finish: {}", ERR_FIELD_TYPE),
};
Ok((agg_share, outbound))
}
macro_rules! helper_prep_fin {
(
$vdaf:ident,
$helper_state:expr,
$leader_message_data:expr
) => {{
let leader_message =
Prio3PrepareMessage::get_decoded_with_param(&$helper_state, $leader_message_data)?;
match $vdaf.prepare_step($helper_state, leader_message)? {
PrepareTransition::Continue(..) => {
panic!("prio3_helper_prepare_finish: {}", ERR_EXPECT_FINISH)
}
PrepareTransition::Finish(out_share) => out_share,
}
}};
}
pub(crate) fn prio3_helper_prepare_finish(
config: &Prio3Config,
state: VdafState,
peer_message_data: &[u8],
) -> Result<VdafAggregateShare, VdafError> {
let agg_share = match (&config, state) {
(Prio3Config::Count, VdafState::Prio3Field64(state)) => {
let vdaf = Prio3::new_aes128_count(2)?;
let out_share = helper_prep_fin!(vdaf, state, peer_message_data);
VdafAggregateShare::Field64(vdaf.aggregate(&(), [out_share])?)
}
(Prio3Config::Histogram { buckets }, VdafState::Prio3Field128(state)) => {
let vdaf = Prio3::new_aes128_histogram(2, buckets)?;
let out_share = helper_prep_fin!(vdaf, state, peer_message_data);
VdafAggregateShare::Field128(vdaf.aggregate(&(), [out_share])?)
}
(Prio3Config::Sum { bits }, VdafState::Prio3Field128(state)) => {
let vdaf = Prio3::new_aes128_sum(2, *bits)?;
let out_share = helper_prep_fin!(vdaf, state, peer_message_data);
VdafAggregateShare::Field128(vdaf.aggregate(&(), [out_share])?)
}
_ => panic!("prio3_helper_prepare_finish: {}", ERR_FIELD_TYPE),
};
Ok(agg_share)
}
pub(crate) fn prio3_append_prepare_state(
bytes: &mut Vec<u8>,
config: &Prio3Config,
state: &VdafState,
) -> Result<(), VdafError> {
match (&config, state) {
(Prio3Config::Count, VdafState::Prio3Field64(state)) => {
state.encode(bytes);
}
(Prio3Config::Histogram { buckets: _ }, VdafState::Prio3Field128(state))
| (Prio3Config::Sum { bits: _ }, VdafState::Prio3Field128(state)) => {
state.encode(bytes);
}
_ => panic!("prio3_append_prepare_state: {}", ERR_FIELD_TYPE),
}
Ok(())
}
pub(crate) fn prio3_decode_prepare_state(
config: &Prio3Config,
agg_id: usize,
bytes: &mut Cursor<&[u8]>,
) -> Result<VdafState, VdafError> {
match config {
Prio3Config::Count => {
let vdaf = Prio3::new_aes128_count(2)?;
Ok(VdafState::Prio3Field64(
Prio3PrepareState::decode_with_param(&(&vdaf, agg_id), bytes)?,
))
}
Prio3Config::Histogram { buckets } => {
let vdaf = Prio3::new_aes128_histogram(2, buckets)?;
Ok(VdafState::Prio3Field128(
Prio3PrepareState::decode_with_param(&(&vdaf, agg_id), bytes)?,
))
}
Prio3Config::Sum { bits } => {
let vdaf = Prio3::new_aes128_sum(2, *bits)?;
Ok(VdafState::Prio3Field128(
Prio3PrepareState::decode_with_param(&(&vdaf, agg_id), bytes)?,
))
}
}
}
pub(crate) fn prio3_encode_prepare_message(message: &VdafMessage) -> Vec<u8> {
match message {
VdafMessage::Prio3ShareField64(message) => message.get_encoded(),
VdafMessage::Prio3ShareField128(message) => message.get_encoded(),
_ => panic!("prio3_encode_prepare_message: unexpected message type"),
}
}
macro_rules! unshard {
(
$vdaf:ident,
$num_measurements:ident,
$agg_shares:expr
) => {{
let mut agg_shares = Vec::with_capacity($vdaf.num_aggregators());
for data in $agg_shares.into_iter() {
let agg_share = AggregateShare::try_from(data.as_ref())
.map_err(|e| CodecError::Other(Box::new(e)))?;
agg_shares.push(agg_share)
}
$vdaf.unshard(&(), agg_shares, $num_measurements)
}};
}
pub(crate) fn prio3_unshard<M: IntoIterator<Item = Vec<u8>>>(
config: &Prio3Config,
num_measurements: usize,
agg_shares: M,
) -> Result<DapAggregateResult, VdafError> {
match &config {
Prio3Config::Count => {
let vdaf = Prio3::new_aes128_count(2)?;
let agg_res = unshard!(vdaf, num_measurements, agg_shares)?;
Ok(DapAggregateResult::U64(agg_res))
}
Prio3Config::Histogram { buckets } => {
let vdaf = Prio3::new_aes128_histogram(2, buckets)?;
let agg_res = unshard!(vdaf, num_measurements, agg_shares)?;
Ok(DapAggregateResult::U128Vec(agg_res))
}
Prio3Config::Sum { bits } => {
let vdaf = Prio3::new_aes128_sum(2, *bits)?;
let agg_res = unshard!(vdaf, num_measurements, agg_shares)?;
Ok(DapAggregateResult::U128(agg_res))
}
}
}