commit 13cc76316ce47cd9522a1710dcc40a6b6cd43f8c Author: Daniel Lubarov Date: Tue Feb 9 21:25:21 2021 -0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..96ef6c0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..b99a4bd3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "plonky2" +version = "0.1.0" +authors = ["Daniel Lubarov "] +edition = "2018" + +[dependencies] +unroll = "0.1.5" +rayon = "1.5.0" + +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 diff --git a/src/circuit_data.rs b/src/circuit_data.rs new file mode 100644 index 00000000..d9e4051e --- /dev/null +++ b/src/circuit_data.rs @@ -0,0 +1,84 @@ +use crate::field::fft::FftPrecomputation; +use crate::field::field::Field; +use crate::proof::{Hash, Proof2}; +use crate::prover::prove2; +use crate::verifier::verify2; + +#[derive(Copy, Clone)] +pub struct CircuitConfig { + pub num_wires: usize, + pub num_routed_wires: usize, + pub security_bits: usize, +} + +impl CircuitConfig { + pub fn advice_wires(&self) -> usize { + self.num_wires - self.num_routed_wires + } +} + +/// Circuit data required by the prover or the verifier. +pub struct CircuitData { + prover_only: ProverOnlyCircuitData, + verifier_only: VerifierOnlyCircuitData, + common: CommonCircuitData, +} + +impl CircuitData { + pub fn prove2(&self) -> Proof2 { + prove2(&self.prover_only, &self.common) + } + + pub fn verify2(&self) { + verify2(&self.verifier_only, &self.common) + } +} + +/// Circuit data required by the prover. +pub struct ProverCircuitData { + prover_only: ProverOnlyCircuitData, + common: CommonCircuitData, +} + +impl ProverCircuitData { + pub fn prove2(&self) -> Proof2 { + prove2(&self.prover_only, &self.common) + } +} + +/// Circuit data required by the prover. +pub struct VerifierCircuitData { + verifier_only: VerifierOnlyCircuitData, + common: CommonCircuitData, +} + +impl VerifierCircuitData { + pub fn verify2(&self) { + verify2(&self.verifier_only, &self.common) + } +} + +/// Circuit data required by the prover, but not the verifier. +pub(crate) struct ProverOnlyCircuitData { + /// A precomputation used for FFTs of degree 8n, where n is the number of gates. + pub fft_precomputation_8n: FftPrecomputation, +} + +/// Circuit data required by the verifier, but not the prover. +pub(crate) struct VerifierOnlyCircuitData {} + +/// Circuit data required by both the prover and the verifier. +pub(crate) struct CommonCircuitData { + pub config: CircuitConfig, + + pub degree: usize, + + /// A commitment to each constant polynomial. + pub constants_root: Hash, + + /// A commitment to each permutation polynomial. + pub sigmas_root: Hash, + + /// A precomputation used for FFTs of degree n, where n is the number of gates. + pub fft_precomputation_n: FftPrecomputation, +} diff --git a/src/constraint_polynomial.rs b/src/constraint_polynomial.rs new file mode 100644 index 00000000..c569bc08 --- /dev/null +++ b/src/constraint_polynomial.rs @@ -0,0 +1,420 @@ +use std::collections::{HashMap, HashSet}; +use std::hash::{Hash, Hasher}; +use std::iter::{Product, Sum}; +use std::ops::{Add, Mul, Neg, Sub}; +use std::ptr; +use std::rc::Rc; + +use crate::field::field::Field; +use crate::wire::Wire; + +pub(crate) struct EvaluationVars<'a, F: Field> { + pub(crate) local_constants: &'a [F], + pub(crate) next_constants: &'a [F], + pub(crate) local_wire_values: &'a [F], + pub(crate) next_wire_values: &'a [F], +} + +/// A polynomial over all the variables that are subject to constraints (local constants, next +/// constants, local wire values, and next wire values). This representation does not require any +/// particular form; it permits arbitrary forms such as `(x + 1)^3 + y z`. +// Implementation note: This is a wrapper because we want to hide complexity behind +// `ConstraintPolynomialInner` and `ConstraintPolynomialRef`. In particular, the caller shouldn't +// need to know that we use reference counting internally, and shouldn't have to deal with wrapper +// types related to reference counting. +#[derive(Clone)] +pub struct ConstraintPolynomial(ConstraintPolynomialRef); + +impl ConstraintPolynomial { + pub fn constant(c: F) -> Self { + Self::from_inner(ConstraintPolynomialInner::Constant(c)) + } + + pub fn constant_usize(c: usize) -> Self { + Self::constant(F::from_canonical_usize(c)) + } + + pub fn zero() -> Self { + Self::constant(F::ZERO) + } + + pub fn one() -> Self { + Self::constant(F::ONE) + } + + pub fn local_constant(index: usize) -> Self { + Self::from_inner(ConstraintPolynomialInner::LocalConstant(index)) + } + + pub fn next_constant(index: usize) -> Self { + Self::from_inner(ConstraintPolynomialInner::NextConstant(index)) + } + + pub fn local_wire_value(index: usize) -> Self { + Self::from_inner(ConstraintPolynomialInner::LocalWireValue(index)) + } + + pub fn next_wire_value(index: usize) -> Self { + Self::from_inner(ConstraintPolynomialInner::NextWireValue(index)) + } + + // TODO: Have these take references? + pub fn add(&self, rhs: &Self) -> Self { + // TODO: Special case for either operand being 0. + Self::from_inner(ConstraintPolynomialInner::Sum { + lhs: self.0.clone(), + rhs: rhs.0.clone(), + }) + } + + pub fn sub(&self, rhs: &Self) -> Self { + // TODO: Special case for either operand being 0. + // TODO: Faster to have a dedicated ConstraintPolynomialInner::Difference? + // TODO: `self + -rhs`? + self.add(&rhs.neg()) + } + + pub fn double(&self) -> Self { + self.clone().add(self) + } + + pub fn triple(&self) -> Self { + self * 3 + } + + pub fn mul(&self, rhs: &Self) -> Self { + // TODO: Special case for either operand being 1. + Self::from_inner(ConstraintPolynomialInner::Product { + lhs: self.0.clone(), + rhs: rhs.0.clone(), + }) + } + + pub fn exp(&self, exponent: usize) -> Self { + Self::from_inner(ConstraintPolynomialInner::Exponentiation { + base: self.0.clone(), + exponent, + }) + } + + pub fn square(&self) -> Self { + self * self + } + + pub(crate) fn degree(&self) -> usize { + (self.0).0.degree() + } + + /// Returns the set of wires that this constraint would depend on if it were applied at a + /// certain gate index. + pub(crate) fn dependencies(&self, gate: usize) -> Vec { + let mut deps = HashSet::new(); + self.0.0.add_dependencies(gate, &mut deps); + deps.into_iter().collect() + } + + /// Find the largest input index among the wires this constraint depends on. + pub(crate) fn max_wire_input_index(&self) -> Option { + self.dependencies(0) + .into_iter() + .map(|wire| wire.input) + .max() + } + + pub(crate) fn max_constant_index(&self) -> Option { + let mut indices = HashSet::new(); + self.0.0.add_constant_indices(&mut indices); + indices.into_iter().max() + } + + pub(crate) fn evaluate(&self, vars: EvaluationVars) -> F { + let results = Self::evaluate_all(&[self.clone()], vars); + assert_eq!(results.len(), 1); + results[0] + } + + /// Evaluate multiple constraint polynomials simultaneously. This can be more efficient than + /// evaluating them sequentially, since shared intermediate results will only be computed once. + pub(crate) fn evaluate_all( + polynomials: &[ConstraintPolynomial], + vars: EvaluationVars, + ) -> Vec { + let mut mem = HashMap::new(); + polynomials.iter() + .map(|p| p.0.evaluate_memoized(&vars, &mut mem)) + .collect() + } + + fn from_inner(inner: ConstraintPolynomialInner) -> Self { + Self(ConstraintPolynomialRef::new(inner)) + } +} + +impl Neg for ConstraintPolynomial { + type Output = Self; + + fn neg(self) -> Self { + // TODO: Faster to have a dedicated ConstraintPolynomialInner::Negation? + self * ConstraintPolynomial::constant(F::NEG_ONE) + } +} + +impl Neg for &ConstraintPolynomial { + type Output = ConstraintPolynomial; + + fn neg(self) -> ConstraintPolynomial { + self.clone().neg() + } +} + +/// Generates the following variants of a binary operation: +/// - `Self . Self` +/// - `&Self . Self` +/// - `Self . &Self` +/// - `&Self . &Self` +/// - `Self . F` +/// - `&Self . F` +/// - `Self . usize` +/// - `&Self . usize` +/// where `Self` is `ConstraintPolynomial`. +/// +/// Takes the following arguments: +/// - `$trait`: the name of the binary operation trait to implement +/// - `$method`: the name of the method in the trait. It is assumed that `ConstraintPolynomial` +/// contains a method with the same name, implementing the `Self . Self` variant. +macro_rules! binop_variants { + ($trait:ident, $method:ident) => { + impl $trait for ConstraintPolynomial { + type Output = Self; + + fn $method(self, rhs: Self) -> Self { + ConstraintPolynomial::$method(&self, &rhs) + } + } + + impl $trait<&Self> for ConstraintPolynomial { + type Output = Self; + + fn $method(self, rhs: &Self) -> Self { + ConstraintPolynomial::$method(&self, rhs) + } + } + + impl $trait> for &ConstraintPolynomial { + type Output = ConstraintPolynomial; + + fn $method(self, rhs: ConstraintPolynomial) -> Self::Output { + ConstraintPolynomial::$method(self, &rhs) + } + } + + impl $trait for &ConstraintPolynomial { + type Output = ConstraintPolynomial; + + fn $method(self, rhs: Self) -> Self::Output { + ConstraintPolynomial::$method(self, rhs) + } + } + + impl $trait for ConstraintPolynomial { + type Output = Self; + + fn $method(self, rhs: F) -> Self { + ConstraintPolynomial::$method(&self, &ConstraintPolynomial::constant(rhs)) + } + } + + impl $trait for &ConstraintPolynomial { + type Output = ConstraintPolynomial; + + fn $method(self, rhs: F) -> Self::Output { + ConstraintPolynomial::$method(self, &ConstraintPolynomial::constant(rhs)) + } + } + + impl $trait for ConstraintPolynomial { + type Output = Self; + + fn $method(self, rhs: usize) -> Self { + ConstraintPolynomial::$method(&self, &ConstraintPolynomial::constant_usize(rhs)) + } + } + + impl $trait for &ConstraintPolynomial { + type Output = ConstraintPolynomial; + + fn $method(self, rhs: usize) -> Self::Output { + ConstraintPolynomial::$method(self, &ConstraintPolynomial::constant_usize(rhs)) + } + } + }; +} + +binop_variants!(Add, add); +binop_variants!(Sub, sub); +binop_variants!(Mul, mul); + +impl Sum for ConstraintPolynomial { + fn sum>(iter: I) -> Self { + iter.fold( + ConstraintPolynomial::zero(), + |sum, x| sum + x) + } +} + +impl Product for ConstraintPolynomial { + fn product>(iter: I) -> Self { + iter.fold( + ConstraintPolynomial::one(), + |product, x| product * x) + } +} + +enum ConstraintPolynomialInner { + Constant(F), + + LocalConstant(usize), + NextConstant(usize), + LocalWireValue(usize), + NextWireValue(usize), + + Sum { + lhs: ConstraintPolynomialRef, + rhs: ConstraintPolynomialRef, + }, + Product { + lhs: ConstraintPolynomialRef, + rhs: ConstraintPolynomialRef, + }, + Exponentiation { + base: ConstraintPolynomialRef, + exponent: usize, + }, +} + +impl ConstraintPolynomialInner { + fn add_dependencies(&self, gate: usize, deps: &mut HashSet) { + match self { + ConstraintPolynomialInner::Constant(_) => (), + ConstraintPolynomialInner::LocalConstant(_) => (), + ConstraintPolynomialInner::NextConstant(_) => (), + ConstraintPolynomialInner::LocalWireValue(i) => + { deps.insert(Wire { gate, input: *i }); }, + ConstraintPolynomialInner::NextWireValue(i) => + { deps.insert(Wire { gate: gate + 1, input: *i }); } + ConstraintPolynomialInner::Sum { lhs, rhs } => { + lhs.0.add_dependencies(gate, deps); + rhs.0.add_dependencies(gate, deps); + }, + ConstraintPolynomialInner::Product { lhs, rhs } => { + lhs.0.add_dependencies(gate, deps); + rhs.0.add_dependencies(gate, deps); + }, + ConstraintPolynomialInner::Exponentiation { base, exponent: _ } => { + base.0.add_dependencies(gate, deps); + }, + } + } + + fn add_constant_indices(&self, indices: &mut HashSet) { + match self { + ConstraintPolynomialInner::Constant(_) => (), + ConstraintPolynomialInner::LocalConstant(i) => { indices.insert(*i); }, + ConstraintPolynomialInner::NextConstant(i) => { indices.insert(*i); }, + ConstraintPolynomialInner::LocalWireValue(_) => (), + ConstraintPolynomialInner::NextWireValue(_) => (), + ConstraintPolynomialInner::Sum { lhs, rhs } => { + lhs.0.add_constant_indices(indices); + rhs.0.add_constant_indices(indices); + }, + ConstraintPolynomialInner::Product { lhs, rhs } => { + lhs.0.add_constant_indices(indices); + rhs.0.add_constant_indices(indices); + }, + ConstraintPolynomialInner::Exponentiation { base, exponent: _ } => { + base.0.add_constant_indices(indices); + }, + } + } + + fn evaluate( + &self, + vars: &EvaluationVars, + mem: &mut HashMap, F>, + ) -> F { + match self { + ConstraintPolynomialInner::Constant(c) => *c, + ConstraintPolynomialInner::LocalConstant(i) => vars.local_constants[*i], + ConstraintPolynomialInner::NextConstant(i) => vars.next_constants[*i], + ConstraintPolynomialInner::LocalWireValue(i) => vars.local_wire_values[*i], + ConstraintPolynomialInner::NextWireValue(i) => vars.next_wire_values[*i], + ConstraintPolynomialInner::Sum { lhs, rhs } => { + let lhs = lhs.evaluate_memoized(vars, mem); + let rhs = rhs.evaluate_memoized(vars, mem); + lhs + rhs + }, + ConstraintPolynomialInner::Product { lhs, rhs } => { + let lhs = lhs.evaluate_memoized(vars, mem); + let rhs = rhs.evaluate_memoized(vars, mem); + lhs * rhs + }, + ConstraintPolynomialInner::Exponentiation { base, exponent } => { + let base = base.evaluate_memoized(vars, mem); + base.exp_usize(*exponent) + }, + } + } + + fn degree(&self) -> usize { + match self { + ConstraintPolynomialInner::Constant(_) => 0, + ConstraintPolynomialInner::LocalConstant(_) => 1, + ConstraintPolynomialInner::NextConstant(_) => 1, + ConstraintPolynomialInner::LocalWireValue(_) => 1, + ConstraintPolynomialInner::NextWireValue(_) => 1, + ConstraintPolynomialInner::Sum { lhs, rhs } => lhs.0.degree().max(rhs.0.degree()), + ConstraintPolynomialInner::Product { lhs, rhs } => lhs.0.degree() + rhs.0.degree(), + ConstraintPolynomialInner::Exponentiation { base, exponent } => base.0.degree() * exponent, + } + } +} + +/// Wraps `Rc`, and implements `Hash` and `Eq` based on references rather +/// than content. This is useful when we want to use constraint polynomials as `HashMap` keys, but +/// we want address-based hashing for performance reasons. +#[derive(Clone)] +struct ConstraintPolynomialRef(Rc>); + +impl ConstraintPolynomialRef { + fn new(inner: ConstraintPolynomialInner) -> Self { + Self(Rc::new(inner)) + } + + fn evaluate_memoized( + &self, + vars: &EvaluationVars, + mem: &mut HashMap, + ) -> F { + if let Some(&result) = mem.get(self) { + result + } else { + let result = self.0.evaluate(vars, mem); + mem.insert(self.clone(), result); + result + } + } +} + +impl PartialEq for ConstraintPolynomialRef { + fn eq(&self, other: &Self) -> bool { + ptr::eq(&*self.0, &*other.0) + } +} + +impl Eq for ConstraintPolynomialRef {} + +impl Hash for ConstraintPolynomialRef { + fn hash(&self, state: &mut H) { + ptr::hash(&*self.0, state); + } +} diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs new file mode 100644 index 00000000..9180e1e1 --- /dev/null +++ b/src/field/crandall_field.rs @@ -0,0 +1,185 @@ +use std::fmt::{Debug, Formatter}; +use std::fmt; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use crate::field::field::Field; + +/// P = 2**64 - EPSILON +/// = 2**64 - 9 * 2**28 + 1 +/// = 2**28 * (2**36 - 9) + 1 +const P: u64 = 18446744071293632513; + +/// EPSILON = 9 * 2**28 - 1 +const EPSILON: u64 = 2415919103; + +const GENERATOR: CrandallField = CrandallField(5); +const TWO_ADICITY: usize = 28; +const POWER_OF_TWO_GENERATOR: CrandallField = CrandallField(10281950781551402419); + +/// A field designed for use with the Crandall reduction algorithm. +// TODO: [Partial]Eq should compare canonical representations. +#[derive(Copy, Clone)] +pub struct CrandallField(pub u64); + +impl PartialEq for CrandallField { + fn eq(&self, other: &Self) -> bool { + self.to_canonical_u64() == other.to_canonical_u64() + } +} + +impl Eq for CrandallField {} + +impl Debug for CrandallField { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Field for CrandallField { + const ZERO: Self = Self(0); + const ONE: Self = Self(1); + const NEG_ONE: Self = Self(P - 1); + + #[inline(always)] + fn sq(&self) -> Self { + *self * *self + } + + #[inline(always)] + fn cube(&self) -> Self { + *self * *self * *self + } + + fn inverse(&self) -> Self { + unimplemented!() + } + + fn primitive_root_of_unity(n_power: usize) -> Self { + assert!(n_power <= TWO_ADICITY); + let base = POWER_OF_TWO_GENERATOR; + base.exp(CrandallField(1u64 << (TWO_ADICITY - n_power))) + } + + fn cyclic_subgroup_known_order(generator: Self, order: usize) -> Vec { + let mut subgroup = Vec::new(); + let mut current = Self::ONE; + for _i in 0..order { + subgroup.push(current); + current = current * generator; + } + subgroup + } + + #[inline(always)] + fn to_canonical_u64(&self) -> u64 { + self.0 + } + + #[inline(always)] + fn from_canonical_u64(n: u64) -> Self { + Self(n) + } +} + +impl Neg for CrandallField { + type Output = Self; + + #[inline] + fn neg(self) -> Self { + let (diff, under) = P.overflowing_sub(self.0); + Self(diff.overflowing_add((under as u64) * P).0) + } +} + +impl Add for CrandallField { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + let (sum, over) = self.0.overflowing_add(rhs.0); + Self(sum.overflowing_sub((over as u64) * P).0) + } +} + +impl AddAssign for CrandallField { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Sub for CrandallField { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self { + let (diff, under) = self.0.overflowing_sub(rhs.0); + Self(diff.overflowing_add((under as u64) * P).0) + } +} + +impl SubAssign for CrandallField { + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Mul for CrandallField { + type Output = Self; + + #[inline] + fn mul(self, rhs: Self) -> Self { + reduce128((self.0 as u128) * (rhs.0 as u128)) + } +} + +impl MulAssign for CrandallField { + fn mul_assign(&mut self, rhs: Self) { + *self = *self * rhs; + } +} + +impl Div for CrandallField { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inverse() + } +} + +impl DivAssign for CrandallField { + fn div_assign(&mut self, rhs: Self) { + *self = *self / rhs; + } +} + +#[inline(always)] +fn reduce64(x: u64) -> u64 { + // TODO: slow? try cond sub + // x % P + let over = x > P; + x - (over as u64) * P +} + +/// no final reduction +#[inline(always)] +fn reduce128(x: u128) -> CrandallField { + // This is Crandall's algorithm. When we have some high-order bits (i.e. with a weight of 2^64), + // we convert them to low-order bits by multiplying by EPSILON (the logic is a simple + // generalization of Mersenne prime reduction). The first time we do this, the product will take + // ~96 bits, so we still have some high-order bits. But when we repeat this another time, the + // product will fit in 64 bits. + let (lo_1, hi_1) = split(x); + let (lo_2, hi_2) = split((EPSILON as u128) * (hi_1 as u128) + (lo_1 as u128)); + let lo_3 = hi_2 * EPSILON; + + CrandallField(lo_2) + CrandallField(lo_3) +} + +#[inline(always)] +fn split(x: u128) -> (u64, u64) { + (x as u64, (x >> 64) as u64) +} + +#[cfg(test)] +mod tests { +} diff --git a/src/field/fft.rs b/src/field/fft.rs new file mode 100644 index 00000000..243b2767 --- /dev/null +++ b/src/field/fft.rs @@ -0,0 +1,199 @@ +use crate::field::field::Field; +use crate::util::{log2_ceil, log2_strict}; + +/// Permutes `arr` such that each index is mapped to its reverse in binary. +fn reverse_index_bits(arr: Vec) -> Vec { + let n = arr.len(); + let n_power = log2_strict(n); + + let mut result = Vec::with_capacity(n); + for i in 0..n { + result.push(arr[reverse_bits(i, n_power)]); + } + result +} + +fn reverse_bits(n: usize, num_bits: usize) -> usize { + let mut result = 0; + for i in 0..num_bits { + let i_rev = num_bits - i - 1; + result |= (n >> i & 1) << i_rev; + } + result +} + +pub struct FftPrecomputation { + /// For each layer index i, stores the cyclic subgroup corresponding to the evaluation domain of + /// layer i. The indices within these subgroup vectors are bit-reversed. + subgroups_rev: Vec>, +} + +impl FftPrecomputation { + pub fn size(&self) -> usize { + self.subgroups_rev.last().unwrap().len() + } +} + +// pub fn fft(coefficients: &[F]) -> Vec { +// let precomputation = fft_precompute(coefficients.len()); +// fft_with_precomputation(coefficients, &precomputation) +// } + +pub fn fft_precompute(degree: usize) -> FftPrecomputation { + let degree_pow = log2_ceil(degree); + + let mut subgroups_rev = Vec::new(); + for i in 0..=degree_pow { + let g_i = F::primitive_root_of_unity(i); + let subgroup = F::cyclic_subgroup_known_order(g_i, 1 << i); + let subgroup_rev = reverse_index_bits(subgroup); + subgroups_rev.push(subgroup_rev); + } + + FftPrecomputation { subgroups_rev } +} + +pub fn ifft_with_precomputation_power_of_2( + points: &[F], + precomputation: &FftPrecomputation, +) -> Vec { + let n = points.len(); + let n_inv = Field(n as u64).multiplicative_inverse().unwrap(); + let mut result = fft_with_precomputation_power_of_2(points, precomputation); + + // We reverse all values except the first, and divide each by n. + result[0] = result[0] * n_inv; + result[n / 2] = result[n / 2] * n_inv; + for i in 1..(n / 2) { + let j = n - i; + let result_i = result[j] * n_inv; + let result_j = result[i] * n_inv; + result[i] = result_i; + result[j] = result_j; + } + result +} + +pub fn fft_with_precomputation_power_of_2( + coefficients: Vec, + precomputation: &FftPrecomputation, +) -> Vec { + debug_assert_eq!( + coefficients.len(), + precomputation.subgroups_rev.last().unwrap().len(), + "Number of coefficients does not match size of subgroup in precomputation" + ); + + let degree = coefficients.len(); + let half_degree = coefficients.len() >> 1; + let degree_pow = log2_strict(degree); + + // In the base layer, we're just evaluating "degree 0 polynomials", i.e. the coefficients + // themselves. + let mut evaluations = reverse_index_bits(coefficients); + + for i in 1..=degree_pow { + // In layer i, we're evaluating a series of polynomials, each at 2^i points. In practice + // we evaluate a pair of points together, so we have 2^(i - 1) pairs. + let points_per_poly = 1 << i; + let pairs_per_poly = 1 << (i - 1); + + let mut new_evaluations = Vec::new(); + for pair_index in 0..half_degree { + let poly_index = pair_index / pairs_per_poly; + let pair_index_within_poly = pair_index % pairs_per_poly; + + let child_index_0 = poly_index * points_per_poly + pair_index_within_poly; + let child_index_1 = child_index_0 + pairs_per_poly; + + let even = evaluations[child_index_0]; + let odd = evaluations[child_index_1]; + + let point_0 = precomputation.subgroups_rev[i][pair_index_within_poly * 2]; + let product = point_0 * odd; + new_evaluations.push(even + product); + new_evaluations.push(even - product); + } + evaluations = new_evaluations; + } + + // Reorder so that evaluations' indices correspond to (g_0, g_1, g_2, ...) + reverse_index_bits(evaluations) +} + +// #[cfg(test)] +// mod tests { +// use crate::{Bls12377Scalar, fft_precompute, fft_with_precomputation, CrandallField, ifft_with_precomputation_power_of_2}; +// use crate::fft::{log2_strict, reverse_bits, reverse_index_bits}; +// use crate::util::log2_ceil; +// +// #[test] +// fn fft_and_ifft() { +// let degree = 200; +// let degree_padded = log2_ceil(degree); +// let mut coefficients = Vec::new(); +// for i in 0..degree { +// coefficients.push(Bls12377Scalar::from_canonical_usize(i * 1337 % 100)); +// } +// +// let precomputation = fft_precompute(degree); +// let points = fft_with_precomputation(&coefficients, &precomputation); +// assert_eq!(points, evaluate_naive(&coefficients)); +// +// let interpolated_coefficients = +// ifft_with_precomputation_power_of_2(&points, &precomputation); +// for i in 0..degree { +// assert_eq!(interpolated_coefficients[i], coefficients[i]); +// } +// for i in degree..degree_padded { +// assert_eq!(interpolated_coefficients[i], Bls12377Scalar::ZERO); +// } +// } +// +// #[test] +// fn test_reverse_bits() { +// assert_eq!(reverse_bits(0b00110101, 8), 0b10101100); +// assert_eq!(reverse_index_bits(vec!["a", "b"]), vec!["a", "b"]); +// assert_eq!( +// reverse_index_bits(vec!["a", "b", "c", "d"]), +// vec!["a", "c", "b", "d"] +// ); +// } +// +// fn evaluate_naive(coefficients: &[CrandallField]) -> Vec { +// let degree = coefficients.len(); +// let degree_padded = 1 << log2_ceil(degree); +// +// let mut coefficients_padded = Vec::with_capacity(degree_padded); +// for c in coefficients { +// coefficients_padded.push(*c); +// } +// for _i in degree..degree_padded { +// coefficients_padded.push(F::ZERO); +// } +// evaluate_naive_power_of_2(&coefficients_padded) +// } +// +// fn evaluate_naive_power_of_2(coefficients: &[CrandallField]) -> Vec { +// let degree = coefficients.len(); +// let degree_pow = log2_strict(degree); +// +// let g = F::primitive_root_of_unity(degree_pow); +// let powers_of_g = F::cyclic_subgroup_known_order(g, degree); +// +// powers_of_g +// .into_iter() +// .map(|x| evaluate_at_naive(&coefficients, x)) +// .collect() +// } +// +// fn evaluate_at_naive(coefficients: &[CrandallField], point: F) -> F { +// let mut sum = F::ZERO; +// let mut point_power = F::ONE; +// for &c in coefficients { +// sum = sum + c * point_power; +// point_power = point_power * point; +// } +// sum +// } +// } diff --git a/src/field/field.rs b/src/field/field.rs new file mode 100644 index 00000000..ecb70adc --- /dev/null +++ b/src/field/field.rs @@ -0,0 +1,62 @@ +use std::fmt::Debug; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +/// A finite field with prime order less than 2^64. +pub trait Field: 'static ++ Copy ++ Clone ++ Eq ++ Neg ++ Add ++ AddAssign ++ Sub ++ SubAssign ++ Mul ++ MulAssign ++ Div ++ DivAssign ++ Debug { + const ZERO: Self; + const ONE: Self; + const NEG_ONE: Self; + + fn sq(&self) -> Self; + + fn cube(&self) -> Self; + + /// Compute the multiplicative inverse of this field element. + fn inverse(&self) -> Self; + + fn primitive_root_of_unity(n_power: usize) -> Self; + + fn cyclic_subgroup_known_order(generator: Self, order: usize) -> Vec; + + fn to_canonical_u64(&self) -> u64; + + fn from_canonical_u64(n: u64) -> Self; + + fn from_canonical_usize(n: usize) -> Self { + Self::from_canonical_u64(n as u64) + } + + fn bits(&self) -> usize { + 64 - self.to_canonical_u64().leading_zeros() as usize + } + + fn exp(&self, power: Self) -> Self { + let mut current = *self; + let mut product = Self::ONE; + + for j in 0..power.bits() { + if (power.to_canonical_u64() >> j & 1) != 0 { + product = product * current; + } + current = current.sq(); + } + product + } + + fn exp_usize(&self, power: usize) -> Self { + self.exp(Self::from_canonical_usize(power)) + } +} diff --git a/src/field/field_search.rs b/src/field/field_search.rs new file mode 100644 index 00000000..d73c99e6 --- /dev/null +++ b/src/field/field_search.rs @@ -0,0 +1,57 @@ +fn field_search() { + for deg in (61..=64).rev() { + for adic in (28..=32).rev() { + for i in 1u128..100000 { + if i.count_ones() == 1 && i != 1 { + continue; + } + let epsilon = i * (1u128 << adic) - 1; + if epsilon > 1 << 32 { + break; + } + let n = ((1u128) << deg) - epsilon; + let n = n as u64; + let prime = is_prime(n); + if prime { + println!("2^{} - ({} * 2**{} - 1) = {}", deg, i, adic, n); + let perm3 = (n - 1) % 3 != 0; + println!(" x^3 {}", perm3); + if perm3 { + let mut exp = n as u128; + while exp % 3 != 0 { + exp += (n - 1) as u128; + } + exp /= 3; + println!(" exp weight {}", exp.count_ones()); + } + let perm5 = (n - 1) % 5 != 0; + println!(" x^5 {}", perm5); + if perm5 { + let mut exp = n as u128; + while exp % 5 != 0 { + exp += (n - 1) as u128; + } + exp /= 5; + println!(" exp weight {}", exp.count_ones()); + } + } + } + } + } +} + +fn is_prime(n: u64) -> bool { + if (n & 1) == 0 { + return false; + } + + let mut d = 3; + while d * d <= n { + if n % d == 0 { + return false; + } + d += 2; + } + + return true; +} diff --git a/src/field/mod.rs b/src/field/mod.rs new file mode 100644 index 00000000..027dd742 --- /dev/null +++ b/src/field/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod crandall_field; +pub(crate) mod field; +pub(crate) mod field_search; +pub(crate) mod fft; diff --git a/src/gates/deterministic_gate.rs b/src/gates/deterministic_gate.rs new file mode 100644 index 00000000..481808ad --- /dev/null +++ b/src/gates/deterministic_gate.rs @@ -0,0 +1,146 @@ +use std::marker::PhantomData; + +use crate::circuit_data::CircuitConfig; +use crate::constraint_polynomial::{ConstraintPolynomial, EvaluationVars}; +use crate::field::field::Field; +use crate::gates::gate::Gate2; +use crate::generator::{SimpleGenerator, WitnessGenerator2}; +use crate::target::Target2; +use crate::wire::Wire; +use crate::witness::PartialWitness2; + +/// A deterministic gate. Each entry in `outputs()` describes how that output is evaluated; this is +/// used to create both the constraint set and the generator set. +/// +/// `DeterministicGate`s do not automatically implement `Gate`; they should instead be wrapped in +/// `DeterministicGateAdapter`. +pub trait DeterministicGate: 'static { + /// A unique identifier for this gate. + fn id(&self) -> String; + + /// A vector of `(i, c)` pairs, where `i` is the index of an output and `c` is the polynomial + /// defining how that output is evaluated. + fn outputs(&self, config: CircuitConfig) -> Vec<(usize, ConstraintPolynomial)>; + + /// Any additional constraints to be enforced, besides the (automatically provided) ones that + /// constraint output values. + fn additional_constraints(&self, _config: CircuitConfig) -> Vec> { + Vec::new() + } + + /// Any additional generators, besides the (automatically provided) ones that generate output + /// values. + fn additional_generators( + &self, + _config: CircuitConfig, + _gate_index: usize, + ) -> Vec>> { + Vec::new() + } +} + +/// A wrapper around `DeterministicGate` which implements `Gate`. Note that a blanket implementation +/// is not possible in this context given Rust's coherence rules. +pub struct DeterministicGateAdapter + ?Sized> { + gate: Box, + _phantom: PhantomData, +} + +impl> DeterministicGateAdapter { + pub fn new(gate: DG) -> Self { + Self { gate: Box::new(gate), _phantom: PhantomData } + } +} + +impl> Gate2 for DeterministicGateAdapter { + fn id(&self) -> String { + self.gate.id() + } + + fn constraints(&self, config: CircuitConfig) -> Vec> { + // For each output, we add a constraint of the form `out - expression = 0`, + // then we append any additional constraints that the gate defines. + self.gate.outputs(config).into_iter() + .map(|(i, out)| out - ConstraintPolynomial::local_wire_value(i)) + .chain(self.gate.additional_constraints(config).into_iter()) + .collect() + } + + fn generators( + &self, + config: CircuitConfig, + gate_index: usize, + local_constants: Vec, + next_constants: Vec, + ) -> Vec>> { + self.gate.outputs(config) + .into_iter() + .map(|(input_index, out)| { + let og = OutputGenerator { + gate_index, + input_index, + out, + local_constants: local_constants.clone(), + next_constants: next_constants.clone(), + }; + + // We need the type system to treat this as a boxed `WitnessGenerator2`, rather + // than a boxed `OutputGenerator`. + let b: Box::> = Box::new(og); + b + }) + .chain(self.gate.additional_generators(config, gate_index)) + .collect() + } +} + +struct OutputGenerator { + gate_index: usize, + input_index: usize, + out: ConstraintPolynomial, + local_constants: Vec, + next_constants: Vec, +} + +impl SimpleGenerator for OutputGenerator { + fn dependencies(&self) -> Vec { + self.out.dependencies(self.gate_index) + .into_iter() + .map(Target2::Wire) + .collect() + } + + fn run_once(&mut self, witness: &PartialWitness2) -> PartialWitness2 { + let mut local_wire_values = Vec::new(); + let mut next_wire_values = Vec::new(); + + // Get an exclusive upper bound on the largest input index in this constraint. + let input_limit_exclusive = self.out.max_wire_input_index() + .map_or(0, |i| i + 1); + + for input in 0..input_limit_exclusive { + let local_wire = Wire { gate: self.gate_index, input }; + let next_wire = Wire { gate: self.gate_index + 1, input }; + + // Lookup the values if they exist. If not, we can just insert a zero, knowing + // that it will not be used. (If it was used, it would have been included in our + // dependencies, and this generator would not have run yet.) + let local_value = witness.try_get_target(Target2::Wire(local_wire)).unwrap_or(F::ZERO); + let next_value = witness.try_get_target(Target2::Wire(next_wire)).unwrap_or(F::ZERO); + + local_wire_values.push(local_value); + next_wire_values.push(next_value); + } + + let vars = EvaluationVars { + local_constants: &self.local_constants, + next_constants: &self.next_constants, + local_wire_values: &local_wire_values, + next_wire_values: &next_wire_values, + }; + + let result_wire = Wire { gate: self.gate_index, input: self.input_index }; + let result_value = self.out.evaluate(vars); + PartialWitness2::singleton(Target2::Wire(result_wire), result_value) + } +} diff --git a/src/gates/gate.rs b/src/gates/gate.rs new file mode 100644 index 00000000..7cce3acb --- /dev/null +++ b/src/gates/gate.rs @@ -0,0 +1,81 @@ +use std::hash::{Hash, Hasher}; +use std::rc::Rc; + +use crate::circuit_data::CircuitConfig; +use crate::constraint_polynomial::ConstraintPolynomial; +use crate::field::field::Field; +use crate::generator::WitnessGenerator2; + +/// A custom gate. +// TODO: Remove CircuitConfig params? Could just use fields within each struct. +pub trait Gate2: 'static { + fn id(&self) -> String; + + /// A set of expressions which must evaluate to zero. + fn constraints(&self, config: CircuitConfig) -> Vec>; + + fn generators( + &self, + config: CircuitConfig, + gate_index: usize, + local_constants: Vec, + next_constants: Vec, + ) -> Vec>>; + + /// The number of constants used by this gate. + fn num_constants(&self, config: CircuitConfig) -> usize { + self.constraints(config) + .into_iter() + .map(|c| c.max_constant_index().map_or(0, |i| i + 1)) + .max() + .unwrap_or(0) + } + + /// The minimum number of wires required to use this gate. + fn min_wires(&self, config: CircuitConfig) -> usize { + self.constraints(config) + .into_iter() + .map(|c| c.max_wire_input_index().map_or(0, |i| i + 1)) + .max() + .unwrap_or(0) + } + + /// The maximum degree among this gate's constraint polynomials. + fn degree(&self, config: CircuitConfig) -> usize { + self.constraints(config) + .into_iter() + .map(|c| c.degree()) + .max() + .unwrap_or(0) + } +} + +/// A wrapper around an `Rc` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs. +#[derive(Clone)] +pub struct GateRef(pub(crate) Rc>); + +impl GateRef { + pub fn new>(gate: G) -> GateRef { + GateRef(Rc::new(gate)) + } +} + +impl PartialEq for GateRef { + fn eq(&self, other: &Self) -> bool { + self.0.id() == other.0.id() + } +} + +impl Hash for GateRef { + fn hash(&self, state: &mut H) { + self.0.id().hash(state) + } +} + +impl Eq for GateRef {} + +/// A gate along with any constants used to configure it. +pub struct GateInstance { + pub gate_type: GateRef, + pub constants: Vec, +} diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs new file mode 100644 index 00000000..259d2fbc --- /dev/null +++ b/src/gates/gmimc.rs @@ -0,0 +1,29 @@ +use crate::circuit_data::CircuitConfig; +use crate::constraint_polynomial::ConstraintPolynomial; +use crate::field::field::Field; +use crate::gates::deterministic_gate::DeterministicGate; +use crate::gates::gate::{Gate2, GateRef}; + +/// Evaluates a full GMiMC permutation. +#[derive(Debug)] +pub struct GMiMCGate { + num_rounds: usize, + width: usize, + round_constants: Vec, +} + +impl GMiMCGate { + fn new(width: usize) -> GateRef { + todo!() + } +} + +impl DeterministicGate for GMiMCGate { + fn id(&self) -> String { + format!("{:?}", self) + } + + fn outputs(&self, config: CircuitConfig) -> Vec<(usize, ConstraintPolynomial)> { + unimplemented!() + } +} diff --git a/src/gates/mod.rs b/src/gates/mod.rs new file mode 100644 index 00000000..e44d34b0 --- /dev/null +++ b/src/gates/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod deterministic_gate; +pub(crate) mod gate; +pub(crate) mod gmimc; diff --git a/src/generator.rs b/src/generator.rs new file mode 100644 index 00000000..9b69acd4 --- /dev/null +++ b/src/generator.rs @@ -0,0 +1,54 @@ +use crate::field::field::Field; +use crate::target::Target2; +use crate::witness::PartialWitness2; + +/// A generator participates in the generation of the witness. +pub trait WitnessGenerator2: 'static { + /// Targets to be "watched" by this generator. Whenever a target in the watch list is populated, + /// the generator will be queued to run. + fn watch_list(&self) -> Vec; + + /// Run this generator, returning a `PartialWitness` containing any new witness elements, and a + /// flag indicating whether the generator is finished. If the flag is true, the generator will + /// never be run again, otherwise it will be queued for another run next time a target in its + /// watch list is populated. + fn run(&mut self, witness: &PartialWitness2) -> (PartialWitness2, bool); +} + +/// A generator which runs once after a list of dependencies is present in the witness. +pub trait SimpleGenerator: 'static { + fn dependencies(&self) -> Vec; + + fn run_once(&mut self, witness: &PartialWitness2) -> PartialWitness2; +} + +impl> WitnessGenerator2 for SG { + fn watch_list(&self) -> Vec { + self.dependencies() + } + + fn run(&mut self, witness: &PartialWitness2) -> (PartialWitness2, bool) { + if witness.contains_all(&self.dependencies()) { + (self.run_once(witness), true) + } else { + (PartialWitness2::new(), false) + } + } +} + +/// A generator which copies one wire to another. +pub(crate) struct CopyGenerator { + pub(crate) src: Target2, + pub(crate) dst: Target2, +} + +impl SimpleGenerator for CopyGenerator { + fn dependencies(&self) -> Vec { + vec![self.src] + } + + fn run_once(&mut self, witness: &PartialWitness2) -> PartialWitness2 { + let value = witness.get_target(self.src); + PartialWitness2::singleton(self.dst, value) + } +} diff --git a/src/gmimc.rs b/src/gmimc.rs new file mode 100644 index 00000000..6bb9c7c3 --- /dev/null +++ b/src/gmimc.rs @@ -0,0 +1,78 @@ +use unroll::unroll_for_loops; + +use crate::field::field::Field; + +const GMIMC_CONSTANTS: [u64; GMIMC_ROUNDS] = [11875528958976719239, 6107683892976199900, 7756999550758271958, 14819109722912164804, 9716579428412441110, 13627117528901194436, 16260683900833506663, 5942251937084147420, 3340009544523273897, 5103423085715007461, 17051583366444092101, 11122892258227244197, 16564300648907092407, 978667924592675864, 17676416205210517593, 1938246372790494499, 8857737698008340728, 1616088456497468086, 15961521580811621978, 17427220057097673602, 14693961562064090188, 694121596646283736, 554241305747273747, 5783347729647881086, 14933083198980931734, 2600898787591841337, 9178797321043036456, 18068112389665928586, 14493389459750307626, 1650694762687203587, 12538946551586403559, 10144328970401184255, 4215161528137084719, 17559540991336287827, 1632269449854444901, 986434918028205468, 14921385763379308253, 4345141219277982730, 2645897826751167170, 9815223670029373528, 7687983869685434132, 13956100321958014639, 519639453142393369, 15617837024229225911, 1557446238053329052, 8130006133842942201, 864716631341688017, 2860289738131495304, 16723700803638270299, 8363528906277648001, 13196016034228493087, 2514677332206134618, 15626342185220554936, 466271571343554681, 17490024028988898434, 6454235936129380878, 15187752952940298536, 18043495619660620405, 17118101079533798167, 13420382916440963101, 535472393366793763, 1071152303676936161, 6351382326603870931, 12029593435043638097, 9983185196487342247, 414304527840226604, 1578977347398530191, 13594880016528059526, 13219707576179925776, 6596253305527634647, 17708788597914990288, 7005038999589109658, 10171979740390484633, 1791376803510914239, 2405996319967739434, 12383033218117026776, 17648019043455213923, 6600216741450137683, 5359884112225925883, 1501497388400572107, 11860887439428904719, 64080876483307031, 11909038931518362287, 14166132102057826906, 14172584203466994499, 593515702472765471, 3423583343794830614, 10041710997716717966, 13434212189787960052, 9943803922749087030, 3216887087479209126, 17385898166602921353, 617799950397934255, 9245115057096506938, 13290383521064450731, 10193883853810413351, 14648839921475785656, 14635698366607946133, 9134302981480720532, 10045888297267997632, 10752096344939765738, 12049167771599274839, 16471532489936095930, 7118567245891966484, 272840212090177715, 7530334979534674340, 12300300144661791831, 14334496540665732547]; + +const GMIMC_ROUNDS: usize = 108; + +const W: usize = 12; + +pub fn gmimc_compress(a: [F; 4], b: [F; 4]) -> [F; 4] { + // Sponge with r=8, c=4. + let state_0 = [a[0], a[1], a[2], a[3], b[0], + b[1], b[2], b[3], + F::ZERO, F::ZERO, F::ZERO, F::ZERO]; + let state_1 = gmimc_permute(state_0); + [state_1[0], state_1[1], state_1[2], state_1[3]] +} + +#[unroll_for_loops] +pub fn gmimc_permute(mut xs: [F; W]) -> [F; W] { + // TODO: Hardcoded width and num rounds for now, since unroll_for_loops doesn't work with + // constants or anything. Maybe use const generics when stable? + + // Value that is implicitly added to each element. + // See https://affine.group/2020/02/starkware-challenge + let mut addition_buffer = F::ZERO; + + for r in 0..108 { + let active = r % 12; + let f = (xs[active] + addition_buffer + F::from_canonical_u64(GMIMC_CONSTANTS[r])).cube(); + addition_buffer += f; + xs[active] -= f; + } + + for i in 0..12 { + xs[i] += addition_buffer; + } + + xs +} + +#[unroll_for_loops] +pub fn gmimc_permute_naive(mut xs: [F; W]) -> [F; W] { + // TODO: Hardcoded width and num rounds for now, since unroll_for_loops doesn't work with + // constants or anything. Maybe use const generics when stable? + + for r in 0..108 { + let active = r % 12; + let f = (xs[active] + F::from_canonical_u64(GMIMC_CONSTANTS[r])).cube(); + for i in 0..12 { + if i != active { + xs[i] = xs[i] + f; + } + } + } + + xs +} + +#[cfg(test)] +mod tests { + use crate::field::crandall_field::CrandallField; + use crate::field::Field; + use crate::gmimc::{gmimc_permute, gmimc_permute_naive}; + + #[test] + fn consistency() { + type F = CrandallField; + let mut xs = [F::ZERO; 12]; + for i in 0..12 { + xs[i] = F::from_canonical_usize(i); + } + let out = gmimc_permute(xs); + let out_naive = gmimc_permute_naive(xs); + assert_eq!(out, out_naive); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..a790c66d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,92 @@ +use std::thread; +use std::time::Instant; + +use rayon::prelude::*; + +use field::crandall_field::CrandallField; +use field::fft; +use field::fft::fft_precompute; + +use crate::field::field::Field; +use crate::util::log2_ceil; + +mod circuit_data; +mod constraint_polynomial; +mod field; +mod gates; +mod generator; +mod gmimc; +mod proof; +mod prover; +mod rescue; +mod target; +mod util; +mod verifier; +mod wire; +mod witness; + +// 12 wire polys, 3 Z polys, 4 parts of quotient poly. +const PROVER_POLYS: usize = 101 + 3 + 4; // TODO: Check + +fn main() { + let overall_start = Instant::now(); + + // bench_fft(); + println!(); + bench_gmimc::(); + + let overall_duration = overall_start.elapsed(); + println!("Overall time: {:?}", overall_duration); + + // field_search() +} + +fn bench_gmimc() { + let threads = 12; + // let hashes_per_poly = 623328; + // let hashes_per_poly = 1 << log2_ceil(hashes_per_poly); + let hashes_per_poly = 1 << (13 + 3); + let threads = (0..threads).map(|_i| thread::spawn(move || { + let mut x = [F::ZERO; 12]; + for i in 0..12 { + x[i] = F::from_canonical_u64((i as u64) * 123456 + 789); + } + + let hashes_per_thread = hashes_per_poly * PROVER_POLYS / threads; + let start = Instant::now(); + for _ in 0..hashes_per_thread { + x = gmimc::gmimc_permute(x); + } + let duration = start.elapsed(); + println!("took {:?}", duration); + println!("avg {:?}us", duration.as_secs_f64() * 1e6 / (hashes_per_thread as f64)); + println!("result {:?}", x); + })).collect::>(); + + for t in threads { + t.join().expect("oops"); + } +} + +fn bench_fft() { + let degree = 1 << log2_ceil(77916); + let lde_bits = 4; + let lde_size = degree << lde_bits; + let precomputation = fft_precompute(lde_size); + println!("{} << {} = {}", degree, lde_bits, lde_size); + + let start = Instant::now(); + (0usize..PROVER_POLYS).into_par_iter().for_each(|i| { + let mut coeffs = vec![CrandallField::ZERO; lde_size]; + for j in 0usize..lde_size { + coeffs[j] = CrandallField((i * j) as u64); + } + + let start = Instant::now(); + let result = fft::fft_with_precomputation_power_of_2(coeffs, &precomputation); + let duration = start.elapsed(); + println!("FFT took {:?}", duration); + println!("FFT result: {:?}", result[0]); + }); + println!("FFT overall took {:?}", start.elapsed()); +} diff --git a/src/proof.rs b/src/proof.rs new file mode 100644 index 00000000..8f323ec6 --- /dev/null +++ b/src/proof.rs @@ -0,0 +1,58 @@ +use crate::field::field::Field; +use crate::target::Target2; + +pub struct Hash { + elements: Vec, +} + +pub struct HashTarget { + elements: Vec, +} + +pub struct Proof2 { + /// Merkle root of LDEs of wire values. + pub wires_root: Hash, + /// Merkle root of LDEs of Z, in the context of Plonk's permutation argument. + pub plonk_z_root: Hash, + /// Merkle root of LDEs of the quotient polynomial components. + pub plonk_t_root: Hash, + + /// Purported values of each polynomial at each challenge point. + pub openings: Vec>, + + // TODO: FRI Merkle proofs. +} + +pub struct ProofTarget2 { + /// Merkle root of LDEs of wire values. + pub wires_root: HashTarget, + /// Merkle root of LDEs of Z, in the context of Plonk's permutation argument. + pub plonk_z_root: HashTarget, + /// Merkle root of LDEs of the quotient polynomial components. + pub plonk_t_root: HashTarget, + + /// Purported values of each polynomial at each challenge point. + pub openings: Vec, + + // TODO: FRI Merkle proofs. +} + +/// The purported values of each polynomial at a single point. +pub struct OpeningSet { + pub constants: Vec, + pub plonk_sigmas: Vec, + pub wires: Vec, + // TODO: One or multiple? + pub plonk_z: Vec, + pub plonk_t: Vec, +} + +/// The purported values of each polynomial at a single point. +pub struct OpeningSetTarget { + pub constants: Vec, + pub plonk_sigmas: Vec, + pub wires: Vec, + // TODO: One or multiple? + pub plonk_z: Vec, + pub plonk_t: Vec, +} diff --git a/src/prover.rs b/src/prover.rs new file mode 100644 index 00000000..8ed7417e --- /dev/null +++ b/src/prover.rs @@ -0,0 +1,10 @@ +use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; +use crate::field::field::Field; +use crate::proof::Proof2; + +pub(crate) fn prove2( + prover_data: &ProverOnlyCircuitData, + common_data: &CommonCircuitData, +) -> Proof2 { + todo!() +} diff --git a/src/rescue.rs b/src/rescue.rs new file mode 100644 index 00000000..a2346bfe --- /dev/null +++ b/src/rescue.rs @@ -0,0 +1,131 @@ +use unroll::unroll_for_loops; + +use crate::field::field::Field; + +const ROUNDS: usize = 10; + +const W: usize = 12; + +const MDS: [[u64; W]; W] = [ + [10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, 11068046442776179508, 13835058053470224385, 6148914690431210838, 9223372035646816257, 1, ], + [5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, 11068046442776179508, 13835058053470224385, 6148914690431210838, 9223372035646816257, ], + [1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, 11068046442776179508, 13835058053470224385, 6148914690431210838, ], + [15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, 11068046442776179508, 13835058053470224385, ], + [17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, 11068046442776179508, ], + [3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, ], + [1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, ], + [9708812669101911849, 1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, ], + [2767011610694044877, 9708812669101911849, 1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, ], + [878416384347315834, 2767011610694044877, 9708812669101911849, 1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, ], + [17608255704416649217, 878416384347315834, 2767011610694044877, 9708812669101911849, 1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, ], + [15238614667590392076, 17608255704416649217, 878416384347315834, 2767011610694044877, 9708812669101911849, 1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, ], +]; + +const RESCUE_CONSTANTS: [[u64; W]; 20] = [ + [12050887499329086906, 1748247961703512657, 315780861775001585, 2827656358919812970, 13335864861236723579, 3010729529365640897, 8463534053828271146, 2528500966106598845, 8969871077123422281, 1002624930202741107, 599979829006456404, 4386170815218774254, ], + [5771413917591851532, 11946802620311685142, 4759792267858670262, 6879094914431255667, 3985911073214909073, 1542850118294175816, 5393560436452023029, 8331250756632997735, 3395511836281190608, 17601255793194446503, 12848459944475727152, 11995465655754698601, ], + [14063960046551560130, 14790209580166185143, 5509023472758717841, 1274395897760495573, 16719545989415697758, 17865948122414223407, 3919263713959798649, 5633741078654387163, 15665612362287352054, 3418834727998553015, 5324019631954832682, 17962066557010997431, ], + [3282193104189649752, 18423507935939999211, 9035104445528866459, 30842260240043277, 3896337933354935129, 6615548113269323045, 6625827707190475694, 6677757329269550670, 11419013193186889337, 17111888851716383760, 12075517898615128691, 8139844272075088233, ], + [8872892112814161072, 17529364346566228604, 7526576514327158912, 850359069964902700, 9679332912197531902, 10591229741059812071, 12759208863825924546, 14552519355635838750, 16066249893409806278, 11283035366525176262, 1047378652379935387, 17032498397644511356, ], + [2938626421478254042, 10375267398354586672, 13728514869380643947, 16707318479225743731, 9785828188762698567, 8610686976269299752, 5478372191917042178, 12716344455538470365, 9968276048553747246, 14746805727771473956, 4822070620124107028, 9901161649549513416, ], + [13458162407040644078, 4045792126424269312, 9709263167782315020, 2163173014916005515, 17079206331095671215, 2556388076102629669, 6582772486087242347, 1239959540200663058, 18268236910639895687, 12499012548657350745, 17213068585339946119, 7641451088868756688, ], + [14674555473338434116, 14624532976317185113, 13625541984298615970, 7612892294159054770, 12294028208969561574, 6067206081581804358, 5778082506883496792, 7389487446513884800, 12929525660730020877, 18244350162788654296, 15285920877034454694, 3640669683987215349, ], + [6737585134029996281, 1826890539455248546, 289376081355380231, 10782622161517803787, 12978425540147835172, 9828233103297278473, 16384075371934678711, 3187492301890791304, 12985433735185968457, 9470935291631377473, 16328323199113140151, 16218490552434224203, ], + [6188809977565251499, 18437718710937437067, 4530469469895539008, 9596355277372723349, 13602518824447658705, 8759976068576854281, 10504320064094929535, 3980760429843656150, 14609448298151012462, 5839843841558860609, 10283805260656050418, 7239168159249274821, ], + [3604243611640027441, 5237321927316578323, 5071861664926666316, 13025405632646149705, 3285281651566464074, 12121596060272825779, 1900602777802961569, 8122527981264852045, 6731303887159752901, 9197659817406857040, 844741616904786364, 14249777686667858094, ], + [8602844218963499297, 10133401373828451640, 11618292280328565166, 8828272598402499582, 4252246265076774689, 9760449011955070998, 10233981507028897480, 10427510555228840014, 1007817664531124790, 4465396600980659145, 7727267420665314215, 7904022788946844554, ], + [11418297156527169222, 15865399053509010196, 1727198235391450850, 16557095577717348672, 1524052121709169653, 14531367160053894310, 4071756280138432327, 10333204220115446291, 16584144375833061215, 12237566480526488368, 11090440024401607208, 18281335018830792766, ], + [16152169547074248135, 18338155611216027761, 15842640128213925612, 14687926435880145351, 13259626900273707210, 6187877366876303234, 10312881470701795438, 1924945292721719446, 2278209355262975917, 3250749056007953206, 11589006946114672195, 241829012299953928, ], + [11244459446597052449, 7319043416418482137, 8148526814449636806, 9054933038587901070, 550333919248348827, 5513167392062632770, 12644459803778263764, 9903621375535446226, 16390581784506871871, 14586524717888286021, 6975796306584548762, 5200407948555191573, ], + [2855794043288846965, 1259443213892506318, 6145351706926586935, 3853784494234324998, 5871277378086513850, 9414363368707862566, 11946957446931890832, 308083693687568600, 12712587722369770461, 6792392698104204991, 16465224002344550280, 10282380383506806095, ], + [12608209810104211593, 11808578423511814760, 16177950852717156460, 9394439296563712221, 12586575762376685187, 17703393198607870393, 9811861465513647715, 14126450959506560131, 12713673607080398908, 18301828072718562389, 11180556590297273821, 4451415492203885059, ], + [10465807219916311101, 1213997644391575261, 17672155373280862521, 1491206970207330736, 10977478805896263804, 13260961975618373124, 16060889403827043708, 3223573072465920682, 17624203443801796697, 10247205738678800822, 11100653267668698651, 14328592975764892571, ], + [6984072551318461094, 3416562710010527326, 12847783919251969270, 12223185134739244472, 12073170519625198198, 6221124633828606855, 17596623990006806590, 1153871693574764968, 2548851681903410721, 9823373270182377847, 16708030507924899244, 9619306826188519218, ], + [5842685042453818473, 12400879353954910914, 647112787845575111, 4893664959929687347, 3759391664155971284, 15871181179823725763, 3629377713951158273, 3439101502554162312, 8325686353010019444, 10630488935940555500, 3478529754946055748, 12681233130980545828, ], +]; + +fn rescue(mut xs: [F; W]) -> [F; W] { + for r in 0..10 { + xs = sbox_layer_a(xs); + xs = mds_layer(xs); + xs = constant_layer(xs, &RESCUE_CONSTANTS[r * 2]); + + xs = sbox_layer_b(xs); + xs = mds_layer(xs); + xs = constant_layer(xs, &RESCUE_CONSTANTS[r * 2 + 1]); + } + + // for i in 0..W { + // xs[i] = xs[i].to_canonical(); + // } + + xs +} + +// #[inline(always)] +#[unroll_for_loops] +fn sbox_layer_a(x: [F; W]) -> [F; W] { + let mut result = [F::ZERO; W]; + for i in 0..W { + result[i] = sbox_a(x[i]); + } + result +} + +// #[inline(always)] +#[unroll_for_loops] +fn sbox_layer_b(x: [F; W]) -> [F; W] { + let mut result = [F::ZERO; W]; + for i in 0..W { + result[i] = sbox_b(x[i]); + } + result +} + +// #[inline(always)] +#[unroll_for_loops] +fn sbox_a(x: F) -> F { + // x^{-5}, via Fermat's little theorem + const EXP: u64 = 7378697628517453005; + + let mut product = F::ONE; + let mut current = x; + + for i in 0..64 { + if ((EXP >> i) & 1) != 0 { + product = product * current; + } + current = current.sq(); + } + product +} + +#[inline(always)] +fn sbox_b(x: F) -> F { + // x^5 + let x2 = x.sq(); + let x3 = x2 * x; + x2 * x3 +} + +// #[inline(always)] +#[unroll_for_loops] +fn mds_layer(x: [F; W]) -> [F; W] { + let mut result = [F::ZERO; W]; + for r in 0..W { + for c in 0..W { + result[r] = result[r] + F::from_canonical_u64(MDS[r][c]) * x[c]; + } + } + result +} + +#[inline(always)] +#[unroll_for_loops] +fn constant_layer(xs: [F; W], con: &[u64; W]) -> [F; W] { + let mut result = [F::ZERO; W]; + for i in 0..W { + result[i] = xs[i] + F::from_canonical_u64(con[i]); + } + result +} diff --git a/src/target.rs b/src/target.rs new file mode 100644 index 00000000..058650d0 --- /dev/null +++ b/src/target.rs @@ -0,0 +1,28 @@ +use std::convert::Infallible; +use std::marker::PhantomData; + +use crate::circuit_data::CircuitConfig; +use crate::field::field::Field; +use crate::wire::Wire; + +/// A location in the witness. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub enum Target2 { + Wire(Wire), + PublicInput { index: usize }, + VirtualAdviceTarget { index: usize }, +} + +impl Target2 { + pub fn wire(gate: usize, input: usize) -> Self { + Self::Wire(Wire { gate, input }) + } + + pub fn is_routable(&self, config: CircuitConfig) -> bool { + match self { + Target2::Wire(wire) => wire.is_routable(config), + Target2::PublicInput { .. } => true, + Target2::VirtualAdviceTarget { .. } => false, + } + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 00000000..967add8a --- /dev/null +++ b/src/util.rs @@ -0,0 +1,31 @@ +// TODO: Can this impl usize? +pub(crate) fn ceil_div_usize(a: usize, b: usize) -> usize { + (a + b - 1) / b +} + +pub(crate) fn pad_to_multiple_usize(a: usize, b: usize) -> usize { + ceil_div_usize(a, b) * b +} + +/// Computes `ceil(log_2(n))`. +pub(crate) fn log2_ceil(n: usize) -> usize { + n.next_power_of_two().trailing_zeros() as usize +} + +/// Computes `log_2(n)`, panicking if `n` is not a power of two. +pub fn log2_strict(n: usize) -> usize { + assert!(n.is_power_of_two(), "Not a power of two"); + log2_ceil(n) +} + +pub(crate) fn transpose(matrix: &[Vec]) -> Vec> { + let old_rows = matrix.len(); + let old_cols = matrix[0].len(); + let mut transposed = vec![Vec::with_capacity(old_rows); old_cols]; + for new_r in 0..old_cols { + for new_c in 0..old_rows { + transposed[new_r].push(matrix[new_c][new_r].clone()); + } + } + transposed +} diff --git a/src/verifier.rs b/src/verifier.rs new file mode 100644 index 00000000..d9d67607 --- /dev/null +++ b/src/verifier.rs @@ -0,0 +1,9 @@ +use crate::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData}; +use crate::field::field::Field; + +pub(crate) fn verify2( + verifier_data: &VerifierOnlyCircuitData, + common_data: &CommonCircuitData, +) { + todo!() +} diff --git a/src/wire.rs b/src/wire.rs new file mode 100644 index 00000000..86bc5779 --- /dev/null +++ b/src/wire.rs @@ -0,0 +1,16 @@ +use crate::circuit_data::CircuitConfig; + +/// Represents a wire in the circuit. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub struct Wire { + /// The index of the associated gate. + pub gate: usize, + /// The index of the gate input wherein this wire is inserted. + pub input: usize, +} + +impl Wire { + pub fn is_routable(&self, config: CircuitConfig) -> bool { + self.input < config.num_routed_wires + } +} diff --git a/src/witness.rs b/src/witness.rs new file mode 100644 index 00000000..47910874 --- /dev/null +++ b/src/witness.rs @@ -0,0 +1,56 @@ +use std::collections::HashMap; + +use crate::field::field::Field; +use crate::target::Target2; +use crate::wire::Wire; + +#[derive(Debug)] +pub struct PartialWitness2 { + target_values: HashMap, +} + +impl PartialWitness2 { + pub fn new() -> Self { + PartialWitness2 { + target_values: HashMap::new(), + } + } + + pub fn singleton(target: Target2, value: F) -> Self { + let mut witness = PartialWitness2::new(); + witness.set_target(target, value); + witness + } + + pub fn is_empty(&self) -> bool { + self.target_values.is_empty() + } + + pub fn get_target(&self, target: Target2) -> F { + self.target_values[&target] + } + + pub fn try_get_target(&self, target: Target2) -> Option { + self.target_values.get(&target).cloned() + } + + pub fn get_wire(&self, wire: Wire) -> F { + self.get_target(Target2::Wire(wire)) + } + + pub fn contains(&self, target: Target2) -> bool { + self.target_values.contains_key(&target) + } + + pub fn contains_all(&self, targets: &[Target2]) -> bool { + targets.iter().all(|&t| self.contains(t)) + } + + pub fn set_target(&mut self, target: Target2, value: F) { + self.target_values.insert(target, value); + } + + pub fn set_wire(&mut self, wire: Wire, value: F) { + self.set_target(Target2::Wire(wire), value) + } +}