2021-06-04 15:40:54 +02:00
|
|
|
use itertools::izip;
|
|
|
|
|
|
|
|
|
|
use crate::circuit_builder::CircuitBuilder;
|
2021-07-12 14:25:28 +02:00
|
|
|
use crate::circuit_data::CommonCircuitData;
|
2021-06-08 19:32:23 +02:00
|
|
|
use crate::field::extension_field::target::{flatten_target, ExtensionTarget};
|
2021-06-14 15:15:22 +02:00
|
|
|
use crate::field::extension_field::Extendable;
|
2021-06-04 15:40:54 +02:00
|
|
|
use crate::field::field::Field;
|
|
|
|
|
use crate::fri::FriConfig;
|
2021-06-14 15:15:22 +02:00
|
|
|
use crate::plonk_challenger::RecursiveChallenger;
|
2021-06-17 11:54:31 +02:00
|
|
|
use crate::plonk_common::PlonkPolynomials;
|
2021-06-04 15:40:54 +02:00
|
|
|
use crate::proof::{
|
2021-06-14 15:15:22 +02:00
|
|
|
FriInitialTreeProofTarget, FriProofTarget, FriQueryRoundTarget, HashTarget, OpeningSetTarget,
|
2021-06-04 15:40:54 +02:00
|
|
|
};
|
2021-06-04 17:36:48 +02:00
|
|
|
use crate::target::Target;
|
2021-06-14 15:15:22 +02:00
|
|
|
use crate::util::{log2_strict, reverse_index_bits_in_place};
|
2021-06-04 15:40:54 +02:00
|
|
|
|
|
|
|
|
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|
|
|
|
/// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity
|
|
|
|
|
/// and P' is the FRI reduced polynomial.
|
2021-06-09 17:39:45 +02:00
|
|
|
fn compute_evaluation(
|
|
|
|
|
&mut self,
|
|
|
|
|
x: Target,
|
|
|
|
|
old_x_index: Target,
|
|
|
|
|
arity_bits: usize,
|
|
|
|
|
last_evals: &[ExtensionTarget<D>],
|
|
|
|
|
beta: ExtensionTarget<D>,
|
|
|
|
|
) -> ExtensionTarget<D> {
|
2021-06-11 16:22:29 +02:00
|
|
|
debug_assert_eq!(last_evals.len(), 1 << arity_bits);
|
|
|
|
|
|
|
|
|
|
let g = F::primitive_root_of_unity(arity_bits);
|
|
|
|
|
|
|
|
|
|
// The evaluation vector needs to be reordered first.
|
|
|
|
|
let mut evals = last_evals.to_vec();
|
|
|
|
|
reverse_index_bits_in_place(&mut evals);
|
|
|
|
|
let mut old_x_index_bits = self.split_le(old_x_index, arity_bits);
|
|
|
|
|
old_x_index_bits.reverse();
|
2021-06-17 11:31:14 +02:00
|
|
|
let evals = self.rotate_left_from_bits(&old_x_index_bits, &evals);
|
2021-06-11 16:22:29 +02:00
|
|
|
|
|
|
|
|
// The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta.
|
|
|
|
|
let points = g
|
|
|
|
|
.powers()
|
2021-06-17 11:31:14 +02:00
|
|
|
.map(|y| {
|
2021-06-11 16:22:29 +02:00
|
|
|
let yt = self.constant(y);
|
2021-06-17 11:31:14 +02:00
|
|
|
self.mul(x, yt)
|
2021-06-11 16:22:29 +02:00
|
|
|
})
|
2021-06-17 11:31:14 +02:00
|
|
|
.zip(evals)
|
2021-06-11 16:22:29 +02:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
self.interpolate(&points, beta)
|
2021-06-04 15:40:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn fri_verify_proof_of_work(
|
|
|
|
|
&mut self,
|
|
|
|
|
proof: &FriProofTarget<D>,
|
|
|
|
|
challenger: &mut RecursiveChallenger,
|
|
|
|
|
config: &FriConfig,
|
2021-06-14 13:26:22 +02:00
|
|
|
) {
|
2021-06-04 15:40:54 +02:00
|
|
|
let mut inputs = challenger.get_hash(self).elements.to_vec();
|
|
|
|
|
inputs.push(proof.pow_witness);
|
|
|
|
|
|
|
|
|
|
let hash = self.hash_n_to_m(inputs, 1, false)[0];
|
2021-06-17 09:49:41 +02:00
|
|
|
self.assert_leading_zeros(hash, config.proof_of_work_bits + F::ORDER.leading_zeros());
|
2021-06-04 15:40:54 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-14 13:26:22 +02:00
|
|
|
pub fn verify_fri_proof(
|
|
|
|
|
&mut self,
|
|
|
|
|
purported_degree_log: usize,
|
|
|
|
|
// Openings of the PLONK polynomials.
|
|
|
|
|
os: &OpeningSetTarget<D>,
|
|
|
|
|
// Point at which the PLONK polynomials are opened.
|
|
|
|
|
zeta: ExtensionTarget<D>,
|
|
|
|
|
// Scaling factor to combine polynomials.
|
|
|
|
|
alpha: ExtensionTarget<D>,
|
|
|
|
|
initial_merkle_roots: &[HashTarget],
|
|
|
|
|
proof: &FriProofTarget<D>,
|
|
|
|
|
challenger: &mut RecursiveChallenger,
|
2021-07-12 14:25:28 +02:00
|
|
|
common_data: &CommonCircuitData<F, D>,
|
2021-06-14 13:26:22 +02:00
|
|
|
) {
|
2021-07-12 14:25:28 +02:00
|
|
|
let config = &common_data.config.fri_config;
|
2021-06-14 13:26:22 +02:00
|
|
|
let total_arities = config.reduction_arity_bits.iter().sum::<usize>();
|
|
|
|
|
debug_assert_eq!(
|
|
|
|
|
purported_degree_log,
|
|
|
|
|
log2_strict(proof.final_poly.len()) + total_arities - config.rate_bits,
|
|
|
|
|
"Final polynomial has wrong degree."
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Size of the LDE domain.
|
|
|
|
|
let n = proof.final_poly.len() << total_arities;
|
|
|
|
|
|
|
|
|
|
// Recover the random betas used in the FRI reductions.
|
|
|
|
|
let betas = proof
|
|
|
|
|
.commit_phase_merkle_roots
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|root| {
|
|
|
|
|
challenger.observe_hash(root);
|
|
|
|
|
challenger.get_extension_challenge(self)
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
challenger.observe_extension_elements(&proof.final_poly.0);
|
|
|
|
|
|
|
|
|
|
// Check PoW.
|
|
|
|
|
self.fri_verify_proof_of_work(proof, challenger, config);
|
|
|
|
|
|
|
|
|
|
// Check that parameters are coherent.
|
|
|
|
|
debug_assert_eq!(
|
|
|
|
|
config.num_query_rounds,
|
|
|
|
|
proof.query_round_proofs.len(),
|
|
|
|
|
"Number of query rounds does not match config."
|
|
|
|
|
);
|
|
|
|
|
debug_assert!(
|
|
|
|
|
!config.reduction_arity_bits.is_empty(),
|
|
|
|
|
"Number of reductions should be non-zero."
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for round_proof in &proof.query_round_proofs {
|
|
|
|
|
self.fri_verifier_query_round(
|
|
|
|
|
os,
|
|
|
|
|
zeta,
|
|
|
|
|
alpha,
|
|
|
|
|
initial_merkle_roots,
|
|
|
|
|
&proof,
|
|
|
|
|
challenger,
|
|
|
|
|
n,
|
|
|
|
|
&betas,
|
|
|
|
|
round_proof,
|
|
|
|
|
config,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-04 17:36:48 +02:00
|
|
|
|
|
|
|
|
fn fri_verify_initial_proof(
|
|
|
|
|
&mut self,
|
|
|
|
|
x_index: Target,
|
|
|
|
|
proof: &FriInitialTreeProofTarget,
|
|
|
|
|
initial_merkle_roots: &[HashTarget],
|
|
|
|
|
) {
|
|
|
|
|
for ((evals, merkle_proof), &root) in proof.evals_proofs.iter().zip(initial_merkle_roots) {
|
|
|
|
|
self.verify_merkle_proof(evals.clone(), x_index, root, merkle_proof);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn fri_combine_initial(
|
|
|
|
|
&mut self,
|
|
|
|
|
proof: &FriInitialTreeProofTarget,
|
|
|
|
|
alpha: ExtensionTarget<D>,
|
|
|
|
|
os: &OpeningSetTarget<D>,
|
|
|
|
|
zeta: ExtensionTarget<D>,
|
|
|
|
|
subgroup_x: Target,
|
|
|
|
|
) -> ExtensionTarget<D> {
|
|
|
|
|
assert!(D > 1, "Not implemented for D=1.");
|
2021-06-07 17:09:53 +02:00
|
|
|
let config = &self.config.fri_config.clone();
|
2021-06-04 17:36:48 +02:00
|
|
|
let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits;
|
|
|
|
|
let subgroup_x = self.convert_to_ext(subgroup_x);
|
|
|
|
|
let mut alpha_powers = self.powers(alpha);
|
|
|
|
|
let mut sum = self.zero_extension();
|
|
|
|
|
|
|
|
|
|
// We will add three terms to `sum`:
|
|
|
|
|
// - one for polynomials opened at `x` only
|
|
|
|
|
// - one for polynomials opened at `x` and `g x`
|
2021-06-17 11:31:14 +02:00
|
|
|
// - one for polynomials opened at `x` and `x.frobenius()`
|
2021-06-04 17:36:48 +02:00
|
|
|
|
2021-06-17 11:35:10 +02:00
|
|
|
// Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials.
|
2021-06-17 11:54:31 +02:00
|
|
|
let single_evals = [
|
2021-06-25 11:24:26 +02:00
|
|
|
PlonkPolynomials::CONSTANTS_SIGMAS,
|
2021-06-17 11:54:31 +02:00
|
|
|
PlonkPolynomials::QUOTIENT,
|
|
|
|
|
]
|
|
|
|
|
.iter()
|
|
|
|
|
.flat_map(|&p| proof.unsalted_evals(p))
|
|
|
|
|
.map(|&e| self.convert_to_ext(e))
|
|
|
|
|
.collect::<Vec<_>>();
|
2021-06-17 11:35:10 +02:00
|
|
|
let single_openings = os
|
2021-06-04 17:36:48 +02:00
|
|
|
.constants
|
|
|
|
|
.iter()
|
|
|
|
|
.chain(&os.plonk_sigmas)
|
|
|
|
|
.chain(&os.quotient_polys);
|
2021-06-17 11:31:14 +02:00
|
|
|
let mut single_numerator = self.zero_extension();
|
|
|
|
|
for (e, &o) in izip!(single_evals, single_openings) {
|
2021-06-07 11:19:54 +02:00
|
|
|
let a = alpha_powers.next(self);
|
|
|
|
|
let diff = self.sub_extension(e, o);
|
2021-06-17 11:31:14 +02:00
|
|
|
single_numerator = self.mul_add_extension(a, diff, single_numerator);
|
2021-06-07 11:19:54 +02:00
|
|
|
}
|
2021-06-17 11:31:14 +02:00
|
|
|
let single_denominator = self.sub_extension(subgroup_x, zeta);
|
|
|
|
|
let quotient = self.div_unsafe_extension(single_numerator, single_denominator);
|
2021-06-14 15:15:22 +02:00
|
|
|
sum = self.add_extension(sum, quotient);
|
2021-06-04 17:36:48 +02:00
|
|
|
|
2021-06-17 11:31:14 +02:00
|
|
|
// Polynomials opened at `x` and `g x`, i.e., the Zs polynomials.
|
|
|
|
|
let zs_evals = proof
|
2021-07-01 17:34:00 +02:00
|
|
|
.unsalted_evals(PlonkPolynomials::ZS_PARTIAL_PRODUCTS)
|
2021-06-07 21:24:41 +02:00
|
|
|
.iter()
|
|
|
|
|
.map(|&e| self.convert_to_ext(e))
|
|
|
|
|
.collect::<Vec<_>>();
|
2021-06-08 14:56:49 +02:00
|
|
|
// TODO: Would probably be more efficient using `CircuitBuilder::reduce_with_powers_recursive`
|
2021-06-17 11:31:14 +02:00
|
|
|
let mut zs_composition_eval = self.zero_extension();
|
|
|
|
|
let mut alpha_powers_cloned = alpha_powers.clone();
|
|
|
|
|
for &e in &zs_evals {
|
|
|
|
|
let a = alpha_powers_cloned.next(self);
|
|
|
|
|
zs_composition_eval = self.mul_add_extension(a, e, zs_composition_eval);
|
2021-06-07 21:24:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let g = self.constant_extension(F::Extension::primitive_root_of_unity(degree_log));
|
2021-06-09 21:12:15 +02:00
|
|
|
let zeta_right = self.mul_extension(g, zeta);
|
2021-06-17 11:31:14 +02:00
|
|
|
let mut zs_ev_zeta = self.zero_extension();
|
|
|
|
|
let mut alpha_powers_cloned = alpha_powers.clone();
|
2021-06-08 14:56:49 +02:00
|
|
|
for &t in &os.plonk_zs {
|
2021-06-17 11:31:14 +02:00
|
|
|
let a = alpha_powers_cloned.next(self);
|
|
|
|
|
zs_ev_zeta = self.mul_add_extension(a, t, zs_ev_zeta);
|
2021-06-08 14:56:49 +02:00
|
|
|
}
|
2021-06-17 11:31:14 +02:00
|
|
|
let mut zs_ev_zeta_right = self.zero_extension();
|
2021-06-08 14:56:49 +02:00
|
|
|
for &t in &os.plonk_zs_right {
|
|
|
|
|
let a = alpha_powers.next(self);
|
2021-06-17 11:31:14 +02:00
|
|
|
zs_ev_zeta_right = self.mul_add_extension(a, t, zs_ev_zeta);
|
2021-06-08 14:56:49 +02:00
|
|
|
}
|
2021-06-17 11:31:14 +02:00
|
|
|
let interpol_val = self.interpolate2(
|
|
|
|
|
[(zeta, zs_ev_zeta), (zeta_right, zs_ev_zeta_right)],
|
|
|
|
|
subgroup_x,
|
|
|
|
|
);
|
|
|
|
|
let zs_numerator = self.sub_extension(zs_composition_eval, interpol_val);
|
|
|
|
|
let vanish_zeta = self.sub_extension(subgroup_x, zeta);
|
|
|
|
|
let vanish_zeta_right = self.sub_extension(subgroup_x, zeta_right);
|
|
|
|
|
let zs_denominator = self.mul_extension(vanish_zeta, vanish_zeta_right);
|
|
|
|
|
let zs_quotient = self.div_unsafe_extension(zs_numerator, zs_denominator);
|
|
|
|
|
sum = self.add_extension(sum, zs_quotient);
|
2021-06-08 14:56:49 +02:00
|
|
|
|
2021-06-17 11:31:14 +02:00
|
|
|
// Polynomials opened at `x` and `x.frobenius()`, i.e., the wires polynomials.
|
|
|
|
|
let wire_evals = proof
|
2021-06-17 11:54:31 +02:00
|
|
|
.unsalted_evals(PlonkPolynomials::WIRES)
|
2021-06-08 14:56:49 +02:00
|
|
|
.iter()
|
|
|
|
|
.map(|&e| self.convert_to_ext(e))
|
|
|
|
|
.collect::<Vec<_>>();
|
2021-06-17 11:31:14 +02:00
|
|
|
let mut wire_composition_eval = self.zero_extension();
|
|
|
|
|
let mut alpha_powers_cloned = alpha_powers.clone();
|
|
|
|
|
for &e in &wire_evals {
|
|
|
|
|
let a = alpha_powers_cloned.next(self);
|
|
|
|
|
wire_composition_eval = self.mul_add_extension(a, e, wire_composition_eval);
|
2021-06-08 14:56:49 +02:00
|
|
|
}
|
2021-06-17 11:31:14 +02:00
|
|
|
let mut alpha_powers_cloned = alpha_powers.clone();
|
2021-06-11 14:16:40 +02:00
|
|
|
let wire_eval = os.wires.iter().fold(self.zero_extension(), |acc, &w| {
|
2021-06-17 11:31:14 +02:00
|
|
|
let a = alpha_powers_cloned.next(self);
|
2021-06-11 14:16:40 +02:00
|
|
|
self.mul_add_extension(a, w, acc)
|
|
|
|
|
});
|
|
|
|
|
let mut alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1, self);
|
|
|
|
|
let wire_eval_frob = os
|
2021-06-08 14:56:49 +02:00
|
|
|
.wires
|
|
|
|
|
.iter()
|
2021-06-11 14:16:40 +02:00
|
|
|
.fold(self.zero_extension(), |acc, &w| {
|
|
|
|
|
let a = alpha_powers_frob.next(self);
|
|
|
|
|
self.mul_add_extension(a, w, acc)
|
|
|
|
|
})
|
|
|
|
|
.frobenius(self);
|
2021-06-17 11:31:14 +02:00
|
|
|
let zeta_frob = zeta.frobenius(self);
|
|
|
|
|
let wire_interpol_val =
|
2021-06-11 16:22:29 +02:00
|
|
|
self.interpolate2([(zeta, wire_eval), (zeta_frob, wire_eval_frob)], subgroup_x);
|
2021-06-17 11:31:14 +02:00
|
|
|
let wire_numerator = self.sub_extension(wire_composition_eval, wire_interpol_val);
|
|
|
|
|
let vanish_zeta_frob = self.sub_extension(subgroup_x, zeta_frob);
|
|
|
|
|
let wire_denominator = self.mul_extension(vanish_zeta, vanish_zeta_frob);
|
|
|
|
|
let wire_quotient = self.div_unsafe_extension(wire_numerator, wire_denominator);
|
|
|
|
|
sum = self.add_extension(sum, wire_quotient);
|
2021-06-04 17:36:48 +02:00
|
|
|
|
|
|
|
|
sum
|
|
|
|
|
}
|
2021-06-08 19:32:23 +02:00
|
|
|
|
|
|
|
|
fn fri_verifier_query_round(
|
|
|
|
|
&mut self,
|
|
|
|
|
os: &OpeningSetTarget<D>,
|
|
|
|
|
zeta: ExtensionTarget<D>,
|
|
|
|
|
alpha: ExtensionTarget<D>,
|
|
|
|
|
initial_merkle_roots: &[HashTarget],
|
|
|
|
|
proof: &FriProofTarget<D>,
|
|
|
|
|
challenger: &mut RecursiveChallenger,
|
|
|
|
|
n: usize,
|
|
|
|
|
betas: &[ExtensionTarget<D>],
|
|
|
|
|
round_proof: &FriQueryRoundTarget<D>,
|
|
|
|
|
config: &FriConfig,
|
2021-06-14 13:26:22 +02:00
|
|
|
) {
|
2021-06-10 16:08:57 +02:00
|
|
|
let n_log = log2_strict(n);
|
2021-06-08 19:32:23 +02:00
|
|
|
let mut evaluations: Vec<Vec<ExtensionTarget<D>>> = Vec::new();
|
|
|
|
|
// TODO: Do we need to range check `x_index` to a target smaller than `p`?
|
|
|
|
|
let mut x_index = challenger.get_challenge(self);
|
2021-06-16 08:40:28 +02:00
|
|
|
x_index = self.split_low_high(x_index, n_log, 64).0;
|
|
|
|
|
let mut x_index_num_bits = n_log;
|
2021-06-08 19:32:23 +02:00
|
|
|
let mut domain_size = n;
|
|
|
|
|
self.fri_verify_initial_proof(
|
|
|
|
|
x_index,
|
|
|
|
|
&round_proof.initial_trees_proof,
|
|
|
|
|
initial_merkle_roots,
|
|
|
|
|
);
|
|
|
|
|
let mut old_x_index = self.zero();
|
|
|
|
|
// `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain.
|
|
|
|
|
let g = self.constant(F::MULTIPLICATIVE_GROUP_GENERATOR);
|
2021-06-10 16:08:57 +02:00
|
|
|
let phi = self.constant(F::primitive_root_of_unity(n_log));
|
2021-06-09 17:39:45 +02:00
|
|
|
|
2021-06-17 11:31:14 +02:00
|
|
|
let reversed_x = self.reverse_limbs::<2>(x_index, n_log);
|
2021-06-16 08:56:58 +02:00
|
|
|
let phi = self.exp(phi, reversed_x, n_log);
|
2021-06-08 19:32:23 +02:00
|
|
|
let mut subgroup_x = self.mul(g, phi);
|
2021-06-09 17:39:45 +02:00
|
|
|
|
2021-06-08 19:32:23 +02:00
|
|
|
for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() {
|
|
|
|
|
let next_domain_size = domain_size >> arity_bits;
|
|
|
|
|
let e_x = if i == 0 {
|
|
|
|
|
self.fri_combine_initial(
|
|
|
|
|
&round_proof.initial_trees_proof,
|
|
|
|
|
alpha,
|
|
|
|
|
os,
|
|
|
|
|
zeta,
|
|
|
|
|
subgroup_x,
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
let last_evals = &evaluations[i - 1];
|
|
|
|
|
// Infer P(y) from {P(x)}_{x^arity=y}.
|
|
|
|
|
self.compute_evaluation(
|
|
|
|
|
subgroup_x,
|
|
|
|
|
old_x_index,
|
|
|
|
|
config.reduction_arity_bits[i - 1],
|
|
|
|
|
last_evals,
|
|
|
|
|
betas[i - 1],
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
let mut evals = round_proof.steps[i].evals.clone();
|
|
|
|
|
// Insert P(y) into the evaluation vector, since it wasn't included by the prover.
|
2021-06-16 08:40:28 +02:00
|
|
|
let (low_x_index, high_x_index) =
|
|
|
|
|
self.split_low_high(x_index, arity_bits, x_index_num_bits);
|
2021-06-14 15:15:22 +02:00
|
|
|
evals = self.insert(low_x_index, e_x, evals);
|
2021-06-14 13:26:22 +02:00
|
|
|
evaluations.push(evals);
|
|
|
|
|
self.verify_merkle_proof(
|
|
|
|
|
flatten_target(&evaluations[i]),
|
|
|
|
|
high_x_index,
|
|
|
|
|
proof.commit_phase_merkle_roots[i],
|
|
|
|
|
&round_proof.steps[i].merkle_proof,
|
|
|
|
|
);
|
2021-06-08 19:32:23 +02:00
|
|
|
|
|
|
|
|
if i > 0 {
|
|
|
|
|
// Update the point x to x^arity.
|
|
|
|
|
for _ in 0..config.reduction_arity_bits[i - 1] {
|
2021-06-17 11:31:14 +02:00
|
|
|
subgroup_x = self.square(subgroup_x);
|
2021-06-08 19:32:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
domain_size = next_domain_size;
|
2021-06-10 17:45:45 +02:00
|
|
|
old_x_index = low_x_index;
|
2021-06-10 16:08:57 +02:00
|
|
|
x_index = high_x_index;
|
2021-06-16 08:40:28 +02:00
|
|
|
x_index_num_bits -= arity_bits;
|
2021-06-08 19:32:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let last_evals = evaluations.last().unwrap();
|
|
|
|
|
let final_arity_bits = *config.reduction_arity_bits.last().unwrap();
|
|
|
|
|
let purported_eval = self.compute_evaluation(
|
|
|
|
|
subgroup_x,
|
|
|
|
|
old_x_index,
|
|
|
|
|
final_arity_bits,
|
|
|
|
|
last_evals,
|
|
|
|
|
*betas.last().unwrap(),
|
|
|
|
|
);
|
|
|
|
|
for _ in 0..final_arity_bits {
|
2021-06-17 11:31:14 +02:00
|
|
|
subgroup_x = self.square(subgroup_x);
|
2021-06-08 19:32:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Final check of FRI. After all the reductions, we check that the final polynomial is equal
|
|
|
|
|
// to the one sent by the prover.
|
2021-06-09 17:55:49 +02:00
|
|
|
let eval = proof.final_poly.eval_scalar(self, subgroup_x);
|
|
|
|
|
self.assert_equal_extension(eval, purported_eval);
|
2021-06-08 19:32:23 +02:00
|
|
|
}
|
2021-06-04 15:40:54 +02:00
|
|
|
}
|