diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index eb30c467..b268e831 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -59,9 +59,12 @@ impl, const D: usize> CircuitBuilder { /// 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); + fn check_config(&self, max_fri_arity: usize) { + let random_access = RandomAccessGate::::new_from_config( + &self.config, + max_fri_arity.max(1 << self.config.cap_height), + ); + let interpolation_gate = InterpolationGate::::new(max_fri_arity); let min_wires = random_access .num_wires() @@ -73,14 +76,14 @@ impl, const D: usize> CircuitBuilder { 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, + max_fri_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, + max_fri_arity, min_routed_wires ); } @@ -372,12 +375,7 @@ impl, const D: usize> CircuitBuilder { let x_index_within_coset = self.le_sum(x_index_within_coset_bits.iter()); // Check consistency with our old evaluation from the previous round. - self.random_access_padded( - x_index_within_coset, - old_eval, - evals.clone(), - 1 << config.cap_height, - ); + self.random_access_extension(x_index_within_coset, old_eval, evals.clone()); // Infer P(y) from {P(x)}_{x^arity=y}. old_eval = with_context!( diff --git a/src/gadgets/random_access.rs b/src/gadgets/random_access.rs index ab0db68c..9875385f 100644 --- a/src/gadgets/random_access.rs +++ b/src/gadgets/random_access.rs @@ -6,55 +6,79 @@ use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; impl, const D: usize> CircuitBuilder { + /// Finds the last available random access gate with the given `vec_size` or add one if there aren't any. + /// Returns `(g,i)` such that there is a random access gate with the given `vec_size` at index + /// `g` and the gate's `i`-th random access is available. + fn find_random_access_gate(&mut self, vec_size: usize) -> (usize, usize) { + let (gate, i) = self + .free_random_access + .get(&vec_size) + .copied() + .unwrap_or_else(|| { + let gate = self.add_gate( + RandomAccessGate::new_from_config(&self.config, vec_size), + vec![], + ); + (gate, 0) + }); + + // Update `free_random_access` with new values. + if i < RandomAccessGate::::max_num_copies( + self.config.num_routed_wires, + self.config.num_wires, + vec_size, + ) - 1 + { + self.free_random_access.insert(vec_size, (gate, i + 1)); + } else { + self.free_random_access.remove(&vec_size); + } + + (gate, i) + } + /// Checks that a `Target` matches a vector at a non-deterministic index. - /// Note: `index` is not range-checked. - pub fn random_access( + /// Note: `access_index` is not range-checked. + pub fn random_access(&mut self, access_index: Target, claimed_element: Target, v: Vec) { + let vec_size = v.len(); + debug_assert!(vec_size > 0); + if vec_size == 1 { + return self.connect(claimed_element, v[0]); + } + let (gate_index, copy) = self.find_random_access_gate(vec_size); + let dummy_gate = RandomAccessGate::::new_from_config(&self.config, vec_size); + + v.iter().enumerate().for_each(|(i, &val)| { + self.connect( + val, + Target::wire(gate_index, dummy_gate.wire_list_item(i, copy)), + ); + }); + self.connect( + access_index, + Target::wire(gate_index, dummy_gate.wire_access_index(copy)), + ); + self.connect( + claimed_element, + Target::wire(gate_index, dummy_gate.wire_claimed_element(copy)), + ); + } + + /// Checks that an `ExtensionTarget` matches a vector at a non-deterministic index. + /// Note: `access_index` is not range-checked. + pub fn random_access_extension( &mut self, access_index: Target, claimed_element: ExtensionTarget, v: Vec>, ) { - debug_assert!(!v.is_empty()); - if v.len() == 1 { - return self.connect_extension(claimed_element, v[0]); - } - let gate = RandomAccessGate::new(v.len()); - let gate_index = self.add_gate(gate.clone(), vec![]); - - v.iter().enumerate().for_each(|(i, &val)| { - self.connect_extension( - val, - ExtensionTarget::from_range(gate_index, gate.wires_list_item(i)), + for i in 0..D { + self.random_access( + access_index, + claimed_element.0[i], + v.iter().map(|et| et.0[i]).collect(), ); - }); - self.connect( - access_index, - Target::wire(gate_index, gate.wire_access_index()), - ); - self.connect_extension( - claimed_element, - ExtensionTarget::from_range(gate_index, gate.wires_claimed_element()), - ); - } - - /// Like `random_access`, but first pads `v` to a given minimum length. This can help to avoid - /// having multiple `RandomAccessGate`s with different sizes. - pub fn random_access_padded( - &mut self, - access_index: Target, - claimed_element: ExtensionTarget, - mut v: Vec>, - min_length: usize, - ) { - debug_assert!(!v.is_empty()); - if v.len() == 1 { - return self.connect_extension(claimed_element, v[0]); } - let zero = self.zero_extension(); - if v.len() < min_length { - v.resize(8, zero); - } - self.random_access(access_index, claimed_element, v); } } @@ -83,7 +107,7 @@ mod tests { for i in 0..len { let it = builder.constant(F::from_canonical_usize(i)); let elem = builder.constant_extension(vec[i]); - builder.random_access(it, elem, v.clone()); + builder.random_access_extension(it, elem, v.clone()); } let data = builder.build(); diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index 4f086ef0..71851725 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -1,8 +1,7 @@ use std::marker::PhantomData; -use std::ops::Range; use crate::field::extension_field::target::ExtensionTarget; -use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::extension_field::Extendable; use crate::field::field_types::{Field, RichField}; use crate::gates::gate::Gate; use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; @@ -10,39 +9,57 @@ use crate::iop::target::Target; use crate::iop::wire::Wire; use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; /// A gate for checking that a particular element of a list matches a given value. -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub(crate) struct RandomAccessGate, const D: usize> { pub vec_size: usize, + pub num_copies: usize, _phantom: PhantomData, } impl, const D: usize> RandomAccessGate { - pub fn new(vec_size: usize) -> Self { + pub fn new(num_copies: usize, vec_size: usize) -> Self { Self { vec_size, + num_copies, _phantom: PhantomData, } } - pub fn wire_access_index(&self) -> usize { - 0 + pub fn new_from_config(config: &CircuitConfig, vec_size: usize) -> Self { + let num_copies = Self::max_num_copies(config.num_routed_wires, config.num_wires, vec_size); + Self::new(num_copies, vec_size) } - pub fn wires_claimed_element(&self) -> Range { - 1..D + 1 + pub fn max_num_copies(num_routed_wires: usize, num_wires: usize, vec_size: usize) -> usize { + // Need `(2 + vec_size) * num_copies` routed wires + (num_routed_wires / (2 + vec_size)).min( + // Need `(2 + 3*vec_size) * num_copies` wires + num_wires / (2 + 3 * vec_size), + ) } - pub fn wires_list_item(&self, i: usize) -> Range { + pub fn wire_access_index(&self, copy: usize) -> usize { + debug_assert!(copy < self.num_copies); + (2 + self.vec_size) * copy + } + + pub fn wire_claimed_element(&self, copy: usize) -> usize { + debug_assert!(copy < self.num_copies); + (2 + self.vec_size) * copy + 1 + } + + pub fn wire_list_item(&self, i: usize, copy: usize) -> usize { debug_assert!(i < self.vec_size); - let start = (i + 1) * D + 1; - start..start + D + debug_assert!(copy < self.num_copies); + (2 + self.vec_size) * copy + 2 + i } fn start_of_intermediate_wires(&self) -> usize { - (self.vec_size + 1) * D + 1 + (2 + self.vec_size) * self.num_copies } pub(crate) fn num_routed_wires(&self) -> usize { @@ -52,16 +69,21 @@ impl, const D: usize> RandomAccessGate { /// 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. - pub fn wire_equality_dummy_for_index(&self, i: usize) -> usize { + pub fn wire_equality_dummy_for_index(&self, i: usize, copy: usize) -> usize { debug_assert!(i < self.vec_size); - self.start_of_intermediate_wires() + i + debug_assert!(copy < self.num_copies); + self.start_of_intermediate_wires() + copy * self.vec_size + i } /// An intermediate wire for the "index_matches" variable (1 if the current index is the index at /// which to compare, 0 otherwise). - pub fn wire_index_matches_for_index(&self, i: usize) -> usize { + pub fn wire_index_matches_for_index(&self, i: usize, copy: usize) -> usize { debug_assert!(i < self.vec_size); - self.start_of_intermediate_wires() + self.vec_size + i + debug_assert!(copy < self.num_copies); + self.start_of_intermediate_wires() + + self.vec_size * self.num_copies + + self.vec_size * copy + + i } } @@ -71,53 +93,54 @@ impl, const D: usize> Gate for RandomAccessGa } fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { - let access_index = vars.local_wires[self.wire_access_index()]; - let list_items = (0..self.vec_size) - .map(|i| vars.get_local_ext_algebra(self.wires_list_item(i))) - .collect::>(); - let claimed_element = vars.get_local_ext_algebra(self.wires_claimed_element()); - let mut constraints = Vec::with_capacity(self.num_constraints()); - for i in 0..self.vec_size { - let cur_index = F::Extension::from_canonical_usize(i); - let difference = cur_index - access_index; - let equality_dummy = vars.local_wires[self.wire_equality_dummy_for_index(i)]; - let index_matches = vars.local_wires[self.wire_index_matches_for_index(i)]; - // The two index equality constraints. - constraints.push(difference * equality_dummy - (F::Extension::ONE - index_matches)); - constraints.push(index_matches * difference); - // Value equality constraint. - constraints.extend( - ((list_items[i] - claimed_element).scalar_mul(index_matches)).to_basefield_array(), - ); + for copy in 0..self.num_copies { + let access_index = vars.local_wires[self.wire_access_index(copy)]; + let list_items = (0..self.vec_size) + .map(|i| vars.local_wires[self.wire_list_item(i, copy)]) + .collect::>(); + let claimed_element = vars.local_wires[self.wire_claimed_element(copy)]; + + for i in 0..self.vec_size { + let cur_index = F::Extension::from_canonical_usize(i); + let difference = cur_index - access_index; + let equality_dummy = vars.local_wires[self.wire_equality_dummy_for_index(i, copy)]; + let index_matches = vars.local_wires[self.wire_index_matches_for_index(i, copy)]; + + // The two index equality constraints. + constraints.push(difference * equality_dummy - (F::Extension::ONE - index_matches)); + constraints.push(index_matches * difference); + // Value equality constraint. + constraints.push((list_items[i] - claimed_element) * index_matches); + } } constraints } fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { - let access_index = vars.local_wires[self.wire_access_index()]; - let list_items = (0..self.vec_size) - .map(|i| vars.get_local_ext(self.wires_list_item(i))) - .collect::>(); - let claimed_element = vars.get_local_ext(self.wires_claimed_element()); - let mut constraints = Vec::with_capacity(self.num_constraints()); - for i in 0..self.vec_size { - let cur_index = F::from_canonical_usize(i); - let difference = cur_index - access_index; - let equality_dummy = vars.local_wires[self.wire_equality_dummy_for_index(i)]; - let index_matches = vars.local_wires[self.wire_index_matches_for_index(i)]; - // The two equality constraints. - constraints.push(difference * equality_dummy - (F::ONE - index_matches)); - constraints.push(index_matches * difference); + for copy in 0..self.num_copies { + let access_index = vars.local_wires[self.wire_access_index(copy)]; + let list_items = (0..self.vec_size) + .map(|i| vars.local_wires[self.wire_list_item(i, copy)]) + .collect::>(); + let claimed_element = vars.local_wires[self.wire_claimed_element(copy)]; - // Value equality constraint. - constraints.extend( - ((list_items[i] - claimed_element).scalar_mul(index_matches)).to_basefield_array(), - ); + for i in 0..self.vec_size { + let cur_index = F::from_canonical_usize(i); + let difference = cur_index - access_index; + let equality_dummy = vars.local_wires[self.wire_equality_dummy_for_index(i, copy)]; + let index_matches = vars.local_wires[self.wire_index_matches_for_index(i, copy)]; + + // The two index equality constraints. + constraints.push(difference * equality_dummy - (F::ONE - index_matches)); + constraints.push(index_matches * difference); + // Value equality constraint. + constraints.push((list_items[i] - claimed_element) * index_matches); + } } constraints @@ -128,35 +151,36 @@ impl, const D: usize> Gate for RandomAccessGa builder: &mut CircuitBuilder, vars: EvaluationTargets, ) -> Vec> { - let access_index = vars.local_wires[self.wire_access_index()]; - let list_items = (0..self.vec_size) - .map(|i| vars.get_local_ext_algebra(self.wires_list_item(i))) - .collect::>(); - let claimed_element = vars.get_local_ext_algebra(self.wires_claimed_element()); - let mut constraints = Vec::with_capacity(self.num_constraints()); - for i in 0..self.vec_size { - let cur_index_ext = F::Extension::from_canonical_usize(i); - let cur_index = builder.constant_extension(cur_index_ext); - let difference = builder.sub_extension(cur_index, access_index); - let equality_dummy = vars.local_wires[self.wire_equality_dummy_for_index(i)]; - let index_matches = vars.local_wires[self.wire_index_matches_for_index(i)]; + for copy in 0..self.num_copies { + let access_index = vars.local_wires[self.wire_access_index(copy)]; + let list_items = (0..self.vec_size) + .map(|i| vars.local_wires[self.wire_list_item(i, copy)]) + .collect::>(); + let claimed_element = vars.local_wires[self.wire_claimed_element(copy)]; - // The two equality constraints. - let one = builder.one_extension(); - let not_index_matches = builder.sub_extension(one, index_matches); - let first_equality_constraint = - builder.mul_sub_extension(difference, equality_dummy, not_index_matches); - constraints.push(first_equality_constraint); + for i in 0..self.vec_size { + let cur_index_ext = F::Extension::from_canonical_usize(i); + let cur_index = builder.constant_extension(cur_index_ext); + let difference = builder.sub_extension(cur_index, access_index); + let equality_dummy = vars.local_wires[self.wire_equality_dummy_for_index(i, copy)]; + let index_matches = vars.local_wires[self.wire_index_matches_for_index(i, copy)]; - let second_equality_constraint = builder.mul_extension(index_matches, difference); - constraints.push(second_equality_constraint); + let one = builder.one_extension(); + let not_index_matches = builder.sub_extension(one, index_matches); + let first_equality_constraint = + builder.mul_sub_extension(difference, equality_dummy, not_index_matches); + constraints.push(first_equality_constraint); - // Output constraint. - let diff = builder.sub_ext_algebra(list_items[i], claimed_element); - let conditional_diff = builder.scalar_mul_ext_algebra(index_matches, diff); - constraints.extend(conditional_diff.to_ext_target_array()); + let second_equality_constraint = builder.mul_extension(index_matches, difference); + constraints.push(second_equality_constraint); + + // Output constraint. + let diff = builder.sub_extension(list_items[i], claimed_element); + let conditional_diff = builder.mul_extension(index_matches, diff); + constraints.push(conditional_diff); + } } constraints @@ -167,15 +191,23 @@ impl, const D: usize> Gate for RandomAccessGa gate_index: usize, _local_constants: &[F], ) -> Vec>> { - let gen = RandomAccessGenerator:: { - gate_index, - gate: self.clone(), - }; - vec![Box::new(gen.adapter())] + (0..self.num_copies) + .map(|copy| { + let g: Box> = Box::new( + RandomAccessGenerator { + gate_index, + gate: *self, + copy, + } + .adapter(), + ); + g + }) + .collect::>() } fn num_wires(&self) -> usize { - self.wire_index_matches_for_index(self.vec_size - 1) + 1 + self.wire_index_matches_for_index(self.vec_size - 1, self.num_copies - 1) + 1 } fn num_constants(&self) -> usize { @@ -187,7 +219,7 @@ impl, const D: usize> Gate for RandomAccessGa } fn num_constraints(&self) -> usize { - self.vec_size * (2 + D) + 3 * self.num_copies * self.vec_size } } @@ -195,6 +227,7 @@ impl, const D: usize> Gate for RandomAccessGa struct RandomAccessGenerator, const D: usize> { gate_index: usize, gate: RandomAccessGate, + copy: usize, } impl, const D: usize> SimpleGenerator @@ -203,13 +236,11 @@ impl, const D: usize> SimpleGenerator fn dependencies(&self) -> Vec { let local_target = |input| Target::wire(self.gate_index, input); - let local_targets = |inputs: Range| inputs.map(local_target); - let mut deps = Vec::new(); - deps.push(local_target(self.gate.wire_access_index())); - deps.extend(local_targets(self.gate.wires_claimed_element())); + deps.push(local_target(self.gate.wire_access_index(self.copy))); + deps.push(local_target(self.gate.wire_claimed_element(self.copy))); for i in 0..self.gate.vec_size { - deps.extend(local_targets(self.gate.wires_list_item(i))); + deps.push(local_target(self.gate.wire_list_item(i, self.copy))); } deps } @@ -224,7 +255,7 @@ impl, const D: usize> SimpleGenerator // Compute the new vector and the values for equality_dummy and index_matches let vec_size = self.gate.vec_size; - let access_index_f = get_local_wire(self.gate.wire_access_index()); + let access_index_f = get_local_wire(self.gate.wire_access_index(self.copy)); let access_index = access_index_f.to_canonical_u64() as usize; debug_assert!( @@ -235,8 +266,10 @@ impl, const D: usize> SimpleGenerator ); for i in 0..vec_size { - let equality_dummy_wire = local_wire(self.gate.wire_equality_dummy_for_index(i)); - let index_matches_wire = local_wire(self.gate.wire_index_matches_for_index(i)); + let equality_dummy_wire = + local_wire(self.gate.wire_equality_dummy_for_index(i, self.copy)); + let index_matches_wire = + local_wire(self.gate.wire_index_matches_for_index(i, self.copy)); if i == access_index { out_buffer.set_wire(equality_dummy_wire, F::ONE); @@ -257,6 +290,7 @@ mod tests { use std::marker::PhantomData; use anyhow::Result; + use rand::{thread_rng, Rng}; use crate::field::crandall_field::CrandallField; use crate::field::extension_field::quartic::QuarticExtension; @@ -267,31 +301,14 @@ mod tests { use crate::hash::hash_types::HashOut; use crate::plonk::vars::EvaluationVars; - #[test] - fn wire_indices() { - let gate = RandomAccessGate:: { - vec_size: 3, - _phantom: PhantomData, - }; - - assert_eq!(gate.wire_access_index(), 0); - assert_eq!(gate.wires_claimed_element(), 1..5); - assert_eq!(gate.wires_list_item(0), 5..9); - assert_eq!(gate.wires_list_item(2), 13..17); - assert_eq!(gate.wire_equality_dummy_for_index(0), 17); - assert_eq!(gate.wire_equality_dummy_for_index(2), 19); - assert_eq!(gate.wire_index_matches_for_index(0), 20); - assert_eq!(gate.wire_index_matches_for_index(2), 22); - } - #[test] fn low_degree() { - test_low_degree::(RandomAccessGate::new(4)); + test_low_degree::(RandomAccessGate::new(4, 4)); } #[test] fn eval_fns() -> Result<()> { - test_eval_fns::(RandomAccessGate::new(4)) + test_eval_fns::(RandomAccessGate::new(4, 4)) } #[test] @@ -300,56 +317,74 @@ mod tests { type FF = QuarticExtension; const D: usize = 4; - /// Returns the local wires for a random access gate given the vector, element to compare, - /// and index. - fn get_wires(list: Vec, access_index: usize, claimed_element: FF) -> Vec { - let vec_size = list.len(); + /// Returns the local wires for a random access gate given the vectors, elements to compare, + /// and indices. + fn get_wires( + lists: Vec>, + access_indices: Vec, + claimed_elements: Vec, + ) -> Vec { + let num_copies = lists.len(); + let vec_size = lists[0].len(); let mut v = Vec::new(); - v.push(F::from_canonical_usize(access_index)); - v.extend(claimed_element.0); - for j in 0..vec_size { - v.extend(list[j].0); - } - let mut equality_dummy_vals = Vec::new(); let mut index_matches_vals = Vec::new(); - for i in 0..vec_size { - if i == access_index { - equality_dummy_vals.push(F::ONE); - index_matches_vals.push(F::ONE); - } else { - equality_dummy_vals.push( - (F::from_canonical_usize(i) - F::from_canonical_usize(access_index)) - .inverse(), - ); - index_matches_vals.push(F::ZERO); + for copy in 0..num_copies { + let access_index = access_indices[copy]; + v.push(F::from_canonical_usize(access_index)); + v.push(claimed_elements[copy]); + for j in 0..vec_size { + v.push(lists[copy][j]); + } + + for i in 0..vec_size { + if i == access_index { + equality_dummy_vals.push(F::ONE); + index_matches_vals.push(F::ONE); + } else { + equality_dummy_vals.push( + (F::from_canonical_usize(i) - F::from_canonical_usize(access_index)) + .inverse(), + ); + index_matches_vals.push(F::ZERO); + } } } - v.extend(equality_dummy_vals); v.extend(index_matches_vals); v.iter().map(|&x| x.into()).collect::>() } - let list = vec![FF::rand(); 3]; - let access_index = 1; + let vec_size = 3; + let num_copies = 4; + let lists = (0..num_copies) + .map(|_| F::rand_vec(vec_size)) + .collect::>(); + let access_indices = (0..num_copies) + .map(|_| thread_rng().gen_range(0..vec_size)) + .collect::>(); let gate = RandomAccessGate:: { - vec_size: 3, + vec_size, + num_copies, _phantom: PhantomData, }; - let good_claimed_element = list[access_index]; + let good_claimed_elements = lists + .iter() + .zip(&access_indices) + .map(|(l, &i)| l[i]) + .collect(); let good_vars = EvaluationVars { local_constants: &[], - local_wires: &get_wires(list.clone(), access_index, good_claimed_element), + local_wires: &get_wires(lists.clone(), access_indices.clone(), good_claimed_elements), public_inputs_hash: &HashOut::rand(), }; - let bad_claimed_element = FF::rand(); + let bad_claimed_elements = F::rand_vec(4); let bad_vars = EvaluationVars { local_constants: &[], - local_wires: &get_wires(list, access_index, bad_claimed_element), + local_wires: &get_wires(lists, access_indices, bad_claimed_elements), public_inputs_hash: &HashOut::rand(), }; @@ -359,7 +394,7 @@ mod tests { ); assert!( !gate.eval_unfiltered(bad_vars).iter().all(|x| x.is_zero()), - "Gate constraints are satisfied but shouold not be." + "Gate constraints are satisfied but should not be." ); } } diff --git a/src/hash/merkle_proofs.rs b/src/hash/merkle_proofs.rs index ac62d8f0..ac40ffef 100644 --- a/src/hash/merkle_proofs.rs +++ b/src/hash/merkle_proofs.rs @@ -3,7 +3,6 @@ use std::convert::TryInto; use anyhow::{ensure, Result}; use serde::{Deserialize, Serialize}; -use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field_types::{Field, RichField}; use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget}; @@ -55,7 +54,6 @@ pub(crate) fn verify_merkle_proof( impl, const D: usize> CircuitBuilder { /// Verifies that the given leaf data is present at the given index in the Merkle tree with the /// given cap. The index is given by it's little-endian bits. - /// Note: Works only for D=4. pub(crate) fn verify_merkle_proof( &mut self, leaf_data: Vec, @@ -75,21 +73,17 @@ impl, const D: usize> CircuitBuilder { } let index = self.le_sum(leaf_index_bits[proof.siblings.len()..].to_vec().into_iter()); - let state_ext = state.elements[..].try_into().expect("requires D = 4"); - let state_ext = ExtensionTarget(state_ext); - let cap_ext = merkle_cap - .0 - .iter() - .map(|h| { - let tmp = h.elements[..].try_into().expect("requires D = 4"); - ExtensionTarget(tmp) - }) - .collect(); - self.random_access(index, state_ext, cap_ext); + + for i in 0..4 { + self.random_access( + index, + state.elements[i], + merkle_cap.0.iter().map(|h| h.elements[i]).collect(), + ); + } } - /// Same a `verify_merkle_proof` but with the final "cap index" as extra parameter. - /// Note: Works only for D=4. + /// Same as `verify_merkle_proof` but with the final "cap index" as extra parameter. pub(crate) fn verify_merkle_proof_with_cap_index( &mut self, leaf_data: Vec, @@ -112,17 +106,13 @@ impl, const D: usize> CircuitBuilder { }; } - let state_ext = state.elements[..].try_into().expect("requires D = 4"); - let state_ext = ExtensionTarget(state_ext); - let cap_ext = merkle_cap - .0 - .iter() - .map(|h| { - let tmp = h.elements[..].try_into().expect("requires D = 4"); - ExtensionTarget(tmp) - }) - .collect(); - self.random_access(cap_index, state_ext, cap_ext); + for i in 0..4 { + self.random_access( + cap_index, + state.elements[i], + merkle_cap.0.iter().map(|h| h.elements[i]).collect(), + ); + } } pub fn assert_hashes_equal(&mut self, x: HashOutTarget, y: HashOutTarget) { diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index ed0bdbdc..ac8f01f1 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -18,6 +18,7 @@ use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate}; use crate::gates::gate_tree::Tree; use crate::gates::noop::NoopGate; use crate::gates::public_input::PublicInputGate; +use crate::gates::random_access::RandomAccessGate; use crate::gates::switch::SwitchGate; use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget}; use crate::hash::hashing::hash_n_to_hash; @@ -73,6 +74,10 @@ pub struct CircuitBuilder, const D: usize> { /// these constants with gate index `g` and already using `i` arithmetic operations. pub(crate) free_arithmetic: HashMap<(F, F), (usize, usize)>, + /// A map `(c0, c1) -> (g, i)` from constants `vec_size` to an available arithmetic gate using + /// these constants with gate index `g` and already using `i` random accesses. + pub(crate) free_random_access: HashMap, + // `current_switch_gates[chunk_size - 1]` contains None if we have no switch gates with the value // chunk_size, and contains `(g, i, c)`, if the gate `g`, at index `i`, already contains `c` copies // of switches @@ -94,6 +99,7 @@ impl, const D: usize> CircuitBuilder { constants_to_targets: HashMap::new(), targets_to_constants: HashMap::new(), free_arithmetic: HashMap::new(), + free_random_access: HashMap::new(), current_switch_gates: Vec::new(), } } @@ -530,6 +536,22 @@ impl, const D: usize> CircuitBuilder { } } + /// Fill the remaining unused random access operations with zeros, so that all + /// `RandomAccessGenerator`s are run. + fn fill_random_access_gates(&mut self) { + let zero = self.zero(); + for (vec_size, (_, i)) in self.free_random_access.clone() { + let max_copies = RandomAccessGate::::max_num_copies( + self.config.num_routed_wires, + self.config.num_wires, + vec_size, + ); + for _ in i..max_copies { + self.random_access(zero, zero, vec![zero; vec_size]); + } + } + } + /// Fill the remaining unused switch gates with dummy values, so that all /// `SwitchGenerator` are run. fn fill_switch_gates(&mut self) { @@ -569,6 +591,7 @@ impl, const D: usize> CircuitBuilder { let start = Instant::now(); self.fill_arithmetic_gates(); + self.fill_random_access_gates(); self.fill_switch_gates(); // Hash the public inputs, and route them to a `PublicInputGate` which will enforce that diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index 7bcc0633..f9bda416 100644 --- a/src/plonk/recursive_verifier.rs +++ b/src/plonk/recursive_verifier.rs @@ -378,7 +378,7 @@ mod tests { fn test_recursive_recursive_verifier() -> Result<()> { init_logger(); type F = GoldilocksField; - const D: usize = 4; + const D: usize = 2; let config = CircuitConfig::standard_recursion_config(); @@ -398,7 +398,7 @@ mod tests { fn test_size_optimized_recursion() -> Result<()> { init_logger(); type F = GoldilocksField; - const D: usize = 4; + const D: usize = 2; let normal_config = CircuitConfig::standard_recursion_config(); let final_config = CircuitConfig {