in fri/src/verifier/mod.rs [227:319]
fn verify_generic<const N: usize>(
&self,
channel: &mut C,
evaluations: &[E],
positions: &[usize],
) -> Result<(), VerifierError> {
// pre-compute roots of unity used in computing x coordinates in the folded domain
let folding_roots = (0..N)
.map(|i| {
self.domain_generator
.exp(((self.domain_size / N * i) as u64).into())
})
.collect::<Vec<_>>();
// 1 ----- verify the recursive components of the FRI proof -----------------------------------
let mut domain_generator = self.domain_generator;
let mut domain_size = self.domain_size;
let mut max_degree_plus_1 = self.max_poly_degree + 1;
let mut positions = positions.to_vec();
let mut evaluations = evaluations.to_vec();
for depth in 0..self.options.num_fri_layers(self.domain_size) {
// determine which evaluations were queried in the folded layer
let mut folded_positions =
fold_positions(&positions, domain_size, self.options.folding_factor());
// determine where these evaluations are in the commitment Merkle tree
let position_indexes = map_positions_to_indexes(
&folded_positions,
domain_size,
self.options.folding_factor(),
self.num_partitions,
);
// read query values from the specified indexes in the Merkle tree
let layer_commitment = self.layer_commitments[depth];
// TODO: add layer depth to the potential error message
let layer_values = channel.read_layer_queries(&position_indexes, &layer_commitment)?;
let query_values =
get_query_values::<E, N>(&layer_values, &positions, &folded_positions, domain_size);
if evaluations != query_values {
return Err(VerifierError::InvalidLayerFolding(depth));
}
// build a set of x coordinates for each row polynomial
#[rustfmt::skip]
let xs = folded_positions.iter().map(|&i| {
let xe = domain_generator.exp((i as u64).into()) * self.options.domain_offset();
folding_roots.iter()
.map(|&r| E::from(xe * r))
.collect::<Vec<_>>().try_into().unwrap()
})
.collect::<Vec<_>>();
// interpolate x and y values into row polynomials
let row_polys = polynom::interpolate_batch(&xs, &layer_values);
// calculate the pseudo-random value used for linear combination in layer folding
let alpha = self.layer_alphas[depth];
// check that when the polynomials are evaluated at alpha, the result is equal to
// the corresponding column value
evaluations = row_polys.iter().map(|p| polynom::eval(p, alpha)).collect();
// make sure next degree reduction does not result in degree truncation
if max_degree_plus_1 % N != 0 {
return Err(VerifierError::DegreeTruncation(
max_degree_plus_1 - 1,
N,
depth,
));
}
// update variables for the next iteration of the loop
domain_generator = domain_generator.exp((N as u32).into());
max_degree_plus_1 /= N;
domain_size /= N;
mem::swap(&mut positions, &mut folded_positions);
}
// 2 ----- verify the remainder of the FRI proof ----------------------------------------------
// read the remainder from the channel and make sure it matches with the columns
// of the previous layer
let remainder_commitment = self.layer_commitments.last().unwrap();
let remainder = channel.read_remainder::<N>(remainder_commitment)?;
for (&position, evaluation) in positions.iter().zip(evaluations) {
if remainder[position] != evaluation {
return Err(VerifierError::InvalidRemainderFolding);
}
}
// make sure the remainder values satisfy the degree
verify_remainder(remainder, max_degree_plus_1 - 1)
}