make succinct transformation working, can now make full protocol
This commit is contained in:
parent
8cfacca862
commit
95b91fc0b0
@ -18,7 +18,7 @@ bit-vec = "0.4.4"
|
||||
futures = "0.1"
|
||||
cfg-if = "0.1.7"
|
||||
|
||||
#pairing = { git = 'https://github.com/matterinc/pairing', tag = "0.16.2" }
|
||||
#pairing_ce = { path = "../pairing" }
|
||||
pairing_ce = { version = "0.17.0" }
|
||||
byteorder = "1"
|
||||
|
||||
|
@ -90,6 +90,16 @@ pub enum Variable {
|
||||
C(usize),
|
||||
}
|
||||
|
||||
impl Variable {
|
||||
pub(crate) fn get_index(&self) -> usize {
|
||||
match *self {
|
||||
Variable::A(index) => index,
|
||||
Variable::B(index) => index,
|
||||
Variable::C(index) => index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Coeff<E: Engine> {
|
||||
Zero,
|
||||
|
@ -41,6 +41,8 @@ pub trait ConstraintSystem<E: Engine> {
|
||||
/// about the circuit for verification, while another actually constructs
|
||||
/// a witness.
|
||||
pub trait Backend<E: Engine> {
|
||||
type LinearConstraintIndex;
|
||||
|
||||
/// Get the value of a variable. Can return None if we don't know.
|
||||
fn get_var(&self, _variable: Variable) -> Option<E::Fr> { None }
|
||||
|
||||
@ -51,11 +53,14 @@ pub trait Backend<E: Engine> {
|
||||
/// Create a new multiplication gate.
|
||||
fn new_multiplication_gate(&mut self) { }
|
||||
|
||||
/// Create a new linear constraint.
|
||||
fn new_linear_constraint(&mut self) { }
|
||||
/// Create a new linear constraint, returning the power of Y for caching purposes.
|
||||
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex;
|
||||
|
||||
/// Insert a term into a linear constraint. TODO: bad name of function
|
||||
fn insert_coefficient(&mut self, _var: Variable, _coeff: Coeff<E>) { }
|
||||
fn insert_coefficient(&mut self, _var: Variable, _coeff: Coeff<E>, y: &Self::LinearConstraintIndex) { }
|
||||
|
||||
/// Compute a `LinearConstraintIndex` from `q`.
|
||||
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex;
|
||||
|
||||
/// Mark y^{_index} as the power of y cooresponding to the public input
|
||||
/// coefficient for the next public input, in the k(Y) polynomial.
|
||||
@ -65,252 +70,4 @@ pub trait Backend<E: Engine> {
|
||||
/// This is an abstraction which synthesizes circuits.
|
||||
pub trait SynthesisDriver {
|
||||
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError>;
|
||||
}
|
||||
|
||||
pub struct Basic;
|
||||
|
||||
impl SynthesisDriver for Basic {
|
||||
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
|
||||
struct Synthesizer<E: Engine, B: Backend<E>> {
|
||||
backend: B,
|
||||
current_variable: Option<usize>,
|
||||
_marker: PhantomData<E>,
|
||||
q: usize,
|
||||
n: usize,
|
||||
}
|
||||
|
||||
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for Synthesizer<E, B> {
|
||||
const ONE: Variable = Variable::A(1);
|
||||
|
||||
fn alloc<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
match self.current_variable.take() {
|
||||
Some(index) => {
|
||||
let var_a = Variable::A(index);
|
||||
let var_b = Variable::B(index);
|
||||
let var_c = Variable::C(index);
|
||||
|
||||
let mut product = None;
|
||||
|
||||
let value_a = self.backend.get_var(var_a);
|
||||
|
||||
self.backend.set_var(var_b, || {
|
||||
let value_b = value()?;
|
||||
product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?);
|
||||
product.as_mut().map(|product| product.mul_assign(&value_b));
|
||||
|
||||
Ok(value_b)
|
||||
})?;
|
||||
|
||||
self.backend.set_var(var_c, || {
|
||||
product.ok_or(SynthesisError::AssignmentMissing)
|
||||
})?;
|
||||
|
||||
self.current_variable = None;
|
||||
|
||||
Ok(var_b)
|
||||
},
|
||||
None => {
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
let var_a = Variable::A(index);
|
||||
|
||||
self.backend.set_var(var_a, value)?;
|
||||
|
||||
self.current_variable = Some(index);
|
||||
|
||||
Ok(var_a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_input<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
let input_var = self.alloc(value)?;
|
||||
|
||||
self.enforce_zero(LinearCombination::zero() + input_var);
|
||||
self.backend.new_k_power(self.q);
|
||||
|
||||
Ok(input_var)
|
||||
}
|
||||
|
||||
fn enforce_zero(&mut self, lc: LinearCombination<E>)
|
||||
{
|
||||
self.q += 1;
|
||||
self.backend.new_linear_constraint();
|
||||
|
||||
for (var, coeff) in lc.as_ref() {
|
||||
self.backend.insert_coefficient(*var, *coeff);
|
||||
}
|
||||
}
|
||||
|
||||
fn multiply<F>(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>
|
||||
{
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
let a = Variable::A(index);
|
||||
let b = Variable::B(index);
|
||||
let c = Variable::C(index);
|
||||
|
||||
let mut b_val = None;
|
||||
let mut c_val = None;
|
||||
|
||||
self.backend.set_var(a, || {
|
||||
let (a, b, c) = values()?;
|
||||
|
||||
b_val = Some(b);
|
||||
c_val = Some(c);
|
||||
|
||||
Ok(a)
|
||||
})?;
|
||||
|
||||
self.backend.set_var(b, || {
|
||||
b_val.ok_or(SynthesisError::AssignmentMissing)
|
||||
})?;
|
||||
|
||||
self.backend.set_var(c, || {
|
||||
c_val.ok_or(SynthesisError::AssignmentMissing)
|
||||
})?;
|
||||
|
||||
Ok((a, b, c))
|
||||
}
|
||||
|
||||
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
|
||||
self.backend.get_var(var).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut tmp: Synthesizer<E, B> = Synthesizer {
|
||||
backend: backend,
|
||||
current_variable: None,
|
||||
_marker: PhantomData,
|
||||
q: 0,
|
||||
n: 0,
|
||||
};
|
||||
|
||||
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
|
||||
|
||||
match (one, <Synthesizer<E, B> as ConstraintSystem<E>>::ONE) {
|
||||
(Variable::A(1), Variable::A(1)) => {},
|
||||
_ => panic!("one variable is incorrect")
|
||||
}
|
||||
|
||||
circuit.synthesize(&mut tmp)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Nonassigning;
|
||||
|
||||
impl SynthesisDriver for Nonassigning {
|
||||
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
|
||||
struct NonassigningSynthesizer<E: Engine, B: Backend<E>> {
|
||||
backend: B,
|
||||
current_variable: Option<usize>,
|
||||
_marker: PhantomData<E>,
|
||||
q: usize,
|
||||
n: usize,
|
||||
}
|
||||
|
||||
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for NonassigningSynthesizer<E, B> {
|
||||
const ONE: Variable = Variable::A(1);
|
||||
|
||||
fn alloc<F>(&mut self, _value: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
match self.current_variable.take() {
|
||||
Some(index) => {
|
||||
let var_b = Variable::B(index);
|
||||
|
||||
self.current_variable = None;
|
||||
|
||||
Ok(var_b)
|
||||
},
|
||||
None => {
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
let var_a = Variable::A(index);
|
||||
|
||||
self.current_variable = Some(index);
|
||||
|
||||
Ok(var_a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_input<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
let input_var = self.alloc(value)?;
|
||||
|
||||
self.enforce_zero(LinearCombination::zero() + input_var);
|
||||
self.backend.new_k_power(self.q);
|
||||
|
||||
Ok(input_var)
|
||||
}
|
||||
|
||||
fn enforce_zero(&mut self, lc: LinearCombination<E>)
|
||||
{
|
||||
self.q += 1;
|
||||
self.backend.new_linear_constraint();
|
||||
|
||||
for (var, coeff) in lc.as_ref() {
|
||||
self.backend.insert_coefficient(*var, *coeff);
|
||||
}
|
||||
}
|
||||
|
||||
fn multiply<F>(&mut self, _values: F) -> Result<(Variable, Variable, Variable), SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>
|
||||
{
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
let a = Variable::A(index);
|
||||
let b = Variable::B(index);
|
||||
let c = Variable::C(index);
|
||||
|
||||
Ok((a, b, c))
|
||||
}
|
||||
|
||||
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
|
||||
self.backend.get_var(var).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut tmp: NonassigningSynthesizer<E, B> = NonassigningSynthesizer {
|
||||
backend: backend,
|
||||
current_variable: None,
|
||||
_marker: PhantomData,
|
||||
q: 0,
|
||||
n: 0,
|
||||
};
|
||||
|
||||
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
|
||||
|
||||
match (one, <NonassigningSynthesizer<E, B> as ConstraintSystem<E>>::ONE) {
|
||||
(Variable::A(1), Variable::A(1)) => {},
|
||||
_ => panic!("one variable is incorrect")
|
||||
}
|
||||
|
||||
circuit.synthesize(&mut tmp)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::{Circuit};
|
||||
use crate::sonic::sonic::AdaptorCircuit;
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::cs::Nonassigning;
|
||||
use crate::sonic::sonic::Nonassigning;
|
||||
use super::helper::create_aggregate as create_aggregate_sonic_circuit;
|
||||
|
||||
pub fn create_aggregate<E: Engine, C: Circuit<E> + Clone>(
|
||||
|
@ -15,10 +15,11 @@ use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::{Circuit};
|
||||
use crate::sonic::sonic::AdaptorCircuit;
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::cs::Basic;
|
||||
use crate::sonic::sonic::Basic;
|
||||
use super::prover::create_advice as create_advice_sonic_circuit;
|
||||
use super::prover::create_advice_on_information_and_srs as create_advice_on_information_and_srs_sonic_circuit;
|
||||
use super::prover::create_proof_on_srs as create_proof_on_srs_sonic_circuit;
|
||||
use crate::sonic::sonic::CountN;
|
||||
|
||||
// pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E> + Clone, S: SynthesisDriver>(
|
||||
pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E> + Clone>(
|
||||
@ -51,23 +52,13 @@ pub fn create_advice_on_srs<E: Engine, C: Circuit<E> + Clone>(
|
||||
srs: &SRS<E>
|
||||
) -> Result<SxyAdvice<E>, SynthesisError>
|
||||
{
|
||||
use crate::sonic::cs::Nonassigning;
|
||||
use crate::sonic::sonic::Nonassigning;
|
||||
|
||||
let adapted_circuit = AdaptorCircuit(circuit.clone());
|
||||
// annoying, but we need n to compute s(z, y), and this isn't
|
||||
// precomputed anywhere yet
|
||||
let n = {
|
||||
struct CountN {
|
||||
n: usize
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> Backend<E> for &'a mut CountN {
|
||||
fn new_multiplication_gate(&mut self) {
|
||||
self.n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut tmp = CountN{n:0};
|
||||
let mut tmp = CountN::<Nonassigning>::new();
|
||||
Nonassigning::synthesize(&mut tmp, &adapted_circuit)?;
|
||||
|
||||
tmp.n
|
||||
|
@ -18,7 +18,7 @@ use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::{Circuit};
|
||||
use crate::sonic::sonic::AdaptorCircuit;
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::cs::Nonassigning;
|
||||
use crate::sonic::sonic::Nonassigning;
|
||||
use super::verifier::verify_aggregate_on_srs as verify_aggregate_on_srs_sonic_circuit;
|
||||
use super::verifier::verify_proofs_on_srs as verify_proofs_on_srs_sonic_circuit;
|
||||
|
||||
|
@ -38,7 +38,7 @@ use crate::multicore::{
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::sonic::cs::{Backend, Basic, SynthesisDriver};
|
||||
use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::cs::LinearCombination as SonicLinearCombination;
|
||||
use crate::sonic::cs::Circuit as SonicCircuit;
|
||||
@ -47,6 +47,9 @@ use crate::sonic::cs::Variable as SonicVariable;
|
||||
use crate::sonic::cs::Coeff;
|
||||
use crate::sonic::sonic::{AdaptorCircuit};
|
||||
use super::parameters::NUM_BLINDINGS;
|
||||
use crate::sonic::sonic::NonassigningSynthesizer;
|
||||
use crate::sonic::sonic::PermutationSynthesizer;
|
||||
use crate::sonic::sonic::{Basic, Preprocess};
|
||||
|
||||
use crate::verbose_flag;
|
||||
|
||||
@ -231,117 +234,11 @@ pub fn get_circuit_parameters<E, C>(
|
||||
where E: Engine, C: Circuit<E>
|
||||
|
||||
{
|
||||
struct NonassigningSynthesizer<E: Engine, B: Backend<E>> {
|
||||
backend: B,
|
||||
current_variable: Option<usize>,
|
||||
_marker: PhantomData<E>,
|
||||
q: usize,
|
||||
n: usize,
|
||||
}
|
||||
|
||||
impl<E: Engine, B: Backend<E>> SonicConstraintSystem<E> for NonassigningSynthesizer<E, B> {
|
||||
const ONE: SonicVariable = SonicVariable::A(1);
|
||||
|
||||
fn alloc<F>(&mut self, _value: F) -> Result<SonicVariable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
match self.current_variable.take() {
|
||||
Some(index) => {
|
||||
let var_b = SonicVariable::B(index);
|
||||
|
||||
self.current_variable = None;
|
||||
|
||||
Ok(var_b)
|
||||
},
|
||||
None => {
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
let var_a = SonicVariable::A(index);
|
||||
|
||||
self.current_variable = Some(index);
|
||||
|
||||
Ok(var_a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_input<F>(&mut self, value: F) -> Result<SonicVariable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
let input_var = self.alloc(value)?;
|
||||
|
||||
self.enforce_zero(SonicLinearCombination::zero() + input_var);
|
||||
self.backend.new_k_power(self.q);
|
||||
|
||||
Ok(input_var)
|
||||
}
|
||||
|
||||
fn enforce_zero(&mut self, lc: SonicLinearCombination<E>)
|
||||
{
|
||||
self.q += 1;
|
||||
self.backend.new_linear_constraint();
|
||||
|
||||
for (var, coeff) in lc.as_ref() {
|
||||
self.backend.insert_coefficient(*var, *coeff);
|
||||
}
|
||||
}
|
||||
|
||||
fn multiply<F>(&mut self, _values: F) -> Result<(SonicVariable, SonicVariable, SonicVariable), SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>
|
||||
{
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
let a = SonicVariable::A(index);
|
||||
let b = SonicVariable::B(index);
|
||||
let c = SonicVariable::C(index);
|
||||
|
||||
Ok((a, b, c))
|
||||
}
|
||||
|
||||
fn get_value(&self, var: SonicVariable) -> Result<E::Fr, ()> {
|
||||
self.backend.get_var(var).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Preprocess<E: Engine> {
|
||||
k_map: Vec<usize>,
|
||||
n: usize,
|
||||
q: usize,
|
||||
_marker: PhantomData<E>
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> Backend<E> for &'a mut Preprocess<E> {
|
||||
fn new_k_power(&mut self, index: usize) {
|
||||
self.k_map.push(index);
|
||||
}
|
||||
|
||||
fn new_multiplication_gate(&mut self) {
|
||||
self.n += 1;
|
||||
}
|
||||
|
||||
fn new_linear_constraint(&mut self) {
|
||||
self.q += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData };
|
||||
let mut preprocess = Preprocess::new();
|
||||
|
||||
let (num_inputs, num_aux, num_constraints) = {
|
||||
|
||||
let mut cs: NonassigningSynthesizer<E, &'_ mut Preprocess<E>> = NonassigningSynthesizer {
|
||||
backend: &mut preprocess,
|
||||
current_variable: None,
|
||||
_marker: PhantomData,
|
||||
q: 0,
|
||||
n: 0,
|
||||
};
|
||||
let mut cs: NonassigningSynthesizer<E, &'_ mut Preprocess<E>> = NonassigningSynthesizer::new(&mut preprocess);
|
||||
|
||||
let one = cs.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
|
||||
|
||||
@ -374,6 +271,52 @@ pub fn get_circuit_parameters<E, C>(
|
||||
})
|
||||
}
|
||||
|
||||
/// Get circuit information such as number of input, variables,
|
||||
/// constraints, and the corresponding SONIC parameters
|
||||
/// k_map, n, q
|
||||
pub fn get_circuit_parameters_for_succinct_sonic<E, C>(
|
||||
circuit: C,
|
||||
) -> Result<CircuitParameters<E>, SynthesisError>
|
||||
where E: Engine, C: Circuit<E>
|
||||
|
||||
{
|
||||
let mut preprocess = Preprocess::new();
|
||||
|
||||
let (num_inputs, num_aux, num_constraints) = {
|
||||
|
||||
let mut cs: PermutationSynthesizer<E, &'_ mut Preprocess<E>> = PermutationSynthesizer::new(&mut preprocess);
|
||||
|
||||
let one = cs.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
|
||||
|
||||
match (one, <PermutationSynthesizer<E, &'_ mut Preprocess<E>> as SonicConstraintSystem<E>>::ONE) {
|
||||
(SonicVariable::A(1), SonicVariable::A(1)) => {},
|
||||
_ => return Err(SynthesisError::UnconstrainedVariable)
|
||||
}
|
||||
|
||||
let mut assembly = GeneratorAssembly::<'_, E, _> {
|
||||
cs: &mut cs,
|
||||
num_inputs: 0,
|
||||
num_aux: 0,
|
||||
num_constraints: 0,
|
||||
_marker: PhantomData
|
||||
};
|
||||
|
||||
circuit.synthesize(&mut assembly)?;
|
||||
|
||||
(assembly.num_inputs, assembly.num_aux, assembly.num_constraints)
|
||||
};
|
||||
|
||||
Ok(CircuitParameters {
|
||||
num_inputs: num_inputs,
|
||||
num_aux: num_aux,
|
||||
num_constraints: num_constraints,
|
||||
k_map: preprocess.k_map,
|
||||
n: preprocess.n,
|
||||
q: preprocess.q,
|
||||
_marker: PhantomData
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generate_parameters<E, C>(
|
||||
circuit: C,
|
||||
alpha: E::Fr,
|
||||
|
@ -14,6 +14,7 @@ use crate::sonic::util::*;
|
||||
use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::sonic::cs::{Circuit, Variable, Coeff};
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::sonic::CountNandQ;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Aggregate<E: Engine> {
|
||||
@ -47,22 +48,8 @@ pub fn create_aggregate_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
{
|
||||
// TODO: precompute this?
|
||||
let (n, q) = {
|
||||
struct CountN {
|
||||
n: usize,
|
||||
q: usize
|
||||
}
|
||||
let mut tmp = CountNandQ::<S>::new();
|
||||
|
||||
impl<'a, E: Engine> Backend<E> for &'a mut CountN {
|
||||
fn new_multiplication_gate(&mut self) {
|
||||
self.n += 1;
|
||||
}
|
||||
|
||||
fn new_linear_constraint(&mut self) {
|
||||
self.q += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut tmp = CountN{n:0, q:0};
|
||||
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
|
||||
|
||||
(tmp.n, tmp.q)
|
||||
|
@ -2,13 +2,13 @@ use crate::pairing::ff::{Field};
|
||||
use crate::pairing::{Engine, CurveProjective};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
mod batch;
|
||||
mod poly;
|
||||
pub mod batch;
|
||||
pub mod poly;
|
||||
pub mod prover;
|
||||
pub mod verifier;
|
||||
pub mod helper;
|
||||
mod parameters;
|
||||
mod generator;
|
||||
pub mod parameters;
|
||||
pub mod generator;
|
||||
mod adapted_prover;
|
||||
mod adapted_verifier;
|
||||
mod adapted_helper;
|
||||
@ -23,7 +23,8 @@ pub use self::generator::{
|
||||
generate_parameters_on_srs_and_information,
|
||||
generate_random_parameters,
|
||||
generate_srs,
|
||||
get_circuit_parameters
|
||||
get_circuit_parameters,
|
||||
get_circuit_parameters_for_succinct_sonic
|
||||
};
|
||||
pub use self::parameters::{
|
||||
Proof,
|
||||
|
@ -250,35 +250,16 @@ impl<E: Engine> VerifyingKey<E> {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::sonic::cs::{Backend, Basic, SynthesisDriver};
|
||||
use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::cs::Circuit as SonicCircuit;
|
||||
use crate::sonic::sonic::{Basic, Preprocess};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
||||
impl<E: Engine> VerifyingKey<E> {
|
||||
pub fn new<C: SonicCircuit<E>, S: SynthesisDriver>(circuit: C, srs: &SRS<E>) -> Result<Self, SynthesisError> {
|
||||
struct Preprocess<E: Engine> {
|
||||
k_map: Vec<usize>,
|
||||
n: usize,
|
||||
q: usize,
|
||||
_marker: PhantomData<E>
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> Backend<E> for &'a mut Preprocess<E> {
|
||||
fn new_k_power(&mut self, index: usize) {
|
||||
self.k_map.push(index);
|
||||
}
|
||||
|
||||
fn new_multiplication_gate(&mut self) {
|
||||
self.n += 1;
|
||||
}
|
||||
|
||||
fn new_linear_constraint(&mut self) {
|
||||
self.q += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData };
|
||||
let mut preprocess = Preprocess::new();
|
||||
|
||||
S::synthesize(&mut preprocess, &circuit)?;
|
||||
|
||||
|
@ -31,6 +31,8 @@ pub struct SxEval<E: Engine> {
|
||||
v: Vec<E::Fr>,
|
||||
// x^{i+N} (-y^i -y^{-i} + \sum\limits_{q=1}^Q y^{q+N} w_{q,i})
|
||||
w: Vec<E::Fr>,
|
||||
|
||||
max_n: usize,
|
||||
}
|
||||
|
||||
impl<E: Engine> SxEval<E> {
|
||||
@ -69,6 +71,7 @@ impl<E: Engine> SxEval<E> {
|
||||
u,
|
||||
v,
|
||||
w,
|
||||
max_n: n
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,11 +117,19 @@ impl<E: Engine> SxEval<E> {
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> Backend<E> for &'a mut SxEval<E> {
|
||||
fn new_linear_constraint(&mut self) {
|
||||
type LinearConstraintIndex = E::Fr;
|
||||
|
||||
fn new_linear_constraint(&mut self) -> E::Fr {
|
||||
self.yqn.mul_assign(&self.y);
|
||||
|
||||
self.yqn
|
||||
}
|
||||
|
||||
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>) {
|
||||
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex {
|
||||
self.y.pow(&[(self.max_n + q) as u64])
|
||||
}
|
||||
|
||||
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>, y: &E::Fr) {
|
||||
let acc = match var {
|
||||
Variable::A(index) => {
|
||||
&mut self.u[index - 1]
|
||||
@ -134,13 +145,13 @@ impl<'a, E: Engine> Backend<E> for &'a mut SxEval<E> {
|
||||
match coeff {
|
||||
Coeff::Zero => { },
|
||||
Coeff::One => {
|
||||
acc.add_assign(&self.yqn);
|
||||
acc.add_assign(&y);
|
||||
},
|
||||
Coeff::NegativeOne => {
|
||||
acc.sub_assign(&self.yqn);
|
||||
acc.sub_assign(&y);
|
||||
},
|
||||
Coeff::Full(mut val) => {
|
||||
val.mul_assign(&self.yqn);
|
||||
val.mul_assign(&y);
|
||||
acc.add_assign(&val);
|
||||
}
|
||||
}
|
||||
@ -270,18 +281,25 @@ impl<E: Engine> SyEval<E> {
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> Backend<E> for &'a mut SyEval<E> {
|
||||
fn new_linear_constraint(&mut self) {
|
||||
type LinearConstraintIndex = usize;
|
||||
|
||||
fn new_linear_constraint(&mut self) -> usize {
|
||||
self.current_q += 1;
|
||||
self.current_q
|
||||
}
|
||||
|
||||
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>) {
|
||||
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex {
|
||||
q
|
||||
}
|
||||
|
||||
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>, q: &usize) {
|
||||
match var {
|
||||
Variable::A(index) => {
|
||||
let index = index - 1;
|
||||
// Y^{q+N} += X^{-i} * coeff
|
||||
let mut tmp = self.a[index];
|
||||
coeff.multiply(&mut tmp);
|
||||
let yindex = self.current_q + self.max_n;
|
||||
let yindex = *q + self.max_n;
|
||||
self.positive_coeffs[yindex - 1].add_assign(&tmp);
|
||||
}
|
||||
Variable::B(index) => {
|
||||
@ -289,7 +307,7 @@ impl<'a, E: Engine> Backend<E> for &'a mut SyEval<E> {
|
||||
// Y^{q+N} += X^{i} * coeff
|
||||
let mut tmp = self.b[index];
|
||||
coeff.multiply(&mut tmp);
|
||||
let yindex = self.current_q + self.max_n;
|
||||
let yindex = *q + self.max_n;
|
||||
self.positive_coeffs[yindex - 1].add_assign(&tmp);
|
||||
}
|
||||
Variable::C(index) => {
|
||||
@ -297,7 +315,7 @@ impl<'a, E: Engine> Backend<E> for &'a mut SyEval<E> {
|
||||
// Y^{q+N} += X^{i+N} * coeff
|
||||
let mut tmp = self.c[index];
|
||||
coeff.multiply(&mut tmp);
|
||||
let yindex = self.current_q + self.max_n;
|
||||
let yindex = *q + self.max_n;
|
||||
self.positive_coeffs[yindex - 1].add_assign(&tmp);
|
||||
}
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ use crate::sonic::util::*;
|
||||
use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::sonic::cs::{Circuit, Variable, Coeff};
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::sonic::{CountN, Basic};
|
||||
|
||||
pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
circuit: &C,
|
||||
@ -119,17 +120,7 @@ pub fn create_advice_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
// annoying, but we need n to compute s(z, y), and this isn't
|
||||
// precomputed anywhere yet
|
||||
let n = {
|
||||
struct CountN {
|
||||
n: usize
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> Backend<E> for &'a mut CountN {
|
||||
fn new_multiplication_gate(&mut self) {
|
||||
self.n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut tmp = CountN{n:0};
|
||||
let mut tmp = CountN::<S>::new();
|
||||
S::synthesize(&mut tmp, circuit)?;
|
||||
|
||||
tmp.n
|
||||
@ -147,65 +138,14 @@ pub fn create_proof<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
|
||||
extern crate rand;
|
||||
use self::rand::{Rand, Rng, thread_rng};
|
||||
use crate::sonic::sonic::Wires;
|
||||
|
||||
pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
circuit: &C,
|
||||
srs: &SRS<E>
|
||||
) -> Result<Proof<E>, SynthesisError>
|
||||
{
|
||||
struct Wires<E: Engine> {
|
||||
a: Vec<E::Fr>,
|
||||
b: Vec<E::Fr>,
|
||||
c: Vec<E::Fr>
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> Backend<E> for &'a mut Wires<E> {
|
||||
fn new_multiplication_gate(&mut self) {
|
||||
self.a.push(E::Fr::zero());
|
||||
self.b.push(E::Fr::zero());
|
||||
self.c.push(E::Fr::zero());
|
||||
}
|
||||
|
||||
fn get_var(&self, variable: Variable) -> Option<E::Fr> {
|
||||
Some(match variable {
|
||||
Variable::A(index) => {
|
||||
self.a[index - 1]
|
||||
},
|
||||
Variable::B(index) => {
|
||||
self.b[index - 1]
|
||||
},
|
||||
Variable::C(index) => {
|
||||
self.c[index - 1]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn set_var<F>(&mut self, variable: Variable, value: F) -> Result<(), SynthesisError>
|
||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
let value = value()?;
|
||||
|
||||
match variable {
|
||||
Variable::A(index) => {
|
||||
self.a[index - 1] = value;
|
||||
},
|
||||
Variable::B(index) => {
|
||||
self.b[index - 1] = value;
|
||||
},
|
||||
Variable::C(index) => {
|
||||
self.c[index - 1] = value;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut wires = Wires {
|
||||
a: vec![],
|
||||
b: vec![],
|
||||
c: vec![],
|
||||
};
|
||||
let mut wires = Wires::new();
|
||||
|
||||
S::synthesize(&mut wires, circuit)?;
|
||||
|
||||
@ -392,7 +332,8 @@ fn my_fun_circuit_test() {
|
||||
use crate::pairing::ff::PrimeField;
|
||||
use crate::pairing::bls12_381::{Bls12, Fr};
|
||||
use super::*;
|
||||
use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination};
|
||||
use crate::sonic::cs::{ConstraintSystem, LinearCombination};
|
||||
use crate::sonic::sonic::Basic;
|
||||
use rand::{thread_rng};
|
||||
|
||||
struct MyCircuit;
|
||||
@ -445,7 +386,8 @@ fn polynomial_commitment_test() {
|
||||
use crate::pairing::ff::PrimeFieldRepr;
|
||||
use crate::pairing::bls12_381::{Bls12, Fr};
|
||||
use super::*;
|
||||
use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination};
|
||||
use crate::sonic::cs::{ConstraintSystem, LinearCombination};
|
||||
use crate::sonic::sonic::Basic;
|
||||
use rand::{thread_rng};
|
||||
use crate::pairing::{CurveAffine};
|
||||
|
||||
|
@ -16,6 +16,7 @@ use crate::sonic::util::*;
|
||||
use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::sonic::cs::{Circuit, Variable, Coeff};
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::sonic::Preprocess;
|
||||
|
||||
pub struct MultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> {
|
||||
circuit: C,
|
||||
@ -30,28 +31,7 @@ pub struct MultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> {
|
||||
impl<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> MultiVerifier<E, C, S, R> {
|
||||
// This constructor consumes randomness source cause it's later used internally
|
||||
pub fn new(circuit: C, srs: &SRS<E>, rng: R) -> Result<Self, SynthesisError> {
|
||||
struct Preprocess<E: Engine> {
|
||||
k_map: Vec<usize>,
|
||||
n: usize,
|
||||
q: usize,
|
||||
_marker: PhantomData<E>
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> Backend<E> for &'a mut Preprocess<E> {
|
||||
fn new_k_power(&mut self, index: usize) {
|
||||
self.k_map.push(index);
|
||||
}
|
||||
|
||||
fn new_multiplication_gate(&mut self) {
|
||||
self.n += 1;
|
||||
}
|
||||
|
||||
fn new_linear_constraint(&mut self) {
|
||||
self.q += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData };
|
||||
let mut preprocess = Preprocess::new();
|
||||
|
||||
S::synthesize(&mut preprocess, &circuit)?;
|
||||
|
||||
|
169
src/sonic/sonic/backends.rs
Normal file
169
src/sonic/sonic/backends.rs
Normal file
@ -0,0 +1,169 @@
|
||||
use crate::pairing::{Engine};
|
||||
use crate::sonic::cs::Backend;
|
||||
use std::marker::PhantomData;
|
||||
use crate::SynthesisError;
|
||||
use crate::sonic::cs::SynthesisDriver;
|
||||
|
||||
use crate::sonic::cs::{Circuit, ConstraintSystem, Variable, LinearCombination};
|
||||
|
||||
use crate::pairing::ff::Field;
|
||||
|
||||
pub struct Preprocess<E: Engine> {
|
||||
pub k_map: Vec<usize>,
|
||||
pub n: usize,
|
||||
pub q: usize,
|
||||
_marker: PhantomData<E>
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> Backend<E> for &'a mut Preprocess<E> {
|
||||
type LinearConstraintIndex = ();
|
||||
|
||||
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { () }
|
||||
|
||||
fn new_k_power(&mut self, index: usize) {
|
||||
self.k_map.push(index);
|
||||
}
|
||||
|
||||
fn new_multiplication_gate(&mut self) {
|
||||
self.n += 1;
|
||||
}
|
||||
|
||||
fn new_linear_constraint(&mut self) {
|
||||
self.q += 1;
|
||||
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Preprocess<E> {
|
||||
pub fn new() -> Self {
|
||||
Preprocess {
|
||||
k_map: vec![],
|
||||
n: 0,
|
||||
q: 0,
|
||||
_marker: PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Wires<E: Engine> {
|
||||
pub a: Vec<E::Fr>,
|
||||
pub b: Vec<E::Fr>,
|
||||
pub c: Vec<E::Fr>
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> Backend<E> for &'a mut Wires<E> {
|
||||
type LinearConstraintIndex = ();
|
||||
|
||||
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex { () }
|
||||
|
||||
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { () }
|
||||
fn new_multiplication_gate(&mut self) {
|
||||
self.a.push(E::Fr::zero());
|
||||
self.b.push(E::Fr::zero());
|
||||
self.c.push(E::Fr::zero());
|
||||
}
|
||||
|
||||
fn get_var(&self, variable: Variable) -> Option<E::Fr> {
|
||||
Some(match variable {
|
||||
Variable::A(index) => {
|
||||
self.a[index - 1]
|
||||
},
|
||||
Variable::B(index) => {
|
||||
self.b[index - 1]
|
||||
},
|
||||
Variable::C(index) => {
|
||||
self.c[index - 1]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn set_var<F>(&mut self, variable: Variable, value: F) -> Result<(), SynthesisError>
|
||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
let value = value()?;
|
||||
|
||||
match variable {
|
||||
Variable::A(index) => {
|
||||
self.a[index - 1] = value;
|
||||
},
|
||||
Variable::B(index) => {
|
||||
self.b[index - 1] = value;
|
||||
},
|
||||
Variable::C(index) => {
|
||||
self.c[index - 1] = value;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Wires<E> {
|
||||
pub fn new() -> Self {
|
||||
Wires {
|
||||
a: vec![],
|
||||
b: vec![],
|
||||
c: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CountNandQ<S: SynthesisDriver> {
|
||||
pub n: usize,
|
||||
pub q: usize,
|
||||
_marker: std::marker::PhantomData<S>
|
||||
}
|
||||
|
||||
impl<'a, E: Engine, S: SynthesisDriver> Backend<E> for &'a mut CountNandQ<S> {
|
||||
type LinearConstraintIndex = ();
|
||||
|
||||
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { () }
|
||||
|
||||
fn new_multiplication_gate(&mut self) {
|
||||
self.n += 1;
|
||||
}
|
||||
|
||||
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex {
|
||||
self.q += 1;
|
||||
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SynthesisDriver> CountNandQ<S> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
n: 0,
|
||||
q: 0,
|
||||
_marker: std::marker::PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CountN<S: SynthesisDriver> {
|
||||
pub n: usize,
|
||||
_marker: std::marker::PhantomData<S>
|
||||
}
|
||||
|
||||
impl<'a, E: Engine, S: SynthesisDriver> Backend<E> for &'a mut CountN<S> {
|
||||
type LinearConstraintIndex = ();
|
||||
|
||||
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex { () }
|
||||
|
||||
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { () }
|
||||
|
||||
fn new_multiplication_gate(&mut self) {
|
||||
self.n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SynthesisDriver> CountN<S> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
n: 0,
|
||||
_marker: std::marker::PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
638
src/sonic/sonic/constraint_systems.rs
Normal file
638
src/sonic/sonic/constraint_systems.rs
Normal file
@ -0,0 +1,638 @@
|
||||
use crate::pairing::{Engine};
|
||||
use crate::sonic::cs::Backend;
|
||||
use std::marker::PhantomData;
|
||||
use std::iter::Peekable;
|
||||
|
||||
use crate::SynthesisError;
|
||||
use crate::sonic::cs::SynthesisDriver;
|
||||
|
||||
use crate::sonic::cs::{Circuit, ConstraintSystem, Variable, LinearCombination, Coeff};
|
||||
|
||||
use crate::pairing::ff::Field;
|
||||
|
||||
use super::M;
|
||||
|
||||
pub struct NonassigningSynthesizer<E: Engine, B: Backend<E>> {
|
||||
backend: B,
|
||||
current_variable: Option<usize>,
|
||||
_marker: PhantomData<E>,
|
||||
q: usize,
|
||||
n: usize,
|
||||
}
|
||||
|
||||
impl<E: Engine, B: Backend<E>>NonassigningSynthesizer<E, B> {
|
||||
pub fn new(backend: B) -> Self {
|
||||
Self {
|
||||
backend: backend,
|
||||
current_variable: None,
|
||||
_marker: PhantomData,
|
||||
q: 0,
|
||||
n: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for NonassigningSynthesizer<E, B> {
|
||||
const ONE: Variable = Variable::A(1);
|
||||
|
||||
fn alloc<F>(&mut self, _value: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
match self.current_variable.take() {
|
||||
Some(index) => {
|
||||
let var_b = Variable::B(index);
|
||||
|
||||
self.current_variable = None;
|
||||
|
||||
Ok(var_b)
|
||||
},
|
||||
None => {
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
let var_a = Variable::A(index);
|
||||
|
||||
self.current_variable = Some(index);
|
||||
|
||||
Ok(var_a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_input<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
let input_var = self.alloc(value)?;
|
||||
|
||||
self.enforce_zero(LinearCombination::zero() + input_var);
|
||||
self.backend.new_k_power(self.q);
|
||||
|
||||
Ok(input_var)
|
||||
}
|
||||
|
||||
fn enforce_zero(&mut self, lc: LinearCombination<E>)
|
||||
{
|
||||
self.q += 1;
|
||||
let y = self.backend.new_linear_constraint();
|
||||
|
||||
for (var, coeff) in lc.as_ref() {
|
||||
self.backend.insert_coefficient(*var, *coeff, &y);
|
||||
}
|
||||
}
|
||||
|
||||
fn multiply<F>(&mut self, _values: F) -> Result<(Variable, Variable, Variable), SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>
|
||||
{
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
let a = Variable::A(index);
|
||||
let b = Variable::B(index);
|
||||
let c = Variable::C(index);
|
||||
|
||||
Ok((a, b, c))
|
||||
}
|
||||
|
||||
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
|
||||
self.backend.get_var(var).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Synthesizer<E: Engine, B: Backend<E>> {
|
||||
backend: B,
|
||||
current_variable: Option<usize>,
|
||||
_marker: PhantomData<E>,
|
||||
q: usize,
|
||||
n: usize,
|
||||
}
|
||||
|
||||
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for Synthesizer<E, B> {
|
||||
const ONE: Variable = Variable::A(1);
|
||||
|
||||
fn alloc<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
match self.current_variable.take() {
|
||||
Some(index) => {
|
||||
let var_a = Variable::A(index);
|
||||
let var_b = Variable::B(index);
|
||||
let var_c = Variable::C(index);
|
||||
|
||||
let mut product = None;
|
||||
|
||||
let value_a = self.backend.get_var(var_a);
|
||||
|
||||
self.backend.set_var(var_b, || {
|
||||
let value_b = value()?;
|
||||
product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?);
|
||||
product.as_mut().map(|product| product.mul_assign(&value_b));
|
||||
|
||||
Ok(value_b)
|
||||
})?;
|
||||
|
||||
self.backend.set_var(var_c, || {
|
||||
product.ok_or(SynthesisError::AssignmentMissing)
|
||||
})?;
|
||||
|
||||
self.current_variable = None;
|
||||
|
||||
Ok(var_b)
|
||||
},
|
||||
None => {
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
let var_a = Variable::A(index);
|
||||
|
||||
self.backend.set_var(var_a, value)?;
|
||||
|
||||
self.current_variable = Some(index);
|
||||
|
||||
Ok(var_a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_input<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
let input_var = self.alloc(value)?;
|
||||
|
||||
self.enforce_zero(LinearCombination::zero() + input_var);
|
||||
self.backend.new_k_power(self.q);
|
||||
|
||||
Ok(input_var)
|
||||
}
|
||||
|
||||
fn enforce_zero(&mut self, lc: LinearCombination<E>)
|
||||
{
|
||||
self.q += 1;
|
||||
let y = self.backend.new_linear_constraint();
|
||||
|
||||
for (var, coeff) in lc.as_ref() {
|
||||
self.backend.insert_coefficient(*var, *coeff, &y);
|
||||
}
|
||||
}
|
||||
|
||||
fn multiply<F>(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>
|
||||
{
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
let a = Variable::A(index);
|
||||
let b = Variable::B(index);
|
||||
let c = Variable::C(index);
|
||||
|
||||
let mut b_val = None;
|
||||
let mut c_val = None;
|
||||
|
||||
self.backend.set_var(a, || {
|
||||
let (a, b, c) = values()?;
|
||||
|
||||
b_val = Some(b);
|
||||
c_val = Some(c);
|
||||
|
||||
Ok(a)
|
||||
})?;
|
||||
|
||||
self.backend.set_var(b, || {
|
||||
b_val.ok_or(SynthesisError::AssignmentMissing)
|
||||
})?;
|
||||
|
||||
self.backend.set_var(c, || {
|
||||
c_val.ok_or(SynthesisError::AssignmentMissing)
|
||||
})?;
|
||||
|
||||
Ok((a, b, c))
|
||||
}
|
||||
|
||||
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
|
||||
self.backend.get_var(var).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, B: Backend<E>>Synthesizer<E, B> {
|
||||
pub fn new(backend: B) -> Self {
|
||||
Self {
|
||||
backend: backend,
|
||||
current_variable: None,
|
||||
_marker: PhantomData,
|
||||
q: 0,
|
||||
n: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PermutationSynthesizer<E: Engine, B: Backend<E>> {
|
||||
backend: B,
|
||||
current_variable: Option<usize>,
|
||||
_marker: PhantomData<E>,
|
||||
q: usize,
|
||||
n: usize,
|
||||
|
||||
// These vectors will hold, for all of the wires, the terms related to these
|
||||
// wires for each of the M permutation polynomials. The Coeff<E> is the
|
||||
// coefficient, and the usize is q, the index of the linear constraint and is
|
||||
// related to the power of Y in the s_1(X, Y) polynomial.
|
||||
pub a: Vec<[Option<(Coeff<E>, usize)>; M]>,
|
||||
pub b: Vec<[Option<(Coeff<E>, usize)>; M]>,
|
||||
pub c: Vec<[Option<(Coeff<E>, usize)>; M]>,
|
||||
}
|
||||
|
||||
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for PermutationSynthesizer<E, B> {
|
||||
const ONE: Variable = Variable::A(1);
|
||||
|
||||
fn alloc<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
match self.current_variable.take() {
|
||||
Some(index) => {
|
||||
let var_a = Variable::A(index);
|
||||
let var_b = Variable::B(index);
|
||||
let var_c = Variable::C(index);
|
||||
|
||||
let mut product = None;
|
||||
|
||||
let value_a = self.backend.get_var(var_a);
|
||||
|
||||
self.backend.set_var(var_b, || {
|
||||
let value_b = value()?;
|
||||
product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?);
|
||||
product.as_mut().map(|product| product.mul_assign(&value_b));
|
||||
|
||||
Ok(value_b)
|
||||
})?;
|
||||
|
||||
self.backend.set_var(var_c, || {
|
||||
product.ok_or(SynthesisError::AssignmentMissing)
|
||||
})?;
|
||||
|
||||
self.current_variable = None;
|
||||
|
||||
Ok(var_b)
|
||||
},
|
||||
None => {
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
// Create slots for the new wires.
|
||||
self.a.push([None; M]);
|
||||
self.b.push([None; M]);
|
||||
self.c.push([None; M]);
|
||||
|
||||
let var_a = Variable::A(index);
|
||||
|
||||
self.backend.set_var(var_a, value)?;
|
||||
|
||||
self.current_variable = Some(index);
|
||||
|
||||
Ok(var_a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_input<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
||||
{
|
||||
let input_var = self.alloc(value)?;
|
||||
|
||||
self.enforce_zero(LinearCombination::zero() + input_var);
|
||||
// The new variable has all free slots, so this shouldn't create
|
||||
// more than one linear combination.
|
||||
self.backend.new_k_power(self.q);
|
||||
|
||||
Ok(input_var)
|
||||
}
|
||||
|
||||
fn enforce_zero(&mut self, lc: LinearCombination<E>)
|
||||
{
|
||||
// We just redirect things into the (recursing) enforce_equals method which
|
||||
// does the actual work. Annoyingly, we need to use dynamic dispatch on the
|
||||
// underlying iterator because once you've taken a Peekable<I> you can't get
|
||||
// the underlying iterator (since .next() may have been called on it) so
|
||||
// at each depth of recursion we'd end up with a new type, which is
|
||||
// impossible for the compiler to reason about.
|
||||
let lc = lc.as_ref();
|
||||
let lc: &mut Iterator<Item=&(Variable, Coeff<E>)> = &mut lc.into_iter();
|
||||
let lc = lc.peekable();
|
||||
|
||||
self.enforce_equals(lc, None);
|
||||
}
|
||||
|
||||
fn multiply<F>(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>
|
||||
{
|
||||
self.n += 1;
|
||||
let index = self.n;
|
||||
self.backend.new_multiplication_gate();
|
||||
|
||||
// Create slots for the new wires.
|
||||
self.a.push([None; M]);
|
||||
self.b.push([None; M]);
|
||||
self.c.push([None; M]);
|
||||
|
||||
let a = Variable::A(index);
|
||||
let b = Variable::B(index);
|
||||
let c = Variable::C(index);
|
||||
|
||||
let mut b_val = None;
|
||||
let mut c_val = None;
|
||||
|
||||
self.backend.set_var(a, || {
|
||||
let (a, b, c) = values()?;
|
||||
|
||||
b_val = Some(b);
|
||||
c_val = Some(c);
|
||||
|
||||
Ok(a)
|
||||
})?;
|
||||
|
||||
self.backend.set_var(b, || {
|
||||
b_val.ok_or(SynthesisError::AssignmentMissing)
|
||||
})?;
|
||||
|
||||
self.backend.set_var(c, || {
|
||||
c_val.ok_or(SynthesisError::AssignmentMissing)
|
||||
})?;
|
||||
|
||||
Ok((a, b, c))
|
||||
}
|
||||
|
||||
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
|
||||
self.backend.get_var(var).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, B: Backend<E>> PermutationSynthesizer<E, B> {
|
||||
pub fn new(backend: B) -> Self {
|
||||
Self {
|
||||
backend: backend,
|
||||
current_variable: None,
|
||||
_marker: PhantomData,
|
||||
q: 0,
|
||||
n: 0,
|
||||
|
||||
a: vec![],
|
||||
b: vec![],
|
||||
c: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
// Enforces that the value of `lhs` equals the value
|
||||
// of `rhs`, returning the value of the left hand side
|
||||
// as determined by the assignment. If rhs is none, it
|
||||
// is interpreted to be zero.
|
||||
fn enforce_equals<'a>(
|
||||
&mut self,
|
||||
mut lhs: Peekable<&mut Iterator<Item=&'a (Variable, Coeff<E>)>>,
|
||||
rhs: Option<Variable>
|
||||
) -> Option<E::Fr>
|
||||
{
|
||||
// First, let's create a new linear constraint. We'll save its y value
|
||||
// for the backend and q as well.
|
||||
self.q += 1;
|
||||
let q = self.q;
|
||||
let y = self.backend.new_linear_constraint();
|
||||
let mut slots_available = [true; M];
|
||||
let mut num_slots_available = M;
|
||||
|
||||
// If the caller is enforce_equals we need to return the value of the lhs
|
||||
// so that rhs can be assigned properly, so we keep track of it here.
|
||||
let mut current_value = if rhs.is_some() { Some(E::Fr::zero()) } else { None };
|
||||
|
||||
// If rhs is Some, then we _need_ to involve it in this
|
||||
// linear constraint, so let's just handle it right away. (This also
|
||||
// helps avoid an infinite recursion issue described later.)
|
||||
if let Some(rhs) = rhs {
|
||||
self.emplace_variable(&mut slots_available, &y, rhs, Coeff::NegativeOne, q);
|
||||
num_slots_available -= 1;
|
||||
}
|
||||
|
||||
// Iterate through the linear combination
|
||||
loop {
|
||||
if let Some(term) = lhs.next() {
|
||||
assert!(num_slots_available > 0);
|
||||
|
||||
if num_slots_available == 1 && lhs.peek().is_some() {
|
||||
// We'll be out of slots if we add this variable to the linear
|
||||
// combination; instead, create an ephemeral variable to hold
|
||||
// the value of the remaining terms and use that. Temporarily,
|
||||
// give the variable "zero" value.
|
||||
let ephemeral = self.alloc(|| Ok(E::Fr::zero())).expect("assignment is provided so this should not fail");
|
||||
|
||||
// One of the annoying "tricks" we have to embrace is that the ephemeral
|
||||
// variable has all of its slots available, and so because it's the rhs
|
||||
// when we recursively call `enforce_equals` we know that it will not trigger
|
||||
// a condition in `emplace_variable` that results in the variable being
|
||||
// duplicated; otherwise, the duplicate variable will have a value of zero
|
||||
// and we'd have to somehow track all of the duplicates when we later assign.
|
||||
let mut iter = Some(term).into_iter().chain(lhs);
|
||||
let iter: &mut Iterator<Item=&(Variable, Coeff<E>)> = &mut iter;
|
||||
let value = self.enforce_equals(iter.peekable(), Some(ephemeral));
|
||||
|
||||
// Set the correct ephemeral value right away
|
||||
self.backend.set_var(ephemeral, || {
|
||||
value.ok_or(SynthesisError::AssignmentMissing)
|
||||
}).expect("assignment is provided so this should not fail");
|
||||
|
||||
// Fix the underlying assignment -- the c-wire value will change if the ephemeral
|
||||
// value was a b-wire.
|
||||
self.fix_variable_assignment(ephemeral);
|
||||
|
||||
// Now we emplace the variable into the linear combination.
|
||||
self.emplace_variable(&mut slots_available, &y, ephemeral, Coeff::One, q);
|
||||
num_slots_available -= 1;
|
||||
|
||||
match (&mut current_value, &value) {
|
||||
(Some(ref mut current_value), Some(ref value)) => {
|
||||
current_value.add_assign(&value);
|
||||
},
|
||||
_ => {
|
||||
current_value = None;
|
||||
}
|
||||
}
|
||||
|
||||
assert!(num_slots_available == 0);
|
||||
|
||||
// We're done, so return.
|
||||
return current_value;
|
||||
} else {
|
||||
self.emplace_variable(&mut slots_available, &y, term.0, term.1, q);
|
||||
num_slots_available -= 1;
|
||||
|
||||
match (&mut current_value, self.backend.get_var(term.0)) {
|
||||
(Some(ref mut current_value), Some(mut value)) => {
|
||||
term.1.multiply(&mut value);
|
||||
current_value.add_assign(&value);
|
||||
},
|
||||
_ => {
|
||||
current_value = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We're done, so return.
|
||||
return current_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This takes a variable and coefficient and places it into a linear combination,
|
||||
// given a set of slots that are available, and updates the slot availability to
|
||||
// reflect which slot was chosen.
|
||||
fn emplace_variable(&mut self, slots_available: &mut [bool; M], y: &B::LinearConstraintIndex, var: Variable, coeff: Coeff<E>, q: usize)
|
||||
{
|
||||
// Get the slots for this wire.
|
||||
let wire_slots = self.get_wire_slots(var);
|
||||
|
||||
// Let's handle the simple case where the linear combination and the
|
||||
// variable have a slot that coincides.
|
||||
let mut available_i = None;
|
||||
for i in 0..M {
|
||||
if slots_available[i] {
|
||||
available_i = Some(i);
|
||||
|
||||
if wire_slots[i] {
|
||||
self.emplace_slot(var, i, coeff, y, q);
|
||||
slots_available[i] = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let available_i = available_i.expect("there is always at least one slot open");
|
||||
|
||||
// available_i corresponds to a slot that is available in the linear
|
||||
// combination; clearly, it is not available for the wire. In order
|
||||
// to rectify this, we will create a new wire with the same value.
|
||||
let ephemeral_value = self.backend.get_var(var);
|
||||
let ephemeral = self.alloc(|| {
|
||||
ephemeral_value.ok_or(SynthesisError::AssignmentMissing)
|
||||
}).expect("assignment is provided so this should not fail");
|
||||
|
||||
// Now, we'll emplace the slot for _this_ variable.
|
||||
self.emplace_slot(ephemeral, available_i, coeff, y, q);
|
||||
slots_available[available_i] = false;
|
||||
|
||||
// Next, we'll free up a slot in the original wire
|
||||
let free_i = (available_i + 1) % M;
|
||||
|
||||
// by moving the term to the ephemeral wire.
|
||||
self.move_slot(free_i, var, ephemeral);
|
||||
|
||||
// The original wire has slot free_i available now, and
|
||||
// the new wire has only available_i and (available_i + 1) % M
|
||||
// occupied. As long as M>=3, this means available_i + 2 % M
|
||||
// is a free wire for the ephemeral and it is distinct from
|
||||
// free_i! So, we can relate the ephemeral variable to the
|
||||
// original.
|
||||
let iter = [(var, Coeff::One), (ephemeral, Coeff::NegativeOne)];
|
||||
let mut iter = iter.into_iter();
|
||||
let iter: &mut Iterator<Item=&(Variable, Coeff<E>)> = &mut iter;
|
||||
self.enforce_equals(iter.peekable(), None);
|
||||
}
|
||||
|
||||
// Move slot value from wire to another
|
||||
fn move_slot(&mut self, slot: usize, from: Variable, to: Variable) {
|
||||
let slot_val;
|
||||
{
|
||||
let from_vals = match from {
|
||||
Variable::A(index) => &mut self.a[index - 1],
|
||||
Variable::B(index) => &mut self.b[index - 1],
|
||||
Variable::C(index) => &mut self.c[index - 1],
|
||||
};
|
||||
|
||||
if from_vals[slot].is_none() {
|
||||
// In this case, we do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
slot_val = from_vals[slot].unwrap();
|
||||
from_vals[slot] = None;
|
||||
}
|
||||
|
||||
// We need the backend to compute the cached y^q value for us,
|
||||
// if it needs it.
|
||||
let y = self.backend.get_for_q(slot_val.1);
|
||||
|
||||
self.backend.insert_coefficient(from, -slot_val.0, &y); // Negate coefficient to undo
|
||||
|
||||
{
|
||||
let to_vals = match to {
|
||||
Variable::A(index) => &mut self.a[index - 1],
|
||||
Variable::B(index) => &mut self.b[index - 1],
|
||||
Variable::C(index) => &mut self.c[index - 1],
|
||||
};
|
||||
|
||||
to_vals[slot] = Some(slot_val);
|
||||
self.backend.insert_coefficient(to, slot_val.0, &y);
|
||||
}
|
||||
}
|
||||
|
||||
// Place a coefficient in a slot
|
||||
fn emplace_slot(&mut self, var: Variable, slot_index: usize, coeff: Coeff<E>, y: &B::LinearConstraintIndex, q: usize)
|
||||
{
|
||||
let vals = match var {
|
||||
Variable::A(index) => &mut self.a[index - 1],
|
||||
Variable::B(index) => &mut self.b[index - 1],
|
||||
Variable::C(index) => &mut self.c[index - 1],
|
||||
};
|
||||
|
||||
vals[slot_index] = Some((coeff, q));
|
||||
|
||||
self.backend.insert_coefficient(var, coeff, &y);
|
||||
}
|
||||
|
||||
// Get available slots for a wire
|
||||
fn get_wire_slots(&self, var: Variable) -> [bool; M] {
|
||||
let vals = match var {
|
||||
Variable::A(index) => &self.a[index - 1],
|
||||
Variable::B(index) => &self.b[index - 1],
|
||||
Variable::C(index) => &self.c[index - 1],
|
||||
};
|
||||
|
||||
let mut slots = [true; M];
|
||||
for i in 0..M {
|
||||
if vals[i].is_some() {
|
||||
slots[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
slots
|
||||
}
|
||||
|
||||
// If a variable changes value, we probably need to adjust.
|
||||
fn fix_variable_assignment(&mut self, var: Variable) {
|
||||
let index = var.get_index();
|
||||
|
||||
let a_value = self.backend.get_var(Variable::A(index));
|
||||
let b_value = self.backend.get_var(Variable::B(index));
|
||||
|
||||
let c_value = match (a_value, b_value) {
|
||||
(Some(mut a), Some(b)) => {
|
||||
a.mul_assign(&b);
|
||||
Some(a)
|
||||
},
|
||||
_ => { None }
|
||||
};
|
||||
|
||||
self.backend.set_var(Variable::C(index), || {
|
||||
c_value.ok_or(SynthesisError::AssignmentMissing)
|
||||
}).expect("assignment exists if the closure is called");
|
||||
}
|
||||
}
|
@ -1,3 +1,11 @@
|
||||
mod adaptor;
|
||||
mod synthesis_drivers;
|
||||
mod backends;
|
||||
mod constraint_systems;
|
||||
|
||||
pub use self::adaptor::{Adaptor, AdaptorCircuit};
|
||||
pub use self::adaptor::{Adaptor, AdaptorCircuit};
|
||||
pub use self::synthesis_drivers::{Basic, Nonassigning, Permutation3};
|
||||
pub use self::backends::{CountNandQ, CountN, Preprocess, Wires};
|
||||
pub use self::constraint_systems::{NonassigningSynthesizer, Synthesizer, PermutationSynthesizer};
|
||||
|
||||
pub const M: usize = 3;
|
126
src/sonic/sonic/synthesis_drivers.rs
Normal file
126
src/sonic/sonic/synthesis_drivers.rs
Normal file
@ -0,0 +1,126 @@
|
||||
use std::marker::PhantomData;
|
||||
use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::pairing::{Engine};
|
||||
use crate::sonic::cs::{Variable, Circuit, ConstraintSystem, LinearCombination};
|
||||
use crate::SynthesisError;
|
||||
|
||||
use crate::pairing::ff::{Field};
|
||||
|
||||
use super::constraint_systems::{NonassigningSynthesizer, Synthesizer, PermutationSynthesizer};
|
||||
|
||||
pub struct Basic;
|
||||
|
||||
impl SynthesisDriver for Basic {
|
||||
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
|
||||
let mut tmp: Synthesizer<E, B> = Synthesizer::new(backend);
|
||||
|
||||
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
|
||||
|
||||
match (one, <Synthesizer<E, B> as ConstraintSystem<E>>::ONE) {
|
||||
(Variable::A(1), Variable::A(1)) => {},
|
||||
_ => panic!("one variable is incorrect")
|
||||
}
|
||||
|
||||
circuit.synthesize(&mut tmp)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Nonassigning;
|
||||
|
||||
impl SynthesisDriver for Nonassigning {
|
||||
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
|
||||
let mut tmp: NonassigningSynthesizer<E, B> = NonassigningSynthesizer::new(backend);
|
||||
|
||||
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
|
||||
|
||||
match (one, <NonassigningSynthesizer<E, B> as ConstraintSystem<E>>::ONE) {
|
||||
(Variable::A(1), Variable::A(1)) => {},
|
||||
_ => panic!("one variable is incorrect")
|
||||
}
|
||||
|
||||
circuit.synthesize(&mut tmp)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
In order to use the fully succinct version of Sonic, the resulting s(X, Y) polynomial
|
||||
must be in a more "trivial" form
|
||||
|
||||
s(X, Y) = X^{-N - 1} Y^N s_1(X, Y) - X^N s_2(X, Y)
|
||||
|
||||
where
|
||||
|
||||
s_1(X, Y) = \sum\limits_{i=1}^N u'_i(Y) X^{-i + N + 1}
|
||||
+ \sum\limits_{i=1}^N v'_i(Y) X^{i + N + 1}
|
||||
+ \sum\limits_{i=1}^N w'_i(Y) X^{i + 2N + 1}
|
||||
s_2(X, Y) = \sum\limits_{i=1}^N (Y^i + Y^{-i}) X^i
|
||||
|
||||
u'_i(Y) = \sum\limits_{q=1}^Q Y^q u_{q,i}
|
||||
v'_i(Y) = \sum\limits_{q=1}^Q Y^q v_{q,i}
|
||||
w'_i(Y) = \sum\limits_{q=1}^Q Y^q w_{q,i}
|
||||
|
||||
such that s_1(X, Y) can be expressed as the sum of M permutation polynomials.
|
||||
|
||||
It is trivial for the verifier to evaluate s_2(X, Y), since polynomials of the form
|
||||
x + x^2 + x^3 + ... can be evaluated with a logarithmic number of field operations.
|
||||
|
||||
In order to get s_1(X, Y) into the form needed, each constituent permutation polynomial
|
||||
is effectively of the form
|
||||
|
||||
s_j(X, Y) = \sum\limits_{i=1}^{3N+1} c_i X^i Y^\sigma_j(i)
|
||||
|
||||
where \sigma_j(i) defines the permutation. The X^i corresponds to the wire, and the
|
||||
Y^\sigma_j(i) corresponds to the index of the linear constraint.
|
||||
|
||||
This effectively means that within each polynomial there can be only one particular
|
||||
X^i term, and so wires can only appear in M different linear combinations. Further,
|
||||
because there is only ever a particular Y^i term in each M permutation polynomial,
|
||||
linear combinations can have only M wires.
|
||||
|
||||
In order to synthesize a constraint system into a form that supports this wonky
|
||||
arrangement, we need M>=3. The general goal is to treat each permutation polynomial
|
||||
as a "slot" and, when constructing linear constraints, keep track of which slots are
|
||||
"occupied" by wires, either with respect to the wires themselves or with respect to
|
||||
the linear combination as it is being assembled.
|
||||
|
||||
If the linear combination has more than M terms, then we need to recursively
|
||||
construct ephemeral wires to hold the values of the remaining terms, and relate those
|
||||
wires to those terms in new linear combinations.
|
||||
|
||||
Once our linear combinations are small enough to fit the terms into the M slots,
|
||||
we eagerly shove the terms in. The easy case is when a slot is available for both
|
||||
the wire and the linear combination. The remaining cases can be addressed generally
|
||||
by imagining that the wire has no available slots. We will create a new ephemeral
|
||||
wire that holds the same value as the original wire and use this wire to insert the
|
||||
linear combination. Then, we'll swap one of the terms from another slot into the new
|
||||
ephemeral wire, freeing a slot in the original wire. Then, we trivially have that the
|
||||
new wire and old wire have distinct slots free (since M>=3) and so we can now force
|
||||
that they become equal.
|
||||
|
||||
In terms of actually implementing this, things can get tricky. We don't want to end
|
||||
up in a circumstance where we are infinitely recursing, which can happen depending on
|
||||
the order we create linear combinations for the ephemeral variables.
|
||||
*/
|
||||
pub struct Permutation3;
|
||||
|
||||
impl SynthesisDriver for Permutation3 {
|
||||
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
|
||||
let mut tmp: PermutationSynthesizer<E, B> = PermutationSynthesizer::new(backend);
|
||||
|
||||
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
|
||||
|
||||
match (one, <PermutationSynthesizer<E, B> as ConstraintSystem<E>>::ONE) {
|
||||
(Variable::A(1), Variable::A(1)) => {},
|
||||
_ => panic!("one variable is incorrect")
|
||||
}
|
||||
|
||||
circuit.synthesize(&mut tmp)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -284,7 +284,7 @@ fn test_sonic_mimc() {
|
||||
constants: &constants
|
||||
};
|
||||
|
||||
use crate::sonic::cs::Basic;
|
||||
use crate::sonic::sonic::Basic;
|
||||
use crate::sonic::sonic::AdaptorCircuit;
|
||||
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
|
||||
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
|
||||
@ -351,6 +351,121 @@ fn test_sonic_mimc() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_succinct_sonic_mimc() {
|
||||
use crate::pairing::ff::{Field, PrimeField};
|
||||
use crate::pairing::{Engine, CurveAffine, CurveProjective};
|
||||
use crate::pairing::bls12_381::{Bls12, Fr};
|
||||
use std::time::{Instant};
|
||||
use crate::sonic::srs::SRS;
|
||||
|
||||
let srs_x = Fr::from_str("23923").unwrap();
|
||||
let srs_alpha = Fr::from_str("23728792").unwrap();
|
||||
println!("making srs");
|
||||
let start = Instant::now();
|
||||
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
|
||||
println!("done in {:?}", start.elapsed());
|
||||
|
||||
{
|
||||
use rand::{XorShiftRng, SeedableRng, Rand, Rng};
|
||||
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
// Generate the MiMC round constants
|
||||
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
|
||||
let samples: usize = 100;
|
||||
|
||||
let xl = rng.gen();
|
||||
let xr = rng.gen();
|
||||
let image = mimc::<Bls12>(xl, xr, &constants);
|
||||
|
||||
// Create an instance of our circuit (with the
|
||||
// witness)
|
||||
let circuit = MiMCDemoNoInputs {
|
||||
xl: Some(xl),
|
||||
xr: Some(xr),
|
||||
image: Some(image),
|
||||
constants: &constants
|
||||
};
|
||||
|
||||
use crate::sonic::sonic::Basic;
|
||||
use crate::sonic::sonic::AdaptorCircuit;
|
||||
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
|
||||
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters_for_succinct_sonic};
|
||||
use crate::sonic::helped::helper::{create_aggregate_on_srs};
|
||||
use crate::sonic::sonic::Permutation3;
|
||||
use crate::sonic::unhelped::permutation_structure::*;
|
||||
|
||||
let perm_structure = create_permutation_structure::<Bls12, _>(&AdaptorCircuit(circuit.clone()));
|
||||
perm_structure.create_permutation_arguments(Fr::one(), Fr::one(), rng, &srs);
|
||||
|
||||
|
||||
println!("N = {}, Q = {}", perm_structure.n, perm_structure.q);
|
||||
return;
|
||||
|
||||
// let info = get_circuit_parameters_for_succinct_sonic::<Bls12, _>(circuit.clone()).expect("Must get circuit info");
|
||||
// println!("{:?}", info);
|
||||
|
||||
// println!("creating proof");
|
||||
// let start = Instant::now();
|
||||
// let proof = create_proof_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
|
||||
// println!("done in {:?}", start.elapsed());
|
||||
|
||||
// println!("creating advice");
|
||||
// let start = Instant::now();
|
||||
// let advice = create_advice_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap();
|
||||
// println!("done in {:?}", start.elapsed());
|
||||
|
||||
// println!("creating aggregate for {} proofs", samples);
|
||||
// let start = Instant::now();
|
||||
// let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect();
|
||||
// let aggregate = create_aggregate_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &proofs, &srs);
|
||||
// println!("done in {:?}", start.elapsed());
|
||||
|
||||
// {
|
||||
// let rng = thread_rng();
|
||||
// let mut verifier = MultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
|
||||
// println!("verifying 1 proof without advice");
|
||||
// let start = Instant::now();
|
||||
// {
|
||||
// for _ in 0..1 {
|
||||
// verifier.add_proof(&proof, &[], |_, _| None);
|
||||
// }
|
||||
// assert_eq!(verifier.check_all(), true); // TODO
|
||||
// }
|
||||
// println!("done in {:?}", start.elapsed());
|
||||
// }
|
||||
|
||||
// {
|
||||
// let rng = thread_rng();
|
||||
// let mut verifier = MultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
|
||||
// println!("verifying {} proofs without advice", samples);
|
||||
// let start = Instant::now();
|
||||
// {
|
||||
// for _ in 0..samples {
|
||||
// verifier.add_proof(&proof, &[], |_, _| None);
|
||||
// }
|
||||
// assert_eq!(verifier.check_all(), true); // TODO
|
||||
// }
|
||||
// println!("done in {:?}", start.elapsed());
|
||||
// }
|
||||
|
||||
// {
|
||||
// let rng = thread_rng();
|
||||
// let mut verifier = MultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
|
||||
// println!("verifying 100 proofs with advice");
|
||||
// let start = Instant::now();
|
||||
// {
|
||||
// for (ref proof, ref advice) in &proofs {
|
||||
// verifier.add_proof_with_advice(proof, &[], advice);
|
||||
// }
|
||||
// verifier.add_aggregate(&proofs, &aggregate);
|
||||
// assert_eq!(verifier.check_all(), true); // TODO
|
||||
// }
|
||||
// println!("done in {:?}", start.elapsed());
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inputs_into_sonic_mimc() {
|
||||
use crate::pairing::ff::{Field, PrimeField};
|
||||
@ -388,7 +503,7 @@ fn test_inputs_into_sonic_mimc() {
|
||||
constants: &constants
|
||||
};
|
||||
|
||||
use crate::sonic::cs::Basic;
|
||||
use crate::sonic::sonic::Basic;
|
||||
use crate::sonic::sonic::AdaptorCircuit;
|
||||
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
|
||||
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
|
||||
@ -541,100 +656,100 @@ fn test_high_level_sonic_api() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_constraints_info() {
|
||||
use crate::pairing::bn256::{Bn256};
|
||||
use std::time::{Instant};
|
||||
use crate::sonic::unhelped::padding::{constraints_info};
|
||||
{
|
||||
// This may not be cryptographically safe, use
|
||||
// `OsRng` (for example) in production software.
|
||||
let mut rng = &mut thread_rng();
|
||||
// #[test]
|
||||
// fn test_constraints_info() {
|
||||
// use crate::pairing::bn256::{Bn256};
|
||||
// use std::time::{Instant};
|
||||
// use crate::sonic::unhelped::padding::{constraints_info};
|
||||
// {
|
||||
// // This may not be cryptographically safe, use
|
||||
// // `OsRng` (for example) in production software.
|
||||
// let mut rng = &mut thread_rng();
|
||||
|
||||
// Generate the MiMC round constants
|
||||
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
|
||||
// // Generate the MiMC round constants
|
||||
// let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
|
||||
|
||||
let xl = rng.gen();
|
||||
let xr = rng.gen();
|
||||
let image = mimc::<Bn256>(xl, xr, &constants);
|
||||
// let xl = rng.gen();
|
||||
// let xr = rng.gen();
|
||||
// let image = mimc::<Bn256>(xl, xr, &constants);
|
||||
|
||||
// Create an instance of our circuit (with the
|
||||
// witness)
|
||||
let circuit = MiMCDemo {
|
||||
xl: Some(xl),
|
||||
xr: Some(xr),
|
||||
constants: &constants
|
||||
};
|
||||
// // Create an instance of our circuit (with the
|
||||
// // witness)
|
||||
// let circuit = MiMCDemo {
|
||||
// xl: Some(xl),
|
||||
// xr: Some(xr),
|
||||
// constants: &constants
|
||||
// };
|
||||
|
||||
constraints_info::<Bn256, _>(circuit.clone());
|
||||
}
|
||||
}
|
||||
// constraints_info::<Bn256, _>(circuit.clone());
|
||||
// }
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn test_padding_using_mimc() {
|
||||
use crate::pairing::ff::{Field, PrimeField};
|
||||
use crate::pairing::{Engine, CurveAffine, CurveProjective};
|
||||
use crate::pairing::bls12_381::{Bls12, Fr};
|
||||
use std::time::{Instant};
|
||||
use crate::sonic::srs::SRS;
|
||||
// #[test]
|
||||
// fn test_padding_using_mimc() {
|
||||
// use crate::pairing::ff::{Field, PrimeField};
|
||||
// use crate::pairing::{Engine, CurveAffine, CurveProjective};
|
||||
// use crate::pairing::bls12_381::{Bls12, Fr};
|
||||
// use std::time::{Instant};
|
||||
// use crate::sonic::srs::SRS;
|
||||
|
||||
let srs_x = Fr::from_str("23923").unwrap();
|
||||
let srs_alpha = Fr::from_str("23728792").unwrap();
|
||||
println!("making srs");
|
||||
let start = Instant::now();
|
||||
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
|
||||
println!("done in {:?}", start.elapsed());
|
||||
// let srs_x = Fr::from_str("23923").unwrap();
|
||||
// let srs_alpha = Fr::from_str("23728792").unwrap();
|
||||
// println!("making srs");
|
||||
// let start = Instant::now();
|
||||
// let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
|
||||
// println!("done in {:?}", start.elapsed());
|
||||
|
||||
{
|
||||
// This may not be cryptographically safe, use
|
||||
// `OsRng` (for example) in production software.
|
||||
let rng = &mut thread_rng();
|
||||
// {
|
||||
// // This may not be cryptographically safe, use
|
||||
// // `OsRng` (for example) in production software.
|
||||
// let rng = &mut thread_rng();
|
||||
|
||||
// Generate the MiMC round constants
|
||||
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
|
||||
let samples: usize = 100;
|
||||
// // Generate the MiMC round constants
|
||||
// let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
|
||||
// let samples: usize = 100;
|
||||
|
||||
let xl = rng.gen();
|
||||
let xr = rng.gen();
|
||||
let image = mimc::<Bls12>(xl, xr, &constants);
|
||||
// let xl = rng.gen();
|
||||
// let xr = rng.gen();
|
||||
// let image = mimc::<Bls12>(xl, xr, &constants);
|
||||
|
||||
// Create an instance of our circuit (with the
|
||||
// witness)
|
||||
let circuit = MiMCDemoNoInputs {
|
||||
xl: Some(xl),
|
||||
xr: Some(xr),
|
||||
image: Some(image),
|
||||
constants: &constants
|
||||
};
|
||||
// // Create an instance of our circuit (with the
|
||||
// // witness)
|
||||
// let circuit = MiMCDemoNoInputs {
|
||||
// xl: Some(xl),
|
||||
// xr: Some(xr),
|
||||
// image: Some(image),
|
||||
// constants: &constants
|
||||
// };
|
||||
|
||||
use crate::sonic::cs::Basic;
|
||||
use crate::sonic::sonic::AdaptorCircuit;
|
||||
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
|
||||
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
|
||||
use crate::sonic::helped::helper::{create_aggregate_on_srs};
|
||||
use crate::sonic::unhelped::padding::Padding;
|
||||
// use crate::sonic::cs::Basic;
|
||||
// use crate::sonic::sonic::AdaptorCircuit;
|
||||
// use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
|
||||
// use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
|
||||
// use crate::sonic::helped::helper::{create_aggregate_on_srs};
|
||||
// use crate::sonic::unhelped::padding::Padding;
|
||||
|
||||
let info = get_circuit_parameters::<Bls12, _>(circuit.clone()).expect("Must get circuit info");
|
||||
println!("{:?}", info);
|
||||
// let info = get_circuit_parameters::<Bls12, _>(circuit.clone()).expect("Must get circuit info");
|
||||
// println!("{:?}", info);
|
||||
|
||||
println!("creating proof");
|
||||
let start = Instant::now();
|
||||
let proof = create_proof_on_srs::<Bls12, _, Padding>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
|
||||
println!("done in {:?}", start.elapsed());
|
||||
// println!("creating proof");
|
||||
// let start = Instant::now();
|
||||
// let proof = create_proof_on_srs::<Bls12, _, Padding>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
|
||||
// println!("done in {:?}", start.elapsed());
|
||||
|
||||
{
|
||||
let rng = thread_rng();
|
||||
let mut verifier = MultiVerifier::<Bls12, _, Padding, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
|
||||
println!("K map = {:?}", verifier.get_k_map());
|
||||
println!("verifying 1 proof without advice");
|
||||
let start = Instant::now();
|
||||
{
|
||||
for _ in 0..1 {
|
||||
verifier.add_proof(&proof, &[], |_, _| None);
|
||||
}
|
||||
assert_eq!(verifier.check_all(), true); // TODO
|
||||
}
|
||||
println!("done in {:?}", start.elapsed());
|
||||
}
|
||||
}
|
||||
}
|
||||
// {
|
||||
// let rng = thread_rng();
|
||||
// let mut verifier = MultiVerifier::<Bls12, _, Padding, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
|
||||
// println!("K map = {:?}", verifier.get_k_map());
|
||||
// println!("verifying 1 proof without advice");
|
||||
// let start = Instant::now();
|
||||
// {
|
||||
// for _ in 0..1 {
|
||||
// verifier.add_proof(&proof, &[], |_, _| None);
|
||||
// }
|
||||
// assert_eq!(verifier.check_all(), true); // TODO
|
||||
// }
|
||||
// println!("done in {:?}", start.elapsed());
|
||||
// }
|
||||
// }
|
||||
// }
|
291
src/sonic/unhelped/helper.rs
Normal file
291
src/sonic/unhelped/helper.rs
Normal file
@ -0,0 +1,291 @@
|
||||
use crate::pairing::ff::{Field};
|
||||
use crate::pairing::{Engine, CurveProjective};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::sonic::helped::{Proof, SxyAdvice};
|
||||
use crate::sonic::helped::batch::Batch;
|
||||
use crate::sonic::helped::poly::{SxEval, SyEval};
|
||||
use crate::sonic::helped::Parameters;
|
||||
|
||||
use crate::SynthesisError;
|
||||
|
||||
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
|
||||
use crate::sonic::util::*;
|
||||
use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::sonic::cs::{Circuit, Variable, Coeff};
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::sonic::CountNandQ;
|
||||
use crate::sonic::sonic::M;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SuccinctAggregate<E: Engine> {
|
||||
pub permutations: []
|
||||
pub a: Vec<[Option<(Coeff<E>, usize)>; M]>,
|
||||
pub b: Vec<[Option<(Coeff<E>, usize)>; M]>,
|
||||
pub c: Vec<[Option<(Coeff<E>, usize)>; M]>,
|
||||
}
|
||||
|
||||
pub fn create_aggregate<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
circuit: &C,
|
||||
inputs: &[(Proof<E>, SxyAdvice<E>)],
|
||||
params: &Parameters<E>,
|
||||
) -> SuccinctAggregate<E>
|
||||
{
|
||||
let n = params.vk.n;
|
||||
let q = params.vk.q;
|
||||
|
||||
create_aggregate_on_srs_using_information::<E, C, S>(circuit, inputs, ¶ms.srs, n, q)
|
||||
}
|
||||
|
||||
pub fn create_aggregate_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
circuit: &C,
|
||||
inputs: &[(Proof<E>, SxyAdvice<E>)],
|
||||
srs: &SRS<E>,
|
||||
) -> SuccinctAggregate<E>
|
||||
{
|
||||
// TODO: precompute this?
|
||||
let (n, q) = {
|
||||
let mut tmp = CountNandQ::<S>::new();
|
||||
|
||||
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
|
||||
|
||||
(tmp.n, tmp.q)
|
||||
};
|
||||
|
||||
create_aggregate_on_srs_using_information::<E, C, S>(circuit, inputs, srs, n, q)
|
||||
}
|
||||
|
||||
pub fn create_aggregate_on_srs_using_information<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
circuit: &C,
|
||||
inputs: &[(Proof<E>, SxyAdvice<E>)],
|
||||
srs: &SRS<E>,
|
||||
n: usize,
|
||||
q: usize,
|
||||
) -> SuccinctAggregate<E>
|
||||
{
|
||||
let mut transcript = Transcript::new(&[]);
|
||||
let mut y_values: Vec<E::Fr> = Vec::with_capacity(inputs.len());
|
||||
for &(ref proof, ref sxyadvice) in inputs {
|
||||
{
|
||||
let mut transcript = Transcript::new(&[]);
|
||||
transcript.commit_point(&proof.r);
|
||||
y_values.push(transcript.get_challenge_scalar());
|
||||
}
|
||||
|
||||
transcript.commit_point(&sxyadvice.s);
|
||||
}
|
||||
|
||||
let z: E::Fr = transcript.get_challenge_scalar();
|
||||
|
||||
let t = {
|
||||
let mut tmp: PermutationSynthesizer<E, B> = PermutationSynthesizer::new(backend);
|
||||
|
||||
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
|
||||
|
||||
match (one, <PermutationSynthesizer<E, B> as ConstraintSystem<E>>::ONE) {
|
||||
(Variable::A(1), Variable::A(1)) => {},
|
||||
_ => panic!("one variable is incorrect")
|
||||
}
|
||||
|
||||
circuit.synthesize(&mut tmp).expect("should synthesize");
|
||||
|
||||
tmp
|
||||
};
|
||||
|
||||
// Compute s(z, Y)
|
||||
let (s_poly_negative, s_poly_positive) = {
|
||||
let mut tmp = SyEval::new(z, n, q);
|
||||
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
|
||||
|
||||
tmp.poly()
|
||||
};
|
||||
|
||||
// Compute C = g^{s(z, x)}
|
||||
let c = multiexp(
|
||||
srs.g_positive_x_alpha[0..(n + q)]
|
||||
.iter()
|
||||
.chain_ext(srs.g_negative_x_alpha[0..n].iter()),
|
||||
s_poly_positive.iter().chain_ext(s_poly_negative.iter())
|
||||
).into_affine();
|
||||
|
||||
transcript.commit_point(&c);
|
||||
|
||||
// Open C at w
|
||||
let w: E::Fr = transcript.get_challenge_scalar();
|
||||
|
||||
let value = compute_value::<E>(&w, &s_poly_positive, &s_poly_negative);
|
||||
|
||||
let opening = {
|
||||
let mut value = value;
|
||||
value.negate();
|
||||
|
||||
polynomial_commitment_opening(
|
||||
n,
|
||||
0,
|
||||
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
|
||||
w,
|
||||
&srs
|
||||
)
|
||||
|
||||
// let poly = kate_divison(
|
||||
// s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
|
||||
// w,
|
||||
// );
|
||||
|
||||
// let negative_poly = poly[0..n].iter().rev();
|
||||
// let positive_poly = poly[n..].iter();
|
||||
// multiexp(
|
||||
// srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
|
||||
// srs.g_positive_x[0..positive_poly.len()].iter()
|
||||
// ),
|
||||
// negative_poly.chain_ext(positive_poly)
|
||||
// ).into_affine()
|
||||
};
|
||||
|
||||
// Let's open up C to every y.
|
||||
fn compute_value<E: Engine>(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr {
|
||||
let mut value = E::Fr::zero();
|
||||
let yinv = y.inverse().unwrap(); // TODO
|
||||
|
||||
let positive_powers_contrib = evaluate_at_consequitive_powers(poly_positive, *y, *y);
|
||||
let negative_powers_contrib = evaluate_at_consequitive_powers(poly_negative, yinv, yinv);
|
||||
value.add_assign(&positive_powers_contrib);
|
||||
value.add_assign(&negative_powers_contrib);
|
||||
|
||||
// let mut tmp = yinv;
|
||||
// for &coeff in poly_negative {
|
||||
// let mut coeff = coeff;
|
||||
// coeff.mul_assign(&tmp);
|
||||
// value.add_assign(&coeff);
|
||||
// tmp.mul_assign(&yinv);
|
||||
// }
|
||||
|
||||
// let mut tmp = *y;
|
||||
// for &coeff in poly_positive {
|
||||
// let mut coeff = coeff;
|
||||
// coeff.mul_assign(&tmp);
|
||||
// value.add_assign(&coeff);
|
||||
// tmp.mul_assign(&y);
|
||||
// }
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
use std::time::Instant;
|
||||
let start = Instant::now();
|
||||
|
||||
let mut c_openings = vec![];
|
||||
for y in &y_values {
|
||||
let value = compute_value::<E>(y, &s_poly_positive, &s_poly_negative);
|
||||
|
||||
let opening = {
|
||||
let mut value = value;
|
||||
value.negate();
|
||||
|
||||
polynomial_commitment_opening(
|
||||
n,
|
||||
0,
|
||||
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
|
||||
*y,
|
||||
&srs
|
||||
)
|
||||
|
||||
// let poly = kate_divison(
|
||||
// s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
|
||||
// *y,
|
||||
// );
|
||||
|
||||
// let negative_poly = poly[0..n].iter().rev();
|
||||
// let positive_poly = poly[n..].iter();
|
||||
// multiexp(
|
||||
// srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
|
||||
// srs.g_positive_x[0..positive_poly.len()].iter()
|
||||
// ),
|
||||
// negative_poly.chain_ext(positive_poly)
|
||||
// ).into_affine()
|
||||
};
|
||||
|
||||
c_openings.push((opening, value));
|
||||
}
|
||||
|
||||
println!("Evaluation of s(z, Y) taken {:?}", start.elapsed());
|
||||
|
||||
// Okay, great. Now we need to open up each S at the same point z to the same value.
|
||||
// Since we're opening up all the S's at the same point, we create a bunch of random
|
||||
// challenges instead and open up a random linear combination.
|
||||
|
||||
let mut poly_negative = vec![E::Fr::zero(); n];
|
||||
let mut poly_positive = vec![E::Fr::zero(); 2*n];
|
||||
let mut expected_value = E::Fr::zero();
|
||||
|
||||
// TODO: this part can be further parallelized due to synthesis of S(X, y) being singlethreaded
|
||||
let start = Instant::now();
|
||||
|
||||
for (y, c_opening) in y_values.iter().zip(c_openings.iter()) {
|
||||
// Compute s(X, y_i)
|
||||
let (s_poly_negative, s_poly_positive) = {
|
||||
let mut tmp = SxEval::new(*y, n);
|
||||
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
|
||||
|
||||
tmp.poly()
|
||||
};
|
||||
|
||||
let mut value = c_opening.1;
|
||||
let r: E::Fr = transcript.get_challenge_scalar();
|
||||
value.mul_assign(&r);
|
||||
expected_value.add_assign(&value);
|
||||
|
||||
mul_add_polynomials(& mut poly_negative[..], &s_poly_negative[..], r);
|
||||
mul_add_polynomials(& mut poly_positive[..], &s_poly_positive[..], r);
|
||||
|
||||
// for (mut coeff, target) in s_poly_negative.into_iter().zip(poly_negative.iter_mut()) {
|
||||
// coeff.mul_assign(&r);
|
||||
// target.add_assign(&coeff);
|
||||
// }
|
||||
|
||||
// for (mut coeff, target) in s_poly_positive.into_iter().zip(poly_positive.iter_mut()) {
|
||||
// coeff.mul_assign(&r);
|
||||
// target.add_assign(&coeff);
|
||||
// }
|
||||
}
|
||||
|
||||
println!("Re-evaluation of {} S polynomials taken {:?}", y_values.len(), start.elapsed());
|
||||
|
||||
let s_opening = {
|
||||
let mut value = expected_value;
|
||||
value.negate();
|
||||
|
||||
polynomial_commitment_opening(
|
||||
n,
|
||||
0,
|
||||
poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()),
|
||||
z,
|
||||
&srs
|
||||
)
|
||||
|
||||
// let poly = kate_divison(
|
||||
// poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()),
|
||||
// z,
|
||||
// );
|
||||
|
||||
// let negative_poly = poly[0..n].iter().rev();
|
||||
// let positive_poly = poly[n..].iter();
|
||||
// multiexp(
|
||||
// srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
|
||||
// srs.g_positive_x[0..positive_poly.len()].iter()
|
||||
// ),
|
||||
// negative_poly.chain_ext(positive_poly)
|
||||
// ).into_affine()
|
||||
};
|
||||
|
||||
Aggregate {
|
||||
// Commitment to s(z, Y)
|
||||
c,
|
||||
// We have to open each of the S commitments to a random point `z`
|
||||
s_opening,
|
||||
// We have to open C to each constituent `y`
|
||||
c_openings,
|
||||
// Then we have to finally open C
|
||||
opening,
|
||||
}
|
||||
}
|
@ -7,7 +7,12 @@ mod s2_proof;
|
||||
mod wellformed_argument;
|
||||
mod grand_product_argument;
|
||||
mod permutation_argument;
|
||||
pub mod padding;
|
||||
mod verifier;
|
||||
pub mod permutation_structure;
|
||||
// mod helper;
|
||||
// mod permutation;
|
||||
// pub mod padding;
|
||||
|
||||
pub use self::wellformed_argument::{WellformednessArgument, WellformednessProof};
|
||||
pub use self::permutation_argument::{PermutationArgument, PermutationProof, Proof};
|
||||
pub use self::permutation_argument::{PermutationArgument, PermutationProof, Proof};
|
||||
pub use self::verifier::SuccinctMultiVerifier;
|
@ -47,6 +47,11 @@ fn permute<F: Field>(coeffs: &[F], permutation: & [usize]) -> Vec<F>{
|
||||
assert_eq!(coeffs.len(), permutation.len());
|
||||
let mut result: Vec<F> = vec![F::zero(); coeffs.len()];
|
||||
for (i, j) in permutation.iter().enumerate() {
|
||||
if *j < 1 {
|
||||
// if permutation information is missing coefficient itself must be zero!
|
||||
assert!(coeffs[i].is_zero());
|
||||
continue;
|
||||
}
|
||||
result[*j - 1] = coeffs[i];
|
||||
}
|
||||
result
|
||||
@ -79,7 +84,7 @@ impl<E: Engine> PermutationArgument<E> {
|
||||
|
||||
let n = non_permuted_coefficients[0].len();
|
||||
|
||||
// p1 is just a commitment to the powers of x
|
||||
// p1 is just a commitment to the powers of x. It's indexed from 0 cause there is no g^0
|
||||
let p_1 = multiexp(srs.g_positive_x_alpha[0..n].iter(), vec![E::Fr::one(); n].iter()).into_affine();
|
||||
|
||||
let mut p_2 = vec![];
|
||||
@ -104,7 +109,6 @@ impl<E: Engine> PermutationArgument<E> {
|
||||
|
||||
// p2 is a commitment to the s^{perm}_i * x^i
|
||||
{
|
||||
// let permuted_coeffs = permute(&c[..], &p[..]);
|
||||
let p2 = multiexp(srs.g_positive_x_alpha[0..n].iter(), c.iter()).into_affine();
|
||||
p_2.push(p2);
|
||||
}
|
||||
@ -253,7 +257,7 @@ impl<E: Engine> PermutationArgument<E> {
|
||||
s_polynomial = Some(c.clone());
|
||||
}
|
||||
}
|
||||
let mut s_polynomial = s_polynomial.unwrap();
|
||||
let s_polynomial = s_polynomial.unwrap();
|
||||
// evaluate at z
|
||||
let s_zy = evaluate_at_consequitive_powers(& s_polynomial[..], z, z);
|
||||
|
||||
@ -590,14 +594,12 @@ fn test_permutation_argument() {
|
||||
let srs_alpha = Fr::from_str("23728792").unwrap();
|
||||
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
|
||||
|
||||
let n: usize = 1 << 1;
|
||||
let n: usize = 1 << 4;
|
||||
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
|
||||
// let mut permutation = (0..n).collect::<Vec<_>>();
|
||||
// rng.shuffle(&mut permutation);
|
||||
|
||||
let coeffs = vec![Fr::from_str("3").unwrap(), Fr::from_str("4").unwrap()];
|
||||
let permutation = vec![2, 1];
|
||||
let mut coeffs = (0..n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
|
||||
coeffs[2] = Fr::zero(); // edge case
|
||||
let mut permutation = (1..=n).collect::<Vec<_>>();
|
||||
rng.shuffle(&mut permutation);
|
||||
|
||||
let coeffs = vec![coeffs];
|
||||
let permutations = vec![permutation];
|
||||
@ -607,12 +609,8 @@ fn test_permutation_argument() {
|
||||
let mut argument = PermutationArgument::new(coeffs, permutations);
|
||||
|
||||
let y : Fr = rng.gen();
|
||||
let y : Fr = Fr::one();
|
||||
let y : Fr = Fr::from_str("2").unwrap();
|
||||
|
||||
// let challenges = (0..1).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
|
||||
|
||||
let challenges = vec![Fr::one()];
|
||||
let challenges = (0..1).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
|
||||
|
||||
let commitments = argument.commit(y, &srs);
|
||||
let mut s_commitments = vec![];
|
||||
@ -623,7 +621,6 @@ fn test_permutation_argument() {
|
||||
}
|
||||
|
||||
let z_prime : Fr = rng.gen();
|
||||
let z_prime : Fr = Fr::one();
|
||||
|
||||
let opening = argument.open_commitments_to_s_prime(&challenges, y, z_prime, &srs);
|
||||
|
||||
|
415
src/sonic/unhelped/permutation_structure.rs
Normal file
415
src/sonic/unhelped/permutation_structure.rs
Normal file
@ -0,0 +1,415 @@
|
||||
use crate::pairing::ff::{Field};
|
||||
use crate::pairing::{Engine, CurveProjective};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::sonic::helped::{Proof, SxyAdvice};
|
||||
use crate::sonic::helped::batch::Batch;
|
||||
use crate::sonic::helped::poly::{SxEval, SyEval};
|
||||
use crate::sonic::helped::Parameters;
|
||||
|
||||
use crate::SynthesisError;
|
||||
|
||||
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
|
||||
use crate::sonic::util::*;
|
||||
use crate::sonic::cs::{Backend, SynthesisDriver, ConstraintSystem};
|
||||
use crate::sonic::cs::{Circuit, Variable, Coeff};
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::sonic::Preprocess;
|
||||
use crate::sonic::sonic::M;
|
||||
use crate::sonic::sonic::PermutationSynthesizer;
|
||||
|
||||
use super::s2_proof::*;
|
||||
use super::permutation_argument::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PermutationStructure<E: Engine> {
|
||||
pub n: usize,
|
||||
pub q: usize,
|
||||
pub a: Vec<[Option<(Coeff<E>, usize)>; M]>,
|
||||
pub b: Vec<[Option<(Coeff<E>, usize)>; M]>,
|
||||
pub c: Vec<[Option<(Coeff<E>, usize)>; M]>,
|
||||
}
|
||||
|
||||
pub fn create_permutation_structure<E: Engine, C: Circuit<E>>(
|
||||
circuit: &C,
|
||||
) -> PermutationStructure<E>
|
||||
{
|
||||
let mut backend: Preprocess<E> = Preprocess::new();
|
||||
|
||||
let (a, b, c) = {
|
||||
|
||||
let mut cs: PermutationSynthesizer<E, &'_ mut Preprocess<E>> = PermutationSynthesizer::new(&mut backend);
|
||||
|
||||
let one = cs.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
|
||||
|
||||
match (one, <PermutationSynthesizer<E, &'_ mut Preprocess<E>> as ConstraintSystem<E>>::ONE) {
|
||||
(Variable::A(1), Variable::A(1)) => {},
|
||||
_ => panic!("one variable is incorrect")
|
||||
}
|
||||
|
||||
circuit.synthesize(&mut cs).expect("should synthesize");
|
||||
|
||||
|
||||
(cs.a, cs.b, cs.c)
|
||||
};
|
||||
|
||||
let n = backend.n;
|
||||
let q = backend.q;
|
||||
|
||||
println!("Will have {} gates and {} linear constraints", n, q);
|
||||
|
||||
PermutationStructure::<E> {
|
||||
n: n,
|
||||
q: q,
|
||||
a: a,
|
||||
b: b,
|
||||
c: c
|
||||
}
|
||||
}
|
||||
|
||||
use rand::{Rng, Rand};
|
||||
|
||||
impl<E: Engine> PermutationStructure<E> {
|
||||
pub fn calculate_s2_commitment_value(&self, srs: &SRS<E>) -> E::G1Affine {
|
||||
S2Eval::calculate_commitment_element(self.n, srs)
|
||||
}
|
||||
|
||||
pub fn calculate_s2_proof(&self, x: E::Fr, y: E::Fr, srs: &SRS<E>) -> S2Proof<E> {
|
||||
let s2_eval = S2Eval::new(self.n);
|
||||
|
||||
s2_eval.evaluate(x, y, &srs)
|
||||
}
|
||||
|
||||
pub fn create_permutation_arguments<R: Rng>(&self, y: E::Fr, z: E::Fr, rng: &mut R, srs: &SRS<E>)
|
||||
// -> PermutationProof<E>
|
||||
{
|
||||
// we have to form non-permuted coefficients, as well as permutation structures;
|
||||
let n = self.n;
|
||||
let mut non_permuted_coeffs = vec![vec![E::Fr::zero(); 3*n+1]; M];
|
||||
let mut permutations = vec![vec![0usize; 3*n+1]; M];
|
||||
|
||||
let one = E::Fr::one();
|
||||
let mut minus_one = E::Fr::one();
|
||||
minus_one.negate();
|
||||
// println!("A");
|
||||
let mut not_empty = [false; M];
|
||||
// go other the permutations
|
||||
for (gate_index, info) in self.a.iter().enumerate() {
|
||||
let offset = n-1;
|
||||
for i in 0..M {
|
||||
// coefficients of A are placed at the offset = 0 from the beginning of the vector
|
||||
if let Some((coeff, place)) = info[i].as_ref() {
|
||||
// place it
|
||||
assert!(*place != 0);
|
||||
let x_power = offset - gate_index + 1;
|
||||
let array_position = offset - gate_index; // special for A
|
||||
let place_coeff_into = &mut non_permuted_coeffs[i];
|
||||
let place_permutation_into = &mut permutations[i];
|
||||
match coeff {
|
||||
Coeff::Zero => {
|
||||
// println!("Variable A({}) coefficient 0 at constraint {} in poly {}", gate_index+1, place, i);
|
||||
// println!("Coeff 0 will be for Y^{} for X^{} for permutation {} ", place, x_power, i);
|
||||
},
|
||||
Coeff::One => {
|
||||
not_empty[i] = true;
|
||||
place_coeff_into[array_position] = one;
|
||||
place_permutation_into[array_position] = *place;
|
||||
// println!("Variable A({}) coefficient 1 at constraint {} in poly {}", gate_index+1, place, i);
|
||||
// println!("Term 1*X^{}*Y^{} in poly {}", x_power, place,i);
|
||||
// println!("Coeff 1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
|
||||
},
|
||||
Coeff::NegativeOne => {
|
||||
not_empty[i] = true;
|
||||
place_coeff_into[array_position] = minus_one;
|
||||
place_permutation_into[array_position] = *place;
|
||||
// println!("Variable A({}) coefficient -1 at constraint {} in poly {}", gate_index+1, place, i);
|
||||
// println!("Term -1*X^{}*Y^{} in poly {}", x_power, place,i);
|
||||
// println!("Coeff -1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
|
||||
},
|
||||
Coeff::Full(value) => {
|
||||
not_empty[i] = true;
|
||||
place_coeff_into[array_position] = *value;
|
||||
place_permutation_into[array_position] = *place;
|
||||
// println!("Variable A({}) coefficient * at constraint {} in poly {}", gate_index+1, place, i);
|
||||
// println!("Term k*X^{}*Y^{} in poly {}", x_power, place,i);
|
||||
// println!("Coeff {} will be at for Y^{} for X^{} for permutation {} ", value, place, x_power, i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// println!("Empty coeff for X^{} in permutation {}", gate_index, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// println!("B");
|
||||
for (gate_index, info) in self.b.iter().enumerate() {
|
||||
let offset = n + 1;
|
||||
for i in 0..M {
|
||||
if let Some((coeff, place)) = info[i].as_ref() {
|
||||
// place it
|
||||
assert!(*place != 0);
|
||||
let x_power = offset + gate_index + 1; // 1 indexed
|
||||
let array_position = offset + gate_index;
|
||||
let place_coeff_into = &mut non_permuted_coeffs[i];
|
||||
let place_permutation_into = &mut permutations[i];
|
||||
match coeff {
|
||||
Coeff::Zero => {
|
||||
// println!("Coeff 0 will be for Y^{} for X^{} for permutation {} ", place, x_power, i);
|
||||
// println!("Variable B({}) coefficient 0 at constraint {} in poly {}", gate_index+1, place, i);
|
||||
},
|
||||
Coeff::One => {
|
||||
not_empty[i] = true;
|
||||
place_coeff_into[array_position] = one;
|
||||
place_permutation_into[array_position] = *place;
|
||||
// println!("Coeff 1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
|
||||
// println!("Variable B({}) coefficient 1 at constraint {} in poly {}", gate_index+1, place, i);
|
||||
// println!("Term 1*X^{}*Y^{} in poly {}", x_power, place,i);
|
||||
},
|
||||
Coeff::NegativeOne => {
|
||||
not_empty[i] = true;
|
||||
place_coeff_into[array_position] = minus_one;
|
||||
place_permutation_into[array_position] = *place;
|
||||
// println!("Coeff -1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
|
||||
// println!("Variable B({}) coefficient -1 at constraint {} in poly {}", gate_index+1, place, i);
|
||||
// println!("Term -1*X^{}*Y^{} in poly {}", x_power, place,i);
|
||||
},
|
||||
Coeff::Full(value) => {
|
||||
not_empty[i] = true;
|
||||
place_coeff_into[array_position] = *value;
|
||||
place_permutation_into[array_position] = *place;
|
||||
// println!("Coeff {} will be at for Y^{} for X^{} for permutation {} ", value, place, x_power, i);
|
||||
// println!("Variable B({}) coefficient * at constraint {} in poly {}", gate_index+1, place, i);
|
||||
// println!("Term k*X^{}*Y^{} in poly {}", x_power, place,i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// println!("Empty coeff for X^{} in permutation {}", gate_index, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// println!("C");
|
||||
for (gate_index, info) in self.c.iter().enumerate() {
|
||||
let offset = 2*n + 1;
|
||||
for i in 0..M {
|
||||
// coefficients of A are placed at the offset = 0 from the beginning of the vector
|
||||
if let Some((coeff, place)) = info[i].as_ref() {
|
||||
// place it
|
||||
assert!(*place != 0);
|
||||
let x_power = offset + gate_index + 1; // 1 indexed
|
||||
let array_position = offset + gate_index;
|
||||
let place_coeff_into = &mut non_permuted_coeffs[i];
|
||||
let place_permutation_into = &mut permutations[i];
|
||||
match coeff {
|
||||
Coeff::Zero => {
|
||||
// println!("Coeff 0 will be for Y^{} for X^{} for permutation {} ", place, x_power, i);
|
||||
// println!("Variable C({}) coefficient 0 at constraint {} in poly {}", gate_index+1, place, i);
|
||||
},
|
||||
Coeff::One => {
|
||||
not_empty[i] = true;
|
||||
place_coeff_into[array_position] = one;
|
||||
place_permutation_into[array_position] = *place;
|
||||
// println!("Coeff 1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
|
||||
// println!("Variable C({}) coefficient 1 at constraint {} in poly {}", gate_index+1, place, i);
|
||||
// println!("Term 1*X^{}*Y^{} in poly {}", x_power, place,i);
|
||||
},
|
||||
Coeff::NegativeOne => {
|
||||
not_empty[i] = true;
|
||||
place_coeff_into[array_position] = minus_one;
|
||||
place_permutation_into[array_position] = *place;
|
||||
// println!("Coeff -1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
|
||||
// println!("Variable C({}) coefficient -1 at constraint {} in poly {}", gate_index+1, place, i);
|
||||
// println!("Term -1*X^{}*Y^{} in poly {}", x_power, place,i);
|
||||
},
|
||||
Coeff::Full(value) => {
|
||||
not_empty[i] = true;
|
||||
place_coeff_into[array_position] = *value;
|
||||
place_permutation_into[array_position] = *place;
|
||||
// println!("Coeff {} will be at for Y^{} for X^{} for permutation {} ", value, place, x_power, i);
|
||||
// println!("Variable C({}) coefficient * at constraint {} in poly {}", gate_index+1, place, i);
|
||||
// println!("Term k*X^{}*Y^{} in poly {}", x_power, place,i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// println!("Empty coeff for X^{} in permutation {}", gate_index, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// need to fill arrays with non-zero indexes just to have full permutation, even while it's just zero coefficient
|
||||
|
||||
// permutations[0][2] = 5;
|
||||
// permutations[0][5] = 6;
|
||||
// permutations[0][6] = 7;
|
||||
|
||||
// permutations[1][0] = 1;
|
||||
// permutations[1][1] = 3;
|
||||
// permutations[1][2] = 5;
|
||||
// permutations[1][5] = 6;
|
||||
// permutations[1][6] = 7;
|
||||
|
||||
|
||||
println!("Not empty = {:?}", not_empty);
|
||||
|
||||
// TODO: fix
|
||||
|
||||
let mut m = M;
|
||||
|
||||
for i in (0..M).into_iter().rev() {
|
||||
if !not_empty[i] {
|
||||
non_permuted_coeffs.pop();
|
||||
permutations.pop();
|
||||
m -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
assert!(m != 0);
|
||||
|
||||
// find something faster, although it's still linear
|
||||
|
||||
for i in 0..m {
|
||||
let mut fillers: Vec<usize> = (1..=(3*n+1)).map(|el| el).collect();
|
||||
for (p, c) in permutations[i].iter_mut().zip(non_permuted_coeffs[i].iter()) {
|
||||
if *p == 0 {
|
||||
assert!(c.is_zero());
|
||||
} else {
|
||||
fillers[*p - 1] = 0;
|
||||
}
|
||||
}
|
||||
let mut fill_from = 0;
|
||||
for p in permutations[i].iter_mut() {
|
||||
if *p == 0 {
|
||||
loop {
|
||||
if fillers[fill_from] != 0 {
|
||||
*p = fillers[fill_from];
|
||||
fill_from += 1;
|
||||
break;
|
||||
} else {
|
||||
fill_from += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Will need {} permutation polynomials", m);
|
||||
|
||||
// println!("Nonpermuted 0 (coeffs for X^1, X^2, ...) = {:?}", non_permuted_coeffs[0]);
|
||||
// println!("Permutation 0 (power of Y for X^1, X^2, ...) = {:?}", permutations[0]);
|
||||
|
||||
// println!("Nonpermuted 1 (coeffs for X^1, X^2, ...)= {:?}", non_permuted_coeffs[1]);
|
||||
// println!("Permutation 1 (power of Y for X^1, X^2, ...) = {:?}", permutations[1]);
|
||||
|
||||
let specialized_srs = PermutationArgument::make_specialized_srs(
|
||||
&non_permuted_coeffs,
|
||||
&permutations,
|
||||
&srs
|
||||
);
|
||||
|
||||
let mut argument = PermutationArgument::new(non_permuted_coeffs, permutations);
|
||||
let challenges = (0..m).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
|
||||
|
||||
let commitments = argument.commit(y, &srs);
|
||||
let mut s_commitments = vec![];
|
||||
let mut s_prime_commitments = vec![];
|
||||
for (s, s_prime) in commitments.into_iter() {
|
||||
s_commitments.push(s);
|
||||
// println!("S' = {}", s_prime);
|
||||
s_prime_commitments.push(s_prime);
|
||||
|
||||
}
|
||||
let z_prime : E::Fr = rng.gen();
|
||||
|
||||
let opening = argument.open_commitments_to_s_prime(&challenges, y, z_prime, &srs);
|
||||
|
||||
let randomness = (0..2).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
|
||||
|
||||
let valid = PermutationArgument::verify_s_prime_commitment(n,
|
||||
&randomness,
|
||||
&challenges,
|
||||
&s_prime_commitments,
|
||||
&opening,
|
||||
y,
|
||||
z_prime,
|
||||
&specialized_srs,
|
||||
&srs);
|
||||
|
||||
assert!(valid, "s' commitment must be valid");
|
||||
|
||||
let beta : E::Fr = rng.gen();
|
||||
let gamma : E::Fr = rng.gen();
|
||||
|
||||
let grand_product_challenges = (0..m).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
|
||||
let wellformed_challenges = (0..(2*m)).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
|
||||
|
||||
let proof = argument.make_argument(
|
||||
beta,
|
||||
gamma,
|
||||
& grand_product_challenges,
|
||||
& wellformed_challenges,
|
||||
y,
|
||||
z,
|
||||
&specialized_srs, &srs);
|
||||
|
||||
let valid = PermutationArgument::verify(&s_commitments, &proof, z, &srs);
|
||||
|
||||
assert!(valid, "permutation argument must be valid");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_succinct_sonic() {
|
||||
use crate::pairing::ff::{Field, PrimeField};
|
||||
use crate::pairing::{Engine, CurveAffine, CurveProjective};
|
||||
use crate::pairing::bls12_381::{Bls12, Fr};
|
||||
use std::time::{Instant};
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::cs::{Circuit, ConstraintSystem, LinearCombination};
|
||||
|
||||
struct MyCircuit;
|
||||
|
||||
impl<E: Engine> Circuit<E> for MyCircuit {
|
||||
fn synthesize<CS: ConstraintSystem<E>>(&self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||
let (a, b, _) = cs.multiply(|| {
|
||||
Ok((
|
||||
E::Fr::from_str("10").unwrap(),
|
||||
E::Fr::from_str("20").unwrap(),
|
||||
E::Fr::from_str("200").unwrap(),
|
||||
))
|
||||
})?;
|
||||
|
||||
cs.enforce_zero(LinearCombination::zero() + (Coeff::Full(E::Fr::from_str("2").unwrap()), a) - b);
|
||||
|
||||
let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?;
|
||||
|
||||
cs.enforce_zero(LinearCombination::from(b) - multiplier);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let srs_x = Fr::from_str("23923").unwrap();
|
||||
let srs_alpha = Fr::from_str("23728792").unwrap();
|
||||
println!("making srs");
|
||||
let start = Instant::now();
|
||||
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
|
||||
println!("done in {:?}", start.elapsed());
|
||||
|
||||
{
|
||||
use rand::{XorShiftRng, SeedableRng, Rand, Rng};
|
||||
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
use crate::sonic::sonic::Basic;
|
||||
use crate::sonic::sonic::AdaptorCircuit;
|
||||
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
|
||||
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters_for_succinct_sonic};
|
||||
use crate::sonic::helped::helper::{create_aggregate_on_srs};
|
||||
use crate::sonic::sonic::Permutation3;
|
||||
use crate::sonic::unhelped::permutation_structure::*;
|
||||
|
||||
let perm_structure = create_permutation_structure::<Bls12, _>(&MyCircuit);
|
||||
perm_structure.create_permutation_arguments(Fr::one(), Fr::one(), rng, &srs);
|
||||
|
||||
println!("N = {}, Q = {}", perm_structure.n, perm_structure.q);
|
||||
}
|
||||
}
|
310
src/sonic/unhelped/prover.rs
Normal file
310
src/sonic/unhelped/prover.rs
Normal file
@ -0,0 +1,310 @@
|
||||
use crate::pairing::ff::{Field};
|
||||
use crate::pairing::{Engine, CurveProjective};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::{Proof, SxyAdvice};
|
||||
use super::batch::Batch;
|
||||
use super::poly::{SxEval, SyEval};
|
||||
use super::parameters::{Parameters, NUM_BLINDINGS};
|
||||
|
||||
use crate::SynthesisError;
|
||||
|
||||
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
|
||||
use crate::sonic::util::*;
|
||||
use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::sonic::cs::{Circuit, Variable, Coeff};
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::sonic::{CountN, Basic};
|
||||
|
||||
pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
circuit: &C,
|
||||
proof: &Proof<E>,
|
||||
srs: &SRS<E>,
|
||||
n: usize
|
||||
) -> Result<SxyAdvice<E>, SynthesisError>
|
||||
{
|
||||
let z: E::Fr;
|
||||
let y: E::Fr;
|
||||
{
|
||||
let mut transcript = Transcript::new(&[]);
|
||||
transcript.commit_point(&proof.r);
|
||||
y = transcript.get_challenge_scalar();
|
||||
transcript.commit_point(&proof.t);
|
||||
z = transcript.get_challenge_scalar();
|
||||
}
|
||||
let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?;
|
||||
|
||||
let (s_poly_negative, s_poly_positive) = {
|
||||
let mut tmp = SxEval::new(y, n);
|
||||
S::synthesize(&mut tmp, circuit)?;
|
||||
|
||||
tmp.poly()
|
||||
};
|
||||
|
||||
// Compute S commitment
|
||||
let s = multiexp(
|
||||
srs.g_positive_x_alpha[0..(2 * n)]
|
||||
.iter()
|
||||
.chain_ext(srs.g_negative_x_alpha[0..(n)].iter()),
|
||||
s_poly_positive.iter().chain_ext(s_poly_negative.iter())
|
||||
).into_affine();
|
||||
|
||||
// Compute s(z, y)
|
||||
let mut szy = E::Fr::zero();
|
||||
{
|
||||
szy.add_assign(& evaluate_at_consequitive_powers(& s_poly_positive[..], z, z));
|
||||
szy.add_assign(& evaluate_at_consequitive_powers(& s_poly_negative[..], z_inv, z_inv));
|
||||
}
|
||||
|
||||
// Compute kate opening
|
||||
let opening = {
|
||||
let mut open = szy;
|
||||
open.negate();
|
||||
|
||||
let poly = kate_divison(
|
||||
s_poly_negative.iter().rev().chain_ext(Some(open).iter()).chain_ext(s_poly_positive.iter()),
|
||||
z,
|
||||
);
|
||||
|
||||
let negative_poly = poly[0..n].iter().rev();
|
||||
let positive_poly = poly[n..].iter();
|
||||
multiexp(
|
||||
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
|
||||
srs.g_positive_x[0..positive_poly.len()].iter()
|
||||
),
|
||||
negative_poly.chain_ext(positive_poly)
|
||||
).into_affine()
|
||||
};
|
||||
|
||||
Ok(SxyAdvice {
|
||||
s,
|
||||
szy,
|
||||
opening
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_advice<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
circuit: &C,
|
||||
proof: &Proof<E>,
|
||||
parameters: &Parameters<E>,
|
||||
) -> Result<SxyAdvice<E>, SynthesisError>
|
||||
{
|
||||
let n = parameters.vk.n;
|
||||
create_advice_on_information_and_srs::<E, C, S>(circuit, proof, ¶meters.srs, n)
|
||||
}
|
||||
|
||||
pub fn create_advice_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
circuit: &C,
|
||||
proof: &Proof<E>,
|
||||
srs: &SRS<E>
|
||||
) -> Result<SxyAdvice<E>, SynthesisError>
|
||||
{
|
||||
// annoying, but we need n to compute s(z, y), and this isn't
|
||||
// precomputed anywhere yet
|
||||
let n = {
|
||||
let mut tmp = CountN::<S>::new();
|
||||
S::synthesize(&mut tmp, circuit)?;
|
||||
|
||||
tmp.n
|
||||
};
|
||||
|
||||
create_advice_on_information_and_srs::<E, C, S>(circuit, proof, srs, n)
|
||||
}
|
||||
|
||||
pub fn create_proof<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
circuit: &C,
|
||||
parameters: &Parameters<E>
|
||||
) -> Result<Proof<E>, SynthesisError> {
|
||||
create_proof_on_srs::<E, C, S>(circuit, ¶meters.srs)
|
||||
}
|
||||
|
||||
extern crate rand;
|
||||
use self::rand::{Rand, Rng, thread_rng};
|
||||
use crate::sonic::sonic::Wires;
|
||||
|
||||
pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
|
||||
circuit: &C,
|
||||
srs: &SRS<E>
|
||||
) -> Result<Proof<E>, SynthesisError>
|
||||
{
|
||||
let mut wires = Wires::new();
|
||||
|
||||
S::synthesize(&mut wires, circuit)?;
|
||||
|
||||
let n = wires.a.len();
|
||||
|
||||
let mut transcript = Transcript::new(&[]);
|
||||
|
||||
let rng = &mut thread_rng();
|
||||
|
||||
// c_{n+1}, c_{n+2}, c_{n+3}, c_{n+4}
|
||||
let blindings: Vec<E::Fr> = (0..NUM_BLINDINGS).into_iter().map(|_| E::Fr::rand(rng)).collect();
|
||||
|
||||
// r is a commitment to r(X, 1)
|
||||
let r = polynomial_commitment::<E, _>(
|
||||
n,
|
||||
2*n + NUM_BLINDINGS,
|
||||
n,
|
||||
&srs,
|
||||
blindings.iter().rev()
|
||||
.chain_ext(wires.c.iter().rev())
|
||||
.chain_ext(wires.b.iter().rev())
|
||||
.chain_ext(Some(E::Fr::zero()).iter())
|
||||
.chain_ext(wires.a.iter()),
|
||||
);
|
||||
|
||||
transcript.commit_point(&r);
|
||||
|
||||
let y: E::Fr = transcript.get_challenge_scalar();
|
||||
|
||||
// create r(X, 1) by observation that it's just a series of coefficients.
|
||||
// Used representation is for powers X^{-2n}...X^{-n-1}, X^{-n}...X^{-1}, X^{1}...X^{n}
|
||||
// Same representation is ok for r(X, Y) too cause powers always match
|
||||
let mut rx1 = wires.b;
|
||||
rx1.extend(wires.c);
|
||||
rx1.extend(blindings.clone());
|
||||
rx1.reverse();
|
||||
rx1.push(E::Fr::zero());
|
||||
rx1.extend(wires.a);
|
||||
|
||||
let mut rxy = rx1.clone();
|
||||
|
||||
let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?;
|
||||
|
||||
// y^(-2n - num blindings)
|
||||
let tmp = y_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]);
|
||||
mut_distribute_consequitive_powers(
|
||||
&mut rxy,
|
||||
tmp,
|
||||
y,
|
||||
);
|
||||
|
||||
// negative powers [-1, -2n], positive [1, n]
|
||||
let (mut s_poly_negative, s_poly_positive) = {
|
||||
let mut tmp = SxEval::new(y, n);
|
||||
S::synthesize(&mut tmp, circuit)?;
|
||||
|
||||
tmp.poly()
|
||||
};
|
||||
|
||||
// r'(X, y) = r(X, y) + s(X, y). Note `y` - those are evaluated at the point already
|
||||
let mut rxy_prime = rxy.clone();
|
||||
{
|
||||
// extend to have powers [n+1, 2n]
|
||||
rxy_prime.resize(4 * n + 1 + NUM_BLINDINGS, E::Fr::zero());
|
||||
s_poly_negative.reverse();
|
||||
|
||||
let neg_poly_len = s_poly_negative.len();
|
||||
add_polynomials(&mut rxy_prime[(NUM_BLINDINGS+neg_poly_len)..(2 * n + NUM_BLINDINGS)], &s_poly_negative[..]);
|
||||
s_poly_negative.reverse();
|
||||
|
||||
add_polynomials(&mut rxy_prime[(2 * n + 1 + NUM_BLINDINGS)..], &s_poly_positive[..])
|
||||
|
||||
// // add coefficients in front of X^{-2n}...X^{-n-1}, X^{-n}...X^{-1}
|
||||
// for (r, s) in rxy_prime[NUM_BLINDINGS..(2 * n + NUM_BLINDINGS)]
|
||||
// .iter_mut()
|
||||
// .rev()
|
||||
// .zip(s_poly_negative)
|
||||
// {
|
||||
// r.add_assign(&s);
|
||||
// }
|
||||
// // add coefficients in front of X^{1}...X^{n}, X^{n+1}...X^{2*n}
|
||||
// for (r, s) in rxy_prime[(2 * n + 1 + NUM_BLINDINGS)..].iter_mut().zip(s_poly_positive) {
|
||||
// r.add_assign(&s);
|
||||
// }
|
||||
}
|
||||
|
||||
// by this point all R related polynomials are blinded and evaluated for Y variable
|
||||
|
||||
// t(X, y) = r'(X, y)*r(X, 1) and will be later evaluated at z
|
||||
// contained degree in respect to X are from -4*n to 3*n including X^0
|
||||
let mut txy = multiply_polynomials::<E>(rx1.clone(), rxy_prime);
|
||||
txy[4 * n + 2 * NUM_BLINDINGS] = E::Fr::zero(); // -k(y)
|
||||
|
||||
// commit to t(X, y) to later open at z
|
||||
let t = polynomial_commitment(
|
||||
srs.d,
|
||||
(4 * n) + 2*NUM_BLINDINGS,
|
||||
3 * n,
|
||||
srs,
|
||||
// skip what would be zero power
|
||||
txy[0..(4 * n) + 2*NUM_BLINDINGS].iter()
|
||||
.chain_ext(txy[(4 * n + 2*NUM_BLINDINGS + 1)..].iter()),
|
||||
);
|
||||
|
||||
transcript.commit_point(&t);
|
||||
|
||||
let z: E::Fr = transcript.get_challenge_scalar();
|
||||
let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?;
|
||||
|
||||
let rz = {
|
||||
let tmp = z_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]);
|
||||
|
||||
evaluate_at_consequitive_powers(&rx1, tmp, z)
|
||||
};
|
||||
|
||||
// rzy is evaluation of r(X, Y) at z, y
|
||||
let rzy = {
|
||||
let tmp = z_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]);
|
||||
|
||||
evaluate_at_consequitive_powers(&rxy, tmp, z)
|
||||
};
|
||||
|
||||
transcript.commit_scalar(&rz);
|
||||
transcript.commit_scalar(&rzy);
|
||||
|
||||
let r1: E::Fr = transcript.get_challenge_scalar();
|
||||
|
||||
let zy_opening = {
|
||||
// r(X, 1) - r(z, y)
|
||||
// subtract constant term from R(X, 1)
|
||||
rx1[(2 * n + NUM_BLINDINGS)].sub_assign(&rzy);
|
||||
|
||||
let mut point = y;
|
||||
point.mul_assign(&z);
|
||||
|
||||
polynomial_commitment_opening(
|
||||
2 * n + NUM_BLINDINGS,
|
||||
n,
|
||||
&rx1,
|
||||
point,
|
||||
srs
|
||||
)
|
||||
};
|
||||
|
||||
assert_eq!(rx1.len(), 3*n + NUM_BLINDINGS + 1);
|
||||
|
||||
// it's an opening of t(X, y) at z
|
||||
let z_opening = {
|
||||
rx1[(2 * n + NUM_BLINDINGS)].add_assign(&rzy); // restore
|
||||
|
||||
let rx1_len = rx1.len();
|
||||
mul_add_polynomials(&mut txy[(2 * n + NUM_BLINDINGS)..(2 * n + NUM_BLINDINGS + rx1_len)], &rx1[..], r1);
|
||||
|
||||
// // skip powers from until reach -2n - NUM_BLINDINGS
|
||||
// for (t, &r) in txy[(2 * n + NUM_BLINDINGS)..].iter_mut().zip(rx1.iter()) {
|
||||
// let mut r = r;
|
||||
// r.mul_assign(&r1);
|
||||
// t.add_assign(&r);
|
||||
// }
|
||||
|
||||
let val = {
|
||||
let tmp = z_inv.pow(&[(4*n + 2*NUM_BLINDINGS) as u64]);
|
||||
|
||||
evaluate_at_consequitive_powers(&txy, tmp, z)
|
||||
};
|
||||
|
||||
txy[(4 * n + 2*NUM_BLINDINGS)].sub_assign(&val);
|
||||
|
||||
polynomial_commitment_opening(
|
||||
4*n + 2*NUM_BLINDINGS,
|
||||
3*n,
|
||||
&txy,
|
||||
z,
|
||||
srs)
|
||||
};
|
||||
|
||||
Ok(Proof {
|
||||
r, rz, rzy, t, z_opening, zy_opening
|
||||
})
|
||||
}
|
314
src/sonic/unhelped/verifier.rs
Normal file
314
src/sonic/unhelped/verifier.rs
Normal file
@ -0,0 +1,314 @@
|
||||
use crate::pairing::ff::{Field};
|
||||
use crate::pairing::{Engine, CurveProjective};
|
||||
use std::marker::PhantomData;
|
||||
use rand::{Rand, Rng};
|
||||
|
||||
use crate::sonic::helped::{Proof, SxyAdvice};
|
||||
use crate::sonic::helped::batch::Batch;
|
||||
use crate::sonic::helped::poly::{SxEval, SyEval};
|
||||
use crate::sonic::helped::helper::Aggregate;
|
||||
use crate::sonic::helped::parameters::{Parameters};
|
||||
|
||||
use crate::SynthesisError;
|
||||
|
||||
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
|
||||
use crate::sonic::util::*;
|
||||
use crate::sonic::cs::{Backend, SynthesisDriver};
|
||||
use crate::sonic::cs::{Circuit, Variable, Coeff};
|
||||
use crate::sonic::srs::SRS;
|
||||
use crate::sonic::sonic::Preprocess;
|
||||
|
||||
pub struct SuccinctMultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> {
|
||||
circuit: C,
|
||||
pub(crate) batch: Batch<E>,
|
||||
k_map: Vec<usize>,
|
||||
n: usize,
|
||||
q: usize,
|
||||
randomness_source: R,
|
||||
_marker: PhantomData<(E, S)>
|
||||
}
|
||||
|
||||
impl<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> SuccinctMultiVerifier<E, C, S, R> {
|
||||
// This constructor consumes randomness source cause it's later used internally
|
||||
pub fn new(circuit: C, srs: &SRS<E>, rng: R) -> Result<Self, SynthesisError> {
|
||||
let mut preprocess = Preprocess::new();
|
||||
|
||||
S::synthesize(&mut preprocess, &circuit)?;
|
||||
|
||||
Ok(SuccinctMultiVerifier {
|
||||
circuit,
|
||||
batch: Batch::new(srs, preprocess.n),
|
||||
k_map: preprocess.k_map,
|
||||
n: preprocess.n,
|
||||
q: preprocess.q,
|
||||
randomness_source: rng,
|
||||
_marker: PhantomData
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_aggregate(
|
||||
&mut self,
|
||||
proofs: &[(Proof<E>, SxyAdvice<E>)],
|
||||
aggregate: &Aggregate<E>,
|
||||
szw: E::Fr
|
||||
)
|
||||
{
|
||||
let mut transcript = Transcript::new(&[]);
|
||||
let mut y_values: Vec<E::Fr> = Vec::with_capacity(proofs.len());
|
||||
for &(ref proof, ref sxyadvice) in proofs {
|
||||
{
|
||||
let mut transcript = Transcript::new(&[]);
|
||||
transcript.commit_point(&proof.r);
|
||||
y_values.push(transcript.get_challenge_scalar());
|
||||
}
|
||||
|
||||
transcript.commit_point(&sxyadvice.s);
|
||||
}
|
||||
|
||||
let z: E::Fr = transcript.get_challenge_scalar();
|
||||
|
||||
transcript.commit_point(&aggregate.c);
|
||||
|
||||
let w: E::Fr = transcript.get_challenge_scalar();
|
||||
|
||||
// let szw = {
|
||||
// let mut tmp = SxEval::new(w, self.n);
|
||||
// S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO
|
||||
|
||||
// tmp.finalize(z)
|
||||
// };
|
||||
|
||||
{
|
||||
let random: E::Fr = self.randomness_source.gen();
|
||||
|
||||
self.batch.add_opening(aggregate.opening, random, w);
|
||||
self.batch.add_commitment(aggregate.c, random);
|
||||
self.batch.add_opening_value(szw, random);
|
||||
}
|
||||
|
||||
for ((opening, value), &y) in aggregate.c_openings.iter().zip(y_values.iter()) {
|
||||
let random: E::Fr = self.randomness_source.gen();
|
||||
|
||||
self.batch.add_opening(*opening, random, y);
|
||||
self.batch.add_commitment(aggregate.c, random);
|
||||
self.batch.add_opening_value(*value, random);
|
||||
}
|
||||
|
||||
let random: E::Fr = self.randomness_source.gen();
|
||||
|
||||
let mut expected_value = E::Fr::zero();
|
||||
for ((_, advice), c_opening) in proofs.iter().zip(aggregate.c_openings.iter()) {
|
||||
let mut r: E::Fr = transcript.get_challenge_scalar();
|
||||
|
||||
// expected value of the later opening
|
||||
{
|
||||
let mut tmp = c_opening.1;
|
||||
tmp.mul_assign(&r);
|
||||
expected_value.add_assign(&tmp);
|
||||
}
|
||||
|
||||
r.mul_assign(&random);
|
||||
|
||||
self.batch.add_commitment(advice.s, r);
|
||||
}
|
||||
|
||||
self.batch.add_opening_value(expected_value, random);
|
||||
self.batch.add_opening(aggregate.s_opening, random, z);
|
||||
}
|
||||
|
||||
/// Caller must ensure to add aggregate after adding a proof
|
||||
pub fn add_proof_with_advice(
|
||||
&mut self,
|
||||
proof: &Proof<E>,
|
||||
inputs: &[E::Fr],
|
||||
advice: &SxyAdvice<E>,
|
||||
)
|
||||
{
|
||||
let mut z = None;
|
||||
|
||||
self.add_proof(proof, inputs, |_z, _y| {
|
||||
z = Some(_z);
|
||||
Some(advice.szy)
|
||||
});
|
||||
|
||||
let z = z.unwrap();
|
||||
|
||||
// We need to open up SxyAdvice.s at z using SxyAdvice.opening
|
||||
let mut transcript = Transcript::new(&[]);
|
||||
transcript.commit_point(&advice.opening);
|
||||
transcript.commit_point(&advice.s);
|
||||
transcript.commit_scalar(&advice.szy);
|
||||
let random: E::Fr = self.randomness_source.gen();
|
||||
|
||||
self.batch.add_opening(advice.opening, random, z);
|
||||
self.batch.add_commitment(advice.s, random);
|
||||
self.batch.add_opening_value(advice.szy, random);
|
||||
}
|
||||
|
||||
pub fn add_proof<F>(
|
||||
&mut self,
|
||||
proof: &Proof<E>,
|
||||
inputs: &[E::Fr],
|
||||
sxy: F
|
||||
)
|
||||
where F: FnOnce(E::Fr, E::Fr) -> Option<E::Fr>
|
||||
{
|
||||
let mut transcript = Transcript::new(&[]);
|
||||
|
||||
transcript.commit_point(&proof.r);
|
||||
|
||||
let y: E::Fr = transcript.get_challenge_scalar();
|
||||
|
||||
transcript.commit_point(&proof.t);
|
||||
|
||||
let z: E::Fr = transcript.get_challenge_scalar();
|
||||
|
||||
transcript.commit_scalar(&proof.rz);
|
||||
transcript.commit_scalar(&proof.rzy);
|
||||
|
||||
let r1: E::Fr = transcript.get_challenge_scalar();
|
||||
|
||||
transcript.commit_point(&proof.z_opening);
|
||||
transcript.commit_point(&proof.zy_opening);
|
||||
|
||||
// First, the easy one. Let's open up proof.r at zy, using proof.zy_opening
|
||||
// as the evidence and proof.rzy as the opening.
|
||||
{
|
||||
let random: E::Fr = self.randomness_source.gen();
|
||||
let mut zy = z;
|
||||
zy.mul_assign(&y);
|
||||
self.batch.add_opening(proof.zy_opening, random, zy);
|
||||
self.batch.add_commitment_max_n(proof.r, random);
|
||||
self.batch.add_opening_value(proof.rzy, random);
|
||||
}
|
||||
|
||||
// Now we need to compute t(z, y) with what we have. Let's compute k(y).
|
||||
let mut ky = E::Fr::zero();
|
||||
for (exp, input) in self.k_map.iter().zip(Some(E::Fr::one()).iter().chain(inputs.iter())) {
|
||||
let mut term = y.pow(&[(*exp + self.n) as u64]);
|
||||
term.mul_assign(input);
|
||||
ky.add_assign(&term);
|
||||
}
|
||||
|
||||
// Compute s(z, y)
|
||||
let szy = sxy(z, y).unwrap_or_else(|| {
|
||||
let mut tmp = SxEval::new(y, self.n);
|
||||
S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO
|
||||
|
||||
tmp.finalize(z)
|
||||
|
||||
// let mut tmp = SyEval::new(z, self.n, self.q);
|
||||
// S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO
|
||||
|
||||
// tmp.finalize(y)
|
||||
});
|
||||
|
||||
// Finally, compute t(z, y)
|
||||
// t(z, y) = (r(z, y) + s(z,y))*r(z, 1) - k(y)
|
||||
let mut tzy = proof.rzy;
|
||||
tzy.add_assign(&szy);
|
||||
tzy.mul_assign(&proof.rz);
|
||||
tzy.sub_assign(&ky);
|
||||
|
||||
// We open these both at the same time by keeping their commitments
|
||||
// linearly independent (using r1).
|
||||
{
|
||||
let mut random: E::Fr = self.randomness_source.gen();
|
||||
|
||||
self.batch.add_opening(proof.z_opening, random, z);
|
||||
self.batch.add_opening_value(tzy, random);
|
||||
self.batch.add_commitment(proof.t, random);
|
||||
|
||||
random.mul_assign(&r1);
|
||||
|
||||
self.batch.add_opening_value(proof.rz, random);
|
||||
self.batch.add_commitment_max_n(proof.r, random);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_k_map(&self) -> Vec<usize> {
|
||||
return self.k_map.clone();
|
||||
}
|
||||
|
||||
pub fn get_n(&self) -> usize {
|
||||
return self.n;
|
||||
}
|
||||
|
||||
pub fn get_q(&self) -> usize {
|
||||
return self.q;
|
||||
}
|
||||
|
||||
pub fn check_all(self) -> bool {
|
||||
self.batch.check_all()
|
||||
}
|
||||
}
|
||||
|
||||
// /// Check multiple proofs without aggregation. Verifier's work is
|
||||
// /// not succint due to `S(X, Y)` evaluation
|
||||
// pub fn verify_proofs<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng>(
|
||||
// proofs: &[Proof<E>],
|
||||
// inputs: &[Vec<E::Fr>],
|
||||
// circuit: C,
|
||||
// rng: R,
|
||||
// params: &Parameters<E>,
|
||||
// ) -> Result<bool, SynthesisError> {
|
||||
// verify_proofs_on_srs::<E, C, S, R>(proofs, inputs, circuit, rng, ¶ms.srs)
|
||||
// }
|
||||
|
||||
// /// Check multiple proofs without aggregation. Verifier's work is
|
||||
// /// not succint due to `S(X, Y)` evaluation
|
||||
// pub fn verify_proofs_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng>(
|
||||
// proofs: &[Proof<E>],
|
||||
// inputs: &[Vec<E::Fr>],
|
||||
// circuit: C,
|
||||
// rng: R,
|
||||
// srs: &SRS<E>,
|
||||
// ) -> Result<bool, SynthesisError> {
|
||||
// let mut verifier = MultiVerifier::<E, C, S, R>::new(circuit, srs, rng)?;
|
||||
// let expected_inputs_size = verifier.get_k_map().len() - 1;
|
||||
// for (proof, inputs) in proofs.iter().zip(inputs.iter()) {
|
||||
// if inputs.len() != expected_inputs_size {
|
||||
// return Err(SynthesisError::Unsatisfiable);
|
||||
// }
|
||||
// verifier.add_proof(proof, &inputs, |_, _| None);
|
||||
// }
|
||||
|
||||
// Ok(verifier.check_all())
|
||||
// }
|
||||
|
||||
// /// Check multiple proofs with aggregation. Verifier's work is
|
||||
// /// not succint due to `S(X, Y)` evaluation
|
||||
// pub fn verify_aggregate<E: Engine, C: Circuit<E>, S: SynthesisDriver,R: Rng>(
|
||||
// proofs: &[(Proof<E>, SxyAdvice<E>)],
|
||||
// aggregate: &Aggregate<E>,
|
||||
// inputs: &[Vec<E::Fr>],
|
||||
// circuit: C,
|
||||
// rng: R,
|
||||
// params: &Parameters<E>,
|
||||
// ) -> Result<bool, SynthesisError> {
|
||||
// verify_aggregate_on_srs::<E, C, S, R>(proofs, aggregate, inputs, circuit, rng, ¶ms.srs)
|
||||
// }
|
||||
|
||||
// /// Check multiple proofs with aggregation. Verifier's work is
|
||||
// /// not succint due to `S(X, Y)` evaluation
|
||||
// pub fn verify_aggregate_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng>(
|
||||
// proofs: &[(Proof<E>, SxyAdvice<E>)],
|
||||
// aggregate: &Aggregate<E>,
|
||||
// inputs: &[Vec<E::Fr>],
|
||||
// circuit: C,
|
||||
// rng: R,
|
||||
// srs: &SRS<E>,
|
||||
// ) -> Result<bool, SynthesisError> {
|
||||
// let mut verifier = MultiVerifier::<E, C, S, R>::new(circuit, srs, rng)?;
|
||||
// let expected_inputs_size = verifier.get_k_map().len() - 1;
|
||||
// for ((proof, advice), inputs) in proofs.iter().zip(inputs.iter()) {
|
||||
// if inputs.len() != expected_inputs_size {
|
||||
// return Err(SynthesisError::Unsatisfiable);
|
||||
// }
|
||||
// verifier.add_proof_with_advice(proof, &inputs, &advice);
|
||||
// }
|
||||
// verifier.add_aggregate(proofs, aggregate);
|
||||
|
||||
// Ok(verifier.check_all())
|
||||
// }
|
||||
|
Loading…
Reference in New Issue
Block a user