From 2ed2944b351f5d39107e9386e590628673596225 Mon Sep 17 00:00:00 2001 From: rebenkoy Date: Sat, 25 Nov 2023 02:40:35 +0300 Subject: [PATCH 1/5] prover simple test, error term computation --- src/circuit.rs | 39 +++++-- src/constraint_system.rs | 90 ++++++++-------- src/folding/shape.rs | 6 +- src/lib.rs | 3 +- src/prover.rs | 220 +++++++++++++++++++++++++++++++++++++++ src/witness.rs | 21 ++-- 6 files changed, 314 insertions(+), 65 deletions(-) create mode 100644 src/prover.rs diff --git a/src/circuit.rs b/src/circuit.rs index 7876e54..e5e76f5 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -3,7 +3,7 @@ use elsa::map::FrozenMap; use ff::PrimeField; use itertools::Itertools; -use crate::{witness::{CSWtns, ProtostarWtns, ProtostarLhsWtns}, gate::{Gatebb, Gate}, constraint_system::{Variable, ConstraintSystem, CommitKind, Visibility, CS, Constraint}, utils::poly_utils::check_poly, circuit::circuit_operations::{AttachedAdvice, AttachedPolynomialAdvice, AttachedAdvicePub}, external_interface::{RunIndex, RunAllocator} }; +use crate::{witness::{CSWtns, ProtostarWtns, ProtostarLhsWtns}, gate::{Gatebb, Gate}, constraint_system::{Variable, ProtoGalaxyConstraintSystem, CommitKind, Visibility, CS, Constraint}, utils::poly_utils::check_poly, circuit::circuit_operations::{AttachedAdvice, AttachedPolynomialAdvice, AttachedAdvicePub}, external_interface::{RunIndex, RunAllocator} }; use self::circuit_operations::CircuitOperation; @@ -184,7 +184,7 @@ pub mod circuit_operations { pub struct Circuit<'circuit, F: PrimeField, G: Gate<'circuit, F> + From>> { gate_registry: FrozenMap>, - pub cs: ConstraintSystem<'circuit, F, G>, + pub cs: ProtoGalaxyConstraintSystem<'circuit, F, G>, ops: Vec + 'circuit>>>, max_degree: usize, // round_counter : usize, @@ -197,7 +197,7 @@ where G: Gate<'circuit, F> + From>, { pub fn new(max_degree: usize, num_rounds: usize) -> Self { - let cs = ConstraintSystem::new(num_rounds, max_degree); + let cs = ProtoGalaxyConstraintSystem::new(num_rounds); let mut prep = Self { gate_registry: FrozenMap::new(), cs, @@ -325,7 +325,7 @@ where } pub struct ConstructedCircuit<'circuit, F: PrimeField, G: Gate<'circuit, F> + From>> { - circuit: Circuit<'circuit, F, G>, + pub circuit: Circuit<'circuit, F, G>, run_allocator: RefCell, } @@ -368,11 +368,11 @@ where } } - pub fn end(&self, mut beta: F) -> ProtostarWtns<'constructed, 'circuit, F, G> { + pub fn end(&self, mut beta: F) -> ProtostarWtns { let m = self.constructed.circuit.cs.constr_spec().num_nonlinear_constraints; let mut p = 1; let mut protostar_challenges = vec![]; - while p <= m { + while p < m { protostar_challenges.push(beta); beta = beta * beta; p = p * 2; @@ -391,7 +391,6 @@ where round_wtns, pubs, protostar_challenges, - circuit: self.constructed, }, error: F::ZERO, } @@ -411,11 +410,35 @@ where } } + pub fn error_term(&self, beta: F) -> F { + let mut resulst = vec![]; + for constr in self.constructed.circuit.cs.iter_constraints() { + let input_values: Vec<_> = constr.inputs.iter().map(|&x| self.cs.getvar(x)).collect(); + resulst.extend(constr.gate.exec(&input_values)); + } + let mut b = 1; + let mut betas = vec![beta]; + while b < resulst.len() { + b *= 2; + let last = *betas.last().unwrap(); + betas.push(last * last); + } + + for beta_pow in betas.iter().rev() { + for i in b..((b * 2).min(resulst.len())) { + resulst[i - b] = resulst[i - b] + resulst[i] * beta_pow; + } + b /= 2; + } + + resulst[0] + } + pub fn iter_constraints(&self) -> impl Iterator> { self.constructed.circuit.cs.iter_constraints() } - pub fn finish(self) -> (CSWtns<'circuit, F, G>, &'constructed ConstraintSystem<'circuit, F, G>) { + pub fn finish(self) -> (CSWtns<'circuit, F, G>, &'constructed ProtoGalaxyConstraintSystem<'circuit, F, G>) { let Self {constructed, cs, round_counter: _, run_idx} = self; constructed.deallocate(run_idx); diff --git a/src/constraint_system.rs b/src/constraint_system.rs index 07cc11c..653351a 100644 --- a/src/constraint_system.rs +++ b/src/constraint_system.rs @@ -1,6 +1,7 @@ -use std::marker::PhantomData; +use std::{marker::PhantomData, collections::BTreeMap}; use ff::PrimeField; +use itertools::Itertools; use crate::{gate::Gate, circuit::ExternalValue}; @@ -46,24 +47,18 @@ pub struct Constraint<'c, F: PrimeField, G: Gate<'c, F>>{ #[derive(Debug, Clone)] struct ConstraintGroup<'c, F: PrimeField, G: Gate<'c, F>> { pub entries: Vec>, - #[allow(dead_code)] - pub kind: CommitKind, pub num_rhs: usize, - pub max_degree: usize, } impl<'c, F: PrimeField, G: Gate<'c, F>> ConstraintGroup<'c, F, G> { - pub fn new(kind: CommitKind, max_degree: usize) -> Self { + pub fn new() -> Self { Self { entries: Default::default(), - kind, num_rhs: Default::default(), - max_degree, } } pub fn constrain(&mut self, inputs: &[Variable], gate: G) { - assert!(gate.d() <= self.max_degree, "Constraint degree is too large for this group."); assert!(gate.i() == inputs.len(), "Invalid amount of arguments supplied."); self.num_rhs += gate.o(); @@ -75,7 +70,10 @@ impl<'c, F: PrimeField, G: Gate<'c, F>> ConstraintGroup<'c, F, G> { /// /// Any witness used for this constraint system has to at least comply with the spec. #[derive(Debug, Default, Clone, PartialEq)] -pub struct RoundWitnessSpec(pub usize, pub usize); +pub struct RoundWitnessSpec{ + pub pubs: usize, + pub privs: usize, +} /// Witness shape specification: a collection of specifications for each round /// @@ -94,7 +92,7 @@ pub struct ConstrSpec { pub max_degree: usize, } -pub trait CS<'c, F: PrimeField, G: Gate<'c, F>> { +pub trait CS<'c, F: PrimeField, G: Gate<'c, F>> { fn num_rounds(&self) -> usize; fn last_round(&self) -> usize { @@ -118,43 +116,42 @@ pub trait CS<'c, F: PrimeField, G: Gate<'c, F>> { fn extval(&mut self, size: usize) -> Vec>; } +/// This CS is made specifically for ProtoGalaxy. +/// We will probably make interface for this part #[derive(Debug, Clone)] -pub struct ConstraintSystem<'c, F: PrimeField, G: Gate<'c, F>> { - spec: WitnessSpec, - constraint_groups: [ConstraintGroup<'c, F, G>; 3], +pub struct ProtoGalaxyConstraintSystem<'c, F: PrimeField, G: Gate<'c, F>> { + pub spec: WitnessSpec, + pub max_degree: usize, + pub linear_constraints: ConstraintGroup<'c, F, G>, + pub non_linear_constraints: BTreeMap>, } -impl<'c, F: PrimeField, G: Gate<'c, F>> ConstraintSystem<'c, F, G> { - pub fn new(num_rounds: usize, max_degree: usize) -> Self { - let constraint_groups = [ - ConstraintGroup::new(CommitKind::Trivial, 0), // FIXME: correct max_degree - ConstraintGroup::new(CommitKind::Group, max_degree), - ConstraintGroup::new(CommitKind::Zero, 1), - ]; - +impl<'c, F: PrimeField, G: Gate<'c, F>> ProtoGalaxyConstraintSystem<'c, F, G> { + pub fn new(num_rounds: usize) -> Self { Self { spec: WitnessSpec{ round_specs: vec![RoundWitnessSpec::default(); num_rounds], num_exts: 0, num_ints: 0 }, - constraint_groups, - } - } - - /// A (short-lived) cursor to the constraint group of a given kind - fn constraint_group(&mut self, kind: CommitKind) -> &mut ConstraintGroup<'c, F, G> { - match kind { - CommitKind::Trivial => &mut self.constraint_groups[0], - CommitKind::Group => &mut self.constraint_groups[1], - CommitKind::Zero => &mut self.constraint_groups[2], + max_degree: 0, + linear_constraints: ConstraintGroup::new(), + non_linear_constraints: BTreeMap::new(), } } // would love to add this to the trait, but crab god said not yet // https://github.com/rust-lang/rust/issues/91611 pub fn iter_constraints(&self) -> impl Iterator> { - self.constraint_groups.iter().flat_map(|cg| cg.entries.iter()) + self.iter_linear_constraints().chain(self.iter_non_linear_constraints()) + } + + pub fn iter_linear_constraints(&self) -> impl Iterator> { + self.linear_constraints.entries.iter() + } + + pub fn iter_non_linear_constraints(&self) -> impl Iterator> { + self.non_linear_constraints.iter().flat_map(|(_, cg)| cg.entries.iter()) } } -impl<'c, F: PrimeField, G: Gate<'c, F>> CS<'c, F, G> for ConstraintSystem<'c, F, G> { +impl<'c, F: PrimeField, G: Gate<'c, F>> CS<'c, F, G> for ProtoGalaxyConstraintSystem<'c, F, G> { fn num_rounds(&self) -> usize { self.spec.round_specs.len() } @@ -168,22 +165,22 @@ impl<'c, F: PrimeField, G: Gate<'c, F>> CS<'c, F, G> for ConstraintSystem<'c, F, } fn constr_spec(&self) -> ConstrSpec { - let num_lin_constraints = self.constraint_groups[2].num_rhs; - let num_nonlinear_constraints = self.constraint_groups[1].num_rhs; - let max_degree = self.constraint_groups[1].max_degree; + let num_lin_constraints = self.linear_constraints.num_rhs; + let num_nonlinear_constraints = self.non_linear_constraints.iter().map(|(_, cg)| cg.num_rhs).sum(); + let max_degree = self.max_degree; ConstrSpec { num_lin_constraints, num_nonlinear_constraints, max_degree } } fn alloc_in_round(&mut self, round: usize, visibility: Visibility, size: usize) -> Vec { let prev = match visibility { Visibility::Public => { - let prev = self.spec.round_specs[round].0; - self.spec.round_specs[round].0 += size; + let prev = self.spec.round_specs[round].pubs; + self.spec.round_specs[round].pubs += size; prev }, Visibility::Private => { - let prev = self.spec.round_specs[round].1; - self.spec.round_specs[round].1 += size; + let prev = self.spec.round_specs[round].privs; + self.spec.round_specs[round].privs += size; prev }, }; @@ -191,8 +188,17 @@ impl<'c, F: PrimeField, G: Gate<'c, F>> CS<'c, F, G> for ConstraintSystem<'c, F, (prev..prev+size).into_iter().map(|index| Variable { visibility, round, index }).collect() } - fn constrain(&mut self, kind: CommitKind, inputs: &[Variable], gate: G) { - self.constraint_group(kind).constrain(inputs, gate); + fn constrain(&mut self, _: CommitKind, inputs: &[Variable], gate: G) { + self.max_degree = self.max_degree.max(gate.d()); + match gate.d().cmp(&1) { + std::cmp::Ordering::Less => panic!("Constraint of degree 0"), + std::cmp::Ordering::Equal => { + self.linear_constraints.constrain(inputs, gate) + }, + std::cmp::Ordering::Greater => { + self.non_linear_constraints.entry(gate.d()).or_insert(ConstraintGroup::new()).constrain(inputs, gate) + }, + } } fn extval(&mut self, size: usize) -> Vec> { diff --git a/src/folding/shape.rs b/src/folding/shape.rs index b04e9bd..103c886 100644 --- a/src/folding/shape.rs +++ b/src/folding/shape.rs @@ -4,7 +4,7 @@ use ff::PrimeField; use halo2::halo2curves::CurveAffine; use itertools::Itertools; -use crate::{utils::arith_helper::{log2_ceil, ev}, constraint_system::{WitnessSpec, ConstraintSystem, CS, ConstrSpec}, gate::Gate, witness::Module}; +use crate::{utils::arith_helper::{log2_ceil, ev}, constraint_system::{WitnessSpec, ProtoGalaxyConstraintSystem, CS, ConstrSpec}, gate::Gate, witness::Module}; /// Encode value as field elements. pub trait FEncoding { @@ -19,7 +19,7 @@ pub struct Shape { } impl Shape { - pub fn new<'c,F:PrimeField,G:Gate<'c,F>>(c: &ConstraintSystem<'c, F, G>) -> Self { + pub fn new<'c,F:PrimeField,G:Gate<'c,F>>(c: &ProtoGalaxyConstraintSystem<'c, F, G>) -> Self { let wspec = c.witness_spec().clone(); let cspec = c.constr_spec().clone(); Self{wspec, cspec} @@ -37,7 +37,7 @@ impl> ProtostarLhs { pub fn validate_shape(&self, shape: &Shape) { shape.wspec.round_specs.iter().zip_eq(self.pubs.iter()) .map(|(rspec,rpubs)|{ - assert_eq!(rspec.0,rpubs.len()) + assert_eq!(rspec.pubs, rpubs.len()) }).count(); assert_eq!(self.pubs.len(), self.round_commitments.len()); diff --git a/src/lib.rs b/src/lib.rs index 9c646c9..88e9ea3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,4 +11,5 @@ pub mod utils; pub mod folding; pub mod test; pub mod gatelib; -pub mod external_interface; \ No newline at end of file +pub mod external_interface; +pub mod prover; \ No newline at end of file diff --git a/src/prover.rs b/src/prover.rs new file mode 100644 index 0000000..8690789 --- /dev/null +++ b/src/prover.rs @@ -0,0 +1,220 @@ +use ff::{PrimeField, BatchInvert}; +use halo2::arithmetic::lagrange_interpolate; +use itertools::Itertools; + +use crate::{witness::ProtostarWtns, gate::Gate, circuit::PolyOp, constraint_system::{ProtoGalaxyConstraintSystem, Visibility}, utils::{cross_terms_combination::{combine_cross_terms, self, EvalLayout}, field_precomp::FieldUtils, inv_lagrange_prod}, gadgets::range::lagrange_choice}; + +pub struct ProtoGalaxyProver { + +} + +impl ProtoGalaxyProver +{ + pub fn new() -> Self { + Self { + + } + } + + fn calculate_powers< + 'circuit, + F: PrimeField + FieldUtils, + G: Gate<'circuit, F> + From> + > ( + &self, + cs: &ProtoGalaxyConstraintSystem<'circuit, F, G>, + template: &ProtostarWtns, + ) -> (Vec>, Vec>) { + let mut pubs_degrees: Vec> = template.lhs.pubs.iter().map(|v| v.iter().map(|_| 0).collect_vec()).collect_vec(); + let mut privs_degrees: Vec> = template.lhs.round_wtns.iter().map(|v| v.iter().map(|_| 0).collect_vec()).collect_vec(); + + for constraint in cs.iter_non_linear_constraints() { + for variable in &constraint.inputs { + match variable.visibility { + Visibility::Public => pubs_degrees[variable.round][variable.index] = pubs_degrees[variable.round][variable.index].max(constraint.gate.d()), + Visibility::Private => privs_degrees[variable.round][variable.index] = privs_degrees[variable.round][variable.index].max(constraint.gate.d()), + } + } + } + (pubs_degrees, privs_degrees) + } + + fn calculate_layout< + 'circuit, + F: PrimeField + FieldUtils, + G: Gate<'circuit, F> + From> + > ( + &self, + cs: &ProtoGalaxyConstraintSystem<'circuit, F, G>, + ) -> Vec { + let mut layout: Vec = vec![]; + for constraint in cs.iter_non_linear_constraints() { + if layout.len() == 0 || layout[layout.len() - 1].deg != constraint.gate.d() { + layout.push(EvalLayout{deg: constraint.gate.d(), amount: 0}) + } + layout.last_mut().unwrap().amount += constraint.gate.o(); + } + layout + } + + fn build_variable_combinations_storage(&self, degrees: &Vec>) -> Vec>> { + degrees.iter().map(|d| d.iter().map(|d| Vec::with_capacity(d + 1)).collect_vec()).collect_vec() + } + + fn fill_variable_combinations(&self, mut storage: &mut Vec>>, degrees: &Vec>, a: &Vec>, b: &Vec>) { + storage.iter_mut().zip_eq(degrees.iter()).zip_eq(a.iter().zip_eq(b.iter())).map(|((s, d), (a, b))| { + s.iter_mut().zip_eq(d.iter()).zip_eq(a.iter().zip_eq(b.iter())).map(|((res, d), (a, b))| { + if *d != 0 { // min gate degree here is 2 (were iterating over nonlinear) + res.push(*a); + res.push(*b); + let diff = *b - *a; + let mut base = *b; + for _ in 1..*d { + base += diff; + res.push(base) + } + } + }).last() + }).last(); + } + + fn combine_challenges(&self, a: &Vec, b: &Vec) -> Vec<[F; 2]> { + let pg_challenges = a.iter().zip_eq(b.iter()).map(|(a, b)| [a.clone(), b.clone()]).collect_vec(); + pg_challenges + } + + fn prepare_interpolation_points(&self, d: usize, log_ceil: usize) -> Vec { + (2..(d + log_ceil + 1)).map(|x| F::from(x as u64)).collect_vec() + } + + fn leave_quotient<'a, F: PrimeField>(&self, evals: &'a mut Vec) ->&'a [F]{ + let e_0 = evals[0]; + evals[0] = F::ZERO; + let mut e_next = evals[1]; + evals[1] = F::ZERO; + let diff = e_next - e_0; + let mut invs = (2..evals.len()).map(|i| F::from((i * (i - 1)) as u64)).collect_vec(); + invs.batch_invert(); + + for (eval, inv) in evals.iter_mut().skip(2).zip_eq(invs.iter()) { + e_next += diff; + *eval -= e_next; + *eval *= inv; + } + evals.split_at(2).1 + } + + fn evaluate< + 'circuit, + F: PrimeField + FieldUtils, + G: Gate<'circuit, F> + From> + > ( + &self, + cs: &ProtoGalaxyConstraintSystem<'circuit, F, G>, + pubs_combinations: &Vec>>, + privs_combinations: &Vec>>, + ) -> Vec{ + let mut evals = vec![]; + for constraint in cs.iter_non_linear_constraints() { + for d in 0..constraint.gate.d() + 1 { + evals.extend(constraint.gate.exec(&constraint.inputs.iter().map(|var| { + match var.visibility { + Visibility::Public => pubs_combinations[var.round][var.index][d], + Visibility::Private => privs_combinations[var.round][var.index][d], + } + }).collect_vec())) + } + } + evals + } + + pub fn prove<'circuit, F: PrimeField + FieldUtils, G: Gate<'circuit, F> + From>>( + &self, + a: &ProtostarWtns, + b: &ProtostarWtns, + cs: &ProtoGalaxyConstraintSystem<'circuit, F, G>, + ) -> Vec { + let (pubs_degrees, privs_degrees) = self.calculate_powers(cs, a); + let layout = self.calculate_layout(cs); + + let mut privs_combinations = self.build_variable_combinations_storage(&privs_degrees); + let mut pubs_combinations = self.build_variable_combinations_storage(&pubs_degrees); + + // ^ that might be moved to 'new' + + self.fill_variable_combinations(&mut privs_combinations, &privs_degrees, &a.lhs.round_wtns, &b.lhs.round_wtns); + self.fill_variable_combinations(&mut pubs_combinations, &pubs_degrees, &a.lhs.pubs, &b.lhs.pubs); + + let evals = self.evaluate(cs, &pubs_combinations, &privs_combinations); + let pg_challenges = self.combine_challenges(&a.lhs.protostar_challenges, &b.lhs.protostar_challenges); + let mut cross_terms = combine_cross_terms(evals, layout, pg_challenges); + let cross_terms = self.leave_quotient(&mut cross_terms); + + let points = self.prepare_interpolation_points(cs.max_degree, a.lhs.protostar_challenges.len()); + lagrange_interpolate(&points, cross_terms) + } +} + +#[cfg(test)] +mod test { + use std::rc::Rc; + + use halo2::halo2curves::bn256; + use crate::{gate::Gatebb, circuit::{Circuit, Advice}, gadgets::input::input}; + + use super::*; + + #[test] + fn pg_prover() { + type F = bn256::Fr; + let mut circuit = Circuit::new(4, 2); + let inputs = circuit.ext_val(4); + let input_vars = inputs.iter().map(|i| input(&mut circuit, *i, 0)).collect_vec(); + let mul_a_res = circuit.advice(1, Advice::new( + 2, + 1, + |args, _| vec![args[0] * args[1]] + ), vec![input_vars[0], input_vars[1]])[0]; + circuit.constrain(&[input_vars[0], input_vars[1], mul_a_res], Gatebb::::new(2, 3, 1, Rc::new(|args, _| vec![args[0] * args[1] - args[2]]), vec![])); + + let mul_b_res = circuit.advice(1, Advice::new( + 2, + 1, + |args, _| vec![args[0] * args[1]] + ), vec![input_vars[2], input_vars[3]])[0]; + circuit.constrain(&[input_vars[2], input_vars[3], mul_b_res], Gatebb::::new(2, 3, 1, Rc::new(|args, _| vec![args[0] * args[1] - args[2]]), vec![])); + + let sum_res = circuit.advice(1, Advice::new( + 2, + 1, + |args, _| vec![args[0] + args[1]] + ), vec![mul_a_res, mul_b_res])[0]; + circuit.constrain(&[mul_a_res, mul_b_res, sum_res], Gatebb::::new(1, 3, 1, Rc::new(|args, _| vec![args[0] + args[1] - args[2]]), vec![])); + + + let constructed = circuit.finalize(); + + let mut run_a = constructed.spawn(); + let mut run_b = constructed.spawn(); + + for (idx, i) in inputs.iter().enumerate() { + run_a.set_ext(*i, F::from((idx + 3) as u64)); + run_b.set_ext(*i, F::from((idx + 10) as u64)); + } + + run_a.execute(1); + run_b.execute(1); + + let beta = F::from(2); + + let pgp = ProtoGalaxyProver::new(); + + let a_wtns = run_a.end(beta); + let b_wtns = run_b.end(beta); + + let res = pgp.prove(&a_wtns, &b_wtns, &constructed.circuit.cs); + run_a.valid_witness(); + run_b.valid_witness(); + + } +} \ No newline at end of file diff --git a/src/witness.rs b/src/witness.rs index 63a8fff..f1a82ea 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -5,7 +5,7 @@ use ff::PrimeField; use halo2::{halo2curves::CurveAffine, arithmetic::best_multiexp}; use itertools::Itertools; -use crate::{gate::Gate, constraint_system::{ConstraintSystem, Variable, CS, Visibility, WitnessSpec}, commitment::{CommitmentKey, CkWtns, CtRound, ErrGroup, CkRelaxed}, circuit::{ExternalValue, ConstructedCircuit, PolyOp}, utils::field_precomp::FieldUtils, folding::shape::{ProtostarLhs, ProtostarInstance}}; +use crate::{gate::Gate, constraint_system::{ProtoGalaxyConstraintSystem, Variable, CS, Visibility, WitnessSpec}, commitment::{CommitmentKey, CkWtns, CtRound, ErrGroup, CkRelaxed}, circuit::{ExternalValue, ConstructedCircuit, PolyOp}, utils::field_precomp::FieldUtils, folding::shape::{ProtostarLhs, ProtostarInstance}}; #[derive(Clone)] pub struct RoundWtns { @@ -31,14 +31,14 @@ pub struct CSWtns<'c, F: PrimeField, G: Gate<'c, F>> { impl<'c, F:PrimeField, G: Gate<'c, F>> CSWtns<'c, F, G>{ - pub fn new(cs: &ConstraintSystem<'c, F, G>) -> Self { + pub fn new(cs: &ProtoGalaxyConstraintSystem<'c, F, G>) -> Self { let mut wtns = vec![]; let WitnessSpec{round_specs, num_exts, num_ints} = cs.witness_spec(); for round_spec in round_specs { - wtns.push(RoundWtns{pubs: vec![None; round_spec.0], privs: vec![None; round_spec.1]}) + wtns.push(RoundWtns{pubs: vec![None; round_spec.pubs], privs: vec![None; round_spec.privs]}) } let ext_vals = repeat(None).take(*num_exts).collect(); @@ -146,14 +146,13 @@ pub trait Module { fn scale(&mut self, scale: F) -> (); } -pub struct ProtostarLhsWtns<'constructed, 'circuit, F: PrimeField, G: Gate<'circuit, F> + From>> { +pub struct ProtostarLhsWtns { pub round_wtns: Vec>, pub pubs: Vec>, pub protostar_challenges: Vec, - pub circuit: &'constructed ConstructedCircuit<'circuit, F, G>, } -impl<'constructed, 'circuit, F: PrimeField, G: Gate<'circuit, F> + From>> ProtostarLhsWtns<'constructed, 'circuit, F, G> { +impl ProtostarLhsWtns { pub fn commit> (&self, commitment_key: Vec>) -> ProtostarLhs { ProtostarLhs { round_commitments: self.round_wtns.iter().zip_eq(commitment_key).map(|(wtns, ck)| best_multiexp(&wtns, &ck).into()).collect_vec(), @@ -163,7 +162,7 @@ impl<'constructed, 'circuit, F: PrimeField, G: Gate<'circuit, F> + From + From>> Module for ProtostarLhsWtns<'constructed, 'circuit, F, G> { +impl Module for ProtostarLhsWtns { fn add_assign(&mut self, other: Self) -> () { self.round_wtns.iter_mut().zip_eq(other.round_wtns.iter()).map(|(s, o)| { s.iter_mut().zip_eq(o.iter()).map(|(s, o)| *s = *s + o) @@ -201,12 +200,12 @@ impl<'constructed, 'circuit, F: PrimeField, G: Gate<'circuit, F> + From + From>> { - pub lhs: ProtostarLhsWtns<'constructed, 'circuit, F, G>, +pub struct ProtostarWtns { + pub lhs: ProtostarLhsWtns, pub error: F } -impl<'constructed, 'circuit, F: PrimeField, G: Gate<'circuit, F> + From>> Module for ProtostarWtns<'constructed, 'circuit, F, G> { +impl Module for ProtostarWtns { fn add_assign(&mut self, other: Self) -> () { self.error += other.error; self.lhs.add_assign(other.lhs); @@ -223,7 +222,7 @@ impl<'constructed, 'circuit, F: PrimeField, G: Gate<'circuit, F> + From + From>> ProtostarWtns<'constructed, 'circuit, F, G> { +impl ProtostarWtns { pub fn commit> (&self, commitment_key: Vec>) -> ProtostarInstance { ProtostarInstance { lhs: self.lhs.commit(commitment_key), From 85ea2911b6fd129cebe5ede1defe6bcbc1236a47 Mon Sep 17 00:00:00 2001 From: rebenkoy Date: Sun, 26 Nov 2023 17:15:11 +0300 Subject: [PATCH 2/5] prover tested --- src/circuit.rs | 47 ++++++----------- src/constraint_system.rs | 4 +- src/prover.rs | 72 ++++++++++++++++++-------- src/witness.rs | 106 ++++++++++++++++++++++++++++++--------- 4 files changed, 148 insertions(+), 81 deletions(-) diff --git a/src/circuit.rs b/src/circuit.rs index e5e76f5..1abaa9d 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -342,6 +342,18 @@ impl<'circuit, F: PrimeField, G: Gate<'circuit, F> + From>> fn deallocate<'constructed>(&'constructed self, idx: RunIndex) { self.run_allocator.borrow_mut().deallocate(idx); } + + pub fn perepare_protostar_chellanges(&self, mut beta: F) -> Vec { + let m = self.circuit.cs.constr_spec().num_nonlinear_constraints; + let mut p = 1; + let mut protostar_challenges = vec![]; + while p < m { + protostar_challenges.push(beta); + beta = beta * beta; + p = p * 2; + } + protostar_challenges + } } pub struct CircuitRun<'constructed, 'circuit, F: PrimeField, G: Gate<'circuit, F> + From>>{ @@ -368,15 +380,8 @@ where } } - pub fn end(&self, mut beta: F) -> ProtostarWtns { - let m = self.constructed.circuit.cs.constr_spec().num_nonlinear_constraints; - let mut p = 1; - let mut protostar_challenges = vec![]; - while p < m { - protostar_challenges.push(beta); - beta = beta * beta; - p = p * 2; - } + pub fn end(&self, beta: F) -> ProtostarWtns { + let protostar_challenges = self.constructed.perepare_protostar_chellanges(beta); let mut pubs = vec![]; let mut round_wtns = vec![]; @@ -410,30 +415,6 @@ where } } - pub fn error_term(&self, beta: F) -> F { - let mut resulst = vec![]; - for constr in self.constructed.circuit.cs.iter_constraints() { - let input_values: Vec<_> = constr.inputs.iter().map(|&x| self.cs.getvar(x)).collect(); - resulst.extend(constr.gate.exec(&input_values)); - } - let mut b = 1; - let mut betas = vec![beta]; - while b < resulst.len() { - b *= 2; - let last = *betas.last().unwrap(); - betas.push(last * last); - } - - for beta_pow in betas.iter().rev() { - for i in b..((b * 2).min(resulst.len())) { - resulst[i - b] = resulst[i - b] + resulst[i] * beta_pow; - } - b /= 2; - } - - resulst[0] - } - pub fn iter_constraints(&self) -> impl Iterator> { self.constructed.circuit.cs.iter_constraints() } diff --git a/src/constraint_system.rs b/src/constraint_system.rs index 653351a..1feac67 100644 --- a/src/constraint_system.rs +++ b/src/constraint_system.rs @@ -122,8 +122,8 @@ pub trait CS<'c, F: PrimeField, G: Gate<'c, F>> { pub struct ProtoGalaxyConstraintSystem<'c, F: PrimeField, G: Gate<'c, F>> { pub spec: WitnessSpec, pub max_degree: usize, - pub linear_constraints: ConstraintGroup<'c, F, G>, - pub non_linear_constraints: BTreeMap>, + linear_constraints: ConstraintGroup<'c, F, G>, + non_linear_constraints: BTreeMap>, } impl<'c, F: PrimeField, G: Gate<'c, F>> ProtoGalaxyConstraintSystem<'c, F, G> { diff --git a/src/prover.rs b/src/prover.rs index 8690789..dee027c 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -2,7 +2,7 @@ use ff::{PrimeField, BatchInvert}; use halo2::arithmetic::lagrange_interpolate; use itertools::Itertools; -use crate::{witness::ProtostarWtns, gate::Gate, circuit::PolyOp, constraint_system::{ProtoGalaxyConstraintSystem, Visibility}, utils::{cross_terms_combination::{combine_cross_terms, self, EvalLayout}, field_precomp::FieldUtils, inv_lagrange_prod}, gadgets::range::lagrange_choice}; +use crate::{witness::{ProtostarWtns, ProtostarLhsWtns}, gate::Gate, circuit::PolyOp, constraint_system::{ProtoGalaxyConstraintSystem, Visibility}, utils::{cross_terms_combination::{combine_cross_terms, self, EvalLayout}, field_precomp::FieldUtils, inv_lagrange_prod}, gadgets::range::lagrange_choice}; pub struct ProtoGalaxyProver { @@ -23,10 +23,10 @@ impl ProtoGalaxyProver > ( &self, cs: &ProtoGalaxyConstraintSystem<'circuit, F, G>, - template: &ProtostarWtns, + template: &ProtostarLhsWtns, ) -> (Vec>, Vec>) { - let mut pubs_degrees: Vec> = template.lhs.pubs.iter().map(|v| v.iter().map(|_| 0).collect_vec()).collect_vec(); - let mut privs_degrees: Vec> = template.lhs.round_wtns.iter().map(|v| v.iter().map(|_| 0).collect_vec()).collect_vec(); + let mut pubs_degrees: Vec> = template.pubs.iter().map(|v| v.iter().map(|_| 0).collect_vec()).collect_vec(); + let mut privs_degrees: Vec> = template.round_wtns.iter().map(|v| v.iter().map(|_| 0).collect_vec()).collect_vec(); for constraint in cs.iter_non_linear_constraints() { for variable in &constraint.inputs { @@ -130,8 +130,8 @@ impl ProtoGalaxyProver pub fn prove<'circuit, F: PrimeField + FieldUtils, G: Gate<'circuit, F> + From>>( &self, - a: &ProtostarWtns, - b: &ProtostarWtns, + a: &ProtostarLhsWtns, + b: &ProtostarLhsWtns, cs: &ProtoGalaxyConstraintSystem<'circuit, F, G>, ) -> Vec { let (pubs_degrees, privs_degrees) = self.calculate_powers(cs, a); @@ -142,15 +142,15 @@ impl ProtoGalaxyProver // ^ that might be moved to 'new' - self.fill_variable_combinations(&mut privs_combinations, &privs_degrees, &a.lhs.round_wtns, &b.lhs.round_wtns); - self.fill_variable_combinations(&mut pubs_combinations, &pubs_degrees, &a.lhs.pubs, &b.lhs.pubs); + self.fill_variable_combinations(&mut privs_combinations, &privs_degrees, &a.round_wtns, &b.round_wtns); + self.fill_variable_combinations(&mut pubs_combinations, &pubs_degrees, &a.pubs, &b.pubs); let evals = self.evaluate(cs, &pubs_combinations, &privs_combinations); - let pg_challenges = self.combine_challenges(&a.lhs.protostar_challenges, &b.lhs.protostar_challenges); + let pg_challenges = self.combine_challenges(&a.protostar_challenges, &b.protostar_challenges); let mut cross_terms = combine_cross_terms(evals, layout, pg_challenges); let cross_terms = self.leave_quotient(&mut cross_terms); - let points = self.prepare_interpolation_points(cs.max_degree, a.lhs.protostar_challenges.len()); + let points = self.prepare_interpolation_points(cs.max_degree, a.protostar_challenges.len()); lagrange_interpolate(&points, cross_terms) } } @@ -159,8 +159,11 @@ impl ProtoGalaxyProver mod test { use std::rc::Rc; + use ff::Field; use halo2::halo2curves::bn256; - use crate::{gate::Gatebb, circuit::{Circuit, Advice}, gadgets::input::input}; + use itertools::{Unfold, unfold}; + use rand_core::OsRng; + use crate::{gate::Gatebb, circuit::{Circuit, Advice}, gadgets::input::input, constraint_system::CS, witness::{Module, compute_error_term, ProtostarLhsWtns}}; use super::*; @@ -175,7 +178,9 @@ mod test { 1, |args, _| vec![args[0] * args[1]] ), vec![input_vars[0], input_vars[1]])[0]; - circuit.constrain(&[input_vars[0], input_vars[1], mul_a_res], Gatebb::::new(2, 3, 1, Rc::new(|args, _| vec![args[0] * args[1] - args[2]]), vec![])); + circuit.constrain(&[input_vars[0], input_vars[1], mul_a_res], Gatebb::::new(2, 3, 1, Rc::new(|args, _| + {let res = vec![args[0] * args[1] - args[2]]; res} + ), vec![])); let mul_b_res = circuit.advice(1, Advice::new( 2, @@ -197,24 +202,49 @@ mod test { let mut run_a = constructed.spawn(); let mut run_b = constructed.spawn(); - for (idx, i) in inputs.iter().enumerate() { - run_a.set_ext(*i, F::from((idx + 3) as u64)); - run_b.set_ext(*i, F::from((idx + 10) as u64)); + for i in inputs { + run_a.set_ext(i, F::random(OsRng)); + run_b.set_ext(i, F::random(OsRng)); } run_a.execute(1); run_b.execute(1); - let beta = F::from(2); + let beta_a = F::random(OsRng); + let beta_b = F::random(OsRng); let pgp = ProtoGalaxyProver::new(); - let a_wtns = run_a.end(beta); - let b_wtns = run_b.end(beta); + let a_wtns = run_a.end(beta_a); + let b_wtns = run_b.end(beta_b); - let res = pgp.prove(&a_wtns, &b_wtns, &constructed.circuit.cs); - run_a.valid_witness(); - run_b.valid_witness(); + + // Now we create random witnesses with same shape + + let a_wtns = ProtostarLhsWtns::random_like(&mut OsRng, &a_wtns.lhs); + let b_wtns = ProtostarLhsWtns::random_like(&mut OsRng, &b_wtns.lhs); + + let q = pgp.prove(&a_wtns, &b_wtns, &constructed.circuit.cs); + + let a_err = compute_error_term(&a_wtns, &constructed.circuit.cs); + let b_err = compute_error_term(&b_wtns, &constructed.circuit.cs); + + let t = F::random(OsRng); + let mut fold_wtns = a_wtns.clone(); + fold_wtns.neg(); + fold_wtns.add_assign(b_wtns.clone()); + fold_wtns.scale(t); + fold_wtns.add_assign(a_wtns.clone()); + + let fold_err = compute_error_term(&fold_wtns, &constructed.circuit.cs); + + let q_eval: F = unfold(F::ONE, |next| { + let tmp = *next; + *next = *next * t; + Some(tmp) + }).zip(q).map(|(pow, c)| c * pow).sum(); + + assert_eq!(fold_err, a_err + (b_err - a_err) * t + t * (t - F::ONE) * q_eval) } } \ No newline at end of file diff --git a/src/witness.rs b/src/witness.rs index f1a82ea..5878531 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use ff::PrimeField; use halo2::{halo2curves::CurveAffine, arithmetic::best_multiexp}; use itertools::Itertools; +use rand_core::RngCore; use crate::{gate::Gate, constraint_system::{ProtoGalaxyConstraintSystem, Variable, CS, Visibility, WitnessSpec}, commitment::{CommitmentKey, CkWtns, CtRound, ErrGroup, CkRelaxed}, circuit::{ExternalValue, ConstructedCircuit, PolyOp}, utils::field_precomp::FieldUtils, folding::shape::{ProtostarLhs, ProtostarInstance}}; @@ -146,6 +147,7 @@ pub trait Module { fn scale(&mut self, scale: F) -> (); } +#[derive(Clone)] pub struct ProtostarLhsWtns { pub round_wtns: Vec>, pub pubs: Vec>, @@ -160,46 +162,56 @@ impl ProtostarLhsWtns { protostar_challenges: self.protostar_challenges.clone(), } } + + pub fn random_like(mut rng: &mut RNG, other: &Self) -> Self { + Self { + round_wtns: other.round_wtns.iter().map(|r| r.iter().map(|_| F::random(&mut rng)).collect_vec()).collect_vec(), + pubs: other.pubs.iter().map(|r| r.iter().map(|_| F::random(&mut rng)).collect_vec()).collect_vec(), + protostar_challenges: other.protostar_challenges.iter().map(|_| F::random(&mut rng)).collect_vec(), + } + } } impl Module for ProtostarLhsWtns { fn add_assign(&mut self, other: Self) -> () { - self.round_wtns.iter_mut().zip_eq(other.round_wtns.iter()).map(|(s, o)| { - s.iter_mut().zip_eq(o.iter()).map(|(s, o)| *s = *s + o) - }).last(); - self.pubs.iter_mut().zip_eq(other.pubs.iter()).map(|(s, o)| { - s.iter_mut().zip_eq(o.iter()).map(|(s, o)| *s = *s + o) - }).last(); - self.protostar_challenges.iter_mut().zip_eq(other.protostar_challenges.iter()).map(|(s, o)| { + self.round_wtns.iter_mut().zip_eq(other.round_wtns.iter()).for_each(|(s, o)| { + s.iter_mut().zip_eq(o.iter()).for_each(|(s, o)| *s = *s + o) + }); + self.pubs.iter_mut().zip_eq(other.pubs.iter()).for_each(|(s, o)| { + s.iter_mut().zip_eq(o.iter()).for_each(|(s, o)| *s = *s + o) + }); + self.protostar_challenges.iter_mut().zip_eq(other.protostar_challenges.iter()).for_each(|(s, o)| { *s = *s + o - }).last(); + }); } fn neg(&mut self) -> () { - self.round_wtns.iter_mut().map(|s| { - s.iter_mut().map(|s| *s = -*s) - }).last(); - self.pubs.iter_mut().map(|s| { - s.iter_mut().map(|s| *s = -*s) - }).last(); - self.protostar_challenges.iter_mut().map(|s| { + self.round_wtns.iter_mut().for_each(|s| { + s.iter_mut().for_each(|s| *s = -*s) + }); + self.pubs.iter_mut().for_each(|s| { + s.iter_mut().for_each(|s| *s = -*s) + }); + self.protostar_challenges.iter_mut().for_each(|s| { *s = -*s - }).last(); + }); } fn scale(&mut self, scale: F) -> () { - self.round_wtns.iter_mut().map(|s| { - s.iter_mut().map(|s| *s = *s * scale) - }).last(); - self.pubs.iter_mut().map(|s| { - s.iter_mut().map(|s| *s = *s * scale) - }).last(); - self.protostar_challenges.iter_mut().map(|s| { + self.round_wtns.iter_mut().for_each(|s| { + s.iter_mut().for_each(|s| *s = *s * scale) + }); + self.pubs.iter_mut().for_each(|s| { + s.iter_mut().for_each(|s| *s = *s * scale) + }); + self.protostar_challenges.iter_mut().for_each(|s| { *s = *s * scale - }).last(); + }); } } + +#[derive(Clone)] pub struct ProtostarWtns { pub lhs: ProtostarLhsWtns, pub error: F @@ -229,4 +241,48 @@ impl ProtostarWtns { error: self.error, } } -} \ No newline at end of file +} + +pub fn compute_error_term<'circuit, F: PrimeField, G: Gate<'circuit, F>>(wtns: &ProtostarLhsWtns, cs: &ProtoGalaxyConstraintSystem<'circuit, F, G>) -> F { + let betas = &wtns.protostar_challenges; + let mut results = vec![]; + for constr in cs.iter_non_linear_constraints() { + let input_values: Vec<_> = constr.inputs.iter().map(|&x| match x.visibility { + Visibility::Public => wtns.pubs[x.round][x.index], + Visibility::Private => wtns.round_wtns[x.round][x.index], + }).collect(); + results.extend(constr.gate.exec(&input_values)); + } + + assert!(betas.len() > 0, "No challenges supplied for error_term"); + let mut mid = 1 << (betas.len() - 1); + assert!(mid < results.len()); + assert!(mid * 2 >= results.len()); + + // example + // results : |.|.|.|.|.|.|.| + // idx : 0 1 2 3 4 5 6 + // mid : ^ + // beta^4 : | | | | |1|1|1| + // beta^2 : | | |1|1| | |1| + // beta^1 : | |1| |1| |1| | + // + // split at mid : |.|.|.|.| |.|.|.| + // old idx : 0 1 2 3 4 5 6 + // + // Now we multiply right half by beta^4 and element-wise add it to first half inplace + // + // results : | r0 + r4 * beta^4 | r1 + r5 * beta^4 | r2 + r6 * beta^4 | r3 | junk | junk | junk | + // + // Now we forget about junk und repeat with first half until single element is left. + + for beta_pow in betas.iter().rev() { + let (left, right) = results.split_at_mut(mid); + for (l, r) in left.iter_mut().zip(right.iter()) { + *l = *l + *r * beta_pow; + } + mid /= 2; + } + + results[0] +} From ba2431205622b28e9f81e353010a3dfc029880fc Mon Sep 17 00:00:00 2001 From: rebenkoy Date: Sun, 26 Nov 2023 17:17:20 +0300 Subject: [PATCH 3/5] unused imports --- src/prover.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/prover.rs b/src/prover.rs index dee027c..765ea01 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -161,9 +161,9 @@ mod test { use ff::Field; use halo2::halo2curves::bn256; - use itertools::{Unfold, unfold}; + use itertools::unfold; use rand_core::OsRng; - use crate::{gate::Gatebb, circuit::{Circuit, Advice}, gadgets::input::input, constraint_system::CS, witness::{Module, compute_error_term, ProtostarLhsWtns}}; + use crate::{gate::Gatebb, circuit::{Circuit, Advice}, gadgets::input::input, witness::{Module, compute_error_term, ProtostarLhsWtns}}; use super::*; From 4d3ce3c5638b562b45d2c56605e3aaed8759e542 Mon Sep 17 00:00:00 2001 From: rebenkoy Date: Sun, 26 Nov 2023 19:23:00 +0300 Subject: [PATCH 4/5] merge fix --- src/prover.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/prover.rs b/src/prover.rs index 765ea01..a0d0800 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -2,7 +2,7 @@ use ff::{PrimeField, BatchInvert}; use halo2::arithmetic::lagrange_interpolate; use itertools::Itertools; -use crate::{witness::{ProtostarWtns, ProtostarLhsWtns}, gate::Gate, circuit::PolyOp, constraint_system::{ProtoGalaxyConstraintSystem, Visibility}, utils::{cross_terms_combination::{combine_cross_terms, self, EvalLayout}, field_precomp::FieldUtils, inv_lagrange_prod}, gadgets::range::lagrange_choice}; +use crate::{witness::ProtostarLhsWtns, gate::Gate, circuit::PolyOp, constraint_system::{ProtoGalaxyConstraintSystem, Visibility}, utils::{cross_terms_combination::{combine_cross_terms, EvalLayout}, field_precomp::FieldUtils}}; pub struct ProtoGalaxyProver { @@ -61,7 +61,7 @@ impl ProtoGalaxyProver degrees.iter().map(|d| d.iter().map(|d| Vec::with_capacity(d + 1)).collect_vec()).collect_vec() } - fn fill_variable_combinations(&self, mut storage: &mut Vec>>, degrees: &Vec>, a: &Vec>, b: &Vec>) { + fn fill_variable_combinations(&self, storage: &mut Vec>>, degrees: &Vec>, a: &Vec>, b: &Vec>) { storage.iter_mut().zip_eq(degrees.iter()).zip_eq(a.iter().zip_eq(b.iter())).map(|((s, d), (a, b))| { s.iter_mut().zip_eq(d.iter()).zip_eq(a.iter().zip_eq(b.iter())).map(|((res, d), (a, b))| { if *d != 0 { // min gate degree here is 2 (were iterating over nonlinear) From 779aa2f4a8c81b1a74f9ed367ba4194636c4134a Mon Sep 17 00:00:00 2001 From: rebenkoy Date: Sun, 26 Nov 2023 20:09:56 +0300 Subject: [PATCH 5/5] verifier added to prover test --- src/folding/shape.rs | 4 ++-- src/prover.rs | 40 +++++++++++++++++++++++++++------------ src/utils/arith_helper.rs | 2 +- src/witness.rs | 15 +++++++++++---- 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/folding/shape.rs b/src/folding/shape.rs index 103c886..ebe2f88 100644 --- a/src/folding/shape.rs +++ b/src/folding/shape.rs @@ -26,7 +26,7 @@ impl Shape { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct ProtostarLhs> { pub round_commitments: Vec, pub pubs: Vec>, @@ -115,7 +115,7 @@ impl> Fold { lhs_acc.add_assign(diff); let lhs = lhs_acc; let nt = F::ONE-t; - let error = nt*error_acc + t*error_inc + t*nt*ev(&cross_terms, t); + let error = nt*error_acc + t*error_inc - t*nt*ev(&cross_terms, t); ProtostarInstance { lhs, error } } } \ No newline at end of file diff --git a/src/prover.rs b/src/prover.rs index a0d0800..7b28ce1 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -160,16 +160,19 @@ mod test { use std::rc::Rc; use ff::Field; - use halo2::halo2curves::bn256; + use group::Group; + use halo2::halo2curves::{bn256, grumpkin}; use itertools::unfold; use rand_core::OsRng; - use crate::{gate::Gatebb, circuit::{Circuit, Advice}, gadgets::input::input, witness::{Module, compute_error_term, ProtostarLhsWtns}}; + use crate::{gate::Gatebb, circuit::{Circuit, Advice}, gadgets::input::input, witness::{Module, compute_error_term, ProtostarLhsWtns, ProtostarWtns}, folding::shape::{Fold, Shape}}; use super::*; #[test] fn pg_prover() { type F = bn256::Fr; + type C = bn256::G1Affine; + let mut circuit = Circuit::new(4, 2); let inputs = circuit.ext_val(4); let input_vars = inputs.iter().map(|i| input(&mut circuit, *i, 0)).collect_vec(); @@ -218,33 +221,46 @@ mod test { let a_wtns = run_a.end(beta_a); let b_wtns = run_b.end(beta_b); - // Now we create random witnesses with same shape - - let a_wtns = ProtostarLhsWtns::random_like(&mut OsRng, &a_wtns.lhs); - let b_wtns = ProtostarLhsWtns::random_like(&mut OsRng, &b_wtns.lhs); - let q = pgp.prove(&a_wtns, &b_wtns, &constructed.circuit.cs); + let mut a_wtns = ProtostarWtns::random_like(&mut OsRng, &a_wtns); + let mut b_wtns = ProtostarWtns::random_like(&mut OsRng, &b_wtns); + + let q = pgp.prove(&a_wtns.lhs, &b_wtns.lhs, &constructed.circuit.cs); - let a_err = compute_error_term(&a_wtns, &constructed.circuit.cs); - let b_err = compute_error_term(&b_wtns, &constructed.circuit.cs); + let a_err = compute_error_term(&a_wtns.lhs, &constructed.circuit.cs); + let b_err = compute_error_term(&b_wtns.lhs, &constructed.circuit.cs); + a_wtns.error = a_err; + b_wtns.error = b_err; let t = F::random(OsRng); + let commitment_key = a_wtns.lhs.round_wtns.iter().map(|w| w.iter().map(|_| C::random(OsRng)).collect_vec()).collect_vec(); + let mut fold_wtns = a_wtns.clone(); fold_wtns.neg(); fold_wtns.add_assign(b_wtns.clone()); fold_wtns.scale(t); fold_wtns.add_assign(a_wtns.clone()); - let fold_err = compute_error_term(&fold_wtns, &constructed.circuit.cs); + let fold_wtns_commited = fold_wtns.commit(&commitment_key); + let fold_err = compute_error_term(&fold_wtns.lhs, &constructed.circuit.cs); let q_eval: F = unfold(F::ONE, |next| { let tmp = *next; *next = *next * t; Some(tmp) - }).zip(q).map(|(pow, c)| c * pow).sum(); + }).zip(q.iter()).map(|(pow, c)| c * pow).sum(); - assert_eq!(fold_err, a_err + (b_err - a_err) * t + t * (t - F::ONE) * q_eval) + assert_eq!(fold_err, a_err + (b_err - a_err) * t + t * (t - F::ONE) * q_eval); + let a_wtns_commited = a_wtns.commit(&commitment_key); + let b_wtns_commited = b_wtns.commit(&commitment_key); + + let mut fold = Fold::new(a_wtns_commited, b_wtns_commited, q, Shape::new(&constructed.circuit.cs)); + fold.challenge(t); + let folded_commited = fold.fold(); + + assert_eq!(folded_commited.lhs, fold_wtns_commited.lhs); + assert_eq!(fold_err, folded_commited.error); } } \ No newline at end of file diff --git a/src/utils/arith_helper.rs b/src/utils/arith_helper.rs index 3415a79..79d81ce 100644 --- a/src/utils/arith_helper.rs +++ b/src/utils/arith_helper.rs @@ -14,7 +14,7 @@ pub fn log2_ceil>(x: T) -> usize { ret } -pub fn ev(poly: &Vec, x: F) -> F { +pub fn ev(poly: &Vec, x: F) -> F { let mut ret = F::ZERO; let l = poly.len(); for i in 0..l { diff --git a/src/witness.rs b/src/witness.rs index 5878531..681b9df 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -147,7 +147,7 @@ pub trait Module { fn scale(&mut self, scale: F) -> (); } -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct ProtostarLhsWtns { pub round_wtns: Vec>, pub pubs: Vec>, @@ -155,7 +155,7 @@ pub struct ProtostarLhsWtns { } impl ProtostarLhsWtns { - pub fn commit> (&self, commitment_key: Vec>) -> ProtostarLhs { + pub fn commit> (&self, commitment_key: &Vec>) -> ProtostarLhs { ProtostarLhs { round_commitments: self.round_wtns.iter().zip_eq(commitment_key).map(|(wtns, ck)| best_multiexp(&wtns, &ck).into()).collect_vec(), pubs: self.pubs.clone(), @@ -211,7 +211,7 @@ impl Module for ProtostarLhsWtns { } -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct ProtostarWtns { pub lhs: ProtostarLhsWtns, pub error: F @@ -235,12 +235,19 @@ impl Module for ProtostarWtns { } impl ProtostarWtns { - pub fn commit> (&self, commitment_key: Vec>) -> ProtostarInstance { + pub fn commit> (&self, commitment_key: &Vec>) -> ProtostarInstance { ProtostarInstance { lhs: self.lhs.commit(commitment_key), error: self.error, } } + + pub fn random_like(mut rng: &mut RNG, other: &Self) -> Self { + Self { + lhs: ProtostarLhsWtns::random_like(rng, &other.lhs), + error: other.error, + } + } } pub fn compute_error_term<'circuit, F: PrimeField, G: Gate<'circuit, F>>(wtns: &ProtostarLhsWtns, cs: &ProtoGalaxyConstraintSystem<'circuit, F, G>) -> F {