diff --git a/src/fri/mod.rs b/src/fri/mod.rs index 4aebbeec..2419b06b 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -32,4 +32,12 @@ impl FriParams { pub(crate) fn total_arities(&self) -> usize { self.reduction_arity_bits.iter().sum() } + + pub(crate) fn max_arity_bits(&self) -> Option { + self.reduction_arity_bits.iter().copied().max() + } + + pub(crate) fn max_arity(&self) -> Option { + self.max_arity_bits().map(|bits| 1 << bits) + } } diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index a077f5ca..eb30c467 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -3,6 +3,9 @@ use crate::field::extension_field::Extendable; use crate::field::field_types::{Field, RichField}; use crate::fri::proof::{FriInitialTreeProofTarget, FriProofTarget, FriQueryRoundTarget}; use crate::fri::FriConfig; +use crate::gates::gate::Gate; +use crate::gates::interpolation::InterpolationGate; +use crate::gates::random_access::RandomAccessGate; use crate::hash::hash_types::MerkleCapTarget; use crate::iop::challenger::RecursiveChallenger; use crate::iop::target::{BoolTarget, Target}; @@ -53,6 +56,35 @@ impl, const D: usize> CircuitBuilder { self.interpolate(&points, beta) } + /// Make sure we have enough wires and routed wires to do the FRI checks efficiently. This check + /// isn't required -- without it we'd get errors elsewhere in the stack -- but just gives more + /// helpful errors. + fn check_config(&self, arity: usize) { + let random_access = RandomAccessGate::::new(arity); + let interpolation_gate = InterpolationGate::::new(arity); + + let min_wires = random_access + .num_wires() + .max(interpolation_gate.num_wires()); + let min_routed_wires = random_access + .num_routed_wires() + .max(interpolation_gate.num_routed_wires()); + + assert!( + self.config.num_wires >= min_wires, + "To efficiently perform FRI checks with an arity of {}, at least {} wires are needed. Consider reducing arity.", + arity, + min_wires + ); + + assert!( + self.config.num_routed_wires >= min_routed_wires, + "To efficiently perform FRI checks with an arity of {}, at least {} routed wires are needed. Consider reducing arity.", + arity, + min_routed_wires + ); + } + fn fri_verify_proof_of_work( &mut self, proof: &FriProofTarget, @@ -81,6 +113,11 @@ impl, const D: usize> CircuitBuilder { common_data: &CommonCircuitData, ) { let config = &common_data.config; + + if let Some(max_arity) = common_data.fri_params.max_arity() { + self.check_config(max_arity); + } + debug_assert_eq!( common_data.final_poly_len(), proof.final_poly.len(), diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index a67c5287..952f26b3 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -81,6 +81,12 @@ impl, const D: usize> InterpolationGate { self.start_evaluation_value() + D } + /// The number of routed wires required in the typical usage of this gate, where the points to + /// interpolate, the evaluation point, and the corresponding value are all routed. + pub(crate) fn num_routed_wires(&self) -> usize { + self.start_coeffs() + } + /// Wire indices of the interpolant's `i`th coefficient. pub fn wires_coeff(&self, i: usize) -> Range { debug_assert!(i < self.num_points); diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index 283a6a1b..4f086ef0 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -45,6 +45,10 @@ impl, const D: usize> RandomAccessGate { (self.vec_size + 1) * D + 1 } + pub(crate) fn num_routed_wires(&self) -> usize { + self.start_of_intermediate_wires() + } + /// An intermediate wire for a dummy variable used to show equality. /// The prover sets this to 1/(x-y) if x != y, or to an arbitrary value if /// x == y.