#![allow(clippy::too_many_arguments)]
use crate::constraint_system::Variable;
use crate::permutation::Permutation;
use dusk_bls12_381::Scalar;
use std::collections::HashMap;
#[derive(Debug)]
pub struct StandardComposer {
pub(crate) n: usize,
pub(crate) q_m: Vec<Scalar>,
pub(crate) q_l: Vec<Scalar>,
pub(crate) q_r: Vec<Scalar>,
pub(crate) q_o: Vec<Scalar>,
pub(crate) q_4: Vec<Scalar>,
pub(crate) q_c: Vec<Scalar>,
pub(crate) q_arith: Vec<Scalar>,
pub(crate) q_range: Vec<Scalar>,
pub(crate) q_logic: Vec<Scalar>,
pub(crate) q_ecc: Vec<Scalar>,
pub(crate) public_inputs: Vec<Scalar>,
pub(crate) w_l: Vec<Variable>,
pub(crate) w_r: Vec<Variable>,
pub(crate) w_o: Vec<Variable>,
pub(crate) w_4: Vec<Variable>,
pub(crate) zero_var: Variable,
pub(crate) variables: HashMap<Variable, Scalar>,
pub(crate) perm: Permutation,
}
impl StandardComposer {
pub fn circuit_size(&self) -> usize {
self.n
}
}
impl Default for StandardComposer {
fn default() -> Self {
Self::new()
}
}
impl StandardComposer {
pub fn new() -> Self {
StandardComposer::with_expected_size(0)
}
pub fn add_witness_to_circuit_description(&mut self, var: Variable, value: Scalar) {
self.poly_gate(
var,
var,
var,
Scalar::zero(),
Scalar::one(),
Scalar::zero(),
Scalar::zero(),
-value,
Scalar::zero(),
);
}
pub fn with_expected_size(expected_size: usize) -> Self {
let mut composer = StandardComposer {
n: 0,
q_m: Vec::with_capacity(expected_size),
q_l: Vec::with_capacity(expected_size),
q_r: Vec::with_capacity(expected_size),
q_o: Vec::with_capacity(expected_size),
q_c: Vec::with_capacity(expected_size),
q_4: Vec::with_capacity(expected_size),
q_arith: Vec::with_capacity(expected_size),
q_range: Vec::with_capacity(expected_size),
q_logic: Vec::with_capacity(expected_size),
q_ecc: Vec::with_capacity(expected_size),
public_inputs: Vec::with_capacity(expected_size),
w_l: Vec::with_capacity(expected_size),
w_r: Vec::with_capacity(expected_size),
w_o: Vec::with_capacity(expected_size),
w_4: Vec::with_capacity(expected_size),
zero_var: Variable(0),
variables: HashMap::with_capacity(expected_size),
perm: Permutation::new(),
};
let zero_var = composer.add_input(Scalar::zero());
composer.add_witness_to_circuit_description(zero_var, Scalar::zero());
composer.zero_var = zero_var;
composer.add_dummy_constraints();
composer
}
pub fn add_input(&mut self, s: Scalar) -> Variable {
let var = self.perm.new_variable();
self.variables.insert(var, s);
var
}
pub fn add_constant_witness(&mut self, s: Scalar) -> Variable {
let var = self.add_input(s);
self.constrain_to_constant(var, s, Scalar::zero());
var
}
pub fn poly_gate(
&mut self,
a: Variable,
b: Variable,
c: Variable,
q_m: Scalar,
q_l: Scalar,
q_r: Scalar,
q_o: Scalar,
q_c: Scalar,
pi: Scalar,
) -> (Variable, Variable, Variable) {
self.w_l.push(a);
self.w_r.push(b);
self.w_o.push(c);
self.w_4.push(self.zero_var);
self.q_l.push(q_l);
self.q_r.push(q_r);
self.q_m.push(q_m);
self.q_o.push(q_o);
self.q_c.push(q_c);
self.q_4.push(Scalar::zero());
self.q_arith.push(Scalar::one());
self.q_range.push(Scalar::zero());
self.q_logic.push(Scalar::zero());
self.q_ecc.push(Scalar::zero());
self.public_inputs.push(pi);
self.perm
.add_variables_to_map(a, b, c, self.zero_var, self.n);
self.n += 1;
(a, b, c)
}
pub fn constrain_to_constant(&mut self, a: Variable, constant: Scalar, pi: Scalar) {
self.poly_gate(
a,
a,
a,
Scalar::zero(),
Scalar::one(),
Scalar::zero(),
Scalar::zero(),
-constant,
pi,
);
}
pub fn assert_equal(&mut self, a: Variable, b: Variable) {
self.poly_gate(
a,
b,
self.zero_var,
Scalar::zero(),
Scalar::one(),
-Scalar::one(),
Scalar::zero(),
Scalar::zero(),
Scalar::zero(),
);
}
pub fn add_dummy_constraints(&mut self) {
self.q_m.push(Scalar::from(1));
self.q_l.push(Scalar::from(2));
self.q_r.push(Scalar::from(3));
self.q_o.push(Scalar::from(4));
self.q_c.push(Scalar::from(4));
self.q_4.push(Scalar::one());
self.q_arith.push(Scalar::one());
self.q_range.push(Scalar::zero());
self.q_logic.push(Scalar::zero());
self.q_ecc.push(Scalar::zero());
self.public_inputs.push(Scalar::zero());
let var_six = self.add_input(Scalar::from(6));
let var_one = self.add_input(Scalar::from(1));
let var_seven = self.add_input(Scalar::from(7));
let var_min_twenty = self.add_input(-Scalar::from(20));
self.w_l.push(var_six);
self.w_r.push(var_seven);
self.w_o.push(var_min_twenty);
self.w_4.push(var_one);
self.perm
.add_variables_to_map(var_six, var_seven, var_min_twenty, var_one, self.n);
self.n += 1;
self.q_m.push(Scalar::from(1));
self.q_l.push(Scalar::from(1));
self.q_r.push(Scalar::from(1));
self.q_o.push(Scalar::from(1));
self.q_c.push(Scalar::from(127));
self.q_4.push(Scalar::zero());
self.q_arith.push(Scalar::one());
self.q_range.push(Scalar::zero());
self.q_logic.push(Scalar::zero());
self.q_ecc.push(Scalar::zero());
self.public_inputs.push(Scalar::zero());
self.w_l.push(var_min_twenty);
self.w_r.push(var_six);
self.w_o.push(var_seven);
self.w_4.push(self.zero_var);
self.perm
.add_variables_to_map(var_min_twenty, var_six, var_seven, self.zero_var, self.n);
self.n += 1;
}
#[cfg(feature = "trace")]
pub fn check_circuit_satisfied(&self) {
let w_l: Vec<&Scalar> = self
.w_l
.iter()
.map(|w_l_i| self.variables.get(&w_l_i).unwrap())
.collect();
let w_r: Vec<&Scalar> = self
.w_r
.iter()
.map(|w_r_i| self.variables.get(&w_r_i).unwrap())
.collect();
let w_o: Vec<&Scalar> = self
.w_o
.iter()
.map(|w_o_i| self.variables.get(&w_o_i).unwrap())
.collect();
let w_4: Vec<&Scalar> = self
.w_4
.iter()
.map(|w_4_i| self.variables.get(&w_4_i).unwrap())
.collect();
let delta = |f: Scalar| -> Scalar {
let f_1 = f - Scalar::one();
let f_2 = f - Scalar::from(2);
let f_3 = f - Scalar::from(3);
f * f_1 * f_2 * f_3
};
let four = Scalar::from(4);
for i in 0..self.n {
let qm = self.q_m[i];
let ql = self.q_l[i];
let qr = self.q_r[i];
let qo = self.q_o[i];
let qc = self.q_c[i];
let q4 = self.q_4[i];
let qarith = self.q_arith[i];
let qrange = self.q_range[i];
let qlogic = self.q_logic[i];
let qecc = self.q_ecc[i];
let pi = self.public_inputs[i];
let a = w_l[i];
let a_next = w_l[(i + 1) % self.n];
let b = w_r[i];
let b_next = w_r[(i + 1) % self.n];
let c = w_o[i];
let d = w_4[i];
let d_next = w_4[(i + 1) % self.n];
#[cfg(feature = "trace-print")]
println!(
"--------------------------------------------\n
#Gate Index = {}
#Selector Polynomials:\n
- qm -> {:?}\n
- ql -> {:?}\n
- qr -> {:?}\n
- q4 -> {:?}\n
- qo -> {:?}\n
- qc -> {:?}\n
- q_arith -> {:?}\n
- q_range -> {:?}\n
- q_logic -> {:?}\n
- q_ecc -> {:?}\n
# Witness polynomials:\n
- w_l -> {:?}\n
- w_r -> {:?}\n
- w_o -> {:?}\n
- w_4 -> {:?}\n",
i, qm, ql, qr, q4, qo, qc, qarith, qrange, qlogic, qecc, a, b, c, d
);
let k = qarith * ((qm * a * b) + (ql * a) + (qr * b) + (qo * c) + (q4 * d) + pi + qc)
+ qlogic
* (((delta(a_next - four * a) - delta(b_next - four * b)) * c)
+ delta(a_next - four * a)
+ delta(b_next - four * b)
+ delta(d_next - four * d)
+ match (qlogic == Scalar::one(), qlogic == -Scalar::one()) {
(true, false) => (a & b) - d,
(false, true) => (a ^ b) - d,
(false, false) => Scalar::zero(),
_ => unreachable!(),
})
+ qrange
* (delta(c - four * d)
+ delta(b - four * c)
+ delta(a - four * b)
+ delta(d_next - four * a));
assert_eq!(k, Scalar::zero(), "Check failed at gate {}", i,);
}
}
}
#[cfg(test)]
mod tests {
use super::super::helper::*;
use super::*;
use crate::commitment_scheme::kzg10::PublicParameters;
use crate::proof_system::{Prover, Verifier};
#[test]
fn test_initial_circuit_size() {
let composer: StandardComposer = StandardComposer::new();
assert_eq!(3, composer.circuit_size())
}
#[allow(unused_variables)]
#[test]
#[ignore]
fn test_prove_verify() {
let res = gadget_tester(
|composer| {
},
200,
);
assert!(res.is_ok());
}
#[test]
fn test_multiple_proofs() {
let public_parameters = PublicParameters::setup(2 * 30, &mut rand::thread_rng()).unwrap();
let mut prover = Prover::new(b"demo");
dummy_gadget(10, prover.mut_cs());
let (ck, _) = public_parameters.trim(2 * 20).unwrap();
prover.preprocess(&ck).unwrap();
let public_inputs = prover.cs.public_inputs.clone();
let mut proofs = Vec::new();
for _ in 0..3 {
proofs.push(prover.prove(&ck).unwrap());
dummy_gadget(10, prover.mut_cs());
}
let mut verifier = Verifier::new(b"demo");
dummy_gadget(10, verifier.mut_cs());
let (ck, vk) = public_parameters.trim(2 * 20).unwrap();
verifier.preprocess(&ck).unwrap();
for proof in proofs {
assert!(verifier.verify(&proof, &vk, &public_inputs).is_ok());
}
}
}