mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-03 14:23:07 +00:00
Initial commit
This commit is contained in:
commit
13cc76316c
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "plonky2"
|
||||
version = "0.1.0"
|
||||
authors = ["Daniel Lubarov <daniel@lubarov.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
unroll = "0.1.5"
|
||||
rayon = "1.5.0"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
84
src/circuit_data.rs
Normal file
84
src/circuit_data.rs
Normal file
@ -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<F: Field> {
|
||||
prover_only: ProverOnlyCircuitData<F>,
|
||||
verifier_only: VerifierOnlyCircuitData,
|
||||
common: CommonCircuitData<F>,
|
||||
}
|
||||
|
||||
impl<F: Field> CircuitData<F> {
|
||||
pub fn prove2(&self) -> Proof2<F> {
|
||||
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<F: Field> {
|
||||
prover_only: ProverOnlyCircuitData<F>,
|
||||
common: CommonCircuitData<F>,
|
||||
}
|
||||
|
||||
impl<F: Field> ProverCircuitData<F> {
|
||||
pub fn prove2(&self) -> Proof2<F> {
|
||||
prove2(&self.prover_only, &self.common)
|
||||
}
|
||||
}
|
||||
|
||||
/// Circuit data required by the prover.
|
||||
pub struct VerifierCircuitData<F: Field> {
|
||||
verifier_only: VerifierOnlyCircuitData,
|
||||
common: CommonCircuitData<F>,
|
||||
}
|
||||
|
||||
impl<F: Field> VerifierCircuitData<F> {
|
||||
pub fn verify2(&self) {
|
||||
verify2(&self.verifier_only, &self.common)
|
||||
}
|
||||
}
|
||||
|
||||
/// Circuit data required by the prover, but not the verifier.
|
||||
pub(crate) struct ProverOnlyCircuitData<F: Field> {
|
||||
/// A precomputation used for FFTs of degree 8n, where n is the number of gates.
|
||||
pub fft_precomputation_8n: FftPrecomputation<F>,
|
||||
}
|
||||
|
||||
/// 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<F: Field> {
|
||||
pub config: CircuitConfig,
|
||||
|
||||
pub degree: usize,
|
||||
|
||||
/// A commitment to each constant polynomial.
|
||||
pub constants_root: Hash<F>,
|
||||
|
||||
/// A commitment to each permutation polynomial.
|
||||
pub sigmas_root: Hash<F>,
|
||||
|
||||
/// A precomputation used for FFTs of degree n, where n is the number of gates.
|
||||
pub fft_precomputation_n: FftPrecomputation<F>,
|
||||
}
|
||||
420
src/constraint_polynomial.rs
Normal file
420
src/constraint_polynomial.rs
Normal file
@ -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<F: Field>(ConstraintPolynomialRef<F>);
|
||||
|
||||
impl<F: Field> ConstraintPolynomial<F> {
|
||||
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<Wire> {
|
||||
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<usize> {
|
||||
self.dependencies(0)
|
||||
.into_iter()
|
||||
.map(|wire| wire.input)
|
||||
.max()
|
||||
}
|
||||
|
||||
pub(crate) fn max_constant_index(&self) -> Option<usize> {
|
||||
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>) -> 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<F>],
|
||||
vars: EvaluationVars<F>,
|
||||
) -> Vec<F> {
|
||||
let mut mem = HashMap::new();
|
||||
polynomials.iter()
|
||||
.map(|p| p.0.evaluate_memoized(&vars, &mut mem))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn from_inner(inner: ConstraintPolynomialInner<F>) -> Self {
|
||||
Self(ConstraintPolynomialRef::new(inner))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Neg for ConstraintPolynomial<F> {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> Self {
|
||||
// TODO: Faster to have a dedicated ConstraintPolynomialInner::Negation?
|
||||
self * ConstraintPolynomial::constant(F::NEG_ONE)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Neg for &ConstraintPolynomial<F> {
|
||||
type Output = ConstraintPolynomial<F>;
|
||||
|
||||
fn neg(self) -> ConstraintPolynomial<F> {
|
||||
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<F>`.
|
||||
///
|
||||
/// 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<F: Field> $trait<Self> for ConstraintPolynomial<F> {
|
||||
type Output = Self;
|
||||
|
||||
fn $method(self, rhs: Self) -> Self {
|
||||
ConstraintPolynomial::$method(&self, &rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> $trait<&Self> for ConstraintPolynomial<F> {
|
||||
type Output = Self;
|
||||
|
||||
fn $method(self, rhs: &Self) -> Self {
|
||||
ConstraintPolynomial::$method(&self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> $trait<ConstraintPolynomial<F>> for &ConstraintPolynomial<F> {
|
||||
type Output = ConstraintPolynomial<F>;
|
||||
|
||||
fn $method(self, rhs: ConstraintPolynomial<F>) -> Self::Output {
|
||||
ConstraintPolynomial::$method(self, &rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> $trait for &ConstraintPolynomial<F> {
|
||||
type Output = ConstraintPolynomial<F>;
|
||||
|
||||
fn $method(self, rhs: Self) -> Self::Output {
|
||||
ConstraintPolynomial::$method(self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> $trait<F> for ConstraintPolynomial<F> {
|
||||
type Output = Self;
|
||||
|
||||
fn $method(self, rhs: F) -> Self {
|
||||
ConstraintPolynomial::$method(&self, &ConstraintPolynomial::constant(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> $trait<F> for &ConstraintPolynomial<F> {
|
||||
type Output = ConstraintPolynomial<F>;
|
||||
|
||||
fn $method(self, rhs: F) -> Self::Output {
|
||||
ConstraintPolynomial::$method(self, &ConstraintPolynomial::constant(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> $trait<usize> for ConstraintPolynomial<F> {
|
||||
type Output = Self;
|
||||
|
||||
fn $method(self, rhs: usize) -> Self {
|
||||
ConstraintPolynomial::$method(&self, &ConstraintPolynomial::constant_usize(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> $trait<usize> for &ConstraintPolynomial<F> {
|
||||
type Output = ConstraintPolynomial<F>;
|
||||
|
||||
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<F: Field> Sum for ConstraintPolynomial<F> {
|
||||
fn sum<I: Iterator<Item=Self>>(iter: I) -> Self {
|
||||
iter.fold(
|
||||
ConstraintPolynomial::zero(),
|
||||
|sum, x| sum + x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Product for ConstraintPolynomial<F> {
|
||||
fn product<I: Iterator<Item=Self>>(iter: I) -> Self {
|
||||
iter.fold(
|
||||
ConstraintPolynomial::one(),
|
||||
|product, x| product * x)
|
||||
}
|
||||
}
|
||||
|
||||
enum ConstraintPolynomialInner<F: Field> {
|
||||
Constant(F),
|
||||
|
||||
LocalConstant(usize),
|
||||
NextConstant(usize),
|
||||
LocalWireValue(usize),
|
||||
NextWireValue(usize),
|
||||
|
||||
Sum {
|
||||
lhs: ConstraintPolynomialRef<F>,
|
||||
rhs: ConstraintPolynomialRef<F>,
|
||||
},
|
||||
Product {
|
||||
lhs: ConstraintPolynomialRef<F>,
|
||||
rhs: ConstraintPolynomialRef<F>,
|
||||
},
|
||||
Exponentiation {
|
||||
base: ConstraintPolynomialRef<F>,
|
||||
exponent: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl<F: Field> ConstraintPolynomialInner<F> {
|
||||
fn add_dependencies(&self, gate: usize, deps: &mut HashSet<Wire>) {
|
||||
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<usize>) {
|
||||
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<F>,
|
||||
mem: &mut HashMap<ConstraintPolynomialRef<F>, 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<ConstraintPolynomialRef>`, 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<F: Field>(Rc<ConstraintPolynomialInner<F>>);
|
||||
|
||||
impl<F: Field> ConstraintPolynomialRef<F> {
|
||||
fn new(inner: ConstraintPolynomialInner<F>) -> Self {
|
||||
Self(Rc::new(inner))
|
||||
}
|
||||
|
||||
fn evaluate_memoized(
|
||||
&self,
|
||||
vars: &EvaluationVars<F>,
|
||||
mem: &mut HashMap<Self, F>,
|
||||
) -> F {
|
||||
if let Some(&result) = mem.get(self) {
|
||||
result
|
||||
} else {
|
||||
let result = self.0.evaluate(vars, mem);
|
||||
mem.insert(self.clone(), result);
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> PartialEq for ConstraintPolynomialRef<F> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
ptr::eq(&*self.0, &*other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Eq for ConstraintPolynomialRef<F> {}
|
||||
|
||||
impl<F: Field> Hash for ConstraintPolynomialRef<F> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
ptr::hash(&*self.0, state);
|
||||
}
|
||||
}
|
||||
185
src/field/crandall_field.rs
Normal file
185
src/field/crandall_field.rs
Normal file
@ -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<Self> {
|
||||
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 {
|
||||
}
|
||||
199
src/field/fft.rs
Normal file
199
src/field/fft.rs
Normal file
@ -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<T: Copy>(arr: Vec<T>) -> Vec<T> {
|
||||
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<F: Field> {
|
||||
/// 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<Vec<F>>,
|
||||
}
|
||||
|
||||
impl<F: Field> FftPrecomputation<F> {
|
||||
pub fn size(&self) -> usize {
|
||||
self.subgroups_rev.last().unwrap().len()
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn fft<F: Field>(coefficients: &[F]) -> Vec<F> {
|
||||
// let precomputation = fft_precompute(coefficients.len());
|
||||
// fft_with_precomputation(coefficients, &precomputation)
|
||||
// }
|
||||
|
||||
pub fn fft_precompute<F: Field>(degree: usize) -> FftPrecomputation<F> {
|
||||
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<F: Field>(
|
||||
points: &[F],
|
||||
precomputation: &FftPrecomputation<F>,
|
||||
) -> Vec<F> {
|
||||
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<F: Field>(
|
||||
coefficients: Vec<F>,
|
||||
precomputation: &FftPrecomputation<F>,
|
||||
) -> Vec<F> {
|
||||
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<CrandallField> {
|
||||
// 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<CrandallField> {
|
||||
// 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
|
||||
// }
|
||||
// }
|
||||
62
src/field/field.rs
Normal file
62
src/field/field.rs
Normal file
@ -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<Output=Self>
|
||||
+ Add<Self, Output=Self>
|
||||
+ AddAssign<Self>
|
||||
+ Sub<Self, Output=Self>
|
||||
+ SubAssign<Self>
|
||||
+ Mul<Self, Output=Self>
|
||||
+ MulAssign<Self>
|
||||
+ Div<Self, Output=Self>
|
||||
+ DivAssign<Self>
|
||||
+ 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<Self>;
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
57
src/field/field_search.rs
Normal file
57
src/field/field_search.rs
Normal file
@ -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;
|
||||
}
|
||||
4
src/field/mod.rs
Normal file
4
src/field/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub(crate) mod crandall_field;
|
||||
pub(crate) mod field;
|
||||
pub(crate) mod field_search;
|
||||
pub(crate) mod fft;
|
||||
146
src/gates/deterministic_gate.rs
Normal file
146
src/gates/deterministic_gate.rs
Normal file
@ -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<F: Field>: '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<F>)>;
|
||||
|
||||
/// Any additional constraints to be enforced, besides the (automatically provided) ones that
|
||||
/// constraint output values.
|
||||
fn additional_constraints(&self, _config: CircuitConfig) -> Vec<ConstraintPolynomial<F>> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
/// Any additional generators, besides the (automatically provided) ones that generate output
|
||||
/// values.
|
||||
fn additional_generators(
|
||||
&self,
|
||||
_config: CircuitConfig,
|
||||
_gate_index: usize,
|
||||
) -> Vec<Box<dyn WitnessGenerator2<F>>> {
|
||||
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<F: Field, DG: DeterministicGate<F> + ?Sized> {
|
||||
gate: Box<DG>,
|
||||
_phantom: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: Field, DG: DeterministicGate<F>> DeterministicGateAdapter<F, DG> {
|
||||
pub fn new(gate: DG) -> Self {
|
||||
Self { gate: Box::new(gate), _phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field, DG: DeterministicGate<F>> Gate2<F> for DeterministicGateAdapter<F, DG> {
|
||||
fn id(&self) -> String {
|
||||
self.gate.id()
|
||||
}
|
||||
|
||||
fn constraints(&self, config: CircuitConfig) -> Vec<ConstraintPolynomial<F>> {
|
||||
// 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<F>,
|
||||
next_constants: Vec<F>,
|
||||
) -> Vec<Box<dyn WitnessGenerator2<F>>> {
|
||||
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<F>`, rather
|
||||
// than a boxed `OutputGenerator<F>`.
|
||||
let b: Box::<dyn WitnessGenerator2<F>> = Box::new(og);
|
||||
b
|
||||
})
|
||||
.chain(self.gate.additional_generators(config, gate_index))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
struct OutputGenerator<F: Field> {
|
||||
gate_index: usize,
|
||||
input_index: usize,
|
||||
out: ConstraintPolynomial<F>,
|
||||
local_constants: Vec<F>,
|
||||
next_constants: Vec<F>,
|
||||
}
|
||||
|
||||
impl<F: Field> SimpleGenerator<F> for OutputGenerator<F> {
|
||||
fn dependencies(&self) -> Vec<Target2> {
|
||||
self.out.dependencies(self.gate_index)
|
||||
.into_iter()
|
||||
.map(Target2::Wire)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn run_once(&mut self, witness: &PartialWitness2<F>) -> PartialWitness2<F> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
81
src/gates/gate.rs
Normal file
81
src/gates/gate.rs
Normal file
@ -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<F: Field>: 'static {
|
||||
fn id(&self) -> String;
|
||||
|
||||
/// A set of expressions which must evaluate to zero.
|
||||
fn constraints(&self, config: CircuitConfig) -> Vec<ConstraintPolynomial<F>>;
|
||||
|
||||
fn generators(
|
||||
&self,
|
||||
config: CircuitConfig,
|
||||
gate_index: usize,
|
||||
local_constants: Vec<F>,
|
||||
next_constants: Vec<F>,
|
||||
) -> Vec<Box<dyn WitnessGenerator2<F>>>;
|
||||
|
||||
/// 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<Gate>` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs.
|
||||
#[derive(Clone)]
|
||||
pub struct GateRef<F: Field>(pub(crate) Rc<dyn Gate2<F>>);
|
||||
|
||||
impl<F: Field> GateRef<F> {
|
||||
pub fn new<G: Gate2<F>>(gate: G) -> GateRef<F> {
|
||||
GateRef(Rc::new(gate))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> PartialEq for GateRef<F> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.id() == other.0.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Hash for GateRef<F> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.id().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Eq for GateRef<F> {}
|
||||
|
||||
/// A gate along with any constants used to configure it.
|
||||
pub struct GateInstance<F: Field> {
|
||||
pub gate_type: GateRef<F>,
|
||||
pub constants: Vec<F>,
|
||||
}
|
||||
29
src/gates/gmimc.rs
Normal file
29
src/gates/gmimc.rs
Normal file
@ -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<F: Field> {
|
||||
num_rounds: usize,
|
||||
width: usize,
|
||||
round_constants: Vec<F>,
|
||||
}
|
||||
|
||||
impl<F: Field> GMiMCGate<F> {
|
||||
fn new(width: usize) -> GateRef<F> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> DeterministicGate<F> for GMiMCGate<F> {
|
||||
fn id(&self) -> String {
|
||||
format!("{:?}", self)
|
||||
}
|
||||
|
||||
fn outputs(&self, config: CircuitConfig) -> Vec<(usize, ConstraintPolynomial<F>)> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
3
src/gates/mod.rs
Normal file
3
src/gates/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub(crate) mod deterministic_gate;
|
||||
pub(crate) mod gate;
|
||||
pub(crate) mod gmimc;
|
||||
54
src/generator.rs
Normal file
54
src/generator.rs
Normal file
@ -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<F: Field>: '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<Target2>;
|
||||
|
||||
/// 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<F>) -> (PartialWitness2<F>, bool);
|
||||
}
|
||||
|
||||
/// A generator which runs once after a list of dependencies is present in the witness.
|
||||
pub trait SimpleGenerator<F: Field>: 'static {
|
||||
fn dependencies(&self) -> Vec<Target2>;
|
||||
|
||||
fn run_once(&mut self, witness: &PartialWitness2<F>) -> PartialWitness2<F>;
|
||||
}
|
||||
|
||||
impl<F: Field, SG: SimpleGenerator<F>> WitnessGenerator2<F> for SG {
|
||||
fn watch_list(&self) -> Vec<Target2> {
|
||||
self.dependencies()
|
||||
}
|
||||
|
||||
fn run(&mut self, witness: &PartialWitness2<F>) -> (PartialWitness2<F>, 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<F: Field> SimpleGenerator<F> for CopyGenerator {
|
||||
fn dependencies(&self) -> Vec<Target2> {
|
||||
vec![self.src]
|
||||
}
|
||||
|
||||
fn run_once(&mut self, witness: &PartialWitness2<F>) -> PartialWitness2<F> {
|
||||
let value = witness.get_target(self.src);
|
||||
PartialWitness2::singleton(self.dst, value)
|
||||
}
|
||||
}
|
||||
78
src/gmimc.rs
Normal file
78
src/gmimc.rs
Normal file
@ -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<F: Field>(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<F: Field>(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<F: Field>(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);
|
||||
}
|
||||
}
|
||||
92
src/main.rs
Normal file
92
src/main.rs
Normal file
@ -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::<CrandallField>();
|
||||
|
||||
let overall_duration = overall_start.elapsed();
|
||||
println!("Overall time: {:?}", overall_duration);
|
||||
|
||||
// field_search()
|
||||
}
|
||||
|
||||
fn bench_gmimc<F: Field>() {
|
||||
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::<Vec<_>>();
|
||||
|
||||
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());
|
||||
}
|
||||
58
src/proof.rs
Normal file
58
src/proof.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use crate::field::field::Field;
|
||||
use crate::target::Target2;
|
||||
|
||||
pub struct Hash<F: Field> {
|
||||
elements: Vec<F>,
|
||||
}
|
||||
|
||||
pub struct HashTarget {
|
||||
elements: Vec<Target2>,
|
||||
}
|
||||
|
||||
pub struct Proof2<F: Field> {
|
||||
/// Merkle root of LDEs of wire values.
|
||||
pub wires_root: Hash<F>,
|
||||
/// Merkle root of LDEs of Z, in the context of Plonk's permutation argument.
|
||||
pub plonk_z_root: Hash<F>,
|
||||
/// Merkle root of LDEs of the quotient polynomial components.
|
||||
pub plonk_t_root: Hash<F>,
|
||||
|
||||
/// Purported values of each polynomial at each challenge point.
|
||||
pub openings: Vec<OpeningSet<F>>,
|
||||
|
||||
// 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<OpeningSetTarget>,
|
||||
|
||||
// TODO: FRI Merkle proofs.
|
||||
}
|
||||
|
||||
/// The purported values of each polynomial at a single point.
|
||||
pub struct OpeningSet<F: Field> {
|
||||
pub constants: Vec<F>,
|
||||
pub plonk_sigmas: Vec<F>,
|
||||
pub wires: Vec<F>,
|
||||
// TODO: One or multiple?
|
||||
pub plonk_z: Vec<F>,
|
||||
pub plonk_t: Vec<F>,
|
||||
}
|
||||
|
||||
/// The purported values of each polynomial at a single point.
|
||||
pub struct OpeningSetTarget {
|
||||
pub constants: Vec<Target2>,
|
||||
pub plonk_sigmas: Vec<Target2>,
|
||||
pub wires: Vec<Target2>,
|
||||
// TODO: One or multiple?
|
||||
pub plonk_z: Vec<Target2>,
|
||||
pub plonk_t: Vec<Target2>,
|
||||
}
|
||||
10
src/prover.rs
Normal file
10
src/prover.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData};
|
||||
use crate::field::field::Field;
|
||||
use crate::proof::Proof2;
|
||||
|
||||
pub(crate) fn prove2<F: Field>(
|
||||
prover_data: &ProverOnlyCircuitData<F>,
|
||||
common_data: &CommonCircuitData<F>,
|
||||
) -> Proof2<F> {
|
||||
todo!()
|
||||
}
|
||||
131
src/rescue.rs
Normal file
131
src/rescue.rs
Normal file
@ -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<F: Field>(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<F: Field>(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<F: Field>(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<F: Field>(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<F: Field>(x: F) -> F {
|
||||
// x^5
|
||||
let x2 = x.sq();
|
||||
let x3 = x2 * x;
|
||||
x2 * x3
|
||||
}
|
||||
|
||||
// #[inline(always)]
|
||||
#[unroll_for_loops]
|
||||
fn mds_layer<F: Field>(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<F: Field>(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
|
||||
}
|
||||
28
src/target.rs
Normal file
28
src/target.rs
Normal file
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/util.rs
Normal file
31
src/util.rs
Normal file
@ -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<T: Clone>(matrix: &[Vec<T>]) -> Vec<Vec<T>> {
|
||||
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
|
||||
}
|
||||
9
src/verifier.rs
Normal file
9
src/verifier.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use crate::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData};
|
||||
use crate::field::field::Field;
|
||||
|
||||
pub(crate) fn verify2<F: Field>(
|
||||
verifier_data: &VerifierOnlyCircuitData,
|
||||
common_data: &CommonCircuitData<F>,
|
||||
) {
|
||||
todo!()
|
||||
}
|
||||
16
src/wire.rs
Normal file
16
src/wire.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
56
src/witness.rs
Normal file
56
src/witness.rs
Normal file
@ -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<F: Field> {
|
||||
target_values: HashMap<Target2, F>,
|
||||
}
|
||||
|
||||
impl<F: Field> PartialWitness2<F> {
|
||||
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<F> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user