diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 6c245167..81cc59ab 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -49,7 +49,7 @@ impl, const D: usize> CircuitBuilder { inputs.push(proof.pow_witness); let hash = self.hash_n_to_m(inputs, 1, false)[0]; - self.assert_trailing_zeros(hash, config.proof_of_work_bits); + self.assert_trailing_zeros::<64>(hash, config.proof_of_work_bits); Ok(()) } diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index e622be75..35d5ac93 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -11,23 +11,85 @@ use crate::witness::PartialWitness; impl, const D: usize> CircuitBuilder { /// Split the given element into a list of 11 targets, where each one represents a /// base-64 limb of the element, with little-endian ordering. - pub(crate) fn split_le_base64(&mut self, x: Target) -> Vec { - let gate = self.add_gate(BaseSumGate::<64>::new(11), vec![]); + pub(crate) fn split_le_base(&mut self, x: Target) -> Vec { + let num_limbs = num_limbs_to_check(64, B); + let gate = self.add_gate(BaseSumGate::::new(num_limbs), vec![]); let sum = Target::Wire(Wire { gate, - input: BaseSumGate::<64>::WIRE_SUM, + input: BaseSumGate::::WIRE_SUM, }); self.route(x, sum); - (BaseSumGate::<64>::WIRE_LIMBS_START..BaseSumGate::<64>::WIRE_LIMBS_START + 11) + (BaseSumGate::::WIRE_LIMBS_START..BaseSumGate::::WIRE_LIMBS_START + num_limbs) .map(|i| Target::Wire(Wire { gate, input: i })) .collect() } /// Asserts that `x`'s bit representation has at least `trailing_zeros` trailing zeros. - pub(crate) fn assert_trailing_zeros(&mut self, x: Target, trailing_zeros: u32) { - let limbs = self.split_le_base64(x); - for i in 0..ceil_div_usize(trailing_zeros as usize, 6) { + pub(crate) fn assert_trailing_zeros(&mut self, x: Target, trailing_zeros: u32) { + let limbs = self.split_le_base::(x); + let num_limbs_to_check = num_limbs_to_check(trailing_zeros, B); + assert!( + num_limbs_to_check < self.config.num_routed_wires, + "Not enough routed wires." + ); + for i in 0..num_limbs_to_check { self.assert_zero(limbs[i]); } } } + +/// Returns `k` such that any number with `k` trailing zeros in base `base` has at least +/// `n` trailing zeros in base 2. +fn num_limbs_to_check(n: u32, base: usize) -> usize { + (n as f64 * (2.0_f64.log(base as f64))).ceil() as usize +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::circuit_data::CircuitConfig; + use crate::field::crandall_field::CrandallField; + use crate::fri::FriConfig; + use crate::prover::PLONK_BLINDING; + use crate::verifier::verify; + use anyhow::Result; + + #[test] + fn test_split_base() -> Result<()> { + type F = CrandallField; + let config = CircuitConfig { + num_wires: 134, + num_routed_wires: 12, + security_bits: 128, + rate_bits: 3, + num_challenges: 3, + fri_config: FriConfig { + proof_of_work_bits: 1, + rate_bits: 3, + reduction_arity_bits: vec![1], + num_query_rounds: 1, + blinding: PLONK_BLINDING.to_vec(), + }, + }; + let mut builder = CircuitBuilder::::new(config); + let x = F::from_canonical_usize(0b10100000); // 160 =1120 in base 5. + let xt = builder.constant(x); + let limbs = builder.split_le_base::<5>(xt); + assert_eq!(limbs.len(), 28); // 5^27 < 2^64 <= 5^28 + let zero = builder.zero(); + let one = builder.one(); + let two = builder.two(); + builder.assert_equal(limbs[0], zero); + builder.assert_equal(limbs[1], two); + builder.assert_equal(limbs[2], one); + builder.assert_equal(limbs[3], one); + + builder.assert_trailing_zeros::<3>(xt, 4); + builder.assert_trailing_zeros::<3>(xt, 5); + builder.assert_trailing_zeros::<13>(xt, 5); + let data = builder.build(); + + let proof = data.prove(PartialWitness::new()); + verify(proof, &data.verifier_only, &data.common) + } +} diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index 9190e19c..8c8064a1 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -102,7 +102,7 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat } fn num_constraints(&self) -> usize { - 1 + B + 1 + self.num_limbs } }