use alloc::vec::Vec;
use p3_commit::Mmcs;
use p3_field::Field;
use p3_matrix::Matrix;
use p3_zk_codes::ZkEncodingWithRandomness;
use rand::CryptoRng;
use crate::zk::data::MaskOracle;
#[allow(clippy::type_complexity)]
pub(crate) fn sample_masks<F, Enc, M, R>(
k: usize,
encoding: &Enc,
mmcs: &M,
rng: &mut R,
) -> (Vec<Vec<F>>, Vec<Vec<F>>, MaskOracle<F, M>)
where
F: Field,
Enc: ZkEncodingWithRandomness<F>,
Enc::Codeword: Matrix<F>,
M: Mmcs<F>,
R: CryptoRng,
{
let masks: Vec<Vec<F>> = (0..k).map(|_| encoding.sample_message(rng)).collect();
let mask_randomness: Vec<Vec<F>> = masks
.iter()
.map(|_| encoding.sample_randomness(rng))
.collect();
let (commit, prover_data) =
mmcs.commit_matrix(encoding.encode_batch_with_randomness(&masks, &mask_randomness));
(masks, mask_randomness, (commit, prover_data))
}
pub(crate) fn mask_endpoints<EF>(masks: &[Vec<EF>], k: usize) -> (EF, EF)
where
EF: Field,
{
let sum_endpoints_init: EF = masks
.iter()
.map(|mask| mask[0].double() + mask[1..].iter().copied().sum::<EF>())
.sum();
debug_assert!(k >= 1, "auxiliary target requires at least one round");
let two_to_k_minus_1 = EF::TWO.exp_u64((k - 1) as u64);
let mu_tilde: EF = two_to_k_minus_1 * sum_endpoints_init;
#[cfg(debug_assertions)]
{
let mut naive = EF::ZERO;
for bits in 0..(1u64 << k) {
for (l, mask) in masks.iter().enumerate() {
let b_l = (bits >> l) & 1;
let s_l_eval = if b_l == 0 {
mask[0]
} else {
mask.iter().copied().sum::<EF>()
};
naive += s_l_eval;
}
}
debug_assert_eq!(
mu_tilde, naive,
"auxiliary target closed form does not match the naive sum",
);
}
(mu_tilde, sum_endpoints_init)
}
#[cfg(test)]
mod tests {
use alloc::vec;
use p3_field::PrimeCharacteristicRing;
use p3_zk_codes::ZkEncoding as _;
use rand::SeedableRng;
use rand::rngs::StdRng;
use super::*;
use crate::zk::test_helpers::{EF, make_setup};
#[test]
fn mask_endpoints_matches_hand_computed_value() {
let masks = vec![
vec![EF::from_u32(1), EF::from_u32(2), EF::from_u32(3)],
vec![EF::from_u32(4), EF::from_u32(5), EF::from_u32(6)],
];
let (mu_tilde, endpoints) = mask_endpoints::<EF>(&masks, 2);
assert_eq!(endpoints, EF::from_u32(26));
assert_eq!(mu_tilde, EF::from_u32(52));
}
#[test]
fn sample_masks_returns_k_masks_of_message_len() {
let k = 3;
let ell_zk = 4;
let seed = 0;
let (_perm, mmcs, encoding) = make_setup(seed, ell_zk);
let mut rng = StdRng::seed_from_u64(seed);
let (masks, randomness, _oracle) =
sample_masks::<EF, _, _, _>(k, &encoding, &mmcs, &mut rng);
assert_eq!(masks.len(), k);
assert_eq!(randomness.len(), k);
for mask in &masks {
assert_eq!(mask.len(), ell_zk);
}
for rand in &randomness {
assert_eq!(rand.len(), encoding.randomness_len());
}
}
#[test]
fn sample_masks_is_deterministic_under_matched_rng_seeds() {
let k = 2;
let ell_zk = 4;
let seed = 42;
let (_perm, mmcs, encoding) = make_setup(seed, ell_zk);
let mut rng1 = StdRng::seed_from_u64(seed);
let (masks1, randomness1, oracle1) =
sample_masks::<EF, _, _, _>(k, &encoding, &mmcs, &mut rng1);
let mut rng2 = StdRng::seed_from_u64(seed);
let (masks2, randomness2, oracle2) =
sample_masks::<EF, _, _, _>(k, &encoding, &mmcs, &mut rng2);
assert_eq!(masks1, masks2);
assert_eq!(randomness1, randomness2);
assert_eq!(oracle1.0, oracle2.0);
}
}