From 64db5b586411bc869bde7dd31670977e40d6b140 Mon Sep 17 00:00:00 2001 From: Adrian Hamelink Date: Tue, 20 Feb 2024 20:20:10 +0100 Subject: [PATCH] Adapt to CurveCycleEquiped --- src/parafold/circuit.rs | 35 ++-- src/parafold/cycle_fold/circuit.rs | 53 +++--- src/parafold/cycle_fold/mod.rs | 48 ++--- src/parafold/cycle_fold/prover.rs | 63 +++--- src/parafold/ecc.rs | 236 +++++++++-------------- src/parafold/nifs/circuit.rs | 44 ++--- src/parafold/nifs/circuit_secondary.rs | 61 +++--- src/parafold/nifs/prover.rs | 253 +++++++++++++------------ src/parafold/nivc/circuit.rs | 87 ++++----- src/parafold/nivc/mod.rs | 34 ++-- src/parafold/nivc/prover.rs | 83 ++++---- src/parafold/prover.rs | 30 ++- src/parafold/transcript/circuit.rs | 31 +-- src/parafold/transcript/mod.rs | 4 +- src/parafold/transcript/prover.rs | 35 ++-- 15 files changed, 517 insertions(+), 580 deletions(-) diff --git a/src/parafold/circuit.rs b/src/parafold/circuit.rs index 49b4ad066..83a6432f4 100644 --- a/src/parafold/circuit.rs +++ b/src/parafold/circuit.rs @@ -5,19 +5,18 @@ use crate::parafold::nivc::{NIVCMergeProof, NIVCUpdateProof, NIVCIO}; use crate::parafold::transcript::circuit::AllocatedTranscript; use crate::parafold::transcript::TranscriptConstants; use crate::supernova::StepCircuit; -use crate::traits::Engine; +use crate::traits::CurveCycleEquipped; -pub fn synthesize_step( +pub fn synthesize_step( mut cs: CS, - ro_consts: &TranscriptConstants, - proof: NIVCUpdateProof, + ro_consts: &TranscriptConstants, + proof: NIVCUpdateProof, step_circuit: &SF, -) -> Result, SynthesisError> +) -> Result, SynthesisError> where - E1: Engine, - E2: Engine, - CS: ConstraintSystem, - SF: StepCircuit, + E: CurveCycleEquipped, + CS: ConstraintSystem, + SF: StepCircuit, { // Fold proof for previous state let (mut state, transcript) = @@ -30,18 +29,18 @@ where io } + /// Circuit -pub fn synthesize_merge( +pub fn synthesize_merge( mut cs: CS, - ro_consts: &TranscriptConstants, - proof_L: NIVCUpdateProof, - proof_R: NIVCUpdateProof, - proof_merge: NIVCMergeProof, -) -> Result, SynthesisError> + ro_consts: &TranscriptConstants, + proof_L: NIVCUpdateProof, + proof_R: NIVCUpdateProof, + proof_merge: NIVCMergeProof, +) -> Result, SynthesisError> where - E1: Engine, - E2: Engine, - CS: ConstraintSystem, + E: CurveCycleEquipped, + CS: ConstraintSystem, { // Verify L let (self_L, transcript_L) = diff --git a/src/parafold/cycle_fold/circuit.rs b/src/parafold/cycle_fold/circuit.rs index c003712e5..c1baa95cd 100644 --- a/src/parafold/cycle_fold/circuit.rs +++ b/src/parafold/cycle_fold/circuit.rs @@ -6,18 +6,14 @@ use crate::parafold::cycle_fold::AllocatedHashedCommitment; use crate::parafold::nifs::circuit_secondary::AllocatedSecondaryRelaxedR1CSInstance; use crate::parafold::nifs::FoldProof; use crate::parafold::transcript::circuit::AllocatedTranscript; - -use crate::traits::Engine; +use crate::traits::{CurveCycleEquipped, Engine}; #[derive(Debug, Clone)] -pub struct AllocatedScalarMulAccumulator { - deferred: Vec>, +pub struct AllocatedScalarMulAccumulator { + deferred: Vec>, } -impl AllocatedScalarMulAccumulator -where - E1: Engine, -{ +impl AllocatedScalarMulAccumulator { pub fn new() -> Self { Self { deferred: vec![] } } @@ -26,13 +22,13 @@ where pub fn scalar_mul( &mut self, mut cs: CS, - A: AllocatedHashedCommitment, - B: AllocatedHashedCommitment, - x: AllocatedNum, - transcript: &mut AllocatedTranscript, - ) -> Result, SynthesisError> + A: AllocatedHashedCommitment, + B: AllocatedHashedCommitment, + x: AllocatedNum, + transcript: &mut AllocatedTranscript, + ) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let A_value = A.value; let B_value = B.value; @@ -59,17 +55,18 @@ where self_L.deferred.append(&mut self_R.deferred); self_L } +} - pub fn finalize( +impl AllocatedScalarMulAccumulator { + pub fn finalize( self, mut cs: CS, - mut acc_cf: AllocatedSecondaryRelaxedR1CSInstance, - proofs: impl IntoIterator>, - transcript: &mut AllocatedTranscript, - ) -> Result, SynthesisError> + mut acc_cf: AllocatedSecondaryRelaxedR1CSInstance, + proofs: impl IntoIterator>, + transcript: &mut AllocatedTranscript, + ) -> Result, SynthesisError> where - CS: ConstraintSystem, - E2: Engine, + CS: ConstraintSystem, { for (instance, proof) in zip_eq(self.deferred, proofs) { let AllocatedScalarMulInstance { A, B, x, C } = instance; @@ -91,15 +88,15 @@ where } #[derive(Debug, Clone)] -pub struct AllocatedScalarMulInstance { - A: AllocatedHashedCommitment, - B: AllocatedHashedCommitment, - x: AllocatedNum, - C: AllocatedHashedCommitment, +pub struct AllocatedScalarMulInstance { + A: AllocatedHashedCommitment, + B: AllocatedHashedCommitment, + x: AllocatedNum, + C: AllocatedHashedCommitment, } -impl AllocatedScalarMulInstance { - pub fn as_preimage(&self) -> impl IntoIterator> + '_ { +impl AllocatedScalarMulInstance { + pub fn as_preimage(&self) -> impl IntoIterator> + '_ { chain![ self.A.as_preimage(), self.B.as_preimage(), diff --git a/src/parafold/cycle_fold/mod.rs b/src/parafold/cycle_fold/mod.rs index f8838bdf5..9179b1a0c 100644 --- a/src/parafold/cycle_fold/mod.rs +++ b/src/parafold/cycle_fold/mod.rs @@ -45,24 +45,24 @@ pub mod prover; /// so this additional hashing that occurs in the secondary circuit ensure we only need to perform this expensive /// operation 4 times. Moreover, the fact that r { - point: Commitment, +pub struct HashedCommitment { + point: Commitment, // Poseidon hash of (x,y) = point. We set hash = 0 when `point` = infinity - hash: E1::Base, + hash: E::Base, // E1 representation of `hash` with `BN_N_LIMBS` limbs of BN_LIMB_WIDTH bits. - hash_limbs: [E1::Scalar; BN_N_LIMBS], + hash_limbs: [E::Scalar; BN_N_LIMBS], } -impl HashedCommitment { +impl HashedCommitment { /// Convert a [Commitment] to it's compressed representation. - pub fn new(point: Commitment) -> Self { - let constants = PoseidonConstants::::new(); + pub fn new(point: Commitment) -> Self { + let constants = PoseidonConstants::::new(); let (x, y, infinity) = point.to_coordinates(); if infinity { Self { point, - hash: E1::Base::ZERO, - hash_limbs: [E1::Scalar::ZERO; BN_N_LIMBS], + hash: E::Base::ZERO, + hash_limbs: [E::Scalar::ZERO; BN_N_LIMBS], } } else { let hash = Poseidon::new_with_preimage(&[x, y], &constants).hash(); @@ -71,17 +71,17 @@ impl HashedCommitment { .chunks_exact(BN_LIMB_WIDTH) .map(|limb_bits| { // TODO: Find more efficient trick - let mut limb = E1::Scalar::ZERO; + let mut limb = E::Scalar::ZERO; for bit in limb_bits.iter().rev() { // double limb limb += limb; if *bit { - limb += E1::Scalar::ONE; + limb += E::Scalar::ONE; } } limb }) - .collect::>(); + .collect::>(); Self { point, @@ -91,7 +91,7 @@ impl HashedCommitment { } } - pub fn as_preimage(&self) -> impl IntoIterator { + pub fn as_preimage(&self) -> impl IntoIterator { self.hash_limbs } } @@ -106,18 +106,18 @@ impl HashedCommitment { /// - Investigate whether a `is_infinity` flag is needed. It could be used to avoid synthesizing secondary circuits /// when the scalar multiplication is trivial. #[derive(Debug, Clone)] -pub struct AllocatedHashedCommitment { - value: Commitment, +pub struct AllocatedHashedCommitment { + value: Commitment, // hash = if let Some(point) = value { H_secondary(point) } else { 0 } - hash_limbs: [AllocatedNum; BN_N_LIMBS], + hash_limbs: [AllocatedNum; BN_N_LIMBS], } -impl AllocatedHashedCommitment { - pub fn alloc(mut cs: CS, c: Commitment) -> Self +impl AllocatedHashedCommitment { + pub fn alloc(mut cs: CS, c: Commitment) -> Self where - CS: ConstraintSystem, + CS: ConstraintSystem, { - let hashed = HashedCommitment::::new(c); + let hashed = HashedCommitment::::new(c); let hash_limbs = hashed .hash_limbs .map(|limb| AllocatedNum::alloc_infallible(cs.namespace(|| "alloc limb"), || limb)); @@ -130,18 +130,18 @@ impl AllocatedHashedCommitment { pub fn alloc_transcript( mut cs: CS, - c: Commitment, - transcript: &mut AllocatedTranscript, + c: Commitment, + transcript: &mut AllocatedTranscript, ) -> Self where - CS: ConstraintSystem, + CS: ConstraintSystem, { let c = AllocatedHashedCommitment::alloc(&mut cs, c); transcript.absorb(c.as_preimage()); c } - pub fn as_preimage(&self) -> impl IntoIterator> { + pub fn as_preimage(&self) -> impl IntoIterator> { self.hash_limbs.clone() } } diff --git a/src/parafold/cycle_fold/prover.rs b/src/parafold/cycle_fold/prover.rs index cd99e4b74..145dcc4f1 100644 --- a/src/parafold/cycle_fold/prover.rs +++ b/src/parafold/cycle_fold/prover.rs @@ -5,7 +5,7 @@ use crate::parafold::nifs::prover::RelaxedR1CS; use crate::parafold::nifs::FoldProof; use crate::parafold::transcript::prover::Transcript; use crate::r1cs::R1CSShape; -use crate::traits::Engine; +use crate::traits::{CurveCycleEquipped, Dual, Engine}; use crate::{Commitment, CommitmentKey}; /// A [ScalarMulAccumulator] represents a coprocessor for efficiently computing non-native ECC scalar multiplications @@ -18,11 +18,11 @@ use crate::{Commitment, CommitmentKey}; /// /// All operations are proved in a batch at the end of the circuit in order to minimize latency for the prover. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ScalarMulAccumulator { - deferred: Vec>, +pub struct ScalarMulAccumulator { + deferred: Vec>, } -impl ScalarMulAccumulator { +impl ScalarMulAccumulator { pub fn new() -> Self { Self { deferred: vec![] } } @@ -34,60 +34,59 @@ impl ScalarMulAccumulator { /// The tuple `[A, B, x, C]` is added to the `deferred` list which will be proved in a batch later on. pub fn scalar_mul( &mut self, - A: Commitment, - B: Commitment, - x: E1::Scalar, - transcript: &mut Transcript, - ) -> Commitment { - let C: Commitment = A + B * x; + A: Commitment, + B: Commitment, + x: E::Scalar, + transcript: &mut Transcript, + ) -> Commitment { + let C: Commitment = A + B * x; - transcript.absorb_commitment_primary(C.clone()); + transcript.absorb_commitment_primary::(C.clone()); self.deferred.push(ScalarMulInstance { A, B, x, C }); C } +} +impl ScalarMulAccumulator { /// Consume all deferred scalar multiplication instances and create a folding proof for each result. /// The proofs are folded into a mutable RelaxedR1CS for the corresponding circuit over the secondary curve. - pub fn finalize( + pub fn finalize( self, - ck: &CommitmentKey, - shape: &R1CSShape, - acc_cf: &mut RelaxedR1CS, - transcript: &mut Transcript, - ) -> Vec> - where - E2: Engine, - { + ck: &CommitmentKey, + shape: &R1CSShape, + acc_cf: &mut RelaxedR1CS, + transcript: &mut Transcript, + ) -> Vec> { self .deferred .into_iter() .map(|_instance| { - let cs = SatisfyingAssignment::::new(); + let cs = SatisfyingAssignment::>::new(); // TODO: synthesize the circuit that proves `instance` let (X, W) = cs.to_assignments(); - acc_cf.fold_secondary(ck, shape, X, &W, transcript) + acc_cf.fold_secondary::(ck, shape, X, &W, transcript) }) .collect() } - pub fn simulate_finalize(self, transcript: &mut Transcript) -> Vec> - where - E2: Engine, - { + pub fn simulate_finalize( + self, + transcript: &mut Transcript, + ) -> Vec> { self .deferred .into_iter() - .map(|_| RelaxedR1CS::::simulate_fold_secondary(transcript)) + .map(|_| RelaxedR1CS::simulate_fold_secondary::(transcript)) .collect() } } #[derive(Debug, Clone, Default, PartialEq, Eq)] -pub struct ScalarMulInstance { - A: Commitment, - B: Commitment, - x: E1::Scalar, - C: Commitment, +pub struct ScalarMulInstance { + A: Commitment, + B: Commitment, + x: E::Scalar, + C: Commitment, } diff --git a/src/parafold/ecc.rs b/src/parafold/ecc.rs index 657e63dc4..96736ac06 100644 --- a/src/parafold/ecc.rs +++ b/src/parafold/ecc.rs @@ -1,10 +1,8 @@ -use std::marker::PhantomData; - use bellpepper::gadgets::Assignment; use bellpepper_core::boolean::{AllocatedBit, Boolean}; use bellpepper_core::num::AllocatedNum; use bellpepper_core::{ConstraintSystem, SynthesisError}; -use ff::PrimeField; +use ff::{Field, PrimeField}; use crate::gadgets::utils::{ alloc_num_equals, alloc_one, alloc_zero, conditionally_select, conditionally_select2, @@ -13,26 +11,21 @@ use crate::gadgets::utils::{ }; use crate::parafold::transcript::circuit::AllocatedTranscript; use crate::traits::commitment::CommitmentTrait; -use crate::traits::{Engine, Group}; +use crate::traits::{CurveCycleEquipped, Engine, Group}; use crate::Commitment; /// `AllocatedPoint` provides an elliptic curve abstraction inside a circuit. #[derive(Debug, Clone)] -pub struct AllocatedPoint { - pub(crate) x: AllocatedNum, - pub(crate) y: AllocatedNum, - pub(crate) is_infinity: AllocatedNum, - _marker: PhantomData, +pub struct AllocatedPoint { + pub(crate) x: AllocatedNum, + pub(crate) y: AllocatedNum, + pub(crate) is_infinity: AllocatedNum, } -impl AllocatedPoint -where - F: PrimeField, - G: Group, -{ +impl AllocatedPoint { pub fn select_default(self, mut cs: CS, is_default: &Boolean) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { let zero = alloc_zero(cs.namespace(|| "alloc 0")); let one = alloc_one(cs.namespace(|| "alloc 1")); @@ -47,17 +40,12 @@ where &is_infinity, is_default, )?; - Ok(Self { - x, - y, - is_infinity, - _marker: Default::default(), - }) + Ok(Self { x, y, is_infinity }) } pub fn enforce_trivial(&self, mut cs: CS, is_trivial: &Boolean) where - CS: ConstraintSystem, + CS: ConstraintSystem, { // is_trivial => (is_identity == 1) // is_trivial == is_identity @@ -65,19 +53,19 @@ where || "is_trivial - E.is_infinity = 0", |lc| lc, |lc| lc, - |_| is_trivial.lc(CS::one(), F::ONE) - self.is_infinity.get_variable(), + |_| is_trivial.lc(CS::one(), G::Base::ONE) - self.is_infinity.get_variable(), ); } - pub fn alloc_transcript( + pub fn alloc_transcript( mut cs: CS, - c: Commitment, - transcript: &mut AllocatedTranscript, + c: Commitment, + transcript: &mut AllocatedTranscript, ) -> Self where - CS: ConstraintSystem, - E1: Engine, - E2: Engine, + CS: ConstraintSystem, + E: CurveCycleEquipped, + E2: Engine, { let c = Self::alloc(&mut cs, Some(c.to_coordinates())).unwrap(); c.check_on_curve(cs.namespace(|| "check on curve")).unwrap(); @@ -87,17 +75,24 @@ where /// Allocates a new point on the curve using coordinates provided by `coords`. /// If coords = None, it allocates the default infinity point - pub fn alloc(mut cs: CS, coords: Option<(F, F, bool)>) -> Result + pub fn alloc( + mut cs: CS, + coords: Option<(G::Base, G::Base, bool)>, + ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { - let x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(coords.map_or(F::ZERO, |c| c.0)))?; - let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(coords.map_or(F::ZERO, |c| c.1)))?; + let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { + Ok(coords.map_or(G::Base::ZERO, |c| c.0)) + })?; + let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { + Ok(coords.map_or(G::Base::ZERO, |c| c.1)) + })?; let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || { Ok(if coords.map_or(true, |c| c.2) { - F::ONE + G::Base::ONE } else { - F::ZERO + G::Base::ZERO }) })?; cs.enforce( @@ -107,18 +102,13 @@ where |lc| lc, ); - Ok(Self { - x, - y, - is_infinity, - _marker: PhantomData, - }) + Ok(Self { x, y, is_infinity }) } /// checks if `self` is on the curve or if it is infinity pub fn check_on_curve(&self, mut cs: CS) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { // check that (x,y) is on the curve if it is not infinity // we will check that (1- is_infinity) * y^2 = (1-is_infinity) * (x^3 + Ax + B) @@ -128,8 +118,8 @@ where let x_cube = self.x.mul(cs.namespace(|| "x_cube"), &x_square)?; let rhs = AllocatedNum::alloc(cs.namespace(|| "rhs"), || { - if *self.is_infinity.get_value().get()? == F::ONE { - Ok(F::ZERO) + if *self.is_infinity.get_value().get()? == G::Base::ONE { + Ok(G::Base::ZERO) } else { Ok( *x_cube.get_value().get()? @@ -164,7 +154,7 @@ where /// Allocates a default point on the curve, set to the identity point. pub fn default(mut cs: CS) -> Self where - CS: ConstraintSystem, + CS: ConstraintSystem, { let zero = alloc_zero(cs.namespace(|| "zero")); let one = alloc_one(cs.namespace(|| "one")); @@ -173,19 +163,24 @@ where x: zero.clone(), y: zero, is_infinity: one, - _marker: PhantomData, } } /// Returns coordinates associated with the point. - pub const fn get_coordinates(&self) -> (&AllocatedNum, &AllocatedNum, &AllocatedNum) { + pub const fn get_coordinates( + &self, + ) -> ( + &AllocatedNum, + &AllocatedNum, + &AllocatedNum, + ) { (&self.x, &self.y, &self.is_infinity) } /// Negates the provided point pub fn negate(&self, mut cs: CS) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(-*self.y.get_value().get()?))?; @@ -200,14 +195,13 @@ where x: self.x.clone(), y, is_infinity: self.is_infinity.clone(), - _marker: PhantomData, }) } /// Add two points (may be equal) pub fn add(&self, mut cs: CS, other: &Self) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute boolean equal indicating if self = other @@ -260,7 +254,7 @@ where equal_x: &AllocatedBit, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { //************************************************************************/ // lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap(); @@ -274,9 +268,9 @@ where // NOT(NOT(self.is_ifninity) AND NOT(other.is_infinity)) let at_least_one_inf = AllocatedNum::alloc(cs.namespace(|| "at least one inf"), || { Ok( - F::ONE - - (F::ONE - *self.is_infinity.get_value().get()?) - * (F::ONE - *other.is_infinity.get_value().get()?), + G::Base::ONE + - (G::Base::ONE - *self.is_infinity.get_value().get()?) + * (G::Base::ONE - *other.is_infinity.get_value().get()?), ) })?; cs.enforce( @@ -290,7 +284,7 @@ where let x_diff_is_actual = AllocatedNum::alloc(cs.namespace(|| "allocate x_diff_is_actual"), || { Ok(if *equal_x.get_value().get()? { - F::ONE + G::Base::ONE } else { *at_least_one_inf.get_value().get()? }) @@ -312,9 +306,9 @@ where )?; let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { - let x_diff_inv = if *x_diff_is_actual.get_value().get()? == F::ONE { + let x_diff_inv = if *x_diff_is_actual.get_value().get()? == G::Base::ONE { // Set to default - F::ONE + G::Base::ONE } else { // Set to the actual inverse (*other.x.get_value().get()? - *self.x.get_value().get()?) @@ -415,18 +409,13 @@ where &self.is_infinity, )?; - Ok(Self { - x, - y, - is_infinity, - _marker: PhantomData, - }) + Ok(Self { x, y, is_infinity }) } /// Doubles the supplied point. pub fn double(&self, mut cs: CS) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { //*************************************************************/ // lambda = (G::Base::from(3) * self.x * self.x + G::A()) @@ -449,19 +438,19 @@ where // Now compute lambda as (G::Base::from(3) * self.x * self.x + G::A()) * tmp_inv let prod_1 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 1"), || { - Ok(F::from(3) * self.x.get_value().get()? * self.x.get_value().get()?) + Ok(G::Base::from(3) * self.x.get_value().get()? * self.x.get_value().get()?) })?; cs.enforce( || "Check prod 1", - |lc| lc + (F::from(3), self.x.get_variable()), + |lc| lc + (G::Base::from(3), self.x.get_variable()), |lc| lc + self.x.get_variable(), |lc| lc + prod_1.get_variable(), ); let lambda = AllocatedNum::alloc(cs.namespace(|| "alloc lambda"), || { - let tmp_inv = if *self.is_infinity.get_value().get()? == F::ONE { + let tmp_inv = if *self.is_infinity.get_value().get()? == G::Base::ONE { // Return default value 1 - F::ONE + G::Base::ONE } else { // Return the actual inverse (*tmp.get_value().get()?).invert().unwrap() @@ -525,12 +514,7 @@ where // is_infinity let is_infinity = self.is_infinity.clone(); - Ok(Self { - x, - y, - is_infinity, - _marker: PhantomData, - }) + Ok(Self { x, y, is_infinity }) } /// A gadget for scalar multiplication, optimized to use incomplete addition law. @@ -542,9 +526,9 @@ where scalar_bits: &[AllocatedBit], ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { - let split_len = core::cmp::min(scalar_bits.len(), (F::NUM_BITS - 2) as usize); + let split_len = core::cmp::min(scalar_bits.len(), (G::Base::NUM_BITS - 2) as usize); let (incomplete_bits, complete_bits) = scalar_bits.split_at(split_len); // we convert AllocatedPoint into AllocatedPointNonInfinity; we deal with the case where self.is_infinity = 1 below @@ -609,7 +593,6 @@ where x, y, is_infinity: res.is_infinity, - _marker: PhantomData, }; let mut p_complete = p.to_allocated_point(&self.is_infinity); @@ -636,7 +619,7 @@ where condition: &Boolean, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { let x = conditionally_select(cs.namespace(|| "select x"), &a.x, &b.x, condition)?; @@ -649,12 +632,7 @@ where condition, )?; - Ok(Self { - x, - y, - is_infinity, - _marker: PhantomData, - }) + Ok(Self { x, y, is_infinity }) } /// If condition outputs a otherwise infinity @@ -664,7 +642,7 @@ where condition: &Boolean, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { let x = select_num_or_zero(cs.namespace(|| "select x"), &a.x, condition)?; @@ -676,45 +654,31 @@ where condition, )?; - Ok(Self { - x, - y, - is_infinity, - _marker: PhantomData, - }) + Ok(Self { x, y, is_infinity }) } - pub fn as_preimage(&self) -> impl IntoIterator> { + pub fn as_preimage(&self) -> impl IntoIterator> { [self.x.clone(), self.y.clone(), self.is_infinity.clone()] } } #[derive(Clone)] /// `AllocatedPoint` but one that is guaranteed to be not infinity -pub struct AllocatedPointNonInfinity { - x: AllocatedNum, - y: AllocatedNum, - _marker: PhantomData, +pub struct AllocatedPointNonInfinity { + x: AllocatedNum, + y: AllocatedNum, } -impl AllocatedPointNonInfinity -where - F: PrimeField, - G: Group, -{ +impl AllocatedPointNonInfinity { /// Creates a new `AllocatedPointNonInfinity` from the specified coordinates - pub fn new(x: AllocatedNum, y: AllocatedNum) -> Self { - Self { - x, - y, - _marker: PhantomData, - } + pub fn new(x: AllocatedNum, y: AllocatedNum) -> Self { + Self { x, y } } /// Allocates a new point on the curve using coordinates provided by `coords`. - pub fn alloc>( + pub fn alloc>( mut cs: CS, - coords: Option<(F, F)>, + coords: Option<(G::Base, G::Base)>, ) -> Result { let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { coords.map_or(Err(SynthesisError::AssignmentMissing), |c| Ok(c.0)) @@ -723,46 +687,40 @@ where coords.map_or(Err(SynthesisError::AssignmentMissing), |c| Ok(c.1)) })?; - Ok(Self { - x, - y, - _marker: PhantomData, - }) + Ok(Self { x, y }) } /// Turns an `AllocatedPoint` into an `AllocatedPointNonInfinity` (assumes it is not infinity) - pub fn from_allocated_point(p: &AllocatedPoint) -> Self { + pub fn from_allocated_point(p: &AllocatedPoint) -> Self { Self { x: p.x.clone(), y: p.y.clone(), - _marker: PhantomData, } } /// Returns an `AllocatedPoint` from an `AllocatedPointNonInfinity` - pub fn to_allocated_point(&self, is_infinity: &AllocatedNum) -> AllocatedPoint { + pub fn to_allocated_point(&self, is_infinity: &AllocatedNum) -> AllocatedPoint { AllocatedPoint { x: self.x.clone(), y: self.y.clone(), is_infinity: is_infinity.clone(), - _marker: PhantomData, } } /// Returns coordinates associated with the point. - pub const fn get_coordinates(&self) -> (&AllocatedNum, &AllocatedNum) { + pub const fn get_coordinates(&self) -> (&AllocatedNum, &AllocatedNum) { (&self.x, &self.y) } /// Add two points assuming self != +/- other pub fn add_incomplete(&self, mut cs: CS, other: &Self) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // allocate a free variable that an honest prover sets to lambda = (y2-y1)/(x2-x1) let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { if *other.x.get_value().get()? == *self.x.get_value().get()? { - Ok(F::ONE) + Ok(G::Base::ONE) } else { Ok( (*other.y.get_value().get()? - *self.y.get_value().get()?) @@ -813,15 +771,11 @@ where |lc| lc + y.get_variable() + self.y.get_variable(), ); - Ok(Self { - x, - y, - _marker: PhantomData, - }) + Ok(Self { x, y }) } /// doubles the point; since this is called with a point not at infinity, it is guaranteed to be not infinity - pub fn double_incomplete>( + pub fn double_incomplete>( &self, mut cs: CS, ) -> Result { @@ -830,10 +784,10 @@ where let x_sq = self.x.square(cs.namespace(|| "x_sq"))?; let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { - let n = F::from(3) * x_sq.get_value().get()? + G::group_params().0; - let d = F::from(2) * *self.y.get_value().get()?; - if d == F::ZERO { - Ok(F::ONE) + let n = G::Base::from(3) * x_sq.get_value().get()? + G::group_params().0; + let d = G::Base::from(2) * *self.y.get_value().get()?; + if d == G::Base::ZERO { + Ok(G::Base::ONE) } else { Ok(n * d.invert().unwrap()) } @@ -841,8 +795,8 @@ where cs.enforce( || "Check that lambda is computed correctly", |lc| lc + lambda.get_variable(), - |lc| lc + (F::from(2), self.y.get_variable()), - |lc| lc + (F::from(3), x_sq.get_variable()) + (G::group_params().0, CS::one()), + |lc| lc + (G::Base::from(2), self.y.get_variable()), + |lc| lc + (G::Base::from(3), x_sq.get_variable()) + (G::group_params().0, CS::one()), ); let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { @@ -857,7 +811,7 @@ where || "check that x is correct", |lc| lc + lambda.get_variable(), |lc| lc + lambda.get_variable(), - |lc| lc + x.get_variable() + (F::from(2), self.x.get_variable()), + |lc| lc + x.get_variable() + (G::Base::from(2), self.x.get_variable()), ); let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { @@ -874,15 +828,11 @@ where |lc| lc + y.get_variable() + self.y.get_variable(), ); - Ok(Self { - x, - y, - _marker: PhantomData, - }) + Ok(Self { x, y }) } /// If condition outputs a otherwise outputs b - pub fn conditionally_select>( + pub fn conditionally_select>( mut cs: CS, a: &Self, b: &Self, @@ -891,10 +841,6 @@ where let x = conditionally_select(cs.namespace(|| "select x"), &a.x, &b.x, condition)?; let y = conditionally_select(cs.namespace(|| "select y"), &a.y, &b.y, condition)?; - Ok(Self { - x, - y, - _marker: PhantomData, - }) + Ok(Self { x, y }) } } diff --git a/src/parafold/nifs/circuit.rs b/src/parafold/nifs/circuit.rs index 8d9401b0e..182d6f286 100644 --- a/src/parafold/nifs/circuit.rs +++ b/src/parafold/nifs/circuit.rs @@ -12,25 +12,25 @@ use crate::traits::Engine; /// Allocated [RelaxedR1CSInstance] for a circuit over the primary curve. #[derive(Debug, Clone)] -pub struct AllocatedRelaxedR1CSInstance { - u: AllocatedNum, - X: Vec>, - W: AllocatedHashedCommitment, - E: AllocatedHashedCommitment, +pub struct AllocatedRelaxedR1CSInstance { + u: AllocatedNum, + X: Vec>, + W: AllocatedHashedCommitment, + E: AllocatedHashedCommitment, } -impl AllocatedRelaxedR1CSInstance { +impl AllocatedRelaxedR1CSInstance { /// Folds an R1CSInstance into `self` pub fn fold( self, mut cs: CS, - X_new: Vec>, - acc_sm: &mut AllocatedScalarMulAccumulator, - fold_proof: FoldProof, - transcript: &mut AllocatedTranscript, + X_new: Vec>, + acc_sm: &mut AllocatedScalarMulAccumulator, + fold_proof: FoldProof, + transcript: &mut AllocatedTranscript, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { let FoldProof { W: W_new, T } = fold_proof; @@ -87,12 +87,12 @@ impl AllocatedRelaxedR1CSInstance { mut cs: CS, accs_L: Vec, accs_R: Vec, - acc_sm: &mut AllocatedScalarMulAccumulator, - proofs: Vec>, - transcript: &mut AllocatedTranscript, + acc_sm: &mut AllocatedScalarMulAccumulator, + proofs: Vec>, + transcript: &mut AllocatedTranscript, ) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Add all cross-term commitments to the transcript. let Ts = proofs @@ -171,19 +171,19 @@ impl AllocatedRelaxedR1CSInstance { pub fn hash( &self, mut cs: CS, - constants: &TranscriptConstants, - ) -> Result, SynthesisError> + constants: &TranscriptConstants, + ) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { - let mut transcript = AllocatedTranscript::::new(constants.clone()); + let mut transcript = AllocatedTranscript::new(constants.clone()); transcript.absorb(self.as_preimage()); transcript.squeeze(&mut cs) } - pub fn alloc(mut cs: CS, instance: RelaxedR1CSInstance) -> Self + pub fn alloc(mut cs: CS, instance: RelaxedR1CSInstance) -> Self where - CS: ConstraintSystem, + CS: ConstraintSystem, { // TODO: Add the circuit digest let RelaxedR1CSInstance { u, X, W, E } = instance; @@ -199,7 +199,7 @@ impl AllocatedRelaxedR1CSInstance { Self { u, X, W, E } } - pub fn as_preimage(&self) -> impl IntoIterator> + '_ { + pub fn as_preimage(&self) -> impl IntoIterator> + '_ { // TODO: Add the circuit digest chain![ [self.u.clone()], diff --git a/src/parafold/nifs/circuit_secondary.rs b/src/parafold/nifs/circuit_secondary.rs index c9fdce3a8..423e89473 100644 --- a/src/parafold/nifs/circuit_secondary.rs +++ b/src/parafold/nifs/circuit_secondary.rs @@ -13,36 +13,32 @@ use crate::gadgets::utils::{alloc_bignat_constant, conditionally_select_bignat, use crate::parafold::ecc::AllocatedPoint; use crate::parafold::nifs::{FoldProof, MergeProof, RelaxedR1CSInstance}; use crate::parafold::transcript::circuit::AllocatedTranscript; -use crate::traits::Engine; +use crate::traits::{CurveCycleEquipped, Engine}; #[derive(Debug, Clone)] -pub struct AllocatedSecondaryRelaxedR1CSInstance { - pub u: BigNat, - pub X: Vec>, - pub W: AllocatedPoint, - pub E: AllocatedPoint, +pub struct AllocatedSecondaryRelaxedR1CSInstance { + pub u: BigNat, + pub X: Vec>, + pub W: AllocatedPoint<::GE>, + pub E: AllocatedPoint<::GE>, // q: BigNat, // = E2::Base::MODULUS } -impl AllocatedSecondaryRelaxedR1CSInstance -where - E1: Engine, - E2: Engine, -{ +impl AllocatedSecondaryRelaxedR1CSInstance { pub fn fold( &mut self, mut cs: CS, - X_new: Vec>, - fold_proof: FoldProof, - transcript: &mut AllocatedTranscript, + X_new: Vec>, + fold_proof: FoldProof, + transcript: &mut AllocatedTranscript, ) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Allocate the order of the non-native field as a constant let q_bn = alloc_bignat_constant( cs.namespace(|| "alloc G::Base::modulus"), - &BigInt::from_str_radix(E2::Scalar::MODULUS, 16).unwrap(), + &BigInt::from_str_radix(E::Base::MODULUS, 16).unwrap(), BN_LIMB_WIDTH, BN_N_LIMBS, )?; @@ -50,13 +46,12 @@ where let FoldProof { W: W_new, T } = fold_proof; // Allocate W_new, T and add them to the transcript - let W_new = AllocatedPoint::alloc_transcript::<_, E1, E2>( + let W_new = AllocatedPoint::alloc_transcript::<_, E, _>( cs.namespace(|| "alloc W_new"), W_new, transcript, ); - let T = - AllocatedPoint::alloc_transcript::<_, E1, E2>(cs.namespace(|| "alloc T"), T, transcript); + let T = AllocatedPoint::alloc_transcript::<_, E, _>(cs.namespace(|| "alloc T"), T, transcript); // Get challenge `r` but truncate the bits for more efficient scalar multiplication let r_bits = transcript.squeeze_bits(cs.namespace(|| "r bits"), NUM_CHALLENGE_BITS)?; @@ -109,20 +104,21 @@ where Ok(()) } + pub fn merge( mut cs: CS, self_L: Self, self_R: Self, - merge_proof: MergeProof, - transcript: &mut AllocatedTranscript, + merge_proof: MergeProof, + transcript: &mut AllocatedTranscript, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Allocate the order of the non-native field as a constant let q_bn = alloc_bignat_constant( cs.namespace(|| "alloc G::Base::modulus"), - &BigInt::from_str_radix(E2::Scalar::MODULUS, 16).unwrap(), + &BigInt::from_str_radix(E::Base::MODULUS, 16).unwrap(), BN_LIMB_WIDTH, BN_N_LIMBS, )?; @@ -130,8 +126,7 @@ where let MergeProof { T } = merge_proof; // Allocate T and add to transcript - let T = - AllocatedPoint::alloc_transcript::<_, E1, E2>(cs.namespace(|| "alloc T"), T, transcript); + let T = AllocatedPoint::alloc_transcript::<_, E, _>(cs.namespace(|| "alloc T"), T, transcript); transcript.absorb(T.as_preimage()); // Get truncated challenge @@ -193,7 +188,7 @@ where pub fn enforce_trivial(&self, mut cs: CS, is_trivial: &Boolean) where - CS: ConstraintSystem, + CS: ConstraintSystem, { // TODO: If is_trivial // u = 0 @@ -206,9 +201,9 @@ where .enforce_trivial(cs.namespace(|| "enforce trivial E"), is_trivial); } - fn alloc(/*mut*/ _cs: CS, _instance: RelaxedR1CSInstance) -> Self + fn alloc(/*mut*/ _cs: CS, _instance: RelaxedR1CSInstance) -> Self where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Both u, X need to be allocated as BigInt todo!() @@ -233,11 +228,11 @@ where /// Allocate and add the result to the transcript pub fn alloc_transcript( mut cs: CS, - instance: RelaxedR1CSInstance, - transcript: &mut AllocatedTranscript, + instance: RelaxedR1CSInstance, + transcript: &mut AllocatedTranscript, ) -> Self where - CS: ConstraintSystem, + CS: ConstraintSystem, { let instance = Self::alloc(&mut cs, instance); transcript.absorb(instance.as_preimage()); @@ -246,7 +241,7 @@ where pub fn select_default(self, mut cs: CS, is_default: &Boolean) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { let bn_zero = alloc_bignat_constant( cs.namespace(|| "alloc zero"), @@ -265,7 +260,7 @@ where Ok(Self { u, X, W, E }) } - pub fn as_preimage(&self) -> impl IntoIterator> { + pub fn as_preimage(&self) -> impl IntoIterator> { vec![] } } diff --git a/src/parafold/nifs/prover.rs b/src/parafold/nifs/prover.rs index eb3bb5e16..79310a78e 100644 --- a/src/parafold/nifs/prover.rs +++ b/src/parafold/nifs/prover.rs @@ -9,7 +9,7 @@ use crate::parafold::transcript::prover::Transcript; use crate::parafold::transcript::TranscriptConstants; use crate::r1cs::R1CSShape; use crate::traits::commitment::{CommitmentEngineTrait, CommitmentTrait}; -use crate::traits::Engine; +use crate::traits::{CurveCycleEquipped, Engine}; use crate::{zip_with, Commitment, CommitmentKey, CE}; /// A full Relaxed-R1CS accumulator for a circuit @@ -46,33 +46,22 @@ impl RelaxedR1CS { /// while updating the transcript with the standard pattern. pub fn simulate_fold_primary( acc_sm: &mut ScalarMulAccumulator, - transcript: &mut Transcript, + transcript: &mut Transcript, ) -> FoldProof { let W = Commitment::::default(); let T = Commitment::::default(); - transcript.absorb_commitment_primary(W); - transcript.absorb_commitment_primary(T); + transcript.absorb_commitment_primary::(W); + transcript.absorb_commitment_primary::(T); let r = transcript.squeeze(); let _ = acc_sm.scalar_mul(W, W, r, transcript); let _ = acc_sm.scalar_mul(T, T, r, transcript); FoldProof { W, T } } - pub fn simulate_fold_secondary>( - transcript: &mut Transcript, - ) -> FoldProof { - let W = Commitment::::default(); - let T = Commitment::::default(); - transcript.absorb_commitment_secondary::(W); - transcript.absorb_commitment_secondary::(T); - let _r = transcript.squeeze(); - FoldProof { W, T } - } - /// Given the public IO `X_new` for a circuit with R1CS representation `shape`, /// along with a satisfying witness vector `W_new`, and assuming `self` is a valid accumulator for the same circuit, /// this function will fold the statement into `self` and return a [FoldProof] that will allow the verifier to perform - /// the same transformation over the corresponding [RelaxedR1CSInstance] of the input `self`. + /// the same transformation over the corresponding [RelaxedR1CSInstance] of the input `self`. /// /// # Warning /// We assume the R1CS IO `X_new` has already been absorbed in some form into the transcript in order to avoid @@ -84,14 +73,14 @@ impl RelaxedR1CS { X_new: Vec, W_new: &[E::Scalar], acc_sm: &mut ScalarMulAccumulator, - transcript: &mut Transcript, + transcript: &mut Transcript, ) -> FoldProof { // TODO: Parallelize both of these operations let W_comm_new = { E::CE::commit(ck, W_new) }; let (T, T_comm) = { self.compute_fold_proof(ck, shape, None, &X_new, W_new) }; - transcript.absorb_commitment_primary(W_comm_new); - transcript.absorb_commitment_primary(T_comm); + transcript.absorb_commitment_primary::(W_comm_new); + transcript.absorb_commitment_primary::(T_comm); let r = transcript.squeeze(); @@ -128,51 +117,6 @@ impl RelaxedR1CS { } } - pub fn fold_secondary>( - &mut self, - ck: &CommitmentKey, - shape: &R1CSShape, - X_new: Vec, - W_new: &[E::Scalar], - transcript: &mut Transcript, - ) -> FoldProof { - // TODO: Parallelize both of these operations - let W_comm_new = { E::CE::commit(ck, W_new) }; - let (T, T_comm) = { self.compute_fold_proof(ck, shape, None, &X_new, W_new) }; - - transcript.absorb(comm_to_base::(&W_comm_new)); - transcript.absorb(comm_to_base::(&T_comm)); - // TODO: Squeeze - let r = transcript.squeeze_bits_secondary(NUM_CHALLENGE_BITS); - - self - .W - .par_iter_mut() - .zip_eq(W_new.par_iter()) - .for_each(|(w, w_new)| *w += r * w_new); - self - .E - .par_iter_mut() - .zip_eq(T.par_iter()) - .for_each(|(e, t)| *e += r * t); - - // For non-relaxed instances, u_new = 1 - self.instance.u += r; - self - .instance - .X - .iter_mut() - .zip_eq(X_new) - .for_each(|(x, x_new)| *x += r * x_new); - self.instance.W = self.instance.W + W_comm_new * r; - self.instance.E = self.instance.E + T_comm * r; - - FoldProof { - W: W_comm_new, - T: T_comm, - } - } - /// Given two lists of [RelaxedR1CS] accumulators, pub fn merge_many( ck: &CommitmentKey, @@ -180,7 +124,7 @@ impl RelaxedR1CS { mut accs_L: Vec, accs_R: &[Self], acc_sm: &mut ScalarMulAccumulator, - transcript: &mut Transcript, + transcript: &mut Transcript, ) -> (Vec, Vec>) { // TODO: parallelize let (Ts, T_comms): (Vec<_>, Vec<_>) = zip_with!( @@ -198,7 +142,7 @@ impl RelaxedR1CS { .unzip(); for T_comm in &T_comms { - transcript.absorb_commitment_primary(*T_comm); + transcript.absorb_commitment_primary::(*T_comm); } let r = transcript.squeeze(); @@ -249,57 +193,6 @@ impl RelaxedR1CS { .unzip() } - /// Given two lists of [RelaxedR1CS] accumulators, - pub fn merge_secondary>( - ck: &CommitmentKey, - shape: &R1CSShape, - acc_L: Self, - acc_R: &Self, - transcript: &mut Transcript, - ) -> (Self, MergeProof) { - let (T, T_comm) = acc_L.compute_fold_proof( - ck, - shape, - Some(acc_R.instance.u), - &acc_R.instance.X, - &acc_R.W, - ); - - transcript.absorb(comm_to_base::(&T_comm)); - let r = transcript.squeeze_bits_secondary(NUM_CHALLENGE_BITS); - - let W = zip_with!( - (acc_L.W.into_par_iter(), acc_R.W.par_iter()), - |w_L, w_R| w_L + r * w_R - ) - .collect(); - - let E = zip_with!( - (acc_L.E.into_par_iter(), T.par_iter(), acc_R.E.par_iter()), - |e_L, t, e_R| e_L + r * (*t + r * e_R) - ) - .collect(); - - let instance = { - let u = acc_L.instance.u + r * acc_R.instance.u; - let X = zip_eq(acc_L.instance.X, &acc_R.instance.X) - .map(|(x_L, x_R)| x_L + r * x_R) - .collect(); - - let W = acc_L.instance.W + acc_R.instance.W * r; - let E_tmp = T_comm + acc_R.instance.E * r; - let E = acc_L.instance.E + E_tmp * r; - - RelaxedR1CSInstance { u, X, W, E } - }; - - let acc = Self { instance, W, E }; - - let merge_proof = MergeProof { T: T_comm }; - - (acc, merge_proof) - } - fn compute_fold_proof( &self, ck: &CommitmentKey, @@ -355,6 +248,122 @@ impl RelaxedR1CS { } } +impl RelaxedR1CS { + pub fn simulate_fold_secondary(transcript: &mut Transcript) -> FoldProof + where + E: CurveCycleEquipped, + { + let W = Commitment::::default(); + let T = Commitment::::default(); + transcript.absorb_commitment_secondary::(W); + transcript.absorb_commitment_secondary::(T); + let _r = transcript.squeeze(); + FoldProof { W, T } + } + + pub fn fold_secondary( + &mut self, + ck: &CommitmentKey, + shape: &R1CSShape, + X_new: Vec, + W_new: &[E2::Scalar], + transcript: &mut Transcript, + ) -> FoldProof + where + E: CurveCycleEquipped, + { + // TODO: Parallelize both of these operations + let W_comm_new = { E2::CE::commit(ck, W_new) }; + let (T, T_comm) = { self.compute_fold_proof(ck, shape, None, &X_new, W_new) }; + + transcript.absorb(comm_to_base::(&W_comm_new)); + transcript.absorb(comm_to_base::(&T_comm)); + // TODO: Squeeze + let r = transcript.squeeze_bits_secondary(NUM_CHALLENGE_BITS); + + self + .W + .par_iter_mut() + .zip_eq(W_new.par_iter()) + .for_each(|(w, w_new)| *w += r * w_new); + self + .E + .par_iter_mut() + .zip_eq(T.par_iter()) + .for_each(|(e, t)| *e += r * t); + + // For non-relaxed instances, u_new = 1 + self.instance.u += r; + self + .instance + .X + .iter_mut() + .zip_eq(X_new) + .for_each(|(x, x_new)| *x += r * x_new); + self.instance.W = self.instance.W + W_comm_new * r; + self.instance.E = self.instance.E + T_comm * r; + + FoldProof { + W: W_comm_new, + T: T_comm, + } + } + + /// Given two lists of [RelaxedR1CS] accumulators, + pub fn merge_secondary( + ck: &CommitmentKey, + shape: &R1CSShape, + acc_L: Self, + acc_R: &Self, + transcript: &mut Transcript, + ) -> (Self, MergeProof) + where + E: CurveCycleEquipped, + { + let (T, T_comm) = acc_L.compute_fold_proof( + ck, + shape, + Some(acc_R.instance.u), + &acc_R.instance.X, + &acc_R.W, + ); + + transcript.absorb(comm_to_base::(&T_comm)); + let r = transcript.squeeze_bits_secondary(NUM_CHALLENGE_BITS); + + let W = zip_with!( + (acc_L.W.into_par_iter(), acc_R.W.par_iter()), + |w_L, w_R| w_L + r * w_R + ) + .collect(); + + let E = zip_with!( + (acc_L.E.into_par_iter(), T.par_iter(), acc_R.E.par_iter()), + |e_L, t, e_R| e_L + r * (*t + r * e_R) + ) + .collect(); + + let instance = { + let u = acc_L.instance.u + r * acc_R.instance.u; + let X = zip_eq(acc_L.instance.X, &acc_R.instance.X) + .map(|(x_L, x_R)| x_L + r * x_R) + .collect(); + + let W = acc_L.instance.W + acc_R.instance.W * r; + let E_tmp = T_comm + acc_R.instance.E * r; + let E = acc_L.instance.E + E_tmp * r; + + RelaxedR1CSInstance { u, X, W, E } + }; + + let acc = Self { instance, W, E }; + + let merge_proof = MergeProof { T: T_comm }; + + (acc, merge_proof) + } +} + impl RelaxedR1CSInstance { pub(crate) fn default(num_io: usize) -> Self { Self { @@ -388,16 +397,16 @@ impl RelaxedR1CSInstance { } } -impl RelaxedR1CSInstance { +impl RelaxedR1CSInstance { /// On the primary curve, the instances are stored as hashes in the recursive state. - pub fn hash(&self, transcript_constants: &TranscriptConstants) -> E1::Scalar { - let mut transcript = Transcript::::new(transcript_constants.clone()); + pub fn hash(&self, transcript_constants: &TranscriptConstants) -> E::Scalar { + let mut transcript = Transcript::new(transcript_constants.clone()); let Self { u, X, W, E } = self; transcript.absorb([*u]); transcript.absorb(X.iter().cloned()); - transcript.absorb_commitment_primary(*W); - transcript.absorb_commitment_primary(*E); + transcript.absorb_commitment_primary::(*W); + transcript.absorb_commitment_primary::(*E); transcript.squeeze() } diff --git a/src/parafold/nivc/circuit.rs b/src/parafold/nivc/circuit.rs index a8327f665..866c04a97 100644 --- a/src/parafold/nivc/circuit.rs +++ b/src/parafold/nivc/circuit.rs @@ -15,31 +15,26 @@ use crate::parafold::nivc::{ use crate::parafold::transcript::circuit::AllocatedTranscript; use crate::parafold::transcript::TranscriptConstants; use crate::supernova::EnforcingStepCircuit; - -use crate::traits::Engine; +use crate::traits::CurveCycleEquipped; /// A representation of a NIVC state, where `io` represents the computations inputs and outputs, /// and the `accs` are the accumulators for each step function that was used to produce this result. #[derive(Debug, Clone)] -pub struct AllocatedNIVCState { - io: AllocatedNIVCIO, - accs_hash: Vec>, - acc_cf: AllocatedSecondaryRelaxedR1CSInstance, +pub struct AllocatedNIVCState { + io: AllocatedNIVCIO, + accs_hash: Vec>, + acc_cf: AllocatedSecondaryRelaxedR1CSInstance, } -impl AllocatedNIVCState -where - E1: Engine, - E2: Engine, -{ +impl AllocatedNIVCState { /// Loads a previously proved state from a proof of its correctness. pub fn from_proof( mut cs: CS, - ro_consts: &TranscriptConstants, - proof: NIVCUpdateProof, - ) -> Result<(Self, AllocatedTranscript), SynthesisError> + ro_consts: &TranscriptConstants, + proof: NIVCUpdateProof, + ) -> Result<(Self, AllocatedTranscript), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let NIVCUpdateProof { transcript_init, @@ -112,10 +107,10 @@ where &mut self, mut cs: CS, step_circuit: &SF, - ) -> Result, SynthesisError> + ) -> Result, SynthesisError> where - CS: ConstraintSystem, - SF: EnforcingStepCircuit, + CS: ConstraintSystem, + SF: EnforcingStepCircuit, { // Run the step circuit let cs_step = &mut cs.namespace(|| "synthesize"); @@ -134,12 +129,12 @@ where mut cs: CS, self_L: Self, self_R: Self, - ro_consts: &TranscriptConstants, - proof: NIVCMergeProof, - transcript: &mut AllocatedTranscript, - ) -> Result<(Self, NIVCIO), SynthesisError> + ro_consts: &TranscriptConstants, + proof: NIVCMergeProof, + transcript: &mut AllocatedTranscript, + ) -> Result<(Self, NIVCIO), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let mut acc_sm = AllocatedScalarMulAccumulator::new(); @@ -218,7 +213,7 @@ where pub fn inputize(&self, mut cs: CS) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { for x in self.as_preimage() { x.inputize(cs.namespace(|| "inputize"))? @@ -228,11 +223,11 @@ where fn alloc_transcript( mut cs: CS, - state: NIVCStateInstance, - transcript: &mut AllocatedTranscript, + state: NIVCStateInstance, + transcript: &mut AllocatedTranscript, ) -> Self where - CS: ConstraintSystem, + CS: ConstraintSystem, { let NIVCStateInstance { io, @@ -240,7 +235,7 @@ where acc_cf, } = state; - let io = AllocatedNIVCIO::alloc_transcript(cs.namespace(|| "alloc io"), io, transcript); + let io = AllocatedNIVCIO::alloc_transcript::<_, E>(cs.namespace(|| "alloc io"), io, transcript); let accs_hash = accs_hash .into_iter() @@ -267,7 +262,7 @@ where fn enforce_base_case(&self, mut cs: CS, is_base_case: &Boolean) where - CS: ConstraintSystem, + CS: ConstraintSystem, { // We only need to enforce that the NIVC IO is trivial. // We do not need to check that `accs` and `acc_sm` are trivial, the only requirement is that they are @@ -286,17 +281,17 @@ where fn update_accs( &mut self, mut cs: CS, - ro_consts: &TranscriptConstants, - transcript_init: AllocatedNum, - acc_prev: RelaxedR1CSInstance, + ro_consts: &TranscriptConstants, + transcript_init: AllocatedNum, + acc_prev: RelaxedR1CSInstance, index_prev: Option, - nifs_fold_proof: FoldProof, + nifs_fold_proof: FoldProof, is_base_case: &Boolean, - acc_sm: &mut AllocatedScalarMulAccumulator, - transcript: &mut AllocatedTranscript, + acc_sm: &mut AllocatedScalarMulAccumulator, + transcript: &mut AllocatedTranscript, ) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let (acc_prev_hash, acc_curr_hash) = { // Load pre-image of accumulator to be updated @@ -361,7 +356,7 @@ where || "is_base.not = ∑_i bits[i]", |lc| lc, |lc| lc, - |_| is_base_case.not().lc(CS::one(), E1::Scalar::ONE) - &lc_sum, + |_| is_base_case.not().lc(CS::one(), E::Scalar::ONE) - &lc_sum, ); bits @@ -384,7 +379,7 @@ where Ok(()) } - fn as_preimage(&self) -> impl IntoIterator> + '_ { + fn as_preimage(&self) -> impl IntoIterator> + '_ { chain![ self.io.as_preimage(), self.accs_hash.iter().cloned(), @@ -394,16 +389,16 @@ where fn load_accs( mut cs: CS, - accs_native: Vec>, - accs_hash: Vec>, - ro_consts: &TranscriptConstants, - ) -> Result>, SynthesisError> + accs_native: Vec>, + accs_hash: Vec>, + ro_consts: &TranscriptConstants, + ) -> Result>, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { zip_eq(accs_native, accs_hash) .map( - |(acc_native, acc_hash): (RelaxedR1CSInstance, AllocatedNum)| { + |(acc_native, acc_hash): (RelaxedR1CSInstance, AllocatedNum)| { let acc = AllocatedRelaxedR1CSInstance::alloc(cs.namespace(|| "alloc acc"), acc_native); let acc_hash_real = acc.hash(cs.namespace(|| "hash acc"), ro_consts)?; @@ -490,10 +485,10 @@ impl AllocatedNIVCIO { ] } - pub fn alloc_transcript>( + pub fn alloc_transcript>( mut cs: CS, state: NIVCIO, - transcript: &mut AllocatedTranscript, + transcript: &mut AllocatedTranscript, ) -> Self where CS: ConstraintSystem, diff --git a/src/parafold/nivc/mod.rs b/src/parafold/nivc/mod.rs index 7899d0003..4bf171577 100644 --- a/src/parafold/nivc/mod.rs +++ b/src/parafold/nivc/mod.rs @@ -2,7 +2,7 @@ use bellpepper_core::num::AllocatedNum; use ff::PrimeField; use crate::parafold::nifs::{FoldProof, MergeProof, RelaxedR1CSInstance}; -use crate::traits::Engine; +use crate::traits::CurveCycleEquipped; pub mod circuit; pub mod prover; @@ -30,33 +30,33 @@ pub struct AllocatedNIVCIO { /// Succinct representation of the recursive NIVC state that is known #[derive(Clone, Debug)] -pub struct NIVCStateInstance { - io: NIVCIO, - accs_hash: Vec, - acc_cf: RelaxedR1CSInstance, +pub struct NIVCStateInstance { + io: NIVCIO, + accs_hash: Vec, + acc_cf: RelaxedR1CSInstance, } /// A proof for loading a previous NIVC output inside a circuit. #[derive(Debug, Clone)] -pub struct NIVCUpdateProof { - transcript_init: E1::Scalar, +pub struct NIVCUpdateProof { + transcript_init: E::Scalar, - state: NIVCStateInstance, + state: NIVCStateInstance, - acc_prev: RelaxedR1CSInstance, + acc_prev: RelaxedR1CSInstance, index_prev: Option, - nifs_fold_proof: FoldProof, + nifs_fold_proof: FoldProof, - sm_fold_proofs: [FoldProof; 2], + sm_fold_proofs: [FoldProof; 2], } #[derive(Debug, Clone)] -pub struct NIVCMergeProof { - accs_L: Vec>, - accs_R: Vec>, - nivc_merge_proof: Vec>, +pub struct NIVCMergeProof { + accs_L: Vec>, + accs_R: Vec>, + nivc_merge_proof: Vec>, - cf_merge_proof: MergeProof, + cf_merge_proof: MergeProof, - sm_fold_proofs: Vec>, + sm_fold_proofs: Vec>, } diff --git a/src/parafold/nivc/prover.rs b/src/parafold/nivc/prover.rs index 339068904..a932f1651 100644 --- a/src/parafold/nivc/prover.rs +++ b/src/parafold/nivc/prover.rs @@ -8,28 +8,24 @@ use crate::parafold::nivc::{NIVCMergeProof, NIVCStateInstance, NIVCUpdateProof, use crate::parafold::transcript::prover::Transcript; use crate::parafold::transcript::TranscriptConstants; use crate::r1cs::R1CSShape; -use crate::traits::Engine; +use crate::traits::{CurveCycleEquipped, Engine}; use crate::CommitmentKey; #[derive(Debug)] -pub struct NIVCState { - transcript: Transcript, - io: NIVCIO, - accs: Vec>, - acc_cf: RelaxedR1CS, +pub struct NIVCState { + transcript: Transcript, + io: NIVCIO, + accs: Vec>, + acc_cf: RelaxedR1CS, } #[derive(Debug)] -pub struct NIVCUpdateWitness { +pub struct NIVCUpdateWitness { pub(crate) index: usize, - pub(crate) W: Vec, + pub(crate) W: Vec, } -impl NIVCState -where - E1: Engine, - E2: Engine, -{ +impl NIVCState { /// Initialize the prover state and create a default proof for the first iteration. /// /// # Details @@ -40,13 +36,13 @@ where /// /// pub fn init( - shapes: &[R1CSShape], - shape_cf: &R1CSShape, - ro_consts: &TranscriptConstants, + shapes: &[R1CSShape], + shape_cf: &R1CSShape, + ro_consts: &TranscriptConstants, pc_init: usize, - z_init: Vec, - ) -> (Self, NIVCUpdateProof) { - let transcript_init = E1::Scalar::ZERO; + z_init: Vec, + ) -> (Self, NIVCUpdateProof) { + let transcript_init = E::Scalar::ZERO; let mut state = Self { transcript: Transcript::new_init(transcript_init, ro_consts.clone()), io: NIVCIO::new(pc_init, z_init), @@ -62,7 +58,7 @@ where let mut acc_sm = ScalarMulAccumulator::new(); let nifs_fold_proof = RelaxedR1CS::simulate_fold_primary(&mut acc_sm, &mut state.transcript); - let sm_fold_proofs: [FoldProof; 2] = acc_sm + let sm_fold_proofs: [FoldProof; 2] = acc_sm .simulate_finalize(&mut state.transcript) .try_into() .unwrap(); @@ -81,14 +77,14 @@ where fn update( &mut self, - ck: &CommitmentKey, - ck_cf: &CommitmentKey, - ro_consts: &TranscriptConstants, - shapes: &[R1CSShape], - shape_cf: &R1CSShape, - witness_prev: &NIVCUpdateWitness, - ) -> NIVCUpdateProof { - let mut acc_sm = ScalarMulAccumulator::::new(); + ck: &CommitmentKey, + ck_cf: &CommitmentKey, + ro_consts: &TranscriptConstants, + shapes: &[R1CSShape], + shape_cf: &R1CSShape, + witness_prev: &NIVCUpdateWitness, + ) -> NIVCUpdateProof { + let mut acc_sm = ScalarMulAccumulator::::new(); let transcript_init = self.transcript.seal(); let state = self.instance(ro_consts); @@ -115,7 +111,7 @@ where &mut self.transcript, ); - let sm_fold_proofs: [FoldProof; 2] = acc_sm + let sm_fold_proofs: [FoldProof; 2] = acc_sm .finalize(ck_cf, shape_cf, &mut self.acc_cf, &mut self.transcript) .try_into() .unwrap(); @@ -131,13 +127,13 @@ where } pub fn merge( - ck: &CommitmentKey, - ck_cf: &CommitmentKey, - shapes: &[R1CSShape], - shape_cf: &R1CSShape, + ck: &CommitmentKey, + ck_cf: &CommitmentKey, + shapes: &[R1CSShape], + shape_cf: &R1CSShape, self_L: Self, self_R: &Self, - ) -> (Self, NIVCMergeProof) { + ) -> (Self, NIVCMergeProof) { let Self { transcript: transcript_L, io: io_L, @@ -170,8 +166,13 @@ where let (accs, nivc_merge_proof) = RelaxedR1CS::merge_many(ck, shapes, accs_L, accs_R, &mut acc_sm, &mut transcript); - let (mut acc_cf, cf_merge_proof) = - RelaxedR1CS::merge_secondary(ck_cf, shape_cf, acc_cf_L, acc_cf_R, &mut transcript); + let (mut acc_cf, cf_merge_proof) = RelaxedR1CS::::merge_secondary::( + ck_cf, + shape_cf, + acc_cf_L, + acc_cf_R, + &mut transcript, + ); let sm_fold_proofs = acc_sm.finalize(ck_cf, shape_cf, &mut acc_cf, &mut transcript); @@ -193,7 +194,7 @@ where (self_next, merge_proof) } - pub fn instance(&self, ro_consts: &TranscriptConstants) -> NIVCStateInstance { + pub fn instance(&self, ro_consts: &TranscriptConstants) -> NIVCStateInstance { let accs_hash = self .accs .iter() @@ -208,12 +209,8 @@ where } } -impl NIVCStateInstance -where - E1: Engine, - E2: Engine, -{ - pub fn as_preimage(&self) -> impl IntoIterator + '_ { +impl NIVCStateInstance { + pub fn as_preimage(&self) -> impl IntoIterator + '_ { chain![ self.io.as_preimage(), self.accs_hash.iter().cloned(), diff --git a/src/parafold/prover.rs b/src/parafold/prover.rs index 13f41fb92..676db712c 100644 --- a/src/parafold/prover.rs +++ b/src/parafold/prover.rs @@ -2,29 +2,27 @@ use crate::parafold::nivc::prover::NIVCState; use crate::parafold::nivc::NIVCUpdateProof; use crate::parafold::transcript::TranscriptConstants; use crate::r1cs::R1CSShape; -use crate::{CommitmentKey, Engine}; -pub struct ProvingKey { +use crate::traits::CurveCycleEquipped; +use crate::CommitmentKey; + +pub struct ProvingKey { // public params - ck: CommitmentKey, - ck_cf: CommitmentKey, + ck: CommitmentKey, + ck_cf: CommitmentKey, // Shapes for each augmented StepCircuit. The last shape is for the merge circuit. - shapes: Vec>, - shape_cf: R1CSShape, - ro_consts: TranscriptConstants, + shapes: Vec>, + shape_cf: R1CSShape, + ro_consts: TranscriptConstants, } -pub struct RecursiveSNARK { +pub struct RecursiveSNARK { // state - state: NIVCState, - proof: NIVCUpdateProof, + state: NIVCState, + proof: NIVCUpdateProof, } -impl RecursiveSNARK -where - E1: Engine, - E2: Engine, -{ - pub fn new(pk: &ProvingKey, pc_init: usize, z_init: Vec) -> Self { +impl RecursiveSNARK { + pub fn new(pk: &ProvingKey, pc_init: usize, z_init: Vec) -> Self { let num_circuits = pk.shapes.len(); assert!(pc_init < num_circuits); // Check arity z_init.len(); diff --git a/src/parafold/transcript/circuit.rs b/src/parafold/transcript/circuit.rs index e063c074d..cc59fbd70 100644 --- a/src/parafold/transcript/circuit.rs +++ b/src/parafold/transcript/circuit.rs @@ -1,6 +1,7 @@ use bellpepper_core::boolean::{AllocatedBit, Boolean}; use bellpepper_core::num::AllocatedNum; use bellpepper_core::{ConstraintSystem, SynthesisError}; +use ff::{PrimeField, PrimeFieldBits}; use neptune::circuit2::Elt; use neptune::sponge::api::{IOPattern, SpongeAPI, SpongeOp}; use neptune::sponge::circuit::SpongeCircuit; @@ -8,15 +9,14 @@ use neptune::sponge::vanilla::Mode::Simplex; use neptune::sponge::vanilla::SpongeTrait; use crate::parafold::transcript::TranscriptConstants; -use crate::traits::Engine; -pub struct AllocatedTranscript { - constants: TranscriptConstants, - state: Vec>, +pub struct AllocatedTranscript { + constants: TranscriptConstants, + state: Vec>, } -impl AllocatedTranscript { - pub fn new(constants: TranscriptConstants) -> Self { +impl AllocatedTranscript { + pub fn new(constants: TranscriptConstants) -> Self { Self { constants, state: vec![], @@ -25,11 +25,11 @@ impl AllocatedTranscript { pub fn new_init( mut cs: CS, - init: E::Scalar, - constants: TranscriptConstants, - ) -> (Self, AllocatedNum) + init: F, + constants: TranscriptConstants, + ) -> (Self, AllocatedNum) where - CS: ConstraintSystem, + CS: ConstraintSystem, { let init = AllocatedNum::alloc_infallible(&mut cs, || init); let init_elt = Elt::Allocated(init.clone()); @@ -42,22 +42,22 @@ impl AllocatedTranscript { ) } - pub fn absorb(&mut self, elements: impl IntoIterator>) { + pub fn absorb(&mut self, elements: impl IntoIterator>) { self.state.extend(elements.into_iter().map(Elt::Allocated)); } pub(crate) fn inputize(&self, mut cs: CS) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { assert_eq!(self.state.len(), 1); let state = self.state[0].ensure_allocated(&mut cs, false)?; state.inputize(&mut cs) } - pub fn squeeze(&mut self, mut cs: CS) -> Result, SynthesisError> + pub fn squeeze(&mut self, mut cs: CS) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let num_absorbs = self.state.len() as u32; @@ -83,7 +83,8 @@ impl AllocatedTranscript { num_bits: usize, ) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, + F: PrimeFieldBits, { let hash = self.squeeze(&mut cs)?; diff --git a/src/parafold/transcript/mod.rs b/src/parafold/transcript/mod.rs index 69b4cc0c0..8eb2aed9b 100644 --- a/src/parafold/transcript/mod.rs +++ b/src/parafold/transcript/mod.rs @@ -1,10 +1,8 @@ use generic_array::typenum::U24; use neptune::poseidon::PoseidonConstants; -use crate::traits::Engine; - pub mod circuit; pub mod prover; /// Poseidon constants for hashing used for the Fiat-Shamir transcript -pub type TranscriptConstants = PoseidonConstants<::Scalar, U24>; +pub type TranscriptConstants = PoseidonConstants; diff --git a/src/parafold/transcript/prover.rs b/src/parafold/transcript/prover.rs index 4ebb32d84..da2b15767 100644 --- a/src/parafold/transcript/prover.rs +++ b/src/parafold/transcript/prover.rs @@ -1,4 +1,4 @@ -use ff::{Field, PrimeFieldBits}; +use ff::{Field, PrimeField, PrimeFieldBits}; use neptune::sponge::api::{IOPattern, SpongeAPI, SpongeOp}; use neptune::sponge::vanilla::Mode::Simplex; use neptune::sponge::vanilla::{Sponge, SpongeTrait}; @@ -10,20 +10,20 @@ use crate::traits::Engine; use crate::Commitment; #[derive(Clone, Debug)] -pub struct Transcript { - constants: TranscriptConstants, - state: Vec, +pub struct Transcript { + constants: TranscriptConstants, + state: Vec, } -impl Transcript { - pub fn new(constants: TranscriptConstants) -> Self { +impl Transcript { + pub fn new(constants: TranscriptConstants) -> Self { Self { constants, state: vec![], } } - pub fn new_init(init: E::Scalar, constants: TranscriptConstants) -> Self { + pub fn new_init(init: F, constants: TranscriptConstants) -> Self { Self { constants, state: vec![init], @@ -32,22 +32,22 @@ impl Transcript { pub fn absorb(&mut self, elements: I) where - I: IntoIterator, + I: IntoIterator, { self.state.extend(elements); } - pub fn absorb_commitment_primary(&mut self, c: Commitment) { - let c_hash = HashedCommitment::::new(c); + pub fn absorb_commitment_primary>(&mut self, c: Commitment) { + let c_hash = HashedCommitment::::new(c); self.absorb(c_hash.as_preimage()); } - pub fn absorb_commitment_secondary>(&mut self, c: Commitment) { + pub fn absorb_commitment_secondary>(&mut self, c: Commitment) { let (x, y, _) = c.to_coordinates(); self.absorb([x, y]); } - pub fn squeeze(&mut self) -> E::Scalar { + pub fn squeeze(&mut self) -> F { let mut sponge = Sponge::new_with_constants(&self.constants, Simplex); let num_absorbs = self.state.len() as u32; let acc = &mut (); @@ -61,13 +61,16 @@ impl Transcript { output } - pub fn squeeze_bits_secondary(&mut self, num_bits: usize) -> E::Base { + pub fn squeeze_bits_secondary(&mut self, num_bits: usize) -> Base + where + F: PrimeFieldBits, + { let hash = self.squeeze(); // Only return `num_bits` let bits = hash.to_le_bits(); - let mut res = E::Base::ZERO; - let mut coeff = E::Base::ONE; + let mut res = Base::ZERO; + let mut coeff = Base::ONE; for bit in bits.into_iter().take(num_bits) { if bit { res += coeff; @@ -77,7 +80,7 @@ impl Transcript { res } - pub fn seal(&self) -> E::Scalar { + pub fn seal(&self) -> F { assert_eq!(self.state.len(), 1); self.state[0] }