mirror of
https://github.com/saymrwulf/pasta_curves-source.git
synced 2026-09-04 20:03:39 +00:00
commit
7250ac8252
11 changed files with 820 additions and 83 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
.vscode
|
||||
|
|
|
|||
|
|
@ -32,6 +32,43 @@ pub trait Group: Copy + Clone + Send + Sync + 'static {
|
|||
fn group_scale(&mut self, by: &Self::Scalar);
|
||||
}
|
||||
|
||||
/// Extension trait for iterators over mutable field elements which allows those
|
||||
/// field elements to be inverted in a batch.
|
||||
pub trait BatchInvert<F: Field> {
|
||||
/// Consume this iterator and invert each field element (when nonzero),
|
||||
/// returning the inverse of all nonzero field elements.
|
||||
fn batch_invert(self) -> F;
|
||||
}
|
||||
|
||||
impl<'a, F, I> BatchInvert<F> for I
|
||||
where
|
||||
F: Field,
|
||||
I: IntoIterator<Item = &'a mut F>,
|
||||
{
|
||||
fn batch_invert(self) -> F {
|
||||
let mut acc = F::one();
|
||||
let mut iter = self.into_iter();
|
||||
let mut tmp = Vec::with_capacity(iter.size_hint().0);
|
||||
while let Some(p) = iter.next() {
|
||||
let q = *p;
|
||||
tmp.push((acc, p));
|
||||
acc = F::conditional_select(&(acc * q), &acc, q.is_zero());
|
||||
}
|
||||
acc = acc.invert().unwrap();
|
||||
let allinv = acc;
|
||||
|
||||
for (tmp, p) in tmp.into_iter().rev() {
|
||||
let skip = p.is_zero();
|
||||
|
||||
let tmp = tmp * acc;
|
||||
acc = F::conditional_select(&(acc * *p), &acc, skip);
|
||||
*p = F::conditional_select(&tmp, p, skip);
|
||||
}
|
||||
|
||||
allinv
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a 128-bit verifier challenge.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Challenge(pub(crate) u128);
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ pub trait Field:
|
|||
/// The value $t^{-1} \mod 2^S$.
|
||||
const UNROLL_S_EXPONENT: u64;
|
||||
|
||||
/// Generator of the $t-order$ multiplicative subgroup
|
||||
const DELTA: Self;
|
||||
|
||||
/// Inverse of $2$ in the field.
|
||||
const TWO_INV: Self;
|
||||
|
||||
|
|
|
|||
|
|
@ -173,6 +173,20 @@ const ROOT_OF_UNITY: Fp = Fp::from_raw([
|
|||
0x2ae45117890ee2fc,
|
||||
]);
|
||||
|
||||
/// GENERATOR^{2^s} where t * 2^s + 1 = p
|
||||
/// with t odd. In other words, this
|
||||
/// is a t root of unity.
|
||||
///
|
||||
/// `GENERATOR = 5 mod p` is a generator
|
||||
/// of the p - 1 order multiplicative
|
||||
/// subgroup.
|
||||
const DELTA: Fp = Fp::from_raw([
|
||||
0x48796f6fde98a425,
|
||||
0xa99d8b67e918805e,
|
||||
0x671383de08b5fe3c,
|
||||
0x1e9372724e80300d,
|
||||
]);
|
||||
|
||||
impl Default for Fp {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
|
|
@ -429,6 +443,7 @@ impl Field for Fp {
|
|||
0x0000000000000000,
|
||||
0x20000000,
|
||||
];
|
||||
const DELTA: Self = DELTA;
|
||||
const UNROLL_S_EXPONENT: u64 = 0x11cb54e91;
|
||||
const TWO_INV: Self = Fp::from_raw([
|
||||
0xd0a0327100000001,
|
||||
|
|
@ -640,3 +655,8 @@ fn test_inv_root_of_unity() {
|
|||
fn test_inv_2() {
|
||||
assert_eq!(Fp::TWO_INV, Fp::from(2).invert().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delta() {
|
||||
assert_eq!(Fp::DELTA, Fp::from(5).pow(&[1u64 << Fp::S, 0, 0, 0]));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,6 +173,20 @@ const ROOT_OF_UNITY: Fq = Fq::from_raw([
|
|||
0x113efc510dc03c0b,
|
||||
]);
|
||||
|
||||
/// GENERATOR^{2^s} where t * 2^s + 1 = q
|
||||
/// with t odd. In other words, this
|
||||
/// is a t root of unity.
|
||||
///
|
||||
/// `GENERATOR = 5 mod q` is a generator
|
||||
/// of the q - 1 order multiplicative
|
||||
/// subgroup.
|
||||
const DELTA: Fq = Fq::from_raw([
|
||||
0x83d2833d15f2bbf9,
|
||||
0x5127e2ce24a8e69c,
|
||||
0x4243423589e0a9b5,
|
||||
0x20daec44973be920,
|
||||
]);
|
||||
|
||||
impl Default for Fq {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
|
|
@ -444,6 +458,7 @@ impl Field for Fq {
|
|||
0x0000000000000000,
|
||||
0x10000000,
|
||||
];
|
||||
const DELTA: Self = DELTA;
|
||||
const UNROLL_S_EXPONENT: u64 = 0x344cfe85d;
|
||||
const TWO_INV: Self = Fq::from_raw([
|
||||
0xc21657ea00000001,
|
||||
|
|
@ -654,3 +669,8 @@ fn test_inv_root_of_unity() {
|
|||
fn test_inv_2() {
|
||||
assert_eq!(Fq::TWO_INV, Fq::from(2).invert().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delta() {
|
||||
assert_eq!(Fq::DELTA, Fq::from(5).pow(&[1u64 << Fq::S, 0, 0, 0]));
|
||||
}
|
||||
|
|
|
|||
88
src/plonk.rs
88
src/plonk.rs
|
|
@ -29,9 +29,14 @@ use domain::EvaluationDomain;
|
|||
#[derive(Debug)]
|
||||
pub struct SRS<C: CurveAffine> {
|
||||
domain: EvaluationDomain<C::Scalar>,
|
||||
l0: Vec<C::Scalar>,
|
||||
fixed_commitments: Vec<C>,
|
||||
fixed_polys: Vec<Vec<C::Scalar>>,
|
||||
fixed_cosets: Vec<Vec<C::Scalar>>,
|
||||
permutation_commitments: Vec<Vec<C>>,
|
||||
permutations: Vec<Vec<Vec<C::Scalar>>>,
|
||||
permutation_polys: Vec<Vec<Vec<C::Scalar>>>,
|
||||
permutation_cosets: Vec<Vec<Vec<C::Scalar>>>,
|
||||
meta: MetaCircuit<C::Scalar>,
|
||||
}
|
||||
|
||||
|
|
@ -41,6 +46,10 @@ pub struct SRS<C: CurveAffine> {
|
|||
pub struct Proof<C: CurveAffine> {
|
||||
advice_commitments: Vec<C>,
|
||||
h_commitments: Vec<C>,
|
||||
permutation_product_commitments: Vec<C>,
|
||||
permutation_product_evals: Vec<C::Scalar>,
|
||||
permutation_product_inv_evals: Vec<C::Scalar>,
|
||||
permutation_evals: Vec<Vec<C::Scalar>>,
|
||||
advice_evals: Vec<C::Scalar>,
|
||||
fixed_evals: Vec<C::Scalar>,
|
||||
h_evals: Vec<C::Scalar>,
|
||||
|
|
@ -87,6 +96,10 @@ fn test_proving() {
|
|||
use std::marker::PhantomData;
|
||||
const K: u32 = 5;
|
||||
|
||||
/// This represents an advice wire at a certain row in the MetaCircuit
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Variable(AdviceWire, usize);
|
||||
|
||||
// Initialize the polynomial commitment parameters
|
||||
let params: Params<EqAffine> = Params::new::<DummyHash<Fq>>(K);
|
||||
|
||||
|
|
@ -94,15 +107,17 @@ fn test_proving() {
|
|||
a: AdviceWire,
|
||||
b: AdviceWire,
|
||||
c: AdviceWire,
|
||||
d: AdviceWire,
|
||||
e: AdviceWire,
|
||||
|
||||
sa: FixedWire,
|
||||
sb: FixedWire,
|
||||
sc: FixedWire,
|
||||
sm: FixedWire,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Variable(AdviceWire, usize);
|
||||
perm: usize,
|
||||
perm2: usize,
|
||||
}
|
||||
|
||||
trait StandardCS<FF: Field> {
|
||||
fn raw_multiply<F>(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error>
|
||||
|
|
@ -111,6 +126,7 @@ fn test_proving() {
|
|||
fn raw_add<F>(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error>
|
||||
where
|
||||
F: FnOnce() -> Result<(FF, FF, FF), Error>;
|
||||
fn copy(&mut self, a: Variable, b: Variable) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
struct MyCircuit<F: Field> {
|
||||
|
|
@ -147,9 +163,15 @@ fn test_proving() {
|
|||
value = Some(f()?);
|
||||
Ok(value.ok_or(Error::SynthesisError)?.0)
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.d, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.0.square().square())
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.b, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.1)
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.e, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.1.square().square())
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.c, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.2)
|
||||
})?;
|
||||
|
|
@ -179,9 +201,15 @@ fn test_proving() {
|
|||
value = Some(f()?);
|
||||
Ok(value.ok_or(Error::SynthesisError)?.0)
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.d, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.0.square().square())
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.b, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.1)
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.e, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.1.square().square())
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.c, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.2)
|
||||
})?;
|
||||
|
|
@ -200,23 +228,51 @@ fn test_proving() {
|
|||
Variable(self.config.c, index),
|
||||
))
|
||||
}
|
||||
fn copy(&mut self, left: Variable, right: Variable) -> Result<(), Error> {
|
||||
let left_wire = match left.0 {
|
||||
x if x == self.config.a => 0,
|
||||
x if x == self.config.b => 1,
|
||||
x if x == self.config.c => 2,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let right_wire = match right.0 {
|
||||
x if x == self.config.a => 0,
|
||||
x if x == self.config.b => 1,
|
||||
x if x == self.config.c => 2,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
self.cs
|
||||
.copy(self.config.perm, left_wire, left.1, right_wire, right.1)?;
|
||||
self.cs
|
||||
.copy(self.config.perm2, left_wire, left.1, right_wire, right.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Circuit<F> for MyCircuit<F> {
|
||||
type Config = PLONKConfig;
|
||||
|
||||
fn configure(meta: &mut MetaCircuit<F>) -> PLONKConfig {
|
||||
let e = meta.advice_wire();
|
||||
let a = meta.advice_wire();
|
||||
let b = meta.advice_wire();
|
||||
let sf = meta.fixed_wire();
|
||||
let c = meta.advice_wire();
|
||||
let d = meta.advice_wire();
|
||||
|
||||
let perm = meta.permutation(&[a, b, c]);
|
||||
let perm2 = meta.permutation(&[a, b, c]);
|
||||
|
||||
let sm = meta.fixed_wire();
|
||||
let sa = meta.fixed_wire();
|
||||
let sb = meta.fixed_wire();
|
||||
let sc = meta.fixed_wire();
|
||||
let sm = meta.fixed_wire();
|
||||
|
||||
meta.create_gate(|meta| {
|
||||
let d = meta.query_advice(d, 1);
|
||||
let a = meta.query_advice(a, 0);
|
||||
let sf = meta.query_fixed(sf, 0);
|
||||
let e = meta.query_advice(e, -1);
|
||||
let b = meta.query_advice(b, 0);
|
||||
let c = meta.query_advice(c, 0);
|
||||
|
||||
|
|
@ -225,17 +281,21 @@ fn test_proving() {
|
|||
let sc = meta.query_fixed(sc, 0);
|
||||
let sm = meta.query_fixed(sm, 0);
|
||||
|
||||
a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one()))
|
||||
a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one())) + sf * (d * e)
|
||||
});
|
||||
|
||||
PLONKConfig {
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
e,
|
||||
sa,
|
||||
sb,
|
||||
sc,
|
||||
sm,
|
||||
perm,
|
||||
perm2,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +308,7 @@ fn test_proving() {
|
|||
|
||||
for _ in 0..10 {
|
||||
let mut a_squared = None;
|
||||
let (_, _, _) = cs.raw_multiply(|| {
|
||||
let (a0, _, c0) = cs.raw_multiply(|| {
|
||||
a_squared = self.a.map(|a| a.square());
|
||||
Ok((
|
||||
self.a.ok_or(Error::SynthesisError)?,
|
||||
|
|
@ -256,7 +316,7 @@ fn test_proving() {
|
|||
a_squared.ok_or(Error::SynthesisError)?,
|
||||
))
|
||||
})?;
|
||||
let (_, _, _) = cs.raw_add(|| {
|
||||
let (a1, b1, _) = cs.raw_add(|| {
|
||||
let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2));
|
||||
Ok((
|
||||
self.a.ok_or(Error::SynthesisError)?,
|
||||
|
|
@ -264,6 +324,8 @@ fn test_proving() {
|
|||
fin.ok_or(Error::SynthesisError)?,
|
||||
))
|
||||
})?;
|
||||
cs.copy(a0, a1)?;
|
||||
cs.copy(b1, c0)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -271,7 +333,7 @@ fn test_proving() {
|
|||
}
|
||||
|
||||
let circuit: MyCircuit<Fp> = MyCircuit {
|
||||
a: Some((-Fp::from_u64(2) + Fp::ROOT_OF_UNITY).pow(&[100, 0, 0, 0])),
|
||||
a: Some(Fp::random()),
|
||||
};
|
||||
|
||||
let empty_circuit: MyCircuit<Fp> = MyCircuit { a: None };
|
||||
|
|
@ -279,9 +341,11 @@ fn test_proving() {
|
|||
// Initialize the SRS
|
||||
let srs = SRS::generate(¶ms, &empty_circuit).expect("SRS generation should not fail");
|
||||
|
||||
// Create a proof
|
||||
let proof = Proof::create::<DummyHash<Fq>, DummyHash<Fp>, _>(¶ms, &srs, &circuit)
|
||||
.expect("proof generation should not fail");
|
||||
for _ in 0..100 {
|
||||
// Create a proof
|
||||
let proof = Proof::create::<DummyHash<Fq>, DummyHash<Fp>, _>(¶ms, &srs, &circuit)
|
||||
.expect("proof generation should not fail");
|
||||
|
||||
assert!(proof.verify::<DummyHash<Fq>, DummyHash<Fp>>(¶ms, &srs));
|
||||
assert!(proof.verify::<DummyHash<Fq>, DummyHash<Fp>>(¶ms, &srs));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use core::cmp::max;
|
||||
use core::ops::{Add, Mul};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::Error;
|
||||
use crate::arithmetic::Field;
|
||||
|
|
@ -33,7 +33,15 @@ pub trait ConstraintSystem<F: Field> {
|
|||
to: impl FnOnce() -> Result<F, Error>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
// fn copy(&mut self, left: Wire, right: Wire);
|
||||
/// Assign two advice wires to have the same value
|
||||
fn copy(
|
||||
&mut self,
|
||||
permutation: usize,
|
||||
left_wire: usize,
|
||||
left_row: usize,
|
||||
right_wire: usize,
|
||||
right_row: usize,
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// This is a trait that circuits provide implementations for so that the
|
||||
|
|
@ -147,18 +155,26 @@ pub struct PointIndex(pub usize);
|
|||
pub struct MetaCircuit<F> {
|
||||
pub(crate) num_fixed_wires: usize,
|
||||
pub(crate) num_advice_wires: usize,
|
||||
// permutations: Vec<Vec<Wire>>,
|
||||
pub(crate) gates: Vec<Polynomial<F>>,
|
||||
pub(crate) advice_queries: Vec<(AdviceWire, Rotation)>,
|
||||
pub(crate) fixed_queries: Vec<(FixedWire, Rotation)>,
|
||||
|
||||
// Mapping from a witness vector rotation to the index in the point vector.
|
||||
pub(crate) rotations: HashMap<Rotation, PointIndex>,
|
||||
pub(crate) rotations: BTreeMap<Rotation, PointIndex>,
|
||||
|
||||
// Vector of permutation arguments, where each corresponds to a set of wires
|
||||
// that are involved in a permutation argument, as well as the corresponding
|
||||
// query index for each wire. As an example, we could have a permutation
|
||||
// argument between wires (A, B, C) which allows copy constraints to be
|
||||
// enforced between advice wire values in A, B and C, and another
|
||||
// permutation between wires (B, C, D) which allows the same with D instead
|
||||
// of A.
|
||||
pub(crate) permutations: Vec<Vec<(AdviceWire, usize)>>,
|
||||
}
|
||||
|
||||
impl<F: Field> Default for MetaCircuit<F> {
|
||||
fn default() -> MetaCircuit<F> {
|
||||
let mut rotations = HashMap::new();
|
||||
let mut rotations = BTreeMap::new();
|
||||
rotations.insert(Rotation::default(), PointIndex(0));
|
||||
|
||||
MetaCircuit {
|
||||
|
|
@ -168,39 +184,79 @@ impl<F: Field> Default for MetaCircuit<F> {
|
|||
fixed_queries: Vec::new(),
|
||||
advice_queries: Vec::new(),
|
||||
rotations,
|
||||
permutations: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> MetaCircuit<F> {
|
||||
/// Query a fixed wire at a relative position
|
||||
pub fn query_fixed(&mut self, wire: FixedWire, at: i32) -> Polynomial<F> {
|
||||
/// Add a permutation argument for some advice wires
|
||||
pub fn permutation(&mut self, wires: &[AdviceWire]) -> usize {
|
||||
let index = self.permutations.len();
|
||||
if index == 0 {
|
||||
let at = Rotation(-1);
|
||||
let len = self.rotations.len();
|
||||
self.rotations.entry(at).or_insert(PointIndex(len));
|
||||
}
|
||||
let wires = wires
|
||||
.iter()
|
||||
.map(|&wire| (wire, self.query_advice_index(wire, 0)))
|
||||
.collect();
|
||||
self.permutations.push(wires);
|
||||
|
||||
index
|
||||
}
|
||||
|
||||
fn query_fixed_index(&mut self, wire: FixedWire, at: i32) -> usize {
|
||||
let at = Rotation(at);
|
||||
{
|
||||
let len = self.rotations.len();
|
||||
self.rotations.entry(at).or_insert(PointIndex(len));
|
||||
}
|
||||
|
||||
// TODO: check for existing query so we don't make redundant queries
|
||||
// Return existing query, if it exists
|
||||
for (index, fixed_query) in self.fixed_queries.iter().enumerate() {
|
||||
if fixed_query == &(wire, at) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
// Make a new query
|
||||
let index = self.fixed_queries.len();
|
||||
self.fixed_queries.push((wire, at));
|
||||
|
||||
Polynomial::Fixed(index)
|
||||
index
|
||||
}
|
||||
|
||||
/// Query a fixed wire at a relative position
|
||||
pub fn query_fixed(&mut self, wire: FixedWire, at: i32) -> Polynomial<F> {
|
||||
Polynomial::Fixed(self.query_fixed_index(wire, at))
|
||||
}
|
||||
|
||||
fn query_advice_index(&mut self, wire: AdviceWire, at: i32) -> usize {
|
||||
let at = Rotation(at);
|
||||
{
|
||||
let len = self.rotations.len();
|
||||
self.rotations.entry(at).or_insert(PointIndex(len));
|
||||
}
|
||||
|
||||
// Return existing query, if it exists
|
||||
for (index, advice_query) in self.advice_queries.iter().enumerate() {
|
||||
if advice_query == &(wire, at) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
// Make a new query
|
||||
let index = self.advice_queries.len();
|
||||
self.advice_queries.push((wire, at));
|
||||
|
||||
index
|
||||
}
|
||||
|
||||
/// Query an advice wire at a relative position
|
||||
pub fn query_advice(&mut self, wire: AdviceWire, at: i32) -> Polynomial<F> {
|
||||
let at = Rotation(at);
|
||||
{
|
||||
let len = self.rotations.len();
|
||||
self.rotations.entry(at).or_insert(PointIndex(len));
|
||||
}
|
||||
|
||||
// TODO: check for existing query so we don't make redundant queries
|
||||
let index = self.advice_queries.len();
|
||||
self.advice_queries.push((wire, at));
|
||||
|
||||
Polynomial::Advice(index)
|
||||
Polynomial::Advice(self.query_advice_index(wire, at))
|
||||
}
|
||||
|
||||
/// Create a new gate
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::arithmetic::{best_fft, parallelize, Field, Group};
|
||||
use crate::arithmetic::{best_fft, parallelize, BatchInvert, Field, Group};
|
||||
|
||||
/// Describes a relative location in the evaluation domain; applying a rotation
|
||||
/// by i will rotate the vector in the evaluation domain by i.
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct Rotation(pub i32);
|
||||
|
||||
impl Default for Rotation {
|
||||
|
|
@ -29,6 +29,7 @@ pub struct EvaluationDomain<G: Group> {
|
|||
ifft_divisor: G::Scalar,
|
||||
extended_ifft_divisor: G::Scalar,
|
||||
t_evaluations: Vec<G::Scalar>,
|
||||
barycentric_weight: G::Scalar,
|
||||
}
|
||||
|
||||
impl<G: Group> EvaluationDomain<G> {
|
||||
|
|
@ -56,24 +57,20 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
extended_omega = extended_omega.square();
|
||||
}
|
||||
let extended_omega = extended_omega; // 2^{j+k}'th root of unity
|
||||
let extended_omega_inv = extended_omega.invert().unwrap();
|
||||
let mut extended_omega_inv = extended_omega; // Inversion computed later
|
||||
|
||||
let mut omega = extended_omega;
|
||||
for _ in k..extended_k {
|
||||
omega = omega.square();
|
||||
}
|
||||
let omega = omega; // 2^{k}'th root of unity
|
||||
let omega_inv = omega.invert().unwrap();
|
||||
let mut omega_inv = omega; // Inversion computed later
|
||||
|
||||
// We use zeta here because we know it generates a coset, and it's available
|
||||
// already.
|
||||
let g_coset = G::Scalar::ZETA;
|
||||
let g_coset_inv = g_coset.square();
|
||||
|
||||
// TODO: merge these inversions together with t_evaluations batch inversion?
|
||||
let ifft_divisor = G::Scalar::from_u64(1 << k).invert().unwrap();
|
||||
let extended_ifft_divisor = G::Scalar::from_u64(1 << extended_k).invert().unwrap();
|
||||
|
||||
let mut t_evaluations = Vec::with_capacity(1 << (extended_k - k));
|
||||
{
|
||||
// Compute the evaluations of t(X) in the coset evaluation domain.
|
||||
|
|
@ -96,9 +93,26 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
}
|
||||
|
||||
// Invert, because we're dividing by this polynomial.
|
||||
G::Scalar::batch_invert(&mut t_evaluations);
|
||||
// We invert in a batch, below.
|
||||
}
|
||||
|
||||
let mut ifft_divisor = G::Scalar::from_u64(1 << k); // Inversion computed later
|
||||
let mut extended_ifft_divisor = G::Scalar::from_u64(1 << extended_k); // Inversion computed later
|
||||
|
||||
// The barycentric weight of 1 over the evaluation domain
|
||||
// 1 / \prod_{i != 0} (1 - omega^i)
|
||||
let mut barycentric_weight = G::Scalar::from(n); // Inversion computed later
|
||||
|
||||
// Compute batch inversion
|
||||
t_evaluations
|
||||
.iter_mut()
|
||||
.chain(Some(&mut ifft_divisor))
|
||||
.chain(Some(&mut extended_ifft_divisor))
|
||||
.chain(Some(&mut barycentric_weight))
|
||||
.chain(Some(&mut extended_omega_inv))
|
||||
.chain(Some(&mut omega_inv))
|
||||
.batch_invert();
|
||||
|
||||
EvaluationDomain {
|
||||
n,
|
||||
k,
|
||||
|
|
@ -113,6 +127,7 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
ifft_divisor,
|
||||
extended_ifft_divisor,
|
||||
t_evaluations,
|
||||
barycentric_weight,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,6 +260,10 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
self.omega
|
||||
}
|
||||
|
||||
pub fn get_extended_omega(&self) -> G::Scalar {
|
||||
self.extended_omega
|
||||
}
|
||||
|
||||
pub fn get_omega_inv(&self) -> G::Scalar {
|
||||
self.omega_inv
|
||||
}
|
||||
|
|
@ -260,4 +279,8 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
}
|
||||
point
|
||||
}
|
||||
|
||||
pub fn get_barycentric_weight(&self) -> G::Scalar {
|
||||
self.barycentric_weight
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use super::{
|
|||
hash_point, Error, Proof, SRS,
|
||||
};
|
||||
use crate::arithmetic::{
|
||||
eval_polynomial, get_challenge_scalar, kate_division, parallelize, Challenge, Curve,
|
||||
CurveAffine, Field,
|
||||
eval_polynomial, get_challenge_scalar, kate_division, parallelize, BatchInvert, Challenge,
|
||||
Curve, CurveAffine, Field,
|
||||
};
|
||||
use crate::polycommit::Params;
|
||||
use crate::transcript::Hasher;
|
||||
|
|
@ -53,6 +53,19 @@ impl<C: CurveAffine> Proof<C> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy(
|
||||
&mut self,
|
||||
_: usize,
|
||||
_: usize,
|
||||
_: usize,
|
||||
_: usize,
|
||||
_: usize,
|
||||
) -> Result<(), Error> {
|
||||
// We only care about advice wires here
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut meta = MetaCircuit::default();
|
||||
|
|
@ -65,17 +78,23 @@ impl<C: CurveAffine> Proof<C> {
|
|||
// Synthesize the circuit to obtain the witness and other information.
|
||||
circuit.synthesize(&mut witness, config)?;
|
||||
|
||||
let witness = witness;
|
||||
|
||||
// Create a transcript for obtaining Fiat-Shamir challenges.
|
||||
let mut transcript = HBase::init(C::Base::one());
|
||||
|
||||
// Compute commitments to advice wire polynomials
|
||||
let advice_blinds: Vec<_> = witness.advice.iter().map(|_| C::Scalar::random()).collect();
|
||||
let advice_commitments = witness
|
||||
let advice_commitments_projective: Vec<_> = witness
|
||||
.advice
|
||||
.iter()
|
||||
.zip(advice_blinds.iter())
|
||||
.map(|(poly, blind)| params.commit_lagrange(poly, *blind).to_affine())
|
||||
.map(|(poly, blind)| params.commit_lagrange(poly, *blind))
|
||||
.collect();
|
||||
let mut advice_commitments = vec![C::zero(); advice_commitments_projective.len()];
|
||||
C::Projective::batch_to_affine(&advice_commitments_projective, &mut advice_commitments);
|
||||
let advice_commitments = advice_commitments;
|
||||
drop(advice_commitments_projective);
|
||||
|
||||
for commitment in &advice_commitments {
|
||||
hash_point(&mut transcript, commitment)?;
|
||||
|
|
@ -85,6 +104,7 @@ impl<C: CurveAffine> Proof<C> {
|
|||
|
||||
let advice_polys: Vec<_> = witness
|
||||
.advice
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|poly| domain.obtain_poly(poly))
|
||||
.collect();
|
||||
|
|
@ -98,6 +118,121 @@ impl<C: CurveAffine> Proof<C> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
// Sample x_0 challenge
|
||||
let x_0: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Sample x_1 challenge
|
||||
let x_1: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Compute permutation product polynomial commitment
|
||||
let mut permutation_product_polys = vec![];
|
||||
let mut permutation_product_cosets = vec![];
|
||||
let mut permutation_product_cosets_inv = vec![];
|
||||
let mut permutation_product_commitments_projective = vec![];
|
||||
let mut permutation_product_blinds = vec![];
|
||||
|
||||
// Iterate over each permutation
|
||||
let mut permutation_modified_advice = vec![];
|
||||
for (wires, permuted_values) in srs.meta.permutations.iter().zip(srs.permutations.iter()) {
|
||||
// Goal is to compute the products of fractions
|
||||
//
|
||||
// (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) /
|
||||
// (p_j(\omega^i) + \beta s_j(\omega^i) + \gamma)
|
||||
//
|
||||
// where p_j(X) is the jth advice wire in this permutation,
|
||||
// and i is the ith row of the wire.
|
||||
let mut modified_advice = vec![C::Scalar::one(); params.n as usize];
|
||||
|
||||
// Iterate over each wire of the permutation
|
||||
for (&(wire, _), permuted_wire_values) in wires.iter().zip(permuted_values.iter()) {
|
||||
parallelize(&mut modified_advice, |modified_advice, start| {
|
||||
for ((modified_advice, advice_value), permuted_advice_value) in modified_advice
|
||||
.iter_mut()
|
||||
.zip(witness.advice[wire.0][start..].iter())
|
||||
.zip(permuted_wire_values[start..].iter())
|
||||
{
|
||||
*modified_advice *= &(x_0 * permuted_advice_value + &x_1 + advice_value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
permutation_modified_advice.push(modified_advice);
|
||||
}
|
||||
|
||||
// Batch invert to obtain the denominators for the permutation product
|
||||
// polynomials
|
||||
permutation_modified_advice
|
||||
.iter_mut()
|
||||
.flat_map(|v| v.iter_mut())
|
||||
.batch_invert();
|
||||
|
||||
for (wires, mut modified_advice) in srs
|
||||
.meta
|
||||
.permutations
|
||||
.iter()
|
||||
.zip(permutation_modified_advice.into_iter())
|
||||
{
|
||||
// Iterate over each wire again, this time finishing the computation
|
||||
// of the entire fraction by computing the numerators
|
||||
let mut deltaomega = C::Scalar::one();
|
||||
for &(wire, _) in wires.iter() {
|
||||
let omega = domain.get_omega();
|
||||
parallelize(&mut modified_advice, |modified_advice, start| {
|
||||
let mut deltaomega = deltaomega * &omega.pow_vartime(&[start as u64, 0, 0, 0]);
|
||||
for (modified_advice, advice_value) in modified_advice
|
||||
.iter_mut()
|
||||
.zip(witness.advice[wire.0][start..].iter())
|
||||
{
|
||||
// Multiply by p_j(\omega^i) + \delta^j \omega^i \beta
|
||||
*modified_advice *= &(deltaomega * &x_0 + &x_1 + advice_value);
|
||||
deltaomega *= ω
|
||||
}
|
||||
});
|
||||
deltaomega *= &C::Scalar::DELTA;
|
||||
}
|
||||
|
||||
// The modified_advice vector is a vector of products of fractions
|
||||
// of the form
|
||||
//
|
||||
// (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) /
|
||||
// (p_j(\omega^i) + \beta s_j(\omega^i) + \gamma)
|
||||
//
|
||||
// where i is the index into modified_advice, for the jth wire in
|
||||
// the permutation
|
||||
|
||||
// Compute the evaluations of the permutation product polynomial
|
||||
// over our domain, starting with z[0] = 1
|
||||
let mut z = vec![C::Scalar::one()];
|
||||
for row in 1..(params.n as usize) {
|
||||
let mut tmp = z[row - 1];
|
||||
|
||||
tmp *= &modified_advice[row];
|
||||
z.push(tmp);
|
||||
}
|
||||
|
||||
let blind = C::Scalar::random();
|
||||
|
||||
permutation_product_commitments_projective.push(params.commit_lagrange(&z, blind));
|
||||
permutation_product_blinds.push(blind);
|
||||
let z = domain.obtain_poly(z);
|
||||
permutation_product_polys.push(z.clone());
|
||||
permutation_product_cosets.push(domain.obtain_coset(z.clone(), Rotation::default()));
|
||||
permutation_product_cosets_inv.push(domain.obtain_coset(z, Rotation(-1)));
|
||||
}
|
||||
let mut permutation_product_commitments =
|
||||
vec![C::zero(); permutation_product_commitments_projective.len()];
|
||||
C::Projective::batch_to_affine(
|
||||
&permutation_product_commitments_projective,
|
||||
&mut permutation_product_commitments,
|
||||
);
|
||||
let permutation_product_commitments = permutation_product_commitments;
|
||||
drop(permutation_product_commitments_projective);
|
||||
|
||||
// Hash each permutation product commitment
|
||||
for c in &permutation_product_commitments {
|
||||
hash_point(&mut transcript, c)?;
|
||||
}
|
||||
|
||||
// Obtain challenge for keeping all separate gates linearly independent
|
||||
let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
|
|
@ -105,9 +240,11 @@ impl<C: CurveAffine> Proof<C> {
|
|||
let mut h_poly = vec![C::Scalar::zero(); domain.coset_len()];
|
||||
for (i, poly) in meta.gates.iter().enumerate() {
|
||||
if i != 0 {
|
||||
for h in h_poly.iter_mut() {
|
||||
*h *= &x_2;
|
||||
}
|
||||
parallelize(&mut h_poly, |a, _| {
|
||||
for a in a.iter_mut() {
|
||||
*a *= &x_2;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let evaluation: Vec<C::Scalar> = poly.evaluate(
|
||||
|
|
@ -144,12 +281,79 @@ impl<C: CurveAffine> Proof<C> {
|
|||
if i == 0 {
|
||||
h_poly = evaluation;
|
||||
} else {
|
||||
for (h, e) in h_poly.iter_mut().zip(evaluation.into_iter()) {
|
||||
*h += &e;
|
||||
}
|
||||
parallelize(&mut h_poly, |a, start| {
|
||||
for (a, b) in a.iter_mut().zip(evaluation[start..].iter()) {
|
||||
*a += b;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// l_0(X) * (1 - z(X)) = 0
|
||||
for coset in permutation_product_cosets.iter() {
|
||||
parallelize(&mut h_poly, |h, start| {
|
||||
for ((h, c), l0) in h
|
||||
.iter_mut()
|
||||
.zip(coset[start..].iter())
|
||||
.zip(srs.l0[start..].iter())
|
||||
{
|
||||
*h *= &x_2;
|
||||
*h += &(*l0 * &(C::Scalar::one() - c));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// z(X) \prod (p(X) + \beta s_i(X) + \gamma) - z(omega^{-1} X) \prod (p(X) + \delta^i \beta X + \gamma)
|
||||
for (permutation_index, wires) in srs.meta.permutations.iter().enumerate() {
|
||||
parallelize(&mut h_poly, |a, _| {
|
||||
for a in a.iter_mut() {
|
||||
*a *= &x_2;
|
||||
}
|
||||
});
|
||||
|
||||
let mut left = permutation_product_cosets[permutation_index].clone();
|
||||
for (advice, permutation) in wires
|
||||
.iter()
|
||||
.map(|&(_, index)| &advice_cosets[index])
|
||||
.zip(srs.permutation_cosets[permutation_index].iter())
|
||||
{
|
||||
parallelize(&mut left, |left, start| {
|
||||
for ((left, advice), permutation) in left
|
||||
.iter_mut()
|
||||
.zip(advice[start..].iter())
|
||||
.zip(permutation[start..].iter())
|
||||
{
|
||||
*left *= &(*advice + &(x_0 * permutation) + &x_1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut right = permutation_product_cosets_inv[permutation_index].clone();
|
||||
let mut current_delta = x_0 * &C::Scalar::ZETA;
|
||||
let step = domain.get_extended_omega();
|
||||
for advice in wires.iter().map(|&(_, index)| &advice_cosets[index]) {
|
||||
parallelize(&mut right, move |right, start| {
|
||||
let mut beta_term = current_delta * &step.pow_vartime(&[start as u64, 0, 0, 0]);
|
||||
for (right, advice) in right.iter_mut().zip(advice[start..].iter()) {
|
||||
*right *= &(*advice + &beta_term + &x_1);
|
||||
beta_term *= &step;
|
||||
}
|
||||
});
|
||||
current_delta *= &C::Scalar::DELTA;
|
||||
}
|
||||
|
||||
parallelize(&mut h_poly, |a, start| {
|
||||
for ((h, left), right) in a
|
||||
.iter_mut()
|
||||
.zip(left[start..].iter())
|
||||
.zip(right[start..].iter())
|
||||
{
|
||||
*h += &left;
|
||||
*h -= &right;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Divide by t(X) = X^{params.n} - 1.
|
||||
let h_poly = domain.divide_by_vanishing_poly(h_poly);
|
||||
|
||||
|
|
@ -165,11 +369,15 @@ impl<C: CurveAffine> Proof<C> {
|
|||
let h_blinds: Vec<_> = h_pieces.iter().map(|_| C::Scalar::random()).collect();
|
||||
|
||||
// Compute commitments to each h(X) piece
|
||||
let h_commitments: Vec<_> = h_pieces
|
||||
let h_commitments_projective: Vec<_> = h_pieces
|
||||
.iter()
|
||||
.zip(h_blinds.iter())
|
||||
.map(|(h_piece, blind)| params.commit(&h_piece, *blind).to_affine())
|
||||
.map(|(h_piece, blind)| params.commit(&h_piece, *blind))
|
||||
.collect();
|
||||
let mut h_commitments = vec![C::zero(); h_commitments_projective.len()];
|
||||
C::Projective::batch_to_affine(&h_commitments_projective, &mut h_commitments);
|
||||
let h_commitments = h_commitments;
|
||||
drop(h_commitments_projective);
|
||||
|
||||
// Hash each h(X) piece
|
||||
for c in h_commitments.iter() {
|
||||
|
|
@ -193,6 +401,27 @@ impl<C: CurveAffine> Proof<C> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let permutation_product_evals: Vec<C::Scalar> = permutation_product_polys
|
||||
.iter()
|
||||
.map(|poly| eval_polynomial(poly, x_3))
|
||||
.collect();
|
||||
|
||||
let permutation_product_inv_evals: Vec<C::Scalar> = permutation_product_polys
|
||||
.iter()
|
||||
.map(|poly| eval_polynomial(poly, domain.rotate_omega(x_3, Rotation(-1))))
|
||||
.collect();
|
||||
|
||||
let permutation_evals: Vec<Vec<C::Scalar>> = srs
|
||||
.permutation_polys
|
||||
.iter()
|
||||
.map(|polys| {
|
||||
polys
|
||||
.iter()
|
||||
.map(|poly| eval_polynomial(poly, x_3))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let h_evals: Vec<_> = h_pieces
|
||||
.iter()
|
||||
.map(|poly| eval_polynomial(poly, x_3))
|
||||
|
|
@ -203,17 +432,14 @@ impl<C: CurveAffine> Proof<C> {
|
|||
let mut transcript_scalar = HScalar::init(C::Scalar::one());
|
||||
|
||||
// Hash each advice evaluation
|
||||
for eval in advice_evals.iter() {
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
// Hash each fixed evaluation
|
||||
for eval in fixed_evals.iter() {
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
// Hash each h(x) piece evaluation
|
||||
for eval in h_evals.iter() {
|
||||
for eval in advice_evals
|
||||
.iter()
|
||||
.chain(fixed_evals.iter())
|
||||
.chain(h_evals.iter())
|
||||
.chain(permutation_product_evals.iter())
|
||||
.chain(permutation_product_inv_evals.iter())
|
||||
.chain(permutation_evals.iter().flat_map(|evals| evals.iter()))
|
||||
{
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
|
|
@ -281,6 +507,38 @@ impl<C: CurveAffine> Proof<C> {
|
|||
{
|
||||
accumulate(current_index, &h_poly, *h_blind, *h_eval);
|
||||
}
|
||||
|
||||
// Handle permutation arguments, if any exist
|
||||
if !srs.meta.permutations.is_empty() {
|
||||
// Open permutation product commitments at x_3
|
||||
for ((poly, blind), eval) in permutation_product_polys
|
||||
.iter()
|
||||
.zip(permutation_product_blinds.iter())
|
||||
.zip(permutation_product_evals.iter())
|
||||
{
|
||||
accumulate(current_index, poly, *blind, *eval);
|
||||
}
|
||||
|
||||
// Open permutation polynomial commitments at x_3
|
||||
for (poly, eval) in srs
|
||||
.permutation_polys
|
||||
.iter()
|
||||
.zip(permutation_evals.iter())
|
||||
.flat_map(|(polys, evals)| polys.iter().zip(evals.iter()))
|
||||
{
|
||||
accumulate(current_index, poly, C::Scalar::one(), *eval);
|
||||
}
|
||||
|
||||
let current_index = (*srs.meta.rotations.get(&Rotation(-1)).unwrap()).0;
|
||||
// Open permutation product commitments at \omega^{-1} x_3
|
||||
for ((poly, blind), eval) in permutation_product_polys
|
||||
.iter()
|
||||
.zip(permutation_product_blinds.iter())
|
||||
.zip(permutation_product_inv_evals.iter())
|
||||
{
|
||||
accumulate(current_index, poly, *blind, *eval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
|
@ -314,13 +572,11 @@ impl<C: CurveAffine> Proof<C> {
|
|||
|
||||
let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut q_evals = vec![];
|
||||
let mut q_evals = vec![C::Scalar::zero(); meta.rotations.len()];
|
||||
|
||||
for (_, &point_index) in meta.rotations.iter() {
|
||||
q_evals.push(eval_polynomial(
|
||||
&q_polys[point_index.0].as_ref().unwrap(),
|
||||
x_6,
|
||||
));
|
||||
q_evals[point_index.0] =
|
||||
eval_polynomial(&q_polys[point_index.0].as_ref().unwrap(), x_6);
|
||||
}
|
||||
|
||||
for eval in q_evals.iter() {
|
||||
|
|
@ -356,6 +612,10 @@ impl<C: CurveAffine> Proof<C> {
|
|||
Ok(Proof {
|
||||
advice_commitments,
|
||||
h_commitments,
|
||||
permutation_product_commitments,
|
||||
permutation_product_evals,
|
||||
permutation_product_inv_evals,
|
||||
permutation_evals,
|
||||
advice_evals,
|
||||
fixed_evals,
|
||||
h_evals,
|
||||
|
|
|
|||
180
src/plonk/srs.rs
180
src/plonk/srs.rs
|
|
@ -1,6 +1,6 @@
|
|||
use super::{
|
||||
circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit},
|
||||
domain::EvaluationDomain,
|
||||
domain::{EvaluationDomain, Rotation},
|
||||
Error, SRS,
|
||||
};
|
||||
use crate::arithmetic::{Curve, CurveAffine, Field};
|
||||
|
|
@ -15,6 +15,9 @@ impl<C: CurveAffine> SRS<C> {
|
|||
) -> Result<Self, Error> {
|
||||
struct Assembly<F: Field> {
|
||||
fixed: Vec<Vec<F>>,
|
||||
mapping: Vec<Vec<Vec<(usize, usize)>>>,
|
||||
aux: Vec<Vec<Vec<(usize, usize)>>>,
|
||||
sizes: Vec<Vec<Vec<usize>>>,
|
||||
}
|
||||
|
||||
impl<F: Field> ConstraintSystem<F> for Assembly<F> {
|
||||
|
|
@ -42,31 +45,180 @@ impl<C: CurveAffine> SRS<C> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy(
|
||||
&mut self,
|
||||
permutation: usize,
|
||||
left_wire: usize,
|
||||
left_row: usize,
|
||||
right_wire: usize,
|
||||
right_row: usize,
|
||||
) -> Result<(), Error> {
|
||||
// Check bounds first
|
||||
if permutation >= self.mapping.len()
|
||||
|| left_wire >= self.mapping[permutation].len()
|
||||
|| left_row >= self.mapping[permutation][left_wire].len()
|
||||
|| right_wire >= self.mapping[permutation].len()
|
||||
|| right_row >= self.mapping[permutation][right_wire].len()
|
||||
{
|
||||
return Err(Error::BoundsFailure);
|
||||
}
|
||||
|
||||
let mut left_cycle = self.aux[permutation][left_wire][left_row];
|
||||
let mut right_cycle = self.aux[permutation][right_wire][right_row];
|
||||
|
||||
if left_cycle == right_cycle {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.sizes[permutation][left_cycle.0][left_cycle.1]
|
||||
< self.sizes[permutation][right_cycle.0][right_cycle.1]
|
||||
{
|
||||
std::mem::swap(&mut left_cycle, &mut right_cycle);
|
||||
}
|
||||
|
||||
self.sizes[permutation][left_cycle.0][left_cycle.1] +=
|
||||
self.sizes[permutation][right_cycle.0][right_cycle.1];
|
||||
let mut i = right_cycle;
|
||||
loop {
|
||||
self.aux[permutation][i.0][i.1] = left_cycle;
|
||||
i = self.mapping[permutation][i.0][i.1];
|
||||
if i == right_cycle {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let tmp = self.mapping[permutation][left_wire][left_row];
|
||||
self.mapping[permutation][left_wire][left_row] =
|
||||
self.mapping[permutation][right_wire][right_row];
|
||||
self.mapping[permutation][right_wire][right_row] = tmp;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut meta = MetaCircuit::default();
|
||||
let config = ConcreteCircuit::configure(&mut meta);
|
||||
|
||||
// Get the largest permutation argument length in terms of the number of
|
||||
// advice wires involved.
|
||||
let mut largest_permutation_length = 0;
|
||||
for permutation in &meta.permutations {
|
||||
largest_permutation_length =
|
||||
std::cmp::max(permutation.len(), largest_permutation_length);
|
||||
}
|
||||
|
||||
// The permutation argument will serve alongside the gates, so must be
|
||||
// accounted for.
|
||||
let mut degree = largest_permutation_length + 1;
|
||||
|
||||
// Account for each gate to ensure our quotient polynomial is the
|
||||
// correct degree and that our extended domain is the right size.
|
||||
for poly in meta.gates.iter() {
|
||||
degree = std::cmp::max(degree, poly.degree());
|
||||
}
|
||||
|
||||
let domain = EvaluationDomain::new(degree as u32, params.k);
|
||||
|
||||
// Compute [omega^0, omega^1, ..., omega^{params.n - 1}]
|
||||
let mut omega_powers = Vec::with_capacity(params.n as usize);
|
||||
{
|
||||
let mut cur = C::Scalar::one();
|
||||
for _ in 0..params.n {
|
||||
omega_powers.push(cur);
|
||||
cur *= &domain.get_omega();
|
||||
}
|
||||
}
|
||||
|
||||
// Compute [omega_powers * \delta^0, omega_powers * \delta^1, ..., omega_powers * \delta^m]
|
||||
let mut deltaomega = Vec::with_capacity(largest_permutation_length);
|
||||
{
|
||||
let mut cur = C::Scalar::one();
|
||||
for _ in 0..largest_permutation_length {
|
||||
let mut omega_powers = omega_powers.clone();
|
||||
for o in &mut omega_powers {
|
||||
*o *= &cur;
|
||||
}
|
||||
|
||||
deltaomega.push(omega_powers);
|
||||
|
||||
cur *= &C::Scalar::DELTA;
|
||||
}
|
||||
}
|
||||
|
||||
let mut assembly: Assembly<C::Scalar> = Assembly {
|
||||
fixed: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_fixed_wires],
|
||||
mapping: vec![],
|
||||
aux: vec![],
|
||||
sizes: vec![],
|
||||
};
|
||||
|
||||
// Initialize the copy vector to keep track of copy constraints in all
|
||||
// the permutation arguments.
|
||||
for permutation in &meta.permutations {
|
||||
let mut wires = vec![];
|
||||
for i in 0..permutation.len() {
|
||||
// Computes [(i, 0), (i, 1), ..., (i, n - 1)]
|
||||
wires.push((0..params.n).map(|j| (i, j as usize)).collect());
|
||||
}
|
||||
assembly.mapping.push(wires.clone());
|
||||
assembly.aux.push(wires);
|
||||
assembly
|
||||
.sizes
|
||||
.push(vec![vec![1usize; params.n as usize]; permutation.len()]);
|
||||
}
|
||||
|
||||
// Synthesize the circuit to obtain SRS
|
||||
circuit.synthesize(&mut assembly, config)?;
|
||||
|
||||
// Compute permutation polynomials, convert to coset form and
|
||||
// pre-compute commitments for the SRS.
|
||||
let mut permutation_commitments = vec![];
|
||||
let mut permutations = vec![];
|
||||
let mut permutation_polys = vec![];
|
||||
let mut permutation_cosets = vec![];
|
||||
for (permutation_index, permutation) in meta.permutations.iter().enumerate() {
|
||||
let mut commitments = vec![];
|
||||
let mut inner_permutations = vec![];
|
||||
let mut polys = vec![];
|
||||
let mut cosets = vec![];
|
||||
for i in 0..permutation.len() {
|
||||
// Computes the permutation polynomial based on the permutation
|
||||
// description in the assembly.
|
||||
let permutation_poly: Vec<_> = (0..params.n as usize)
|
||||
.map(|j| {
|
||||
// assembly.copy[permutation_index] is indexed by wire
|
||||
// i, and then indexed by row j, obtaining the index of
|
||||
// the permuted value in deltaomega.
|
||||
let (permuted_i, permuted_j) = assembly.mapping[permutation_index][i][j];
|
||||
deltaomega[permuted_i][permuted_j]
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Compute commitment to permutation polynomial
|
||||
commitments.push(
|
||||
params
|
||||
.commit_lagrange(&permutation_poly, C::Scalar::one())
|
||||
.to_affine(),
|
||||
);
|
||||
// Store permutation polynomial and precompute its coset evaluation
|
||||
inner_permutations.push(permutation_poly.clone());
|
||||
let poly = domain.obtain_poly(permutation_poly);
|
||||
polys.push(poly.clone());
|
||||
cosets.push(domain.obtain_coset(poly, Rotation::default()));
|
||||
}
|
||||
permutation_commitments.push(commitments);
|
||||
permutations.push(inner_permutations);
|
||||
permutation_polys.push(polys);
|
||||
permutation_cosets.push(cosets);
|
||||
}
|
||||
|
||||
let fixed_commitments = assembly
|
||||
.fixed
|
||||
.iter()
|
||||
.map(|poly| params.commit_lagrange(poly, C::Scalar::one()).to_affine())
|
||||
.collect();
|
||||
|
||||
let mut degree = 1;
|
||||
for poly in meta.gates.iter() {
|
||||
degree = std::cmp::max(degree, poly.degree());
|
||||
}
|
||||
|
||||
let domain = EvaluationDomain::new(degree as u32, params.k);
|
||||
|
||||
let fixed_polys: Vec<_> = assembly
|
||||
.fixed
|
||||
.into_iter()
|
||||
|
|
@ -82,11 +234,23 @@ impl<C: CurveAffine> SRS<C> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
// Compute l_0(X)
|
||||
// TODO: this can be done more efficiently
|
||||
let mut l0 = vec![C::Scalar::zero(); params.n as usize];
|
||||
l0[0] = C::Scalar::one();
|
||||
let l0 = domain.obtain_poly(l0);
|
||||
let l0 = domain.obtain_coset(l0, Rotation::default());
|
||||
|
||||
Ok(SRS {
|
||||
domain,
|
||||
l0,
|
||||
fixed_commitments,
|
||||
fixed_polys,
|
||||
fixed_cosets,
|
||||
permutation_commitments,
|
||||
permutations,
|
||||
permutation_polys,
|
||||
permutation_cosets,
|
||||
meta,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,17 @@ impl<C: CurveAffine> Proof<C> {
|
|||
.expect("proof cannot contain points at infinity");
|
||||
}
|
||||
|
||||
// Sample x_0 challenge
|
||||
let x_0: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Sample x_1 challenge
|
||||
let x_1: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Hash each permutation product commitment
|
||||
for c in &self.permutation_product_commitments {
|
||||
hash_point(&mut transcript, c).expect("proof cannot contain points at infinity");
|
||||
}
|
||||
|
||||
// Sample x_2 challenge, which keeps the gates linearly independent.
|
||||
let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
|
|
@ -30,6 +41,7 @@ impl<C: CurveAffine> Proof<C> {
|
|||
// Sample x_3 challenge, which is used to ensure the circuit is
|
||||
// satisfied with high probability.
|
||||
let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
let x_3n = x_3.pow(&[params.n as u64, 0, 0, 0]);
|
||||
|
||||
// Hash together all the openings provided by the prover into a new
|
||||
// transcript on the scalar field.
|
||||
|
|
@ -40,6 +52,9 @@ impl<C: CurveAffine> Proof<C> {
|
|||
.iter()
|
||||
.chain(self.fixed_evals.iter())
|
||||
.chain(self.h_evals.iter())
|
||||
.chain(self.permutation_product_evals.iter())
|
||||
.chain(self.permutation_product_inv_evals.iter())
|
||||
.chain(self.permutation_evals.iter().flat_map(|evals| evals.iter()))
|
||||
{
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
|
@ -63,17 +78,61 @@ impl<C: CurveAffine> Proof<C> {
|
|||
|
||||
h_eval += &evaluation;
|
||||
}
|
||||
let xn = x_3.pow(&[params.n as u64, 0, 0, 0]);
|
||||
|
||||
// First element in each permutation product should be 1
|
||||
// l_0(X) * (1 - z(X)) = 0
|
||||
{
|
||||
// TODO: bubble this error up
|
||||
let denominator = (x_3 - &C::Scalar::one()).invert().unwrap();
|
||||
|
||||
for eval in self.permutation_product_evals.iter() {
|
||||
h_eval *= &x_2;
|
||||
|
||||
let mut tmp = denominator; // 1 / (x_3 - 1)
|
||||
tmp *= &(x_3n - &C::Scalar::one()); // (x_3^n - 1) / (x_3 - 1)
|
||||
tmp *= &srs.domain.get_barycentric_weight(); // l_0(x_3)
|
||||
tmp *= &(C::Scalar::one() - &eval); // l_0(X) * (1 - z(X))
|
||||
|
||||
h_eval += &tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// z(X) \prod (p(X) + \beta s_i(X) + \gamma) - z(omega^{-1} X) \prod (p(X) + \delta^i \beta X + \gamma)
|
||||
for (permutation_index, wires) in srs.meta.permutations.iter().enumerate() {
|
||||
h_eval *= &x_2;
|
||||
|
||||
let mut left = self.permutation_product_evals[permutation_index];
|
||||
for (advice_eval, permutation_eval) in wires
|
||||
.iter()
|
||||
.map(|&(_, query_index)| self.advice_evals[query_index])
|
||||
.zip(self.permutation_evals[permutation_index].iter())
|
||||
{
|
||||
left *= &(advice_eval + &(x_0 * permutation_eval) + &x_1);
|
||||
}
|
||||
|
||||
let mut right = self.permutation_product_inv_evals[permutation_index];
|
||||
let mut current_delta = x_0 * &x_3;
|
||||
for advice_eval in wires
|
||||
.iter()
|
||||
.map(|&(_, query_index)| self.advice_evals[query_index])
|
||||
{
|
||||
right *= &(advice_eval + ¤t_delta + &x_1);
|
||||
current_delta *= &C::Scalar::DELTA;
|
||||
}
|
||||
|
||||
h_eval += &left;
|
||||
h_eval -= &right;
|
||||
}
|
||||
|
||||
// Compute the expected h(x) value
|
||||
let mut expected_h_eval = C::Scalar::zero();
|
||||
let mut cur = C::Scalar::one();
|
||||
for eval in &self.h_evals {
|
||||
expected_h_eval += &(cur * eval);
|
||||
cur *= &xn;
|
||||
cur *= &x_3n;
|
||||
}
|
||||
|
||||
if h_eval != (expected_h_eval * &(xn - &C::Scalar::one())) {
|
||||
if h_eval != (expected_h_eval * &(x_3n - &C::Scalar::one())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -119,8 +178,38 @@ impl<C: CurveAffine> Proof<C> {
|
|||
}
|
||||
|
||||
let current_index = (*srs.meta.rotations.get(&Rotation::default()).unwrap()).0;
|
||||
for (h_commitment, h_eval) in self.h_commitments.iter().zip(self.h_evals.iter()) {
|
||||
accumulate(current_index, *h_commitment, *h_eval);
|
||||
for (commitment, eval) in self.h_commitments.iter().zip(self.h_evals.iter()) {
|
||||
accumulate(current_index, *commitment, *eval);
|
||||
}
|
||||
|
||||
// Handle permutation arguments, if any exist
|
||||
if !srs.meta.permutations.is_empty() {
|
||||
// Open permutation product commitments at x_3
|
||||
for (commitment, eval) in self
|
||||
.permutation_product_commitments
|
||||
.iter()
|
||||
.zip(self.permutation_product_evals.iter())
|
||||
{
|
||||
accumulate(current_index, *commitment, *eval);
|
||||
}
|
||||
// Open permutation commitments for each permutation argument at x_3
|
||||
for (commitment, eval) in srs
|
||||
.permutation_commitments
|
||||
.iter()
|
||||
.zip(self.permutation_evals.iter())
|
||||
.flat_map(|(commitments, evals)| commitments.iter().zip(evals.iter()))
|
||||
{
|
||||
accumulate(current_index, *commitment, *eval);
|
||||
}
|
||||
let current_index = (*srs.meta.rotations.get(&Rotation(-1)).unwrap()).0;
|
||||
// Open permutation product commitments at \omega^{-1} x_3
|
||||
for (commitment, eval) in self
|
||||
.permutation_product_commitments
|
||||
.iter()
|
||||
.zip(self.permutation_product_inv_evals.iter())
|
||||
{
|
||||
accumulate(current_index, *commitment, *eval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +236,7 @@ impl<C: CurveAffine> Proof<C> {
|
|||
// We can compute the expected f_eval at x_6 using the q_evals provided
|
||||
// by the prover and from x_5
|
||||
let mut f_eval = C::Scalar::zero();
|
||||
for (&row, &point_index) in srs.meta.rotations.iter() {
|
||||
for (&row, point_index) in srs.meta.rotations.iter() {
|
||||
let mut eval = self.q_evals[point_index.0];
|
||||
|
||||
let point = srs.domain.rotate_omega(x_3, row);
|
||||
|
|
|
|||
Loading…
Reference in a new issue