diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 7067bd49..d3e4e1e0 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -32,7 +32,6 @@ fn bench_prove, const D: usize>() -> Result<()> { proof_of_work_bits: 20, reduction_arity_bits: vec![2, 2, 2, 2, 2, 2], num_query_rounds: 35, - cap_height: 1, }, }; diff --git a/src/field/field_types.rs b/src/field/field_types.rs index fd5f8ac1..8d2f8872 100644 --- a/src/field/field_types.rs +++ b/src/field/field_types.rs @@ -185,6 +185,10 @@ pub trait Field: Self::from_canonical_u64(n as u64) } + fn from_bool(b: bool) -> Self { + Self::from_canonical_u64(b as u64) + } + fn to_canonical_biguint(&self) -> BigUint; fn from_canonical_biguint(n: BigUint) -> Self; diff --git a/src/fri/commitment.rs b/src/fri/commitment.rs index f665e058..bd051e3d 100644 --- a/src/fri/commitment.rs +++ b/src/fri/commitment.rs @@ -205,7 +205,7 @@ impl PolynomialBatchCommitment { lde_final_poly, lde_final_values, challenger, - &config.fri_config, + &config, timing, ); @@ -281,8 +281,8 @@ mod tests { proof_of_work_bits: 2, reduction_arity_bits: vec![2, 3, 1, 2], num_query_rounds: 3, - cap_height: 1, }; + // We only care about `fri_config, num_constants`, and `num_routed_wires` here. let common_data = CommonCircuitData { config: CircuitConfig { @@ -301,12 +301,12 @@ mod tests { circuit_digest: HashOut::from_partial(vec![]), }; - let lpcs = (0..4) + let commitments = (0..4) .map(|i| { PolynomialBatchCommitment::::from_values( gen_random_test_case(ks[i], degree_bits), common_data.config.rate_bits, - PlonkPolynomials::polynomials(i).blinding, + common_data.config.zero_knowledge && PlonkPolynomials::polynomials(i).blinding, common_data.config.cap_height, &mut TimingTree::default(), ) @@ -315,7 +315,12 @@ mod tests { let zeta = gen_random_point::(degree_bits); let (proof, os) = PolynomialBatchCommitment::open_plonk::( - &[&lpcs[0], &lpcs[1], &lpcs[2], &lpcs[3]], + &[ + &commitments[0], + &commitments[1], + &commitments[2], + &commitments[3], + ], zeta, &mut Challenger::new(), &common_data, @@ -323,10 +328,10 @@ mod tests { ); let merkle_caps = &[ - lpcs[0].merkle_tree.cap.clone(), - lpcs[1].merkle_tree.cap.clone(), - lpcs[2].merkle_tree.cap.clone(), - lpcs[3].merkle_tree.cap.clone(), + commitments[0].merkle_tree.cap.clone(), + commitments[1].merkle_tree.cap.clone(), + commitments[2].merkle_tree.cap.clone(), + commitments[3].merkle_tree.cap.clone(), ]; verify_fri_proof( diff --git a/src/fri/mod.rs b/src/fri/mod.rs index 29d99d61..716260a3 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -4,10 +4,6 @@ pub mod prover; pub mod recursive_verifier; pub mod verifier; -/// Somewhat arbitrary. Smaller values will increase delta, but with diminishing returns, -/// while increasing L, potentially requiring more challenge points. -const EPSILON: f64 = 0.01; - #[derive(Debug, Clone, Eq, PartialEq)] pub struct FriConfig { pub proof_of_work_bits: u32, @@ -20,29 +16,4 @@ pub struct FriConfig { /// Number of query rounds to perform. pub num_query_rounds: usize, - - pub cap_height: usize, -} - -fn fri_delta(rate_log: usize, conjecture: bool) -> f64 { - let rate = (1 << rate_log) as f64; - if conjecture { - // See Conjecture 2.3 in DEEP-FRI. - 1.0 - rate - EPSILON - } else { - // See the Johnson radius. - 1.0 - rate.sqrt() - EPSILON - } -} - -fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 { - let rate = (1 << rate_log) as f64; - if conjecture { - // See Conjecture 2.3 in DEEP-FRI. - // We assume the conjecture holds with a constant of 1 (as do other STARK implementations). - (codeword_len as f64) / EPSILON - } else { - // See the Johnson bound. - 1.0 / (2.0 * EPSILON * rate.sqrt()) - } } diff --git a/src/fri/prover.rs b/src/fri/prover.rs index 52db4579..c1f53a1c 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -8,6 +8,7 @@ use crate::hash::hash_types::HashOut; use crate::hash::hashing::hash_n_to_1; use crate::hash::merkle_tree::MerkleTree; use crate::iop::challenger::Challenger; +use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::plonk_common::reduce_with_powers; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::timed; @@ -22,7 +23,7 @@ pub fn fri_proof, const D: usize>( // Evaluation of the polynomial on the large domain. lde_polynomial_values: PolynomialValues, challenger: &mut Challenger, - config: &FriConfig, + config: &CircuitConfig, timing: &mut TimingTree, ) -> FriProof { let n = lde_polynomial_values.values.len(); @@ -45,12 +46,17 @@ pub fn fri_proof, const D: usize>( let pow_witness = timed!( timing, "find for proof-of-work witness", - fri_proof_of_work(current_hash, config) + fri_proof_of_work(current_hash, &config.fri_config) ); // Query phase - let query_round_proofs = - fri_prover_query_rounds(initial_merkle_trees, &trees, challenger, n, config); + let query_round_proofs = fri_prover_query_rounds( + initial_merkle_trees, + &trees, + challenger, + n, + &config.fri_config, + ); FriProof { commit_phase_merkle_caps: trees.iter().map(|t| t.cap.clone()).collect(), @@ -64,14 +70,14 @@ fn fri_committed_trees, const D: usize>( mut coeffs: PolynomialCoeffs, mut values: PolynomialValues, challenger: &mut Challenger, - config: &FriConfig, + config: &CircuitConfig, ) -> (Vec>, PolynomialCoeffs) { let mut trees = Vec::new(); let mut shift = F::MULTIPLICATIVE_GROUP_GENERATOR; - let num_reductions = config.reduction_arity_bits.len(); + let num_reductions = config.fri_config.reduction_arity_bits.len(); for i in 0..num_reductions { - let arity = 1 << config.reduction_arity_bits[i]; + let arity = 1 << config.fri_config.reduction_arity_bits[i]; reverse_index_bits_in_place(&mut values.values); let chunked_values = values @@ -97,7 +103,9 @@ fn fri_committed_trees, const D: usize>( values = coeffs.coset_fft(shift.into()) } - coeffs.trim(); + /// The coefficients being removed here should always be zero. + coeffs.coeffs.truncate(coeffs.len() >> config.rate_bits); + challenger.observe_extension_elements(&coeffs.coeffs); (trees, coeffs) } diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 0e7fa381..c9f15aec 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -5,7 +5,7 @@ use crate::fri::proof::{FriInitialTreeProofTarget, FriProofTarget, FriQueryRound use crate::fri::FriConfig; use crate::hash::hash_types::MerkleCapTarget; use crate::iop::challenger::RecursiveChallenger; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CommonCircuitData; use crate::plonk::plonk_common::PlonkPolynomials; @@ -20,20 +20,20 @@ impl, const D: usize> CircuitBuilder { fn compute_evaluation( &mut self, x: Target, - x_index_within_coset_bits: &[Target], + x_index_within_coset_bits: &[BoolTarget], arity_bits: usize, - last_evals: &[ExtensionTarget], + evals: &[ExtensionTarget], beta: ExtensionTarget, ) -> ExtensionTarget { let arity = 1 << arity_bits; - debug_assert_eq!(last_evals.len(), arity); + debug_assert_eq!(evals.len(), arity); let g = F::primitive_root_of_unity(arity_bits); let g_inv = g.exp((arity as u64) - 1); let g_inv_t = self.constant(g_inv); // The evaluation vector needs to be reordered first. - let mut evals = last_evals.to_vec(); + let mut evals = evals.to_vec(); reverse_index_bits_in_place(&mut evals); // Want `g^(arity - rev_x_index_within_coset)` as in the out-of-circuit version. Compute it // as `(g^-1)^rev_x_index_within_coset`. @@ -181,7 +181,7 @@ impl, const D: usize> CircuitBuilder { fn fri_verify_initial_proof( &mut self, - x_index_bits: &[Target], + x_index_bits: &[BoolTarget], proof: &FriInitialTreeProofTarget, initial_merkle_caps: &[MerkleCapTarget], cap_index: Target, @@ -291,15 +291,13 @@ impl, const D: usize> CircuitBuilder { round_proof: &FriQueryRoundTarget, common_data: &CommonCircuitData, ) { - let config = &common_data.config.fri_config; + let config = &common_data.config; let n_log = log2_strict(n); // TODO: Do we need to range check `x_index` to a target smaller than `p`? let x_index = challenger.get_challenge(self); let mut x_index_bits = self.low_bits(x_index, n_log, 64); - let cap_index = self.le_sum( - x_index_bits[x_index_bits.len() - common_data.config.fri_config.cap_height..] - .into_iter(), - ); + let cap_index = self + .le_sum(x_index_bits[x_index_bits.len() - common_data.config.cap_height..].into_iter()); with_context!( self, "check FRI initial proof", @@ -320,6 +318,7 @@ impl, const D: usize> CircuitBuilder { let g_ext = self.convert_to_ext(g); let phi_ext = self.convert_to_ext(phi); let zero = self.zero_extension(); + // `subgroup_x = g*phi, vanish_zeta = g*phi - zeta` let tmp = self.double_arithmetic_extension( F::ONE, F::NEG_ONE, @@ -348,7 +347,7 @@ impl, const D: usize> CircuitBuilder { ) ); - for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() { + for (i, &arity_bits) in config.fri_config.reduction_arity_bits.iter().enumerate() { let evals = &round_proof.steps[i].evals; // Split x_index into the index of the coset x is in, and the index of x within that coset. @@ -411,6 +410,7 @@ struct PrecomputedReducedEvalsTarget { pub single: ExtensionTarget, pub zs: ExtensionTarget, pub zs_right: ExtensionTarget, + /// Slope of the line from `(zeta, zs)` to `(zeta_right, zs_right)`. pub slope: ExtensionTarget, pub zeta_right: ExtensionTarget, } diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 63359cd4..ff5dc87d 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -21,24 +21,24 @@ fn compute_evaluation, const D: usize>( x: F, x_index_within_coset: usize, arity_bits: usize, - last_evals: &[F::Extension], + evals: &[F::Extension], beta: F::Extension, ) -> F::Extension { let arity = 1 << arity_bits; - debug_assert_eq!(last_evals.len(), arity); + debug_assert_eq!(evals.len(), arity); let g = F::primitive_root_of_unity(arity_bits); // The evaluation vector needs to be reordered first. - let mut evals = last_evals.to_vec(); + let mut evals = evals.to_vec(); reverse_index_bits_in_place(&mut evals); let rev_x_index_within_coset = reverse_bits(x_index_within_coset, arity_bits); let coset_start = x * g.exp((arity - rev_x_index_within_coset) as u64); // The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta. let points = g .powers() + .map(|y| (coset_start * y).into()) .zip(evals) - .map(|(y, e)| ((coset_start * y).into(), e)) .collect::>(); let barycentric_weights = barycentric_weights(&points); interpolate(&points, beta, &barycentric_weights) diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 27a204bc..2c41482e 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -2,7 +2,7 @@ use std::borrow::Borrow; use crate::field::extension_field::Extendable; use crate::gates::exponentiation::ExponentiationGate; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::plonk::circuit_builder::CircuitBuilder; impl, const D: usize> CircuitBuilder { @@ -115,21 +115,21 @@ impl, const D: usize> CircuitBuilder { pub fn exp_from_bits( &mut self, base: Target, - exponent_bits: impl IntoIterator>, + exponent_bits: impl IntoIterator>, ) -> Target { - let zero = self.zero(); + let _false = self._false(); let gate = ExponentiationGate::new(self.config.clone()); let num_power_bits = gate.num_power_bits; - let mut exp_bits_vec: Vec = + let mut exp_bits_vec: Vec = exponent_bits.into_iter().map(|b| *b.borrow()).collect(); while exp_bits_vec.len() < num_power_bits { - exp_bits_vec.push(zero); + exp_bits_vec.push(_false); } let gate_index = self.add_gate(gate.clone(), vec![]); self.route(base, Target::wire(gate_index, gate.wire_base())); exp_bits_vec.iter().enumerate().for_each(|(i, bit)| { - self.route(*bit, Target::wire(gate_index, gate.wire_power_bit(i))); + self.route(bit.target, Target::wire(gate_index, gate.wire_power_bit(i))); }); Target::wire(gate_index, gate.wire_output()) @@ -148,8 +148,8 @@ impl, const D: usize> CircuitBuilder { pub fn exp_u64(&mut self, base: Target, mut exponent: u64) -> Target { let mut exp_bits = Vec::new(); while exponent != 0 { - let bit = exponent & 1; - let bit_target = self.constant(F::from_canonical_u64(bit)); + let bit = (exponent & 1) == 1; + let bit_target = self.constant_bool(bit); exp_bits.push(bit_target); exponent >>= 1; } diff --git a/src/gadgets/arithmetic_extension.rs b/src/gadgets/arithmetic_extension.rs index cf1bc82b..127600a1 100644 --- a/src/gadgets/arithmetic_extension.rs +++ b/src/gadgets/arithmetic_extension.rs @@ -802,7 +802,7 @@ mod tests { type FF = QuarticCrandallField; const D: usize = 4; - let config = CircuitConfig::large_config(); + let config = CircuitConfig::large_zk_config(); let pw = PartialWitness::new(config.num_wires); let mut builder = CircuitBuilder::::new(config); diff --git a/src/gadgets/interpolation.rs b/src/gadgets/interpolation.rs index 92090d90..9d48a204 100644 --- a/src/gadgets/interpolation.rs +++ b/src/gadgets/interpolation.rs @@ -113,7 +113,7 @@ mod tests { fn test_interpolate2() -> Result<()> { type F = CrandallField; type FF = QuarticCrandallField; - let config = CircuitConfig::large_config(); + let config = CircuitConfig::large_zk_config(); let pw = PartialWitness::new(config.num_wires); let mut builder = CircuitBuilder::::new(config); diff --git a/src/gadgets/range_check.rs b/src/gadgets/range_check.rs index 53bbf55c..be7eaf2e 100644 --- a/src/gadgets/range_check.rs +++ b/src/gadgets/range_check.rs @@ -2,7 +2,7 @@ use crate::field::extension_field::Extendable; use crate::field::field_types::Field; use crate::gates::base_sum::BaseSumGate; use crate::iop::generator::{GeneratedValues, SimpleGenerator}; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; @@ -15,7 +15,7 @@ impl, const D: usize> CircuitBuilder { } /// Returns the first `num_low_bits` little-endian bits of `x`. - pub fn low_bits(&mut self, x: Target, num_low_bits: usize, num_bits: usize) -> Vec { + pub fn low_bits(&mut self, x: Target, num_low_bits: usize, num_bits: usize) -> Vec { let mut res = self.split_le(x, num_bits); res.truncate(num_low_bits); res diff --git a/src/gadgets/select.rs b/src/gadgets/select.rs index f77a38f5..fbfdb6e8 100644 --- a/src/gadgets/select.rs +++ b/src/gadgets/select.rs @@ -1,14 +1,25 @@ use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::arithmetic::ArithmeticExtensionGate; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::plonk::circuit_builder::CircuitBuilder; impl, const D: usize> CircuitBuilder { - /// Selects `x` or `y` based on `b`, which is assumed to be binary, i.e., this returns `if b { x } else { y }`. - /// This expression is gotten as `bx - (by-y)`, which can be computed with a single `ArithmeticExtensionGate`. - /// Note: This does not range-check `b`. + /// Selects `x` or `y` based on `b`, i.e., this returns `if b { x } else { y }`. pub fn select_ext( + &mut self, + b: BoolTarget, + x: ExtensionTarget, + y: ExtensionTarget, + ) -> ExtensionTarget { + let b_ext = self.convert_to_ext(b.target); + self.select_ext_generalized(b_ext, x, y) + } + + /// Like `select_ext`, but accepts a condition input which does not necessarily have to be + /// binary. In this case, it computes the arithmetic generalization of `if b { x } else { y }`, + /// i.e. `bx - (by-y)`, which can be computed with a single `ArithmeticExtensionGate`. + pub fn select_ext_generalized( &mut self, b: ExtensionTarget, x: ExtensionTarget, @@ -36,11 +47,10 @@ impl, const D: usize> CircuitBuilder { } /// See `select_ext`. - pub fn select(&mut self, b: Target, x: Target, y: Target) -> Target { - let b_ext = self.convert_to_ext(b); + pub fn select(&mut self, b: BoolTarget, x: Target, y: Target) -> Target { let x_ext = self.convert_to_ext(x); let y_ext = self.convert_to_ext(y); - self.select_ext(b_ext, x_ext, y_ext).to_target_array()[0] + self.select_ext(b, x_ext, y_ext).to_target_array()[0] } } @@ -67,13 +77,11 @@ mod tests { let (x, y) = (FF::rand(), FF::rand()); let xt = builder.add_virtual_extension_target(); let yt = builder.add_virtual_extension_target(); - let truet = builder.add_virtual_extension_target(); - let falset = builder.add_virtual_extension_target(); + let truet = builder._true(); + let falset = builder._false(); pw.set_extension_target(xt, x); pw.set_extension_target(yt, y); - pw.set_extension_target(truet, FF::ONE); - pw.set_extension_target(falset, FF::ZERO); let should_be_x = builder.select_ext(truet, xt, yt); let should_be_y = builder.select_ext(falset, xt, yt); diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 9b481e78..69cfa377 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -4,7 +4,7 @@ use crate::field::extension_field::Extendable; use crate::field::field_types::Field; use crate::gates::base_sum::BaseSumGate; use crate::iop::generator::{GeneratedValues, SimpleGenerator}; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; @@ -33,7 +33,7 @@ impl, const D: usize> CircuitBuilder { /// the number with little-endian bit representation given by `bits`. pub(crate) fn le_sum( &mut self, - bits: impl ExactSizeIterator> + Clone, + bits: impl ExactSizeIterator> + Clone, ) -> Target { let num_bits = bits.len(); debug_assert!( @@ -45,7 +45,7 @@ impl, const D: usize> CircuitBuilder { .clone() .zip(BaseSumGate::<2>::START_LIMBS..BaseSumGate::<2>::START_LIMBS + num_bits) { - self.route(*limb.borrow(), Target::wire(gate_index, wire)); + self.route(limb.borrow().target, Target::wire(gate_index, wire)); } self.add_generator(BaseSumGenerator::<2> { @@ -60,21 +60,23 @@ impl, const D: usize> CircuitBuilder { #[derive(Debug)] struct BaseSumGenerator { gate_index: usize, - limbs: Vec, + limbs: Vec, } impl SimpleGenerator for BaseSumGenerator { fn dependencies(&self) -> Vec { - self.limbs.clone() + self.limbs.iter().map(|b| b.target).collect() } fn run_once(&self, witness: &PartialWitness, out_buffer: &mut GeneratedValues) { let sum = self .limbs .iter() - .map(|&t| witness.get_target(t)) + .map(|&t| witness.get_bool_target(t)) .rev() - .fold(F::ZERO, |acc, limb| acc * F::from_canonical_usize(B) + limb); + .fold(F::ZERO, |acc, limb| { + acc * F::from_canonical_usize(B) + F::from_bool(limb) + }); out_buffer.set_target( Target::wire(self.gate_index, BaseSumGate::::WIRE_SUM), @@ -131,8 +133,8 @@ mod tests { let n = thread_rng().gen_range(0..(1 << 10)); let x = builder.constant(F::from_canonical_usize(n)); - let zero = builder.zero(); - let one = builder.one(); + let zero = builder._false(); + let one = builder._true(); let y = builder.le_sum( (0..10) diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index 45422d5d..71c171f0 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -1,34 +1,18 @@ use crate::field::extension_field::Extendable; use crate::field::field_types::Field; use crate::gates::base_sum::BaseSumGate; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; -use crate::iop::target::Target; -use crate::iop::wire::Wire; +use crate::iop::generator::{GeneratedValues, SimpleGenerator}; +use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; use crate::util::ceil_div_usize; impl, const D: usize> CircuitBuilder { - /// Split the given integer into a list of virtual targets, where each one represents a bit of - /// the integer, with little-endian ordering. - /// - /// Note that this only handles witness generation; it does not enforce that the decomposition - /// is correct. The output should be treated as a "purported" decomposition which must be - /// enforced elsewhere. - pub(crate) fn split_le_virtual(&mut self, integer: Target, num_bits: usize) -> Vec { - let bit_targets = self.add_virtual_targets(num_bits); - self.add_generator(SplitGenerator { - integer, - bits: bit_targets.clone(), - }); - bit_targets - } - /// Split the given integer into a list of wires, where each one represents a /// bit of the integer, with little-endian ordering. /// Verifies that the decomposition is correct by using `k` `BaseSum<2>` gates - /// with `k` such that `k*num_routed_wires>=num_bits`. - pub(crate) fn split_le(&mut self, integer: Target, num_bits: usize) -> Vec { + /// with `k` such that `k * num_routed_wires >= num_bits`. + pub(crate) fn split_le(&mut self, integer: Target, num_bits: usize) -> Vec { if num_bits == 0 { return Vec::new(); } @@ -40,10 +24,11 @@ impl, const D: usize> CircuitBuilder { let mut bits = Vec::with_capacity(num_bits); for &gate in &gates { - bits.extend(Target::wires_from_range( - gate, - BaseSumGate::<2>::START_LIMBS..BaseSumGate::<2>::START_LIMBS + bits_per_gate, - )); + let start_limbs = BaseSumGate::<2>::START_LIMBS; + for limb_input in start_limbs..start_limbs + bits_per_gate { + // `new_unsafe` is safe here because BaseSumGate::<2> forces it to be in `{0, 1}`. + bits.push(BoolTarget::new_unsafe(Target::wire(gate, limb_input))); + } } bits.drain(num_bits..); @@ -72,33 +57,6 @@ impl, const D: usize> CircuitBuilder { } } -/// Generator for a little-endian split. -#[must_use] -pub fn split_le_generator( - integer: Target, - bits: Vec, -) -> Box> { - Box::new(SplitGenerator { integer, bits }) -} - -/// Generator for a little-endian split. -#[must_use] -pub fn split_le_generator_local_wires( - gate: usize, - integer_input_index: usize, - bit_input_indices: &[usize], -) -> Box> { - let integer = Target::Wire(Wire { - gate, - input: integer_input_index, - }); - let bits = bit_input_indices - .iter() - .map(|&input| Target::Wire(Wire { gate, input })) - .collect(); - Box::new(SplitGenerator { integer, bits }) -} - #[derive(Debug)] struct SplitGenerator { integer: Target, diff --git a/src/gates/exponentiation.rs b/src/gates/exponentiation.rs index 5b4075bd..468f58e2 100644 --- a/src/gates/exponentiation.rs +++ b/src/gates/exponentiation.rs @@ -158,7 +158,7 @@ impl, const D: usize> Gate for ExponentiationGate { // power_bits is in LE order, but we accumulate in BE order. let cur_bit = power_bits[self.num_power_bits - i - 1]; - let mul_by = builder.select_ext(cur_bit, base, one); + let mul_by = builder.select_ext_generalized(cur_bit, base, one); let intermediate_value_diff = builder.mul_sub_extension(prev_intermediate_value, mul_by, intermediate_values[i]); constraints.push(intermediate_value_diff); @@ -322,14 +322,14 @@ mod tests { let num_power_bits = power_bits.len(); - let power_bits_F: Vec<_> = power_bits + let power_bits_f: Vec<_> = power_bits .iter() .map(|b| F::from_canonical_u64(*b)) .collect(); let mut v = Vec::new(); v.push(base); - v.extend(power_bits_F.clone()); + v.extend(power_bits_f.clone()); let mut intermediate_values = Vec::new(); let mut current_intermediate_value = F::ONE; diff --git a/src/gates/gate_testing.rs b/src/gates/gate_testing.rs index 66bed775..4d336454 100644 --- a/src/gates/gate_testing.rs +++ b/src/gates/gate_testing.rs @@ -137,9 +137,7 @@ pub(crate) fn test_eval_fns, G: Gate, const D: usize>( let vars = EvaluationVars { local_constants: &constants, local_wires: &wires, - public_inputs_hash: &HashOut { - elements: [F::ZERO; 4], - }, + public_inputs_hash: &public_inputs_hash, }; let evals = gate.eval_unfiltered(vars); diff --git a/src/gates/noop.rs b/src/gates/noop.rs index d9a3953e..69c5c1f9 100644 --- a/src/gates/noop.rs +++ b/src/gates/noop.rs @@ -56,7 +56,6 @@ impl, const D: usize> Gate for NoopGate { #[cfg(test)] mod tests { - use crate::field::crandall_field::CrandallField; use crate::gates::gate_testing::{test_eval_fns, test_low_degree}; use crate::gates::noop::NoopGate; diff --git a/src/gates/public_input.rs b/src/gates/public_input.rs index 3d3626e6..a3f05738 100644 --- a/src/gates/public_input.rs +++ b/src/gates/public_input.rs @@ -77,11 +77,16 @@ impl, const D: usize> Gate for PublicInputGate { #[cfg(test)] mod tests { use crate::field::crandall_field::CrandallField; - use crate::gates::gate_testing::test_low_degree; + use crate::gates::gate_testing::{test_eval_fns, test_low_degree}; use crate::gates::public_input::PublicInputGate; #[test] fn low_degree() { test_low_degree::(PublicInputGate) } + + #[test] + fn eval_fns() -> anyhow::Result<()> { + test_eval_fns::(PublicInputGate) + } } diff --git a/src/hash/merkle_proofs.rs b/src/hash/merkle_proofs.rs index 45009c7d..1b42e393 100644 --- a/src/hash/merkle_proofs.rs +++ b/src/hash/merkle_proofs.rs @@ -10,7 +10,7 @@ use crate::gates::gmimc::GMiMCGate; use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget}; use crate::hash::hashing::{compress, hash_or_noop, GMIMC_ROUNDS}; use crate::hash::merkle_tree::MerkleCap; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::iop::wire::Wire; use crate::plonk::circuit_builder::CircuitBuilder; @@ -66,7 +66,7 @@ impl, const D: usize> CircuitBuilder { pub(crate) fn verify_merkle_proof( &mut self, leaf_data: Vec, - leaf_index_bits: &[Target], + leaf_index_bits: &[BoolTarget], merkle_cap: &MerkleCapTarget, proof: &MerkleProofTarget, ) { @@ -83,7 +83,7 @@ impl, const D: usize> CircuitBuilder { gate, input: swap_wire, }); - self.generate_copy(bit, swap_wire); + self.generate_copy(bit.target, swap_wire); let input_wires = (0..12) .map(|i| { @@ -131,7 +131,7 @@ impl, const D: usize> CircuitBuilder { pub(crate) fn verify_merkle_proof_with_cap_index( &mut self, leaf_data: Vec, - leaf_index_bits: &[Target], + leaf_index_bits: &[BoolTarget], cap_index: Target, merkle_cap: &MerkleCapTarget, proof: &MerkleProofTarget, @@ -149,7 +149,7 @@ impl, const D: usize> CircuitBuilder { gate, input: swap_wire, }); - self.generate_copy(bit, swap_wire); + self.generate_copy(bit.target, swap_wire); let input_wires = (0..12) .map(|i| { diff --git a/src/hash/merkle_tree.rs b/src/hash/merkle_tree.rs index 334e65bf..c96a0c23 100644 --- a/src/hash/merkle_tree.rs +++ b/src/hash/merkle_tree.rs @@ -13,6 +13,12 @@ use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; #[serde(bound = "")] pub struct MerkleCap(pub Vec>); +impl MerkleCap { + pub fn flatten(&self) -> Vec { + self.0.iter().flat_map(|h| h.elements).collect() + } +} + #[derive(Clone, Debug)] pub struct MerkleTree { /// The data in the leaves of the Merkle tree. diff --git a/src/iop/target.rs b/src/iop/target.rs index 50bd6bb6..877da5b8 100644 --- a/src/iop/target.rs +++ b/src/iop/target.rs @@ -31,3 +31,20 @@ impl Target { range.map(|i| Self::wire(gate, i)).collect() } } + +/// A `Target` which has already been constrained such that it can only be 0 or 1. +#[derive(Copy, Clone, Debug)] +pub struct BoolTarget { + pub target: Target, + /// This private field is here to force all instantiations to go through `new_unsafe`. + _private: (), +} + +impl BoolTarget { + pub fn new_unsafe(target: Target) -> BoolTarget { + BoolTarget { + target, + _private: (), + } + } +} diff --git a/src/iop/witness.rs b/src/iop/witness.rs index f2c0d453..51b5a182 100644 --- a/src/iop/witness.rs +++ b/src/iop/witness.rs @@ -9,7 +9,7 @@ use crate::gates::gate::GateInstance; use crate::hash::hash_types::HashOutTarget; use crate::hash::hash_types::{HashOut, MerkleCapTarget}; use crate::hash::merkle_tree::MerkleCap; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::iop::wire::Wire; use crate::plonk::copy_constraint::CopyConstraint; @@ -70,6 +70,15 @@ impl PartialWitness { .collect() } + pub fn get_bool_target(&self, target: BoolTarget) -> bool { + let value = self.get_target(target.target).to_canonical_u64(); + match value { + 0 => false, + 1 => true, + _ => panic!("not a bool"), + } + } + pub fn get_hash_target(&self, ht: HashOutTarget) -> HashOut { HashOut { elements: self.get_targets(&ht.elements).try_into().unwrap(), @@ -180,6 +189,10 @@ impl PartialWitness { .for_each(|(&et, &v)| self.set_extension_target(et, v)); } + pub fn set_bool_target(&mut self, target: BoolTarget, value: bool) { + self.set_target(target.target, F::from_bool(value)) + } + pub fn set_wire(&mut self, wire: Wire, value: F) { self.set_target(Target::Wire(wire), value) } diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index bae4b5d6..25f760c0 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -16,7 +16,7 @@ use crate::gates::public_input::PublicInputGate; use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget}; use crate::hash::hashing::hash_n_to_hash; use crate::iop::generator::{CopyGenerator, RandomValueGenerator, WitnessGenerator}; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::iop::wire::Wire; use crate::plonk::circuit_data::{ CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, ProverOnlyCircuitData, @@ -132,6 +132,11 @@ impl, const D: usize> CircuitBuilder { .collect() } + // TODO: Unsafe + pub fn add_virtual_bool_target(&mut self) -> BoolTarget { + BoolTarget::new_unsafe(self.add_virtual_target()) + } + /// Adds a gate to the circuit, and returns its index. pub fn add_gate>(&mut self, gate_type: G, constants: Vec) -> usize { self.check_gate_compatibility(&gate_type); @@ -282,6 +287,14 @@ impl, const D: usize> CircuitBuilder { self.constant(F::NEG_ONE) } + pub fn _false(&mut self) -> BoolTarget { + BoolTarget::new_unsafe(self.zero()) + } + + pub fn _true(&mut self) -> BoolTarget { + BoolTarget::new_unsafe(self.one()) + } + /// Returns a routable target with the given constant value. pub fn constant(&mut self, c: F) -> Target { if let Some(&target) = self.constants_to_targets.get(&c) { @@ -303,6 +316,14 @@ impl, const D: usize> CircuitBuilder { constants.iter().map(|&c| self.constant(c)).collect() } + pub fn constant_bool(&mut self, b: bool) -> BoolTarget { + if b { + self._true() + } else { + self._false() + } + } + /// If the given target is a constant (i.e. it was created by the `constant(F)` method), returns /// its constant value. Otherwise, returns `None`. pub fn target_as_constant(&self, target: Target) -> Option { @@ -605,11 +626,7 @@ impl, const D: usize> CircuitBuilder { // TODO: This should also include an encoding of gate constraints. let circuit_digest_parts = [ - constants_sigmas_cap - .0 - .into_iter() - .flat_map(|h| h.elements) - .collect::>(), + constants_sigmas_cap.flatten(), vec![/* Add other circuit data here */], ]; let circuit_digest = hash_n_to_hash(circuit_digest_parts.concat(), false); diff --git a/src/plonk/circuit_data.rs b/src/plonk/circuit_data.rs index 861bf1b0..e4353c70 100644 --- a/src/plonk/circuit_data.rs +++ b/src/plonk/circuit_data.rs @@ -48,7 +48,6 @@ impl Default for CircuitConfig { proof_of_work_bits: 1, reduction_arity_bits: vec![1, 1, 1, 1], num_query_rounds: 1, - cap_height: 1, }, } } @@ -72,10 +71,22 @@ impl CircuitConfig { proof_of_work_bits: 1, reduction_arity_bits: vec![1], num_query_rounds: 1, - cap_height: 1, }, } } + + pub(crate) fn large_zk_config() -> Self { + CircuitConfig { + zero_knowledge: true, + cap_height: 1, + fri_config: FriConfig { + proof_of_work_bits: 1, + reduction_arity_bits: vec![1, 1, 1, 1], + num_query_rounds: 1, + }, + ..Self::large_config() + } + } } /// Circuit data required by the prover or the verifier. diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index 64846567..a551c809 100644 --- a/src/plonk/recursive_verifier.rs +++ b/src/plonk/recursive_verifier.rs @@ -9,9 +9,6 @@ use crate::plonk::vars::EvaluationTargets; use crate::util::reducing::ReducingFactorTarget; use crate::with_context; -const MIN_WIRES: usize = 120; // TODO: Double check. -const MIN_ROUTED_WIRES: usize = 28; // TODO: Double check. - impl, const D: usize> CircuitBuilder { /// Recursively verifies an inner proof. pub fn add_recursive_verifier( @@ -21,8 +18,6 @@ impl, const D: usize> CircuitBuilder { inner_verifier_data: &VerifierCircuitTarget, inner_common_data: &CommonCircuitData, ) { - assert!(self.config.num_wires >= MIN_WIRES); - assert!(self.config.num_wires >= MIN_ROUTED_WIRES); let ProofWithPublicInputsTarget { proof, public_inputs, @@ -380,7 +375,6 @@ mod tests { proof_of_work_bits: 1, reduction_arity_bits: vec![2, 2, 2, 2, 2, 2], num_query_rounds: 40, - cap_height: 1, }, }; let (proof_with_pis, vd, cd) = { @@ -436,7 +430,6 @@ mod tests { proof_of_work_bits: 20, reduction_arity_bits: vec![3, 3, 3], num_query_rounds: 27, - cap_height: 3, }, }; let (proof_with_pis, vd, cd) = { diff --git a/src/util/mod.rs b/src/util/mod.rs index 7ee995f5..b468ab35 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -26,7 +26,7 @@ pub(crate) fn log2_ceil(n: usize) -> usize { /// Computes `log_2(n)`, panicking if `n` is not a power of two. pub(crate) fn log2_strict(n: usize) -> usize { - assert!(n.is_power_of_two(), "Not a power of two"); + assert!(n.is_power_of_two(), "Not a power of two: {}", n); log2_ceil(n) } diff --git a/src/util/reducing.rs b/src/util/reducing.rs index 228795f6..459454db 100644 --- a/src/util/reducing.rs +++ b/src/util/reducing.rs @@ -301,10 +301,7 @@ mod tests { type FF = QuarticCrandallField; const D: usize = 4; - let config = CircuitConfig { - num_routed_wires: 64, - ..CircuitConfig::large_config() - }; + let config = CircuitConfig::large_config(); let pw = PartialWitness::new(config.num_wires); let mut builder = CircuitBuilder::::new(config);