diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 588ad423..4c3a3704 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -20,7 +20,7 @@ impl, const D: usize> CircuitBuilder { fn compute_evaluation( &mut self, x: Target, - old_x_index: Target, + old_x_index_bits: &[Target], arity_bits: usize, last_evals: &[ExtensionTarget], beta: ExtensionTarget, @@ -33,7 +33,7 @@ impl, const D: usize> CircuitBuilder { // 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); + let mut old_x_index_bits = old_x_index_bits.to_vec(); old_x_index_bits.reverse(); // Want `g^(arity - rev_old_x_index)` as in the out-of-circuit version. // Compute it as `g^(arity-1-rev_old_x_index) * g`, where the first term is gotten using two's complement. @@ -148,7 +148,7 @@ impl, const D: usize> CircuitBuilder { fn fri_verify_initial_proof( &mut self, - x_index: Target, + x_index_bits: &[Target], proof: &FriInitialTreeProofTarget, initial_merkle_roots: &[HashTarget], ) { @@ -161,7 +161,7 @@ impl, const D: usize> CircuitBuilder { context!( self, &format!("verify {}'th initial Merkle proof", i), - self.verify_merkle_proof(evals.clone(), x_index, root, merkle_proof) + self.verify_merkle_proof(evals.clone(), x_index_bits, root, merkle_proof) ); } } @@ -255,18 +255,19 @@ impl, const D: usize> CircuitBuilder { // TODO: Do we need to range check `x_index` to a target smaller than `p`? let mut x_index = challenger.get_challenge(self); x_index = self.split_low_high(x_index, n_log, 64).0; + let mut x_index_bits = self.low_bits(x_index, n_log, 64); let mut x_index_num_bits = n_log; let mut domain_size = n; context!( self, "check FRI initial proof", self.fri_verify_initial_proof( - x_index, + &x_index_bits, &round_proof.initial_trees_proof, initial_merkle_roots, ) ); - let mut old_x_index = self.zero(); + let mut old_x_index_bits = Vec::new(); // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. let mut subgroup_x = context!(self, "compute x from its index", { @@ -302,7 +303,7 @@ impl, const D: usize> CircuitBuilder { "infer evaluation using interpolation", self.compute_evaluation( subgroup_x, - old_x_index, + &old_x_index_bits, config.reduction_arity_bits[i - 1], last_evals, betas[i - 1], @@ -313,13 +314,15 @@ impl, const D: usize> CircuitBuilder { // Insert P(y) into the evaluation vector, since it wasn't included by the prover. let (low_x_index, high_x_index) = self.split_low_high(x_index, arity_bits, x_index_num_bits); + let high_x_index_bits = x_index_bits.split_off(arity_bits); + old_x_index_bits = x_index_bits; evals = self.insert(low_x_index, e_x, evals); context!( self, "verify FRI round Merkle proof.", self.verify_merkle_proof( flatten_target(&evals), - high_x_index, + &high_x_index_bits, proof.commit_phase_merkle_roots[i], &round_proof.steps[i].merkle_proof, ) @@ -331,9 +334,9 @@ impl, const D: usize> CircuitBuilder { subgroup_x = self.exp_power_of_2(subgroup_x, config.reduction_arity_bits[i - 1]); } domain_size = next_domain_size; - old_x_index = low_x_index; x_index = high_x_index; x_index_num_bits -= arity_bits; + x_index_bits = high_x_index_bits; } let last_evals = evaluations.last().unwrap(); @@ -343,7 +346,7 @@ impl, const D: usize> CircuitBuilder { "infer final evaluation using interpolation", self.compute_evaluation( subgroup_x, - old_x_index, + &old_x_index_bits, final_arity_bits, last_evals, *betas.last().unwrap(), diff --git a/src/gadgets/range_check.rs b/src/gadgets/range_check.rs index c0848af8..261e5c7d 100644 --- a/src/gadgets/range_check.rs +++ b/src/gadgets/range_check.rs @@ -14,6 +14,14 @@ impl, const D: usize> CircuitBuilder { self.route(x, sum); } + /// Returns the first `n_log` little-endian bits of `x`. + /// Note: `x` is assumed to be range-checked for having `num_bits` bits. + pub fn low_bits(&mut self, x: Target, n_log: usize, num_bits: usize) -> Vec { + let mut res = self.split_le(x, num_bits); + res.truncate(n_log); + res + } + /// Returns `(a,b)` such that `x = a + 2^n_log * b` with `a < 2^n_log`. /// `x` is assumed to be range-checked for having `num_bits` bits. pub fn split_low_high(&mut self, x: Target, n_log: usize, num_bits: usize) -> (Target, Target) { diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs index 66558ba7..e971b3c5 100644 --- a/src/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -59,23 +59,20 @@ 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 root. + /// given root. The index is given by it's little-endian bits. pub(crate) fn verify_merkle_proof( &mut self, leaf_data: Vec, - leaf_index: Target, + leaf_index_bits: &[Target], merkle_root: HashTarget, proof: &MerkleProofTarget, ) { let zero = self.zero(); - let two = self.two(); let height = proof.siblings.len(); - let purported_index_bits = self.split_le_virtual(leaf_index, height); let mut state: HashTarget = self.hash_or_noop(leaf_data); - let mut acc_leaf_index = zero; - for (bit, &sibling) in purported_index_bits.into_iter().zip(&proof.siblings) { + for (&bit, &sibling) in leaf_index_bits.iter().zip(&proof.siblings) { let gate = self .add_gate_no_constants(GMiMCGate::::with_automatic_constants()); @@ -86,8 +83,6 @@ impl, const D: usize> CircuitBuilder { }); self.generate_copy(bit, swap_wire); - acc_leaf_index = self.mul_add(two, acc_leaf_index, bit); - let input_wires = (0..12) .map(|i| { Target::Wire(Wire { @@ -115,10 +110,6 @@ impl, const D: usize> CircuitBuilder { ) } - // TODO: this is far from optimal. - let leaf_index_rev = self.reverse_limbs::<2>(leaf_index, height); - self.assert_equal(acc_leaf_index, leaf_index_rev); - self.named_assert_hashes_equal(state, merkle_root, "check Merkle root".into()) } @@ -180,13 +171,14 @@ mod tests { pw.set_hash_target(root_t, tree.root); let i_c = builder.constant(F::from_canonical_usize(i)); + let i_bits = builder.split_le(i_c, log_n); let data = builder.add_virtual_targets(tree.leaves[i].len()); for j in 0..data.len() { pw.set_target(data[j], tree.leaves[i][j]); } - builder.verify_merkle_proof(data, i_c, root_t, &proof_t); + builder.verify_merkle_proof(data, &i_bits, root_t, &proof_t); let data = builder.build(); let proof = data.prove(pw)?;