use alloc::vec::Vec;
use core::cell::RefCell;
use core::fmt::Debug;
use p3_challenger::{CanObserve, FieldChallenger, GrindingChallenger};
use p3_commit::{Mmcs, OpenedValues, Pcs, TwoAdicMultiplicativeCoset};
use p3_dft::TwoAdicSubgroupDft;
use p3_field::{ExtensionField, Field, TwoAdicField};
use p3_matrix::dense::RowMajorMatrix;
use p3_matrix::horizontally_truncated::HorizontallyTruncated;
use p3_matrix::Matrix;
use rand::distributions::{Distribution, Standard};
use rand::Rng;
use tracing::instrument;
use crate::verifier::FriError;
use crate::{BatchOpening, FriConfig, FriProof, TwoAdicFriPcs};
#[derive(Debug)]
pub struct HidingFriPcs<Val, Dft, InputMmcs, FriMmcs, R> {
inner: TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs>,
num_random_codewords: usize,
rng: RefCell<R>,
}
impl<Val, Dft, InputMmcs, FriMmcs, R> HidingFriPcs<Val, Dft, InputMmcs, FriMmcs, R> {
pub fn new(
dft: Dft,
mmcs: InputMmcs,
fri: FriConfig<FriMmcs>,
num_random_codewords: usize,
rng: R,
) -> Self {
let inner = TwoAdicFriPcs::new(dft, mmcs, fri);
Self {
inner,
num_random_codewords,
rng: rng.into(),
}
}
}
impl<Val, Dft, InputMmcs, FriMmcs, Challenge, Challenger, R> Pcs<Challenge, Challenger>
for HidingFriPcs<Val, Dft, InputMmcs, FriMmcs, R>
where
Val: TwoAdicField,
Standard: Distribution<Val>,
Dft: TwoAdicSubgroupDft<Val>,
InputMmcs: Mmcs<Val>,
FriMmcs: Mmcs<Challenge>,
Challenge: TwoAdicField + ExtensionField<Val>,
Challenger:
FieldChallenger<Val> + CanObserve<FriMmcs::Commitment> + GrindingChallenger<Witness = Val>,
R: Rng + Send + Sync,
{
type Domain = TwoAdicMultiplicativeCoset<Val>;
type Commitment = InputMmcs::Commitment;
type ProverData = InputMmcs::ProverData<RowMajorMatrix<Val>>;
type Proof = (
OpenedValues<Challenge>,
FriProof<Challenge, FriMmcs, Val, Vec<BatchOpening<Val, InputMmcs>>>,
);
type Error = FriError<FriMmcs::Error, InputMmcs::Error>;
fn natural_domain_for_degree(&self, degree: usize) -> Self::Domain {
<TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs> as Pcs<Challenge, Challenger>>::natural_domain_for_degree(
&self.inner, degree)
}
fn commit(
&self,
evaluations: Vec<(Self::Domain, RowMajorMatrix<Val>)>,
) -> (Self::Commitment, Self::ProverData) {
let randomized_evaluations = evaluations
.into_iter()
.map(|(domain, mat)| {
(
domain,
add_random_cols(mat, self.num_random_codewords, &mut *self.rng.borrow_mut()),
)
})
.collect();
<TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs> as Pcs<Challenge, Challenger>>::commit(
&self.inner,
randomized_evaluations,
)
}
fn get_evaluations_on_domain<'a>(
&self,
prover_data: &'a Self::ProverData,
idx: usize,
domain: Self::Domain,
) -> impl Matrix<Val> + 'a {
let inner_evals = <TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs> as Pcs<
Challenge,
Challenger,
>>::get_evaluations_on_domain(
&self.inner, prover_data, idx, domain
);
let inner_width = inner_evals.width();
HorizontallyTruncated::new(inner_evals, inner_width - self.num_random_codewords)
}
fn open(
&self,
rounds: Vec<(
&Self::ProverData,
Vec<
Vec<Challenge>,
>,
)>,
challenger: &mut Challenger,
) -> (OpenedValues<Challenge>, Self::Proof) {
let (mut inner_opened_values, inner_proof) = self.inner.open(rounds, challenger);
let opened_values_rand = inner_opened_values
.iter_mut()
.map(|opened_values_for_round| {
opened_values_for_round
.iter_mut()
.map(|opened_values_for_mat| {
opened_values_for_mat
.iter_mut()
.map(|opened_values_for_point| {
let split =
opened_values_for_point.len() - self.num_random_codewords;
opened_values_for_point.drain(split..).collect()
})
.collect()
})
.collect()
})
.collect();
(inner_opened_values, (opened_values_rand, inner_proof))
}
fn verify(
&self,
mut rounds: Vec<(
Self::Commitment,
Vec<(
Self::Domain,
Vec<(
Challenge,
Vec<Challenge>,
)>,
)>,
)>,
proof: &Self::Proof,
challenger: &mut Challenger,
) -> Result<(), Self::Error> {
let (opened_values_for_rand_cws, inner_proof) = proof;
for (round, rand_round) in rounds.iter_mut().zip(opened_values_for_rand_cws) {
for (mat, rand_mat) in round.1.iter_mut().zip(rand_round) {
for (point, rand_point) in mat.1.iter_mut().zip(rand_mat) {
point.1.extend(rand_point);
}
}
}
self.inner.verify(rounds, inner_proof, challenger)
}
}
#[instrument(level = "debug", skip_all)]
fn add_random_cols<Val, R>(
mat: RowMajorMatrix<Val>,
num_random_codewords: usize,
mut rng: R,
) -> RowMajorMatrix<Val>
where
Val: Field,
R: Rng + Send + Sync,
Standard: Distribution<Val>,
{
let old_w = mat.width();
let new_w = old_w + num_random_codewords;
let h = mat.height();
let new_values = Val::zero_vec(new_w * h);
let mut result = RowMajorMatrix::new(new_values, new_w);
result
.rows_mut()
.zip(mat.row_slices())
.for_each(|(new_row, old_row)| {
new_row[..old_w].copy_from_slice(old_row);
new_row[old_w..].iter_mut().for_each(|v| *v = rng.gen());
});
result
}