mirror of
https://github.com/saymrwulf/pasta_curves-source.git
synced 2026-09-04 20:03:39 +00:00
Merge pull request #2 from zcash/universal-circuits
Implementation of generalized PLONK
This commit is contained in:
commit
4c9a05ba74
7 changed files with 946 additions and 397 deletions
|
|
@ -313,6 +313,29 @@ pub fn compute_inner_product<F: Field>(a: &[F], b: &[F]) -> F {
|
|||
acc
|
||||
}
|
||||
|
||||
/// Divides polynomial `a` in `X` by `X - b` with
|
||||
/// no remainder.
|
||||
pub fn kate_division<'a, F: Field, I: IntoIterator<Item = &'a F>>(a: I, mut b: F) -> Vec<F>
|
||||
where
|
||||
I::IntoIter: DoubleEndedIterator + ExactSizeIterator,
|
||||
{
|
||||
b = -b;
|
||||
let a = a.into_iter();
|
||||
|
||||
let mut q = vec![F::zero(); a.len() - 1];
|
||||
|
||||
let mut tmp = F::zero();
|
||||
for (q, r) in q.iter_mut().rev().zip(a.rev()) {
|
||||
let mut lead_coeff = *r;
|
||||
lead_coeff.sub_assign(&tmp);
|
||||
*q = lead_coeff;
|
||||
tmp = lead_coeff;
|
||||
tmp.mul_assign(&b);
|
||||
}
|
||||
|
||||
q
|
||||
}
|
||||
|
||||
/// This simple utility function will parallelize an operation that is to be
|
||||
/// performed over a mutable slice.
|
||||
pub fn parallelize<T: Send, F: Fn(&mut [T], usize) + Send + Clone>(v: &mut [T], f: F) {
|
||||
|
|
|
|||
224
src/plonk.rs
224
src/plonk.rs
|
|
@ -23,45 +23,29 @@ pub use verifier::*;
|
|||
|
||||
use domain::EvaluationDomain;
|
||||
|
||||
// TODO: remove this
|
||||
const GATE_DEGREE: u32 = 3;
|
||||
|
||||
/// This is a structured reference string (SRS) that is (deterministically)
|
||||
/// computed from a specific circuit and parameters for the polynomial
|
||||
/// commitment scheme.
|
||||
#[derive(Debug)]
|
||||
pub struct SRS<C: CurveAffine> {
|
||||
sa: (Vec<C::Scalar>, Vec<C::Scalar>),
|
||||
sb: (Vec<C::Scalar>, Vec<C::Scalar>),
|
||||
sc: (Vec<C::Scalar>, Vec<C::Scalar>),
|
||||
sd: (Vec<C::Scalar>, Vec<C::Scalar>),
|
||||
sm: (Vec<C::Scalar>, Vec<C::Scalar>),
|
||||
sa_commitment: C,
|
||||
sb_commitment: C,
|
||||
sc_commitment: C,
|
||||
sd_commitment: C,
|
||||
sm_commitment: C,
|
||||
domain: EvaluationDomain<C::Scalar>,
|
||||
fixed_commitments: Vec<C>,
|
||||
fixed_polys: Vec<Vec<C::Scalar>>,
|
||||
fixed_cosets: Vec<Vec<C::Scalar>>,
|
||||
meta: MetaCircuit<C::Scalar>,
|
||||
}
|
||||
|
||||
/// This is an object which represents a (Turbo)PLONK proof.
|
||||
// This structure must never allow points at infinity.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Proof<C: CurveAffine> {
|
||||
a_commitment: C,
|
||||
b_commitment: C,
|
||||
c_commitment: C,
|
||||
d_commitment: C,
|
||||
advice_commitments: Vec<C>,
|
||||
h_commitments: Vec<C>,
|
||||
a_eval_x: C::Scalar,
|
||||
b_eval_x: C::Scalar,
|
||||
c_eval_x: C::Scalar,
|
||||
d_eval_x: C::Scalar,
|
||||
sa_eval_x: C::Scalar,
|
||||
sb_eval_x: C::Scalar,
|
||||
sc_eval_x: C::Scalar,
|
||||
sd_eval_x: C::Scalar,
|
||||
sm_eval_x: C::Scalar,
|
||||
h_evals_x: Vec<C::Scalar>,
|
||||
advice_evals: Vec<C::Scalar>,
|
||||
fixed_evals: Vec<C::Scalar>,
|
||||
h_evals: Vec<C::Scalar>,
|
||||
f_commitment: C,
|
||||
q_evals: Vec<C::Scalar>,
|
||||
opening: OpeningProof<C>,
|
||||
}
|
||||
|
||||
|
|
@ -77,6 +61,8 @@ pub enum Error {
|
|||
IncompatibleParams,
|
||||
/// The constraint system is not satisfied.
|
||||
ConstraintSystemFailure,
|
||||
/// Out of bounds index passed to a backend
|
||||
BoundsFailure,
|
||||
}
|
||||
|
||||
fn hash_point<C: CurveAffine, H: Hasher<C::Base>>(
|
||||
|
|
@ -98,32 +84,186 @@ fn test_proving() {
|
|||
use crate::arithmetic::{EqAffine, Field, Fp, Fq};
|
||||
use crate::polycommit::Params;
|
||||
use crate::transcript::DummyHash;
|
||||
use std::marker::PhantomData;
|
||||
const K: u32 = 5;
|
||||
|
||||
// Initialize the polynomial commitment parameters
|
||||
let params: Params<EqAffine> = Params::new::<DummyHash<Fq>>(K);
|
||||
|
||||
struct PLONKConfig {
|
||||
a: AdviceWire,
|
||||
b: AdviceWire,
|
||||
c: AdviceWire,
|
||||
|
||||
sa: FixedWire,
|
||||
sb: FixedWire,
|
||||
sc: FixedWire,
|
||||
sm: FixedWire,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Variable(AdviceWire, usize);
|
||||
|
||||
trait StandardCS<FF: Field> {
|
||||
fn raw_multiply<F>(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error>
|
||||
where
|
||||
F: FnOnce() -> Result<(FF, FF, FF), Error>;
|
||||
fn raw_add<F>(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error>
|
||||
where
|
||||
F: FnOnce() -> Result<(FF, FF, FF), Error>;
|
||||
}
|
||||
|
||||
struct MyCircuit<F: Field> {
|
||||
a: Option<F>,
|
||||
}
|
||||
|
||||
struct StandardPLONK<'a, F: Field, CS: ConstraintSystem<F> + 'a> {
|
||||
cs: &'a mut CS,
|
||||
config: PLONKConfig,
|
||||
current_gate: usize,
|
||||
_marker: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<'a, FF: Field, CS: ConstraintSystem<FF>> StandardPLONK<'a, FF, CS> {
|
||||
fn new(cs: &'a mut CS, config: PLONKConfig) -> Self {
|
||||
StandardPLONK {
|
||||
cs,
|
||||
config,
|
||||
current_gate: 0,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, FF: Field, CS: ConstraintSystem<FF>> StandardCS<FF> for StandardPLONK<'a, FF, CS> {
|
||||
fn raw_multiply<F>(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error>
|
||||
where
|
||||
F: FnOnce() -> Result<(FF, FF, FF), Error>,
|
||||
{
|
||||
let index = self.current_gate;
|
||||
self.current_gate += 1;
|
||||
let mut value = None;
|
||||
self.cs.assign_advice(self.config.a, index, || {
|
||||
value = Some(f()?);
|
||||
Ok(value.ok_or(Error::SynthesisError)?.0)
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.b, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.1)
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.c, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.2)
|
||||
})?;
|
||||
|
||||
self.cs
|
||||
.assign_fixed(self.config.sa, index, || Ok(FF::zero()))?;
|
||||
self.cs
|
||||
.assign_fixed(self.config.sb, index, || Ok(FF::zero()))?;
|
||||
self.cs
|
||||
.assign_fixed(self.config.sc, index, || Ok(FF::one()))?;
|
||||
self.cs
|
||||
.assign_fixed(self.config.sm, index, || Ok(FF::one()))?;
|
||||
Ok((
|
||||
Variable(self.config.a, index),
|
||||
Variable(self.config.b, index),
|
||||
Variable(self.config.c, index),
|
||||
))
|
||||
}
|
||||
fn raw_add<F>(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error>
|
||||
where
|
||||
F: FnOnce() -> Result<(FF, FF, FF), Error>,
|
||||
{
|
||||
let index = self.current_gate;
|
||||
self.current_gate += 1;
|
||||
let mut value = None;
|
||||
self.cs.assign_advice(self.config.a, index, || {
|
||||
value = Some(f()?);
|
||||
Ok(value.ok_or(Error::SynthesisError)?.0)
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.b, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.1)
|
||||
})?;
|
||||
self.cs.assign_advice(self.config.c, index, || {
|
||||
Ok(value.ok_or(Error::SynthesisError)?.2)
|
||||
})?;
|
||||
|
||||
self.cs
|
||||
.assign_fixed(self.config.sa, index, || Ok(FF::one()))?;
|
||||
self.cs
|
||||
.assign_fixed(self.config.sb, index, || Ok(FF::one()))?;
|
||||
self.cs
|
||||
.assign_fixed(self.config.sc, index, || Ok(FF::one()))?;
|
||||
self.cs
|
||||
.assign_fixed(self.config.sm, index, || Ok(FF::zero()))?;
|
||||
Ok((
|
||||
Variable(self.config.a, index),
|
||||
Variable(self.config.b, index),
|
||||
Variable(self.config.c, index),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Circuit<F> for MyCircuit<F> {
|
||||
fn synthesize(&self, cs: &mut impl ConstraintSystem<F>) -> Result<(), Error> {
|
||||
type Config = PLONKConfig;
|
||||
|
||||
fn configure(meta: &mut MetaCircuit<F>) -> PLONKConfig {
|
||||
let a = meta.advice_wire();
|
||||
let b = meta.advice_wire();
|
||||
let c = meta.advice_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 a = meta.query_advice(a, 0);
|
||||
let b = meta.query_advice(b, 0);
|
||||
let c = meta.query_advice(c, 0);
|
||||
|
||||
let sa = meta.query_fixed(sa, 0);
|
||||
let sb = meta.query_fixed(sb, 0);
|
||||
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()))
|
||||
});
|
||||
|
||||
PLONKConfig {
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
sa,
|
||||
sb,
|
||||
sc,
|
||||
sm,
|
||||
}
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
&self,
|
||||
cs: &mut impl ConstraintSystem<F>,
|
||||
config: PLONKConfig,
|
||||
) -> Result<(), Error> {
|
||||
let mut cs = StandardPLONK::new(cs, config);
|
||||
|
||||
for _ in 0..10 {
|
||||
let (_, _, _, _) = cs.multiply(|| {
|
||||
let a = self.a.ok_or(Error::SynthesisError)?;
|
||||
let a2 = a.square();
|
||||
Ok((a, a, a2))
|
||||
let mut a_squared = None;
|
||||
let (_, _, _) = cs.raw_multiply(|| {
|
||||
a_squared = self.a.map(|a| a.square());
|
||||
Ok((
|
||||
self.a.ok_or(Error::SynthesisError)?,
|
||||
self.a.ok_or(Error::SynthesisError)?,
|
||||
a_squared.ok_or(Error::SynthesisError)?,
|
||||
))
|
||||
})?;
|
||||
//cs.copy(a, b);
|
||||
let (_, _, _, _) = cs.add(|| {
|
||||
let a = self.a.ok_or(Error::SynthesisError)?;
|
||||
let a2 = a.square();
|
||||
let a3 = a + a2;
|
||||
Ok((a, a2, a3))
|
||||
let (_, _, _) = cs.raw_add(|| {
|
||||
let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2));
|
||||
Ok((
|
||||
self.a.ok_or(Error::SynthesisError)?,
|
||||
a_squared.ok_or(Error::SynthesisError)?,
|
||||
fin.ok_or(Error::SynthesisError)?,
|
||||
))
|
||||
})?;
|
||||
//cs.copy(a, d);
|
||||
//cs.copy(c, e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -134,8 +274,10 @@ fn test_proving() {
|
|||
a: Some((-Fp::from_u64(2) + Fp::ROOT_OF_UNITY).pow(&[100, 0, 0, 0])),
|
||||
};
|
||||
|
||||
let empty_circuit: MyCircuit<Fp> = MyCircuit { a: None };
|
||||
|
||||
// Initialize the SRS
|
||||
let srs = SRS::generate(¶ms, &circuit).expect("SRS generation should not fail");
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -1,56 +1,37 @@
|
|||
use super::Error;
|
||||
use core::cmp::max;
|
||||
use core::ops::{Add, Mul};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::Error;
|
||||
use crate::arithmetic::Field;
|
||||
|
||||
/// This represents a PLONK wire, which could be a fixed (selector) wire or an
|
||||
/// advice wire.
|
||||
#[derive(Debug)]
|
||||
pub enum Wire {
|
||||
/// A wires
|
||||
A(usize),
|
||||
/// B wires
|
||||
B(usize),
|
||||
/// C wires
|
||||
C(usize),
|
||||
/// D wires
|
||||
D(usize),
|
||||
}
|
||||
use super::domain::Rotation;
|
||||
/// This represents a wire which has a fixed (permanent) value
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct FixedWire(pub usize);
|
||||
|
||||
/// This represents a wire which has a witness-specific value
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct AdviceWire(pub usize);
|
||||
|
||||
/// This trait allows a [`Circuit`] to direct some backend to assign a witness
|
||||
/// for a constraint system.
|
||||
pub trait ConstraintSystem<F: Field> {
|
||||
/// Creates a gate.
|
||||
fn create_gate(
|
||||
/// Assign an advice wire value (witness)
|
||||
fn assign_advice(
|
||||
&mut self,
|
||||
sa: F,
|
||||
sb: F,
|
||||
sc: F,
|
||||
sd: F,
|
||||
sm: F,
|
||||
f: impl Fn() -> Result<(F, F, F, F), Error>,
|
||||
) -> Result<(Wire, Wire, Wire, Wire), Error>;
|
||||
wire: AdviceWire,
|
||||
row: usize,
|
||||
to: impl FnOnce() -> Result<F, Error>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// a * b - c = 0
|
||||
fn multiply(
|
||||
/// Assign a fixed value
|
||||
fn assign_fixed(
|
||||
&mut self,
|
||||
f: impl Fn() -> Result<(F, F, F), Error>,
|
||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
||||
self.create_gate(F::zero(), F::zero(), F::one(), F::zero(), F::one(), || {
|
||||
let (a, b, c) = f()?;
|
||||
Ok((a, b, c, F::zero()))
|
||||
})
|
||||
}
|
||||
|
||||
/// a + b - c = 0
|
||||
fn add(
|
||||
&mut self,
|
||||
f: impl Fn() -> Result<(F, F, F), Error>,
|
||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
||||
self.create_gate(F::one(), F::one(), F::one(), F::zero(), F::zero(), || {
|
||||
let (a, b, c) = f()?;
|
||||
Ok((a, b, c, F::zero()))
|
||||
})
|
||||
}
|
||||
wire: FixedWire,
|
||||
row: usize,
|
||||
to: impl FnOnce() -> Result<F, Error>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
// fn copy(&mut self, left: Wire, right: Wire);
|
||||
}
|
||||
|
|
@ -59,8 +40,186 @@ pub trait ConstraintSystem<F: Field> {
|
|||
/// backend prover can ask the circuit to synthesize using some given
|
||||
/// [`ConstraintSystem`] implementation.
|
||||
pub trait Circuit<F: Field> {
|
||||
/// This is a configuration object that stores things like wires.
|
||||
type Config;
|
||||
|
||||
/// The circuit is given an opportunity to describe the exact gate
|
||||
/// arrangement, wire arrangement, etc.
|
||||
fn configure(meta: &mut MetaCircuit<F>) -> Self::Config;
|
||||
|
||||
/// Given the provided `cs`, synthesize the circuit. The concrete type of
|
||||
/// the caller will be different depending on the context, and they may or
|
||||
/// may not expect to have a witness present.
|
||||
fn synthesize(&self, cs: &mut impl ConstraintSystem<F>) -> Result<(), Error>;
|
||||
fn synthesize(
|
||||
&self,
|
||||
cs: &mut impl ConstraintSystem<F>,
|
||||
config: Self::Config,
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Low-degree polynomial representing an identity that must hold over the committed wires.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Polynomial<F> {
|
||||
/// This is a fixed wire queried at a certain relative location
|
||||
Fixed(usize),
|
||||
/// This is an advice (witness) wire queried at a certain relative location
|
||||
Advice(usize),
|
||||
/// This is the sum of two polynomials
|
||||
Sum(Box<Polynomial<F>>, Box<Polynomial<F>>),
|
||||
/// This is the product of two polynomials
|
||||
Product(Box<Polynomial<F>>, Box<Polynomial<F>>),
|
||||
/// This is a scaled polynomial
|
||||
Scaled(Box<Polynomial<F>>, F),
|
||||
}
|
||||
|
||||
impl<F: Field> Polynomial<F> {
|
||||
/// Evaluate the polynomial using the provided closures to perform the
|
||||
/// operations.
|
||||
pub fn evaluate<T>(
|
||||
&self,
|
||||
fixed_wire: &impl Fn(usize) -> T,
|
||||
advice_wire: &impl Fn(usize) -> T,
|
||||
sum: &impl Fn(T, T) -> T,
|
||||
product: &impl Fn(T, T) -> T,
|
||||
scaled: &impl Fn(T, F) -> T,
|
||||
) -> T {
|
||||
match self {
|
||||
Polynomial::Fixed(index) => fixed_wire(*index),
|
||||
Polynomial::Advice(index) => advice_wire(*index),
|
||||
Polynomial::Sum(a, b) => {
|
||||
let a = a.evaluate(fixed_wire, advice_wire, sum, product, scaled);
|
||||
let b = b.evaluate(fixed_wire, advice_wire, sum, product, scaled);
|
||||
sum(a, b)
|
||||
}
|
||||
Polynomial::Product(a, b) => {
|
||||
let a = a.evaluate(fixed_wire, advice_wire, sum, product, scaled);
|
||||
let b = b.evaluate(fixed_wire, advice_wire, sum, product, scaled);
|
||||
product(a, b)
|
||||
}
|
||||
Polynomial::Scaled(a, f) => {
|
||||
let a = a.evaluate(fixed_wire, advice_wire, sum, product, scaled);
|
||||
scaled(a, *f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the degree of this polynomial
|
||||
pub fn degree(&self) -> usize {
|
||||
match self {
|
||||
Polynomial::Fixed(_) => 1,
|
||||
Polynomial::Advice(_) => 1,
|
||||
Polynomial::Sum(a, b) => max(a.degree(), b.degree()),
|
||||
Polynomial::Product(a, b) => a.degree() + b.degree(),
|
||||
Polynomial::Scaled(poly, _) => poly.degree(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Add for Polynomial<F> {
|
||||
type Output = Polynomial<F>;
|
||||
fn add(self, rhs: Polynomial<F>) -> Polynomial<F> {
|
||||
Polynomial::Sum(Box::new(self), Box::new(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Mul for Polynomial<F> {
|
||||
type Output = Polynomial<F>;
|
||||
fn mul(self, rhs: Polynomial<F>) -> Polynomial<F> {
|
||||
Polynomial::Product(Box::new(self), Box::new(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Mul<F> for Polynomial<F> {
|
||||
type Output = Polynomial<F>;
|
||||
fn mul(self, rhs: F) -> Polynomial<F> {
|
||||
Polynomial::Scaled(Box::new(self), rhs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an index into a vector where each entry corresponds to a distinct
|
||||
/// point that polynomials are queried at.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct PointIndex(pub usize);
|
||||
|
||||
/// This is a description of the circuit environment, such as the gate, wire and
|
||||
/// permutation arrangements.
|
||||
#[derive(Debug, Clone)]
|
||||
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>,
|
||||
}
|
||||
|
||||
impl<F: Field> Default for MetaCircuit<F> {
|
||||
fn default() -> MetaCircuit<F> {
|
||||
let mut rotations = HashMap::new();
|
||||
rotations.insert(Rotation::default(), PointIndex(0));
|
||||
|
||||
MetaCircuit {
|
||||
num_fixed_wires: 0,
|
||||
num_advice_wires: 0,
|
||||
gates: vec![],
|
||||
fixed_queries: Vec::new(),
|
||||
advice_queries: Vec::new(),
|
||||
rotations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
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.fixed_queries.len();
|
||||
self.fixed_queries.push((wire, at));
|
||||
|
||||
Polynomial::Fixed(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)
|
||||
}
|
||||
|
||||
/// Create a new gate
|
||||
pub fn create_gate(&mut self, f: impl FnOnce(&mut Self) -> Polynomial<F>) {
|
||||
let poly = f(self);
|
||||
self.gates.push(poly);
|
||||
}
|
||||
|
||||
/// Allocate a new fixed wire
|
||||
pub fn fixed_wire(&mut self) -> FixedWire {
|
||||
let tmp = FixedWire(self.num_fixed_wires);
|
||||
self.num_fixed_wires += 1;
|
||||
tmp
|
||||
}
|
||||
|
||||
/// Allocate a new advice wire
|
||||
pub fn advice_wire(&mut self) -> AdviceWire {
|
||||
let tmp = AdviceWire(self.num_advice_wires);
|
||||
self.num_advice_wires += 1;
|
||||
tmp
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
use crate::arithmetic::{best_fft, parallelize, 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)]
|
||||
pub struct Rotation(pub i32);
|
||||
|
||||
impl Default for Rotation {
|
||||
fn default() -> Rotation {
|
||||
Rotation(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// This structure contains precomputed constants and other details needed for
|
||||
/// performing operations on an evaluation domain of size $2^k$ in the context
|
||||
/// of PLONK.
|
||||
|
|
@ -8,6 +19,7 @@ pub struct EvaluationDomain<G: Group> {
|
|||
n: u64,
|
||||
k: u32,
|
||||
extended_k: u32,
|
||||
omega: G::Scalar,
|
||||
omega_inv: G::Scalar,
|
||||
extended_omega: G::Scalar,
|
||||
extended_omega_inv: G::Scalar,
|
||||
|
|
@ -91,6 +103,7 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
n,
|
||||
k,
|
||||
extended_k,
|
||||
omega,
|
||||
omega_inv,
|
||||
extended_omega,
|
||||
extended_omega_inv,
|
||||
|
|
@ -103,32 +116,46 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
}
|
||||
}
|
||||
|
||||
/// This takes us from an n-length vector into the coset evaluation domain.
|
||||
/// Also returns the polynomial.
|
||||
/// This takes us from an n-length vector into the coefficient form.
|
||||
///
|
||||
/// This function will panic if the provided vector is not the correct
|
||||
/// length.
|
||||
pub fn obtain_coset(&self, mut a: Vec<G>) -> (Vec<G>, Vec<G>) {
|
||||
pub fn obtain_poly(&self, mut a: Vec<G>) -> Vec<G> {
|
||||
assert_eq!(a.len(), 1 << self.k);
|
||||
|
||||
// Perform inverse FFT to obtain the polynomial in coefficient form
|
||||
Self::ifft(&mut a, self.omega_inv, self.k, self.ifft_divisor);
|
||||
|
||||
// Keep this polynomial around; we'll need to evaluate it at arbitrary
|
||||
// points later.
|
||||
let old = a.clone();
|
||||
a
|
||||
}
|
||||
|
||||
// Distributes powers so that an FFT will move us into the coset
|
||||
// evaluation domain.
|
||||
Self::distribute_powers(&mut a, self.g_coset);
|
||||
/// This takes us from an n-length coefficient vector into the coset
|
||||
/// evaluation domain, rotating by `rotation` if desired.
|
||||
///
|
||||
/// This function will panic if the provided vector is not the correct
|
||||
/// length.
|
||||
pub fn obtain_coset(&self, mut a: Vec<G>, rotation: Rotation) -> Vec<G> {
|
||||
assert_eq!(a.len(), 1 << self.k);
|
||||
|
||||
// Resize to account for the quotient polynomial's size
|
||||
a.resize(1 << self.extended_k, G::group_zero());
|
||||
|
||||
// Move into coset evaluation domain
|
||||
assert!(rotation.0 != i32::MIN);
|
||||
if rotation.0 == 0 {
|
||||
// In this special case, the powers of zeta repeat so we do not need
|
||||
// to compute them.
|
||||
Self::distribute_powers_zeta(&mut a, self.g_coset);
|
||||
} else {
|
||||
let mut g = G::Scalar::ZETA;
|
||||
if rotation.0 > 0 {
|
||||
g *= &self.omega.pow_vartime(&[rotation.0 as u64, 0, 0, 0]);
|
||||
} else {
|
||||
g *= &self
|
||||
.omega_inv
|
||||
.pow_vartime(&[rotation.0.abs() as u64, 0, 0, 0]);
|
||||
}
|
||||
Self::distribute_powers(&mut a, g);
|
||||
}
|
||||
a.resize(self.coset_len(), G::group_zero());
|
||||
best_fft(&mut a, self.extended_omega, self.extended_k);
|
||||
|
||||
(a, old)
|
||||
a
|
||||
}
|
||||
|
||||
/// This takes us from the coset evaluation domain and gets us the quotient
|
||||
|
|
@ -137,7 +164,7 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
/// This function will panic if the provided vector is not the correct
|
||||
/// length.
|
||||
pub fn from_coset(&self, mut a: Vec<G>) -> Vec<G> {
|
||||
assert_eq!(a.len(), 1 << self.extended_k);
|
||||
assert_eq!(a.len(), self.coset_len());
|
||||
|
||||
// Inverse FFT
|
||||
Self::ifft(
|
||||
|
|
@ -162,7 +189,7 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
/// This divides the polynomial (in the coset domain) by the vanishing
|
||||
/// polynomial.
|
||||
pub fn divide_by_vanishing_poly(&self, mut h_poly: Vec<G>) -> Vec<G> {
|
||||
assert_eq!(h_poly.len(), 1 << self.extended_k);
|
||||
assert_eq!(h_poly.len(), self.coset_len());
|
||||
|
||||
// Divide to obtain the quotient polynomial in the coset evaluation
|
||||
// domain.
|
||||
|
|
@ -176,7 +203,7 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
h_poly
|
||||
}
|
||||
|
||||
fn distribute_powers(mut a: &mut [G], g: G::Scalar) {
|
||||
fn distribute_powers_zeta(mut a: &mut [G], g: G::Scalar) {
|
||||
let coset_powers = [g, g.square()];
|
||||
parallelize(&mut a, |a, mut index| {
|
||||
for a in a {
|
||||
|
|
@ -190,6 +217,16 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
});
|
||||
}
|
||||
|
||||
fn distribute_powers(mut a: &mut [G], g: G::Scalar) {
|
||||
parallelize(&mut a, |a, index| {
|
||||
let mut cur = g.pow_vartime(&[index as u64, 0, 0, 0]);
|
||||
for a in a {
|
||||
a.group_scale(&cur);
|
||||
cur *= &g;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn ifft(a: &mut [G], omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) {
|
||||
best_fft(a, omega_inv, log_n);
|
||||
parallelize(a, |a, _| {
|
||||
|
|
@ -199,4 +236,28 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn coset_len(&self) -> usize {
|
||||
1 << self.extended_k
|
||||
}
|
||||
|
||||
pub fn get_omega(&self) -> G::Scalar {
|
||||
self.omega
|
||||
}
|
||||
|
||||
pub fn get_omega_inv(&self) -> G::Scalar {
|
||||
self.omega_inv
|
||||
}
|
||||
|
||||
pub fn rotate_omega(&self, constant: G::Scalar, rotation: Rotation) -> G::Scalar {
|
||||
let mut point = constant;
|
||||
if rotation.0 >= 0 {
|
||||
point *= &self.get_omega().pow(&[rotation.0 as u64, 0, 0, 0]);
|
||||
} else {
|
||||
point *= &self
|
||||
.get_omega_inv()
|
||||
.pow(&[rotation.0.abs() as u64, 0, 0, 0]);
|
||||
}
|
||||
point
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use super::{
|
||||
circuit::{Circuit, ConstraintSystem, Wire},
|
||||
circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit},
|
||||
domain::Rotation,
|
||||
hash_point, Error, Proof, SRS,
|
||||
};
|
||||
use crate::arithmetic::{
|
||||
eval_polynomial, get_challenge_scalar, Challenge, Curve, CurveAffine, Field,
|
||||
eval_polynomial, get_challenge_scalar, kate_division, parallelize, Challenge, Curve,
|
||||
CurveAffine, Field,
|
||||
};
|
||||
use crate::polycommit::Params;
|
||||
use crate::transcript::Hasher;
|
||||
|
|
@ -22,121 +24,130 @@ impl<C: CurveAffine> Proof<C> {
|
|||
circuit: &ConcreteCircuit,
|
||||
) -> Result<Self, Error> {
|
||||
struct WitnessCollection<F: Field> {
|
||||
a: Vec<F>,
|
||||
b: Vec<F>,
|
||||
c: Vec<F>,
|
||||
d: Vec<F>,
|
||||
sa: Vec<F>,
|
||||
sb: Vec<F>,
|
||||
sc: Vec<F>,
|
||||
sd: Vec<F>,
|
||||
sm: Vec<F>,
|
||||
advice: Vec<Vec<F>>,
|
||||
}
|
||||
|
||||
impl<F: Field> ConstraintSystem<F> for WitnessCollection<F> {
|
||||
fn create_gate(
|
||||
fn assign_advice(
|
||||
&mut self,
|
||||
sa: F,
|
||||
sb: F,
|
||||
sc: F,
|
||||
sd: F,
|
||||
sm: F,
|
||||
f: impl Fn() -> Result<(F, F, F, F), Error>,
|
||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
||||
let (a, b, c, d) = f()?;
|
||||
let tmp = Ok((
|
||||
Wire::A(self.a.len()),
|
||||
Wire::B(self.a.len()),
|
||||
Wire::C(self.a.len()),
|
||||
Wire::D(self.a.len()),
|
||||
));
|
||||
self.a.push(a);
|
||||
self.b.push(b);
|
||||
self.c.push(c);
|
||||
self.d.push(d);
|
||||
self.sa.push(sa);
|
||||
self.sb.push(sb);
|
||||
self.sc.push(sc);
|
||||
self.sd.push(sd);
|
||||
self.sm.push(sm);
|
||||
tmp
|
||||
wire: AdviceWire,
|
||||
row: usize,
|
||||
to: impl FnOnce() -> Result<F, Error>,
|
||||
) -> Result<(), Error> {
|
||||
*self
|
||||
.advice
|
||||
.get_mut(wire.0)
|
||||
.and_then(|v| v.get_mut(row))
|
||||
.ok_or(Error::BoundsFailure)? = to()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_fixed(
|
||||
&mut self,
|
||||
_: FixedWire,
|
||||
_: usize,
|
||||
_: impl FnOnce() -> Result<F, Error>,
|
||||
) -> Result<(), Error> {
|
||||
// We only care about advice wires here
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// fn copy(&mut self, left: Wire, right: Wire) {
|
||||
// unimplemented!()
|
||||
// }
|
||||
}
|
||||
|
||||
let mut meta = MetaCircuit::default();
|
||||
let config = ConcreteCircuit::configure(&mut meta);
|
||||
|
||||
let mut witness = WitnessCollection {
|
||||
a: vec![],
|
||||
b: vec![],
|
||||
c: vec![],
|
||||
d: vec![],
|
||||
sa: vec![],
|
||||
sb: vec![],
|
||||
sc: vec![],
|
||||
sd: vec![],
|
||||
sm: vec![],
|
||||
advice: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_advice_wires],
|
||||
};
|
||||
|
||||
// Synthesize the circuit to obtain the witness and other information.
|
||||
circuit.synthesize(&mut witness)?;
|
||||
circuit.synthesize(&mut witness, config)?;
|
||||
|
||||
// Create a transcript for obtaining Fiat-Shamir challenges.
|
||||
let mut transcript = HBase::init(C::Base::one());
|
||||
|
||||
if witness.a.len() > params.n as usize {
|
||||
// The polynomial commitment does not support a high enough degree
|
||||
// polynomial to commit to our wires because this circuit has too
|
||||
// many gates.
|
||||
return Err(Error::IncompatibleParams);
|
||||
// Compute commitments to advice wire polynomials
|
||||
let advice_blinds: Vec<_> = witness.advice.iter().map(|_| C::Scalar::random()).collect();
|
||||
let advice_commitments = witness
|
||||
.advice
|
||||
.iter()
|
||||
.zip(advice_blinds.iter())
|
||||
.map(|(poly, blind)| params.commit_lagrange(poly, *blind).to_affine())
|
||||
.collect();
|
||||
|
||||
for commitment in &advice_commitments {
|
||||
hash_point(&mut transcript, commitment)?;
|
||||
}
|
||||
|
||||
witness.a.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.b.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.c.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.d.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.sa.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.sb.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.sc.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.sd.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.sm.resize(params.n as usize, C::Scalar::zero());
|
||||
|
||||
// Compute commitments to the various wire values
|
||||
let a_blind = C::Scalar::one(); // TODO: not random
|
||||
let b_blind = C::Scalar::one(); // TODO: not random
|
||||
let c_blind = C::Scalar::one(); // TODO: not random
|
||||
let d_blind = C::Scalar::one(); // TODO: not random
|
||||
let a_commitment = params.commit_lagrange(&witness.a, a_blind).to_affine();
|
||||
let b_commitment = params.commit_lagrange(&witness.b, b_blind).to_affine();
|
||||
let c_commitment = params.commit_lagrange(&witness.c, c_blind).to_affine();
|
||||
let d_commitment = params.commit_lagrange(&witness.d, d_blind).to_affine();
|
||||
|
||||
hash_point(&mut transcript, &a_commitment)?;
|
||||
hash_point(&mut transcript, &b_commitment)?;
|
||||
hash_point(&mut transcript, &c_commitment)?;
|
||||
hash_point(&mut transcript, &d_commitment)?;
|
||||
|
||||
let domain = &srs.domain;
|
||||
|
||||
let (a_coset, a_poly) = domain.obtain_coset(witness.a);
|
||||
let (b_coset, b_poly) = domain.obtain_coset(witness.b);
|
||||
let (c_coset, c_poly) = domain.obtain_coset(witness.c);
|
||||
let (d_coset, d_poly) = domain.obtain_coset(witness.d);
|
||||
let advice_polys: Vec<_> = witness
|
||||
.advice
|
||||
.into_iter()
|
||||
.map(|poly| domain.obtain_poly(poly))
|
||||
.collect();
|
||||
|
||||
// (a * sa) + (b * sb) + (a * sm * b) + (d * sd) - (c * sc)
|
||||
let mut h_poly = Vec::with_capacity(a_coset.len());
|
||||
for ((((((((a, b), c), d), sa), sb), sc), sd), sm) in a_coset
|
||||
let advice_cosets: Vec<_> = meta
|
||||
.advice_queries
|
||||
.iter()
|
||||
.zip(b_coset.iter())
|
||||
.zip(c_coset.iter())
|
||||
.zip(d_coset.iter())
|
||||
.zip(srs.sa.0.iter())
|
||||
.zip(srs.sb.0.iter())
|
||||
.zip(srs.sc.0.iter())
|
||||
.zip(srs.sd.0.iter())
|
||||
.zip(srs.sm.0.iter())
|
||||
{
|
||||
h_poly.push((*a) * sa + &((*b) * sb) + &((*a) * sm * b) + &((*d) * sd) - &((*c) * sc));
|
||||
.map(|&(wire, at)| {
|
||||
let poly = advice_polys[wire.0].clone();
|
||||
domain.obtain_coset(poly, at)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Obtain challenge for keeping all separate gates linearly independent
|
||||
let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Evaluate the circuit using the custom gates provided
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
let evaluation: Vec<C::Scalar> = poly.evaluate(
|
||||
&|index| srs.fixed_cosets[index].clone(),
|
||||
&|index| advice_cosets[index].clone(),
|
||||
&|mut a, b| {
|
||||
parallelize(&mut a, |a, start| {
|
||||
for (a, b) in a.iter_mut().zip(b[start..].iter()) {
|
||||
*a += b;
|
||||
}
|
||||
});
|
||||
a
|
||||
},
|
||||
&|mut a, b| {
|
||||
parallelize(&mut a, |a, start| {
|
||||
for (a, b) in a.iter_mut().zip(b[start..].iter()) {
|
||||
*a *= b;
|
||||
}
|
||||
});
|
||||
a
|
||||
},
|
||||
&|mut a, scalar| {
|
||||
parallelize(&mut a, |a, _| {
|
||||
for a in a {
|
||||
*a *= &scalar;
|
||||
}
|
||||
});
|
||||
a
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(h_poly.len(), evaluation.len());
|
||||
|
||||
if i == 0 {
|
||||
h_poly = evaluation;
|
||||
} else {
|
||||
for (h, e) in h_poly.iter_mut().zip(evaluation.into_iter()) {
|
||||
*h += &e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divide by t(X) = X^{params.n} - 1.
|
||||
|
|
@ -151,7 +162,7 @@ impl<C: CurveAffine> Proof<C> {
|
|||
.map(|v| v.to_vec())
|
||||
.collect::<Vec<_>>();
|
||||
drop(h_poly);
|
||||
let h_blinds = vec![C::Scalar::one(); h_pieces.len()]; // TODO: not random
|
||||
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
|
||||
|
|
@ -165,39 +176,44 @@ impl<C: CurveAffine> Proof<C> {
|
|||
hash_point(&mut transcript, c)?;
|
||||
}
|
||||
|
||||
let x: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Evaluate polynomials at x
|
||||
let a_eval_x = eval_polynomial(&a_poly, x);
|
||||
let b_eval_x = eval_polynomial(&b_poly, x);
|
||||
let c_eval_x = eval_polynomial(&c_poly, x);
|
||||
let d_eval_x = eval_polynomial(&d_poly, x);
|
||||
let sa_eval_x = eval_polynomial(&srs.sa.1, x);
|
||||
let sb_eval_x = eval_polynomial(&srs.sb.1, x);
|
||||
let sc_eval_x = eval_polynomial(&srs.sc.1, x);
|
||||
let sd_eval_x = eval_polynomial(&srs.sd.1, x);
|
||||
let sm_eval_x = eval_polynomial(&srs.sm.1, x);
|
||||
|
||||
let h_evals_x: Vec<_> = h_pieces
|
||||
// Evaluate polynomials at omega^i x_3
|
||||
let advice_evals: Vec<_> = meta
|
||||
.advice_queries
|
||||
.iter()
|
||||
.map(|poly| eval_polynomial(poly, x))
|
||||
.map(|&(wire, at)| eval_polynomial(&advice_polys[wire.0], domain.rotate_omega(x_3, at)))
|
||||
.collect();
|
||||
|
||||
let fixed_evals: Vec<_> = meta
|
||||
.fixed_queries
|
||||
.iter()
|
||||
.map(|&(wire, at)| {
|
||||
eval_polynomial(&srs.fixed_polys[wire.0], domain.rotate_omega(x_3, at))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let h_evals: Vec<_> = h_pieces
|
||||
.iter()
|
||||
.map(|poly| eval_polynomial(poly, x_3))
|
||||
.collect();
|
||||
|
||||
// We set up a second transcript on the scalar field to hash in openings of
|
||||
// our polynomial commitments.
|
||||
let mut transcript_scalar = HScalar::init(C::Scalar::one());
|
||||
transcript_scalar.absorb(a_eval_x);
|
||||
transcript_scalar.absorb(b_eval_x);
|
||||
transcript_scalar.absorb(c_eval_x);
|
||||
transcript_scalar.absorb(d_eval_x);
|
||||
transcript_scalar.absorb(sa_eval_x);
|
||||
transcript_scalar.absorb(sb_eval_x);
|
||||
transcript_scalar.absorb(sc_eval_x);
|
||||
transcript_scalar.absorb(sd_eval_x);
|
||||
transcript_scalar.absorb(sm_eval_x);
|
||||
|
||||
// Hash each h(x) piece
|
||||
for eval in h_evals_x.iter() {
|
||||
// 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() {
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
|
|
@ -205,61 +221,146 @@ impl<C: CurveAffine> Proof<C> {
|
|||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
let y: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut q_commitment = h_commitments[0].clone().to_projective();
|
||||
let mut q_poly = h_pieces[0].clone();
|
||||
let mut q_blind = h_blinds[0];
|
||||
// Collapse openings at same points together into single openings using
|
||||
// x_4 challenge.
|
||||
let mut q_polys: Vec<Option<Vec<_>>> = vec![None; meta.rotations.len()];
|
||||
let mut q_blinds = vec![C::Scalar::zero(); meta.rotations.len()];
|
||||
let mut q_evals: Vec<_> = vec![C::Scalar::zero(); meta.rotations.len()];
|
||||
{
|
||||
let mut accumulate = |poly: &[_], blind: &C::Scalar, commitment: C| {
|
||||
for (a, q) in poly.iter().zip(q_poly.iter_mut()) {
|
||||
*q = (*q * &y) + a;
|
||||
}
|
||||
q_commitment = (q_commitment * y) + &commitment.to_projective();
|
||||
q_blind = (q_blind * &y) + blind;
|
||||
let mut accumulate = |point_index: usize, new_poly: &Vec<_>, blind, eval| {
|
||||
q_polys[point_index]
|
||||
.as_mut()
|
||||
.map(|poly| {
|
||||
parallelize(poly, |q, start| {
|
||||
for (q, a) in q.iter_mut().zip(new_poly[start..].iter()) {
|
||||
*q *= &x_4;
|
||||
*q += a;
|
||||
}
|
||||
});
|
||||
})
|
||||
.or_else(|| {
|
||||
q_polys[point_index] = Some(new_poly.clone());
|
||||
Some(())
|
||||
});
|
||||
q_blinds[point_index] *= &x_4;
|
||||
q_blinds[point_index] += &blind;
|
||||
q_evals[point_index] *= &x_4;
|
||||
q_evals[point_index] += &eval;
|
||||
};
|
||||
|
||||
for ((poly, blind), commitment) in h_pieces
|
||||
.iter()
|
||||
.zip(h_blinds.iter())
|
||||
.zip(h_commitments.iter())
|
||||
.skip(1)
|
||||
{
|
||||
accumulate(&poly, blind, *commitment);
|
||||
for (query_index, &(wire, ref at)) in meta.advice_queries.iter().enumerate() {
|
||||
let point_index = (*meta.rotations.get(at).unwrap()).0;
|
||||
|
||||
accumulate(
|
||||
point_index,
|
||||
&advice_polys[wire.0],
|
||||
advice_blinds[wire.0],
|
||||
advice_evals[query_index],
|
||||
);
|
||||
}
|
||||
|
||||
accumulate(&a_poly, &a_blind, a_commitment);
|
||||
accumulate(&b_poly, &b_blind, b_commitment);
|
||||
accumulate(&c_poly, &c_blind, c_commitment);
|
||||
accumulate(&d_poly, &d_blind, d_commitment);
|
||||
accumulate(&srs.sa.1, &Field::one(), srs.sa_commitment);
|
||||
accumulate(&srs.sb.1, &Field::one(), srs.sb_commitment);
|
||||
accumulate(&srs.sc.1, &Field::one(), srs.sc_commitment);
|
||||
accumulate(&srs.sd.1, &Field::one(), srs.sd_commitment);
|
||||
accumulate(&srs.sm.1, &Field::one(), srs.sm_commitment);
|
||||
for (query_index, &(wire, ref at)) in meta.fixed_queries.iter().enumerate() {
|
||||
let point_index = (*meta.rotations.get(at).unwrap()).0;
|
||||
|
||||
accumulate(
|
||||
point_index,
|
||||
&srs.fixed_polys[wire.0],
|
||||
C::Scalar::one(),
|
||||
fixed_evals[query_index],
|
||||
);
|
||||
}
|
||||
|
||||
// We query the h(X) polynomial at x_3
|
||||
let current_index = (*meta.rotations.get(&Rotation::default()).unwrap()).0;
|
||||
for ((h_poly, h_blind), h_eval) in h_pieces
|
||||
.into_iter()
|
||||
.zip(h_blinds.iter())
|
||||
.zip(h_evals.iter())
|
||||
{
|
||||
accumulate(current_index, &h_poly, *h_blind, *h_eval);
|
||||
}
|
||||
}
|
||||
|
||||
let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut f_poly: Option<Vec<C::Scalar>> = None;
|
||||
for (&row, &point_index) in meta.rotations.iter() {
|
||||
let mut poly = q_polys[point_index.0].as_ref().unwrap().clone();
|
||||
let point = domain.rotate_omega(x_3, row);
|
||||
poly[0] -= &q_evals[point_index.0];
|
||||
let mut poly = kate_division(&poly, point);
|
||||
poly.push(C::Scalar::zero());
|
||||
|
||||
f_poly = f_poly
|
||||
.map(|mut f_poly| {
|
||||
parallelize(&mut f_poly, |q, start| {
|
||||
for (q, a) in q.iter_mut().zip(poly[start..].iter()) {
|
||||
*q *= &x_5;
|
||||
*q += a;
|
||||
}
|
||||
});
|
||||
f_poly
|
||||
})
|
||||
.or_else(|| Some(poly));
|
||||
}
|
||||
let mut f_poly = f_poly.unwrap();
|
||||
let mut f_blind = C::Scalar::random();
|
||||
|
||||
let f_commitment = params.commit(&f_poly, f_blind).to_affine();
|
||||
|
||||
hash_point(&mut transcript, &f_commitment)?;
|
||||
|
||||
let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut q_evals = vec![];
|
||||
|
||||
for (_, &point_index) in meta.rotations.iter() {
|
||||
q_evals.push(eval_polynomial(
|
||||
&q_polys[point_index.0].as_ref().unwrap(),
|
||||
x_6,
|
||||
));
|
||||
}
|
||||
|
||||
for eval in q_evals.iter() {
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
let transcript_scalar_point =
|
||||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
for (_, &point_index) in meta.rotations.iter() {
|
||||
f_blind *= &x_7;
|
||||
f_blind += &q_blinds[point_index.0];
|
||||
|
||||
parallelize(&mut f_poly, |f, start| {
|
||||
for (f, a) in f
|
||||
.iter_mut()
|
||||
.zip(q_polys[point_index.0].as_ref().unwrap()[start..].iter())
|
||||
{
|
||||
*f *= &x_7;
|
||||
*f += a;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Let's prove that the q_commitment opens at x to the expected value.
|
||||
let opening = params
|
||||
.create_proof(&mut transcript, &q_poly, q_blind, x)
|
||||
.create_proof(&mut transcript, &f_poly, f_blind, x_6)
|
||||
.map_err(|_| Error::ConstraintSystemFailure)?;
|
||||
|
||||
Ok(Proof {
|
||||
a_commitment,
|
||||
b_commitment,
|
||||
c_commitment,
|
||||
d_commitment,
|
||||
advice_commitments,
|
||||
h_commitments,
|
||||
a_eval_x,
|
||||
b_eval_x,
|
||||
c_eval_x,
|
||||
d_eval_x,
|
||||
sa_eval_x,
|
||||
sb_eval_x,
|
||||
sc_eval_x,
|
||||
sd_eval_x,
|
||||
sm_eval_x,
|
||||
h_evals_x,
|
||||
advice_evals,
|
||||
fixed_evals,
|
||||
h_evals,
|
||||
f_commitment,
|
||||
q_evals,
|
||||
opening,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
128
src/plonk/srs.rs
128
src/plonk/srs.rs
|
|
@ -1,7 +1,7 @@
|
|||
use super::{
|
||||
circuit::{Circuit, ConstraintSystem, Wire},
|
||||
circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit},
|
||||
domain::EvaluationDomain,
|
||||
Error, GATE_DEGREE, SRS,
|
||||
Error, SRS,
|
||||
};
|
||||
use crate::arithmetic::{Curve, CurveAffine, Field};
|
||||
use crate::polycommit::Params;
|
||||
|
|
@ -14,92 +14,80 @@ impl<C: CurveAffine> SRS<C> {
|
|||
circuit: &ConcreteCircuit,
|
||||
) -> Result<Self, Error> {
|
||||
struct Assembly<F: Field> {
|
||||
sa: Vec<F>,
|
||||
sb: Vec<F>,
|
||||
sc: Vec<F>,
|
||||
sd: Vec<F>,
|
||||
sm: Vec<F>,
|
||||
fixed: Vec<Vec<F>>,
|
||||
}
|
||||
|
||||
impl<F: Field> ConstraintSystem<F> for Assembly<F> {
|
||||
fn create_gate(
|
||||
fn assign_advice(
|
||||
&mut self,
|
||||
sa: F,
|
||||
sb: F,
|
||||
sc: F,
|
||||
sd: F,
|
||||
sm: F,
|
||||
_: impl Fn() -> Result<(F, F, F, F), Error>,
|
||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
||||
let tmp = Ok((
|
||||
Wire::A(self.sa.len()),
|
||||
Wire::B(self.sa.len()),
|
||||
Wire::C(self.sa.len()),
|
||||
Wire::D(self.sa.len()),
|
||||
));
|
||||
self.sa.push(sa);
|
||||
self.sb.push(sb);
|
||||
self.sc.push(sc);
|
||||
self.sd.push(sd);
|
||||
self.sm.push(sm);
|
||||
tmp
|
||||
_: AdviceWire,
|
||||
_: usize,
|
||||
_: impl FnOnce() -> Result<F, Error>,
|
||||
) -> Result<(), Error> {
|
||||
// We only care about fixed wires here
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_fixed(
|
||||
&mut self,
|
||||
wire: FixedWire,
|
||||
row: usize,
|
||||
to: impl FnOnce() -> Result<F, Error>,
|
||||
) -> Result<(), Error> {
|
||||
*self
|
||||
.fixed
|
||||
.get_mut(wire.0)
|
||||
.and_then(|v| v.get_mut(row))
|
||||
.ok_or(Error::BoundsFailure)? = to()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut meta = MetaCircuit::default();
|
||||
let config = ConcreteCircuit::configure(&mut meta);
|
||||
|
||||
let mut assembly: Assembly<C::Scalar> = Assembly {
|
||||
sa: vec![],
|
||||
sb: vec![],
|
||||
sc: vec![],
|
||||
sd: vec![],
|
||||
sm: vec![],
|
||||
fixed: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_fixed_wires],
|
||||
};
|
||||
|
||||
// Synthesize the circuit to obtain SRS
|
||||
circuit.synthesize(&mut assembly)?;
|
||||
circuit.synthesize(&mut assembly, config)?;
|
||||
|
||||
assembly.sa.resize(params.n as usize, C::Scalar::zero());
|
||||
assembly.sb.resize(params.n as usize, C::Scalar::zero());
|
||||
assembly.sc.resize(params.n as usize, C::Scalar::zero());
|
||||
assembly.sd.resize(params.n as usize, C::Scalar::zero());
|
||||
assembly.sm.resize(params.n as usize, C::Scalar::zero());
|
||||
let fixed_commitments = assembly
|
||||
.fixed
|
||||
.iter()
|
||||
.map(|poly| params.commit_lagrange(poly, C::Scalar::one()).to_affine())
|
||||
.collect();
|
||||
|
||||
// Compute commitments to the fixed wire values
|
||||
let sa_commitment = params
|
||||
.commit_lagrange(&assembly.sa, C::Scalar::one())
|
||||
.to_affine();
|
||||
let sb_commitment = params
|
||||
.commit_lagrange(&assembly.sb, C::Scalar::one())
|
||||
.to_affine();
|
||||
let sc_commitment = params
|
||||
.commit_lagrange(&assembly.sc, C::Scalar::one())
|
||||
.to_affine();
|
||||
let sd_commitment = params
|
||||
.commit_lagrange(&assembly.sd, C::Scalar::one())
|
||||
.to_affine();
|
||||
let sm_commitment = params
|
||||
.commit_lagrange(&assembly.sm, C::Scalar::one())
|
||||
.to_affine();
|
||||
let mut degree = 1;
|
||||
for poly in meta.gates.iter() {
|
||||
degree = std::cmp::max(degree, poly.degree());
|
||||
}
|
||||
|
||||
let domain = EvaluationDomain::new(GATE_DEGREE, params.k);
|
||||
let domain = EvaluationDomain::new(degree as u32, params.k);
|
||||
|
||||
let sa = domain.obtain_coset(assembly.sa);
|
||||
let sb = domain.obtain_coset(assembly.sb);
|
||||
let sc = domain.obtain_coset(assembly.sc);
|
||||
let sd = domain.obtain_coset(assembly.sd);
|
||||
let sm = domain.obtain_coset(assembly.sm);
|
||||
let fixed_polys: Vec<_> = assembly
|
||||
.fixed
|
||||
.into_iter()
|
||||
.map(|poly| domain.obtain_poly(poly))
|
||||
.collect();
|
||||
|
||||
let fixed_cosets = meta
|
||||
.fixed_queries
|
||||
.iter()
|
||||
.map(|&(wire, at)| {
|
||||
let poly = fixed_polys[wire.0].clone();
|
||||
domain.obtain_coset(poly, at)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(SRS {
|
||||
sa,
|
||||
sb,
|
||||
sc,
|
||||
sd,
|
||||
sm,
|
||||
sa_commitment,
|
||||
sb_commitment,
|
||||
sc_commitment,
|
||||
sd_commitment,
|
||||
sm_commitment,
|
||||
domain,
|
||||
fixed_commitments,
|
||||
fixed_polys,
|
||||
fixed_cosets,
|
||||
meta,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use super::{hash_point, Proof, SRS};
|
||||
use super::{domain::Rotation, hash_point, Proof, SRS};
|
||||
use crate::arithmetic::{get_challenge_scalar, Challenge, Curve, CurveAffine, Field};
|
||||
use crate::polycommit::Params;
|
||||
use crate::transcript::Hasher;
|
||||
|
|
@ -13,35 +13,34 @@ impl<C: CurveAffine> Proof<C> {
|
|||
// Create a transcript for obtaining Fiat-Shamir challenges.
|
||||
let mut transcript = HBase::init(C::Base::one());
|
||||
|
||||
hash_point(&mut transcript, &self.a_commitment)
|
||||
.expect("proof cannot contain points at infinity");
|
||||
hash_point(&mut transcript, &self.b_commitment)
|
||||
.expect("proof cannot contain points at infinity");
|
||||
hash_point(&mut transcript, &self.c_commitment)
|
||||
.expect("proof cannot contain points at infinity");
|
||||
hash_point(&mut transcript, &self.d_commitment)
|
||||
.expect("proof cannot contain points at infinity");
|
||||
// Hash the prover's advice commitments into the transcript
|
||||
for commitment in &self.advice_commitments {
|
||||
hash_point(&mut transcript, commitment)
|
||||
.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()));
|
||||
|
||||
// Obtain a commitment to h(X) in the form of multiple pieces of degree n - 1
|
||||
for c in &self.h_commitments {
|
||||
hash_point(&mut transcript, c).expect("proof cannot contain points at infinity");
|
||||
}
|
||||
|
||||
let x: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
// 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()));
|
||||
|
||||
// We set up a second transcript on the scalar field to hash in openings of
|
||||
// our polynomial commitments.
|
||||
// Hash together all the openings provided by the prover into a new
|
||||
// transcript on the scalar field.
|
||||
let mut transcript_scalar = HScalar::init(C::Scalar::one());
|
||||
transcript_scalar.absorb(self.a_eval_x);
|
||||
transcript_scalar.absorb(self.b_eval_x);
|
||||
transcript_scalar.absorb(self.c_eval_x);
|
||||
transcript_scalar.absorb(self.d_eval_x);
|
||||
transcript_scalar.absorb(self.sa_eval_x);
|
||||
transcript_scalar.absorb(self.sb_eval_x);
|
||||
transcript_scalar.absorb(self.sc_eval_x);
|
||||
transcript_scalar.absorb(self.sd_eval_x);
|
||||
transcript_scalar.absorb(self.sm_eval_x);
|
||||
|
||||
for eval in &self.h_evals_x {
|
||||
for eval in self
|
||||
.advice_evals
|
||||
.iter()
|
||||
.chain(self.fixed_evals.iter())
|
||||
.chain(self.h_evals.iter())
|
||||
{
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
|
|
@ -49,60 +48,136 @@ impl<C: CurveAffine> Proof<C> {
|
|||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
let y: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
// Evaluate the circuit using the custom gates provided
|
||||
let mut h_eval = C::Scalar::zero();
|
||||
for poly in srs.meta.gates.iter() {
|
||||
h_eval *= &x_2;
|
||||
|
||||
let mut q_commitment = self.h_commitments[0].clone().to_projective();
|
||||
let mut expected_opening = self.h_evals_x[0];
|
||||
{
|
||||
let mut accumulate = |commitment: C, opening: C::Scalar| {
|
||||
q_commitment = commitment.to_projective() + &(q_commitment * y);
|
||||
expected_opening = opening + &(expected_opening * &y);
|
||||
};
|
||||
let evaluation: C::Scalar = poly.evaluate(
|
||||
&|index| self.fixed_evals[index],
|
||||
&|index| self.advice_evals[index],
|
||||
&|a, b| a + &b,
|
||||
&|a, b| a * &b,
|
||||
&|a, scalar| a * &scalar,
|
||||
);
|
||||
|
||||
for (commitment, eval) in self.h_commitments.iter().zip(self.h_evals_x.iter()).skip(1) {
|
||||
accumulate(*commitment, *eval);
|
||||
}
|
||||
|
||||
accumulate(self.a_commitment, self.a_eval_x);
|
||||
accumulate(self.b_commitment, self.b_eval_x);
|
||||
accumulate(self.c_commitment, self.c_eval_x);
|
||||
accumulate(self.d_commitment, self.d_eval_x);
|
||||
accumulate(srs.sa_commitment, self.sa_eval_x);
|
||||
accumulate(srs.sb_commitment, self.sb_eval_x);
|
||||
accumulate(srs.sc_commitment, self.sc_eval_x);
|
||||
accumulate(srs.sd_commitment, self.sd_eval_x);
|
||||
accumulate(srs.sm_commitment, self.sm_eval_x);
|
||||
h_eval += &evaluation;
|
||||
}
|
||||
let q_commitment = q_commitment.to_affine();
|
||||
|
||||
let xn = x.pow(&[params.n as u64, 0, 0, 0]);
|
||||
let xn = x_3.pow(&[params.n as u64, 0, 0, 0]);
|
||||
|
||||
// Compute the expected h(x) value
|
||||
let mut h_eval_x = C::Scalar::zero();
|
||||
let mut expected_h_eval = C::Scalar::zero();
|
||||
let mut cur = C::Scalar::one();
|
||||
for eval in &self.h_evals_x {
|
||||
h_eval_x += &(cur * eval);
|
||||
for eval in &self.h_evals {
|
||||
expected_h_eval += &(cur * eval);
|
||||
cur *= &xn;
|
||||
}
|
||||
|
||||
// Check that the circuit is satisfied.
|
||||
// (a * sa) + (b * sb) + (a * sm * b) + (d * sd) - (c * sc)
|
||||
if self.a_eval_x * &self.sa_eval_x
|
||||
+ &(self.b_eval_x * &self.sb_eval_x)
|
||||
+ &(self.a_eval_x * &self.sm_eval_x * &self.b_eval_x)
|
||||
+ &(self.d_eval_x * &self.sd_eval_x)
|
||||
- &(self.c_eval_x * &self.sc_eval_x)
|
||||
!= h_eval_x * &(xn - &C::Scalar::one())
|
||||
{
|
||||
if h_eval != (expected_h_eval * &(xn - &C::Scalar::one())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are now convinced the circuit is satisfied so long as the
|
||||
// polynomial commitments open to the correct values.
|
||||
|
||||
// Sample x_4 for compressing openings at the same points together
|
||||
let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Compress the commitments and expected evaluations at x_3 together
|
||||
// using the challenge x_4
|
||||
let mut q_commitments: Vec<Option<C::Projective>> = vec![None; srs.meta.rotations.len()];
|
||||
let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.meta.rotations.len()];
|
||||
{
|
||||
let mut accumulate = |point_index: usize, new_commitment, eval| {
|
||||
q_commitments[point_index] = q_commitments[point_index]
|
||||
.map(|mut commitment| {
|
||||
commitment *= x_4;
|
||||
commitment += new_commitment;
|
||||
commitment
|
||||
})
|
||||
.or_else(|| Some(new_commitment.to_projective()));
|
||||
q_evals[point_index] *= &x_4;
|
||||
q_evals[point_index] += &eval;
|
||||
};
|
||||
|
||||
for (query_index, &(wire, ref at)) in srs.meta.advice_queries.iter().enumerate() {
|
||||
let point_index = (*srs.meta.rotations.get(at).unwrap()).0;
|
||||
accumulate(
|
||||
point_index,
|
||||
self.advice_commitments[wire.0],
|
||||
self.advice_evals[query_index],
|
||||
);
|
||||
}
|
||||
|
||||
for (query_index, &(wire, ref at)) in srs.meta.fixed_queries.iter().enumerate() {
|
||||
let point_index = (*srs.meta.rotations.get(at).unwrap()).0;
|
||||
accumulate(
|
||||
point_index,
|
||||
srs.fixed_commitments[wire.0],
|
||||
self.fixed_evals[query_index],
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Sample a challenge x_5 for keeping the multi-point quotient
|
||||
// polynomial terms linearly independent.
|
||||
let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Obtain the commitment to the multi-point quotient polynomial f(X).
|
||||
hash_point(&mut transcript, &self.f_commitment)
|
||||
.expect("proof cannot contain points at infinity");
|
||||
|
||||
// Sample a challenge x_6 for checking that f(X) was committed to
|
||||
// correctly.
|
||||
let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
for eval in self.q_evals.iter() {
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
let transcript_scalar_point =
|
||||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
// 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() {
|
||||
let mut eval = self.q_evals[point_index.0];
|
||||
|
||||
let point = srs.domain.rotate_omega(x_3, row);
|
||||
eval = eval - &q_evals[point_index.0];
|
||||
eval = eval * &(x_6 - &point).invert().unwrap();
|
||||
|
||||
f_eval *= &x_5;
|
||||
f_eval += &eval;
|
||||
}
|
||||
|
||||
// Sample a challenge x_7 that we will use to collapse the openings of
|
||||
// the various remaining polynomials at x_6 together.
|
||||
let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Compute the final commitment that has to be opened
|
||||
let mut f_commitment: C::Projective = self.f_commitment.to_projective();
|
||||
for (_, &point_index) in srs.meta.rotations.iter() {
|
||||
f_commitment *= x_7;
|
||||
f_commitment = f_commitment + &q_commitments[point_index.0].as_ref().unwrap();
|
||||
f_eval *= &x_7;
|
||||
f_eval += &self.q_evals[point_index.0];
|
||||
}
|
||||
|
||||
// Verify the opening proof
|
||||
params.verify_proof(
|
||||
&self.opening,
|
||||
&mut transcript,
|
||||
x,
|
||||
&q_commitment,
|
||||
expected_opening,
|
||||
x_6,
|
||||
&f_commitment.to_affine(),
|
||||
f_eval,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue