use ark_ff::FftField;
use spongefish::{Codec, DuplexSpongeInterface};
use zeroize::{Zeroize, ZeroizeOnDrop};
use super::{utils::ProtocolDims, Config};
use crate::{
hash::Hash,
protocols::irs_commit,
transcript::{ProverMessage, ProverState},
};
#[derive(Zeroize, ZeroizeOnDrop)]
pub(super) struct BlindingSecrets<F: FftField> {
pub(super) masking_polys: Vec<Vec<F>>,
pub(super) g_polys: Vec<Vec<F>>,
pub(super) blinding_vectors: Vec<Vec<F>>,
}
#[must_use]
#[allow(clippy::struct_field_names)]
pub struct Witness<F: FftField> {
pub(super) f_hat_witness: irs_commit::Witness<F, F>,
pub(super) blinding_poly_witness: irs_commit::Witness<F, F>,
pub(super) f_hat_polys: Vec<Vec<F>>,
pub(super) secrets: BlindingSecrets<F>,
}
impl<F: FftField> Config<F> {
pub fn commit<H, R>(
&self,
prover_state: &mut ProverState<H, R>,
polynomials: &[&[F]],
) -> Witness<F>
where
H: DuplexSpongeInterface,
R: ark_std::rand::RngCore + ark_std::rand::CryptoRng,
F: Codec<[H::U]>,
Hash: ProverMessage<[H::U]>,
{
let num_polys = polynomials.len();
assert!(!polynomials.is_empty(), "must have at least one polynomial");
let expected_len = polynomials[0].len();
assert!(
expected_len.is_power_of_two(),
"polynomial length must be a power of 2"
);
for (i, poly) in polynomials.iter().enumerate() {
assert_eq!(
poly.len(),
expected_len,
"polynomials[{i}] has length {}, expected {expected_len}",
poly.len()
);
}
let dims = ProtocolDims::new(self, num_polys);
let ell_variate_size = 1usize << dims.ell;
let shift = dims.mu - dims.ell;
let mut masking_polys = Vec::with_capacity(num_polys);
let mut f_hat_polys = Vec::with_capacity(num_polys);
for poly in polynomials {
let masking_poly: Vec<F> = (0..ell_variate_size)
.map(|_| F::rand(prover_state.rng()))
.collect();
let f_hat_poly: Vec<F> = poly
.iter()
.enumerate()
.map(|(idx, &coeff)| coeff + masking_poly[idx >> shift])
.collect();
masking_polys.push(masking_poly);
f_hat_polys.push(f_hat_poly);
}
let f_hat_refs: Vec<&[F]> = f_hat_polys.iter().map(|p| p.as_slice()).collect();
let f_hat_witness = self.blinded_polynomial.commit(prover_state, &f_hat_refs);
let num_blinding_polys = dims.num_g_polys();
let mut g_polys = Vec::with_capacity(num_blinding_polys);
for _ in 0..num_blinding_polys {
g_polys.push(
(0..ell_variate_size)
.map(|_| F::rand(prover_state.rng()))
.collect::<Vec<_>>(),
);
}
let interleaved_blinding_vectors: Vec<Vec<F>> = masking_polys
.iter()
.map(|msk| {
g_polys[0]
.iter()
.zip(msk.iter())
.flat_map(|(&g, &m)| [g, m])
.collect()
})
.collect();
let mut blinding_vectors: Vec<Vec<F>> = interleaved_blinding_vectors;
for g in &g_polys[1..] {
blinding_vectors.push(g.iter().flat_map(|&c| [c, F::ZERO]).collect());
}
let blinding_refs: Vec<&[F]> = blinding_vectors.iter().map(|v| v.as_slice()).collect();
let blinding_poly_witness = self
.blinding_polynomial
.commit(prover_state, &blinding_refs);
Witness {
f_hat_witness,
blinding_poly_witness,
f_hat_polys,
secrets: BlindingSecrets {
masking_polys,
g_polys,
blinding_vectors,
},
}
}
}