use core::iter::zip;
use itertools::Itertools;
use std_shims::Vec;
use super::super::circle::CirclePoint;
use super::super::fields::qm31::SecureField;
use super::super::fri::{CirclePolyDegreeBound, FriVerifier};
use super::quotients::{fri_answers, PointSample};
use super::utils::TreeVec;
use super::PcsConfig;
use crate::core::channel::{Channel, MerkleChannel};
use crate::core::pcs::quotients::CommitmentSchemeProof;
use crate::core::pcs::utils::prepare_preprocessed_query_positions;
use crate::core::vcs_lifted::merkle_hasher::MerkleHasherLifted;
use crate::core::vcs_lifted::verifier::MerkleVerifierLifted;
use crate::core::verifier::VerificationError;
use crate::core::ColumnVec;
#[derive(Default)]
pub struct CommitmentSchemeVerifier<MC: MerkleChannel> {
pub trees: TreeVec<MerkleVerifierLifted<MC::H>>,
pub config: PcsConfig,
}
impl<MC: MerkleChannel> CommitmentSchemeVerifier<MC> {
pub fn new(config: PcsConfig) -> Self {
Self {
trees: TreeVec::default(),
config,
}
}
fn column_log_sizes(&self) -> TreeVec<ColumnVec<u32>> {
self.trees
.as_ref()
.map(|tree| tree.column_log_sizes.clone())
}
pub fn commit(
&mut self,
commitment: <MC::H as MerkleHasherLifted>::Hash,
log_sizes: &[u32],
channel: &mut MC::C,
) {
MC::mix_root(channel, commitment);
let extended_log_sizes = log_sizes
.iter()
.map(|&log_size| log_size + self.config.fri_config.log_blowup_factor)
.collect();
let verifier = MerkleVerifierLifted::new(commitment, extended_log_sizes);
self.trees.push(verifier);
}
pub fn verify_values(
&self,
sampled_points: TreeVec<ColumnVec<Vec<CirclePoint<SecureField>>>>,
proof: CommitmentSchemeProof<MC::H>,
channel: &mut MC::C,
) -> Result<(), VerificationError> {
channel.mix_felts(&proof.sampled_values.clone().flatten_cols());
let random_coeff = channel.draw_secure_felt();
let lifting_log_size = self
.column_log_sizes()
.zip_cols(&sampled_points)
.flatten()
.iter()
.filter(|(_, sampled_points)| !sampled_points.is_empty())
.map(|(log_size, _)| *log_size)
.max()
.unwrap();
let bound =
CirclePolyDegreeBound::new(lifting_log_size - self.config.fri_config.log_blowup_factor);
let mut fri_verifier =
FriVerifier::<MC>::commit(channel, self.config.fri_config, proof.fri_proof, bound)?;
if !channel.verify_pow_nonce(self.config.pow_bits, proof.proof_of_work) {
return Err(VerificationError::ProofOfWork);
}
channel.mix_u64(proof.proof_of_work);
let query_positions = fri_verifier.sample_query_positions(channel);
let preprocessed_query_positions = prepare_preprocessed_query_positions(
&query_positions,
lifting_log_size,
self.column_log_sizes()[0]
.iter()
.max()
.copied()
.unwrap_or_default(),
);
let query_positions_tree = TreeVec::new(
self.trees
.iter()
.enumerate()
.map(|(i, _)| {
if i == 0 {
preprocessed_query_positions.as_slice()
} else {
query_positions.as_slice()
}
})
.collect::<Vec<_>>(),
);
self.trees
.as_ref()
.zip_eq(proof.decommitments)
.zip_eq(proof.queried_values.clone())
.zip_eq(query_positions_tree)
.map(
|(((tree, decommitment), queried_values), query_positions)| {
tree.verify(query_positions, queried_values, decommitment)
},
)
.0
.into_iter()
.collect::<Result<(), _>>()?;
let samples = sampled_points.zip_cols(proof.sampled_values).map_cols(
|(sampled_points, sampled_values)| {
zip(sampled_points, sampled_values)
.map(|(point, value)| PointSample { point, value })
.collect_vec()
},
);
let fri_answers = fri_answers(
self.column_log_sizes(),
samples,
random_coeff,
&query_positions,
proof.queried_values,
lifting_log_size,
)?;
fri_verifier.decommit(fri_answers)?;
Ok(())
}
}