From d59a0d1de58697c4dd9bfb06f3643b1bba590221 Mon Sep 17 00:00:00 2001 From: Adrian Hamelink Date: Mon, 4 Mar 2024 17:54:08 +0100 Subject: [PATCH] Small refactors - remove unused/commented out functions - revert to 128-bit limb hashing due to security concern - make ScalarMulAccumulator take ownership of R1CS accumulator - add ability to generate dummy input proof for circuit - use sponge for hashing the NIVC instance due to problems with variable-sized hashing - add test for `from_proof` - comment out merge-related code - ensure namespaces have unique names - remove wrong mersenne prime parameters - add emulatedbase tests - add cycle fold circuit - add inputize_hashed methods in allocated point for cyclefold circuit --- src/parafold/circuit.rs | 1 + src/parafold/cycle_fold/circuit.rs | 23 ++- src/parafold/cycle_fold/gadgets/ecc.rs | 89 ++++++---- src/parafold/cycle_fold/gadgets/emulated.rs | 154 +++++++++++++---- src/parafold/cycle_fold/gadgets/mod.rs | 3 +- src/parafold/cycle_fold/mod.rs | 14 +- src/parafold/cycle_fold/nifs/circuit.rs | 2 +- src/parafold/cycle_fold/nifs/mod.rs | 6 + src/parafold/cycle_fold/nifs/prover.rs | 41 +++-- src/parafold/cycle_fold/prover.rs | 62 ++++--- src/parafold/mod.rs | 9 +- src/parafold/nifs/circuit.rs | 178 ++++++++++---------- src/parafold/nivc/circuit.rs | 28 ++- src/parafold/nivc/mod.rs | 74 +++++++- src/parafold/nivc/prover.rs | 40 ++++- src/parafold/transcript/circuit.rs | 40 +---- src/parafold/transcript/mod.rs | 42 ++--- src/parafold/transcript/prover.rs | 22 +-- 18 files changed, 504 insertions(+), 324 deletions(-) diff --git a/src/parafold/circuit.rs b/src/parafold/circuit.rs index 1c02057d5..440b328d1 100644 --- a/src/parafold/circuit.rs +++ b/src/parafold/circuit.rs @@ -6,6 +6,7 @@ use crate::parafold::transcript::TranscriptConstants; use crate::supernova::StepCircuit; use crate::traits::CurveCycleEquipped; +#[allow(unused)] pub fn synthesize_step( mut cs: CS, ro_consts: &TranscriptConstants, diff --git a/src/parafold/cycle_fold/circuit.rs b/src/parafold/cycle_fold/circuit.rs index aa478b0a4..efb38c721 100644 --- a/src/parafold/cycle_fold/circuit.rs +++ b/src/parafold/cycle_fold/circuit.rs @@ -1,9 +1,11 @@ use bellpepper_core::boolean::Boolean; use bellpepper_core::{ConstraintSystem, SynthesisError, Variable}; +use ff::PrimeField; use crate::parafold::cycle_fold::gadgets::emulated::AllocatedBase; use crate::parafold::cycle_fold::nifs::circuit::AllocatedSecondaryRelaxedR1CSInstance; -use crate::parafold::cycle_fold::{AllocatedPrimaryCommitment, NUM_IO_SECONDARY}; +use crate::parafold::cycle_fold::nifs::NUM_IO_SECONDARY; +use crate::parafold::cycle_fold::AllocatedPrimaryCommitment; use crate::parafold::transcript::circuit::AllocatedTranscript; use crate::traits::CurveCycleEquipped; @@ -33,6 +35,9 @@ impl AllocatedScalarMulAccumulator { where CS: ConstraintSystem, { + // Ensure the number of bits in the scalar matches fits. + assert!(x_bits.len() <= E::Scalar::NUM_BITS as usize); + let C = transcript.read_commitment_primary(cs.namespace(|| "transcript C"))?; self.deferred.push(AllocatedScalarMulInstance { @@ -45,6 +50,8 @@ impl AllocatedScalarMulAccumulator { Ok(C) } + /// Consume a set of accumulated scalar multiplications, proving each instance by folding a proof + /// into the internal secondary curve accumulator. pub fn finalize( mut self, mut cs: CS, @@ -53,22 +60,20 @@ impl AllocatedScalarMulAccumulator { where CS: ConstraintSystem, { - for instance in self.deferred.drain(..) { + for (i, instance) in self.deferred.drain(..).enumerate() { let X = instance.to_io(CS::one()); // TODO: In order to avoid computing unnecessary proofs, we can check // - x = 0 => C = A - self - .acc - .fold(cs.namespace(|| "fold cf instance"), X, transcript)?; + self.acc.fold( + cs.namespace(|| format!("fold cf instance {i}")), + X, + transcript, + )?; } Ok(self.acc) } - - pub fn is_finalized(&self) -> bool { - self.deferred.is_empty() - } } #[derive(Debug, Clone)] diff --git a/src/parafold/cycle_fold/gadgets/ecc.rs b/src/parafold/cycle_fold/gadgets/ecc.rs index deddac095..51a655f37 100644 --- a/src/parafold/cycle_fold/gadgets/ecc.rs +++ b/src/parafold/cycle_fold/gadgets/ecc.rs @@ -1,9 +1,13 @@ use bellpepper::gadgets::Assignment; use bellpepper_core::boolean::{AllocatedBit, Boolean}; use bellpepper_core::num::AllocatedNum; + +use bellpepper_core::SynthesisError::AssignmentMissing; use bellpepper_core::{ConstraintSystem, SynthesisError}; use ff::{Field, PrimeField}; -use neptune::circuit2::Elt; +use neptune::circuit2::{Elt, PoseidonCircuit2}; +use neptune::generic_array::typenum::U2; +use neptune::poseidon::PoseidonConstants; use crate::gadgets::utils::{ alloc_num_equals, alloc_one, alloc_zero, conditionally_select, conditionally_select2, @@ -78,6 +82,53 @@ impl AllocatedPoint { commitment } + pub fn alloc_hashed_input( + mut cs: CS, + coords: (G::Base, G::Base, bool), + ) -> Result + where + CS: ConstraintSystem, + { + let point = Self::alloc_unchecked(cs.namespace(|| "alloc"), coords); + point.check_on_curve(cs.namespace(|| "check on curve"))?; + point.inputize_hashed(cs.namespace(|| "inputize"))?; + Ok(point) + } + + /// Inputize a point by computing H(x,y), or 0 if `is_infinity = true`. + pub fn inputize_hashed(&self, mut cs: CS) -> Result<(), SynthesisError> + where + CS: ConstraintSystem, + { + let constants = PoseidonConstants::::new(); + let hash = PoseidonCircuit2::new( + vec![ + Elt::Allocated(self.x.clone()), + Elt::Allocated(self.y.clone()), + ], + &constants, + ) + .hash_to_allocated(cs.namespace(|| "hash"))?; + + let hash_final = AllocatedNum::alloc(cs.namespace(|| "alloc hash"), || { + let is_infinity = self.is_infinity.get_value().ok_or(AssignmentMissing)?; + if is_infinity == G::Base::ONE { + Ok(G::Base::ZERO) + } else { + hash.get_value().ok_or(AssignmentMissing) + } + })?; + + // hash_final = (1-is_infinity)hash + cs.enforce( + || "select hash", + |lc| lc + CS::one() - self.is_infinity.get_variable(), + |lc| lc + hash.get_variable(), + |lc| lc + hash_final.get_variable(), + ); + hash_final.inputize(cs.namespace(|| "inputize")) + } + /// Allocates a new point on the curve using coordinates provided by `coords`. /// If coords = None, it allocates the default infinity point pub fn alloc_unchecked(mut cs: CS, coords: (G::Base, G::Base, bool)) -> Self @@ -166,17 +217,6 @@ impl AllocatedPoint { } } - /// Returns coordinates associated with the point. - 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 @@ -669,26 +709,6 @@ pub struct AllocatedPointNonInfinity { } impl AllocatedPointNonInfinity { - /// Creates a new `AllocatedPointNonInfinity` from the specified coordinates - 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>( - mut cs: CS, - coords: Option<(G::Base, G::Base)>, - ) -> Result { - let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { - coords.map_or(Err(SynthesisError::AssignmentMissing), |c| Ok(c.0)) - })?; - let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { - coords.map_or(Err(SynthesisError::AssignmentMissing), |c| Ok(c.1)) - })?; - - Ok(Self { x, y }) - } - /// Turns an `AllocatedPoint` into an `AllocatedPointNonInfinity` (assumes it is not infinity) pub fn from_allocated_point(p: &AllocatedPoint) -> Self { Self { @@ -706,11 +726,6 @@ impl AllocatedPointNonInfinity { } } - /// Returns coordinates associated with the point. - 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 diff --git a/src/parafold/cycle_fold/gadgets/emulated.rs b/src/parafold/cycle_fold/gadgets/emulated.rs index 68e1f20df..bf6c183f9 100644 --- a/src/parafold/cycle_fold/gadgets/emulated.rs +++ b/src/parafold/cycle_fold/gadgets/emulated.rs @@ -1,12 +1,14 @@ use std::marker::PhantomData; +use std::ops::{BitAnd, Shr}; -use bellpepper_core::boolean::Boolean; -use bellpepper_core::num::Num; +use bellpepper_core::boolean::{AllocatedBit, Boolean}; +use bellpepper_core::num::{AllocatedNum, Num}; use bellpepper_core::{ConstraintSystem, SynthesisError, Variable}; use bellpepper_emulated::field_element::{ - EmulatedFieldElement, EmulatedFieldParams, EmulatedLimbs, PseudoMersennePrime, + EmulatedFieldElement, EmulatedFieldParams, EmulatedLimbs, }; -use ff::{Field, PrimeField}; +use bellpepper_emulated::util::bigint_to_scalar; +use ff::{Field, PrimeField, PrimeFieldBits}; use itertools::zip_eq; use neptune::circuit2::Elt; use num_bigint::{BigInt, Sign}; @@ -16,6 +18,7 @@ use crate::constants::{BN_LIMB_WIDTH, BN_N_LIMBS}; use crate::traits::CurveCycleEquipped; #[derive(Debug, Clone)] +/// Explicit parameters for performing base-field arithmetic in a circuit defined over the scalar field. pub struct BaseParams(PhantomData); impl EmulatedFieldParams for BaseParams { @@ -32,18 +35,31 @@ impl EmulatedFieldParams for BaseParams { } fn is_modulus_pseudo_mersenne() -> bool { - true + false } - fn pseudo_mersenne_params() -> Option { - let p = Self::modulus(); - let e: u32 = p.bits().try_into().unwrap(); - let two_pow_e = BigInt::one() << e; - let c = two_pow_e - p; - Some(PseudoMersennePrime { e, c }) +} + +impl BaseParams { + pub fn base_to_limb(base: E::Base) -> Vec { + Self::big_int_to_limbs(field_to_big_int(base)) + } + pub fn big_int_to_limbs(base: BigInt) -> Vec { + let num_bits = BaseParams::::bits_per_limb() as u32; + let num_limbs = BaseParams::::num_limbs() as u32; + let mask = BigInt::from(2).pow(num_bits) - BigInt::one(); + + (0..num_limbs) + .map(|limb_index| { + let shift = (limb_index * num_bits) as u8; + let limb = base.clone().shr(shift).bitand(&mask); + bigint_to_scalar::(&limb) + }) + .collect() } } #[derive(Debug, Clone)] +/// Allocated base field element, represented as a non-native field element. pub struct AllocatedBase(EmulatedFieldElement>); impl AllocatedBase { @@ -52,13 +68,12 @@ impl AllocatedBase { } pub fn from_bits_le(one: Variable, bits: &[Boolean]) -> Self { - let num_bits = BaseParams::::bits_per_limb() * BaseParams::::num_limbs(); let limb_bases = std::iter::successors(Some(E::Scalar::ONE), |base: &E::Scalar| Some(base.double())) .take(BaseParams::::bits_per_limb()) .collect::>(); - assert!(bits.len() <= num_bits); + assert!(bits.len() <= E::Base::NUM_BITS as usize); let limbs = bits .chunks(BaseParams::::bits_per_limb()) @@ -80,12 +95,18 @@ impl AllocatedBase { } pub fn alloc>(mut cs: CS, base: E::Base) -> Self { - let base = Self::alloc_unchecked(cs.namespace(|| "alloc unchecked"), base); - base - .0 - .check_field_membership(&mut cs.namespace(|| "check membership")) - .unwrap(); - base + let base_bits = base + .to_le_bits() + .into_iter() + .take(E::Base::NUM_BITS as usize) + .enumerate() + .map(|(i, bit)| { + Boolean::from( + AllocatedBit::alloc(cs.namespace(|| format!("alloc bit {i}")), Some(bit)).unwrap(), + ) + }) + .collect::>(); + Self::from_bits_le(CS::one(), &base_bits) } pub fn alloc_unchecked>(mut cs: CS, base: E::Base) -> Self { @@ -97,8 +118,13 @@ impl AllocatedBase { } pub fn as_preimage(&self) -> impl IntoIterator> { - // Merge into two 128-bit limbs for more efficient hashing - let limbs = self.0.compact_limbs(2, 128).unwrap(); + let limbs = self + .0 + .compact_limbs( + BaseParams::::num_limbs(), + BaseParams::::bits_per_limb(), + ) + .unwrap(); let EmulatedLimbs::Allocated(limbs) = limbs else { unreachable!() }; @@ -144,17 +170,39 @@ impl AllocatedBase { fn to_big_int(self) -> BigInt { (&self.0).into() } + + fn alloc_big_int>(mut cs: CS, value: BigInt) -> Self { + let limbs = BaseParams::::big_int_to_limbs(value); + let limbs = limbs + .into_iter() + .enumerate() + .map(|(i, limb)| { + Num::from(AllocatedNum::alloc_infallible( + cs.namespace(|| format!("alloc limb {i}")), + || limb, + )) + }) + .collect::>(); + Self(EmulatedFieldElement::new_internal_element( + EmulatedLimbs::Allocated(limbs), + 0, + )) + } } fn field_to_big_int(f: F) -> BigInt { - BigInt::from_bytes_le(Sign::Plus, f.to_repr().as_ref()) + let repr = f.to_repr(); + BigInt::from_bytes_le(Sign::Plus, repr.as_ref()) } #[cfg(test)] mod tests { + use std::ops::Sub; + use bellpepper_core::boolean::AllocatedBit; use bellpepper_core::num::AllocatedNum; use bellpepper_core::test_cs::TestConstraintSystem; + use expect_test::expect; use num_traits::Zero; use rand_chacha::ChaCha20Rng; use rand_core::SeedableRng; @@ -253,14 +301,17 @@ mod tests { let mut cs = CS::new(); - let result_expected = AllocatedBase::::alloc(cs.namespace(|| "result_expected"), a + b); + let result_expected = + AllocatedBase::::alloc_unchecked(cs.namespace(|| "result_expected"), a + b); - let a = AllocatedBase::::alloc(cs.namespace(|| "a"), a); - let b = AllocatedBase::::alloc(cs.namespace(|| "b"), b); + let a = AllocatedBase::::alloc_unchecked(cs.namespace(|| "a"), a); + let b = AllocatedBase::::alloc_unchecked(cs.namespace(|| "b"), b); let result = a.add(cs.namespace(|| "result"), &b).unwrap(); assert_eq!(result.to_big_int(), result_expected.to_big_int()); assert!(cs.is_satisfied()); + expect!["450"].assert_eq(&cs.num_constraints().to_string()); + expect!["460"].assert_eq(&cs.scalar_aux().len().to_string()); } #[test] @@ -269,17 +320,60 @@ mod tests { let a = Base::random(&mut rng); let b = Base::random(&mut rng); - let result = a + b; + let r = Base::random(&mut rng); + let result = a + r * b; let mut cs = CS::new(); - let a = AllocatedBase::::alloc(cs.namespace(|| "a"), a); - let b = AllocatedBase::::alloc(cs.namespace(|| "b"), b); - let c = a.add(cs.namespace(|| "c"), &b).unwrap(); + let a = AllocatedBase::::alloc_unchecked(cs.namespace(|| "a"), a); + let b = AllocatedBase::::alloc_unchecked(cs.namespace(|| "b"), b); + let r = AllocatedBase::::alloc_unchecked(cs.namespace(|| "r"), r); + let c = a.lc(cs.namespace(|| "c"), &r, &b).unwrap(); - let c_expected = AllocatedBase::::alloc(cs.namespace(|| "result"), result); + let c_expected = AllocatedBase::::alloc_unchecked(cs.namespace(|| "result"), result); assert_eq!(c.to_big_int(), c_expected.to_big_int()); assert!(cs.is_satisfied()); + expect!["723"].assert_eq(&cs.num_constraints().to_string()); + expect!["737"].assert_eq(&cs.scalar_aux().len().to_string()); + } + + #[test] + fn test_lc_overflow() { + let mut rng = ChaCha20Rng::from_seed([0u8; 32]); + + let a = Base::random(&mut rng); + let b = Base::random(&mut rng); + let r = Base::random(&mut rng); + let result = a + r * b; + + // Add a multiple of the modulus while staying in the limb bounds + let b_bi = field_to_big_int(b) + BaseParams::::modulus() * BigInt::from(4); + + let mut cs = CS::new(); + + let a = AllocatedBase::::alloc_unchecked(cs.namespace(|| "a"), a); + let b = AllocatedBase::::alloc_big_int(cs.namespace(|| "b"), b_bi.clone()); + let r = AllocatedBase::::alloc_unchecked(cs.namespace(|| "r"), r); + let c = a.lc(cs.namespace(|| "c"), &r, &b).unwrap(); + + let c_expected = AllocatedBase::::alloc_unchecked(cs.namespace(|| "result"), result); + + assert_eq!(c.to_big_int(), c_expected.to_big_int()); + assert!(cs.is_satisfied()); + expect!["723"].assert_eq(&cs.num_constraints().to_string()); + expect!["737"].assert_eq(&cs.scalar_aux().len().to_string()); + } + + #[test] + fn test_alloc_big_int() { + // let mut rng = ChaCha20Rng::from_seed([0u8; 32]); + + let a = BaseParams::::modulus().sub(BigInt::one()); + + let mut cs = CS::new(); + + let a_alloc = AllocatedBase::::alloc_big_int(cs.namespace(|| "a"), a.clone()); + assert_eq!(a, a_alloc.to_big_int()); } } diff --git a/src/parafold/cycle_fold/gadgets/mod.rs b/src/parafold/cycle_fold/gadgets/mod.rs index 065fc3fc8..06e14b8ba 100644 --- a/src/parafold/cycle_fold/gadgets/mod.rs +++ b/src/parafold/cycle_fold/gadgets/mod.rs @@ -1,4 +1,3 @@ -#[allow(dead_code)] -mod ecc; +pub mod ecc; pub mod emulated; pub mod secondary_commitment; diff --git a/src/parafold/cycle_fold/mod.rs b/src/parafold/cycle_fold/mod.rs index 68d633f52..a28bb2e57 100644 --- a/src/parafold/cycle_fold/mod.rs +++ b/src/parafold/cycle_fold/mod.rs @@ -10,14 +10,12 @@ use crate::traits::commitment::CommitmentTrait; use crate::traits::CurveCycleEquipped; use crate::Commitment; -const NUM_IO_SECONDARY: usize = 4; - pub mod circuit; pub mod gadgets; pub mod nifs; pub mod prover; -pub fn hash_commitment(commitment: Commitment) -> E::Base { +pub fn hash_commitment(commitment: &Commitment) -> E::Base { // TODO: Find a way to cache this let constants = PoseidonConstants::::new(); @@ -71,17 +69,13 @@ pub fn hash_commitment(commitment: Commitment) -> E::B /// when the scalar multiplication is trivial. #[derive(Debug, Clone)] pub struct AllocatedPrimaryCommitment { - // hash = if let Some(point) = value { H_secondary(point) } else { 0 } - // TODO: Should this be a BigNat? pub(crate) hash: AllocatedBase, } impl AllocatedPrimaryCommitment { - pub fn alloc>(mut cs: CS, commitment: Commitment) -> Self { - let hash = AllocatedBase::alloc( - cs.namespace(|| "alloc hash"), - hash_commitment::(commitment), - ); + pub fn alloc>(mut cs: CS, commitment: &Commitment) -> Self { + let hash = hash_commitment::(commitment); + let hash = AllocatedBase::alloc(cs.namespace(|| "alloc hash"), hash); Self { hash } } diff --git a/src/parafold/cycle_fold/nifs/circuit.rs b/src/parafold/cycle_fold/nifs/circuit.rs index f2246bdfa..1d36991dd 100644 --- a/src/parafold/cycle_fold/nifs/circuit.rs +++ b/src/parafold/cycle_fold/nifs/circuit.rs @@ -7,7 +7,7 @@ use crate::constants::NUM_CHALLENGE_BITS; use crate::parafold::cycle_fold::gadgets::emulated::AllocatedBase; use crate::parafold::cycle_fold::gadgets::secondary_commitment::AllocatedSecondaryCommitment; use crate::parafold::cycle_fold::nifs::prover::RelaxedSecondaryR1CSInstance; -use crate::parafold::cycle_fold::NUM_IO_SECONDARY; +use crate::parafold::cycle_fold::nifs::NUM_IO_SECONDARY; use crate::parafold::transcript::circuit::AllocatedTranscript; use crate::traits::CurveCycleEquipped; diff --git a/src/parafold/cycle_fold/nifs/mod.rs b/src/parafold/cycle_fold/nifs/mod.rs index e41bd94d3..283c61662 100644 --- a/src/parafold/cycle_fold/nifs/mod.rs +++ b/src/parafold/cycle_fold/nifs/mod.rs @@ -1,2 +1,8 @@ pub mod circuit; pub mod prover; + +/// The IO of the secondary circuit is always composed of 4 base field elements +/// `[hash_A, hash_B, scalar, hash_C]` +pub const NUM_IO_SECONDARY: usize = 4; + + diff --git a/src/parafold/cycle_fold/nifs/prover.rs b/src/parafold/cycle_fold/nifs/prover.rs index a2a85bdef..a13a8f90a 100644 --- a/src/parafold/cycle_fold/nifs/prover.rs +++ b/src/parafold/cycle_fold/nifs/prover.rs @@ -3,7 +3,8 @@ use itertools::{chain, Itertools}; use rayon::prelude::*; use crate::constants::NUM_CHALLENGE_BITS; -use crate::parafold::cycle_fold::NUM_IO_SECONDARY; + +use crate::parafold::cycle_fold::nifs::NUM_IO_SECONDARY; use crate::parafold::nifs::compute_fold_proof; use crate::parafold::transcript::prover::Transcript; use crate::parafold::transcript::TranscriptElement; @@ -22,11 +23,7 @@ pub struct RelaxedSecondaryR1CSInstance { } /// A full Relaxed-R1CS accumulator for a circuit -/// # TODO: -/// It would make sense to store the [R1CSShape] here since -/// - There is only one accumulator per shape -/// - We can probably use an Arc to avoid copying -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub struct RelaxedSecondaryR1CS { instance: RelaxedSecondaryR1CSInstance, W: Vec, @@ -48,6 +45,19 @@ impl RelaxedSecondaryR1CS { } } + pub fn dummy() -> Self { + Self { + instance: RelaxedSecondaryR1CSInstance { + u: E::Base::ZERO, + X: vec![E::Base::ZERO; NUM_IO_SECONDARY], + W: Commitment::::default(), + E: Commitment::::default(), + }, + W: vec![], + E: vec![], + } + } + pub fn instance(&self) -> &RelaxedSecondaryR1CSInstance { &self.instance } @@ -58,7 +68,7 @@ impl RelaxedSecondaryR1CS { transcript.absorb(TranscriptElement::CommitmentSecondary(W)); transcript.absorb(TranscriptElement::CommitmentSecondary(T)); - let _r = transcript.squeeze(); + let _r = transcript.squeeze_bits(NUM_CHALLENGE_BITS); } pub fn fold( @@ -173,6 +183,15 @@ impl RelaxedSecondaryR1CS { } impl RelaxedSecondaryR1CSInstance { + pub fn dummy() -> Self { + Self { + u: Default::default(), + X: vec![Default::default(); NUM_IO_SECONDARY], + W: Default::default(), + E: Default::default(), + } + } + pub fn as_preimage(&self) -> impl IntoIterator> + '_ { let u = TranscriptElement::Base(self.u); let X = self.X.iter().cloned().map(TranscriptElement::Base); @@ -181,11 +200,3 @@ impl RelaxedSecondaryR1CSInstance { chain![[u], X, [W, E]] } } - -// /// Convert a commitment over the secondary curve to its coordinates to it can be added to a transcript defined -// /// over the primary curve. -// /// The `is_infinity` flag is not added since it is computed in the circuit and the coordinates are checked. -// fn comm_to_base(comm: &Commitment) -> [E::Scalar; 2] { -// let (x, y, _) = comm.to_coordinates(); -// [x, y] -// } diff --git a/src/parafold/cycle_fold/prover.rs b/src/parafold/cycle_fold/prover.rs index e9b803d20..e541913b0 100644 --- a/src/parafold/cycle_fold/prover.rs +++ b/src/parafold/cycle_fold/prover.rs @@ -1,10 +1,14 @@ -use bellpepper_core::ConstraintSystem; +use bellpepper_core::num::AllocatedNum; +use bellpepper_core::{ConstraintSystem, SynthesisError}; use crate::bellpepper::solver::SatisfyingAssignment; +use crate::gadgets::utils::scalar_as_base; +use crate::parafold::cycle_fold::gadgets::ecc::AllocatedPoint; use crate::parafold::cycle_fold::nifs::prover::RelaxedSecondaryR1CS; use crate::parafold::transcript::prover::Transcript; use crate::parafold::transcript::TranscriptElement; use crate::r1cs::R1CSShape; +use crate::traits::commitment::CommitmentTrait; use crate::traits::CurveCycleEquipped; use crate::{Commitment, CommitmentKey}; @@ -17,7 +21,7 @@ use crate::{Commitment, CommitmentKey}; /// added to the Fiat-Shamir transcript as it represents a value "provided by the prover". /// /// 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)] +#[derive(Debug, PartialEq, Eq)] pub struct ScalarMulAccumulator { deferred: Vec>, acc: RelaxedSecondaryR1CS, @@ -31,6 +35,14 @@ impl ScalarMulAccumulator { } } + /// Create a dummy accumulator for simulation purposes + pub fn dummy() -> Self { + Self { + deferred: vec![], + acc: RelaxedSecondaryR1CS::dummy(), + } + } + /// Given two commitments `A`, `B` and a scalar `x`, compute `C <- A + x * B` /// /// # Details @@ -52,20 +64,6 @@ impl ScalarMulAccumulator { C } - // pub fn merge( - // ck: &CommitmentKey, - // shape: &R1CSShape, - // mut self_L: Self, - // self_R: &Self, - // transcript: &mut Transcript, - // ) -> Self { - // let mut deferred = self_L.deferred; - // deferred.extend(self_R.deferred.clone()); - // let acc = RelaxedSecondaryR1CS::merge(ck, shape, self_L.acc, &self_R.acc, transcript); - // Self { deferred, acc } - // } - // - /// 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( @@ -90,10 +88,6 @@ impl ScalarMulAccumulator { .for_each(|_| RelaxedSecondaryR1CS::simulate_fold(transcript)); self.acc } - - pub fn is_finalized(&self) -> bool { - self.deferred.is_empty() - } } #[derive(Debug, Clone, Default, PartialEq, Eq)] @@ -103,3 +97,31 @@ pub struct ScalarMulInstance { x: E::Scalar, C: Commitment, } + +impl ScalarMulInstance { + #[allow(unused)] + pub fn synthesize(&self, mut cs: CS) -> Result<(), SynthesisError> + where + CS: ConstraintSystem, + { + let a = AllocatedPoint::::alloc_hashed_input( + cs.namespace(|| "alloc a"), + self.A.to_coordinates(), + )?; + let b = AllocatedPoint::::alloc_hashed_input( + cs.namespace(|| "alloc b"), + self.B.to_coordinates(), + )?; + + let x = AllocatedNum::alloc_input(cs.namespace(|| "alloc x"), || { + Ok(scalar_as_base::(self.x)) + })?; + + let x_bits = x.to_bits_le(cs.namespace(|| "x_bits"))?; + + let xb = b.scalar_mul(cs.namespace(|| "x*b"), &x_bits)?; + let c = a.add(cs.namespace(|| "a + xb"), &xb)?; + + c.inputize_hashed(cs.namespace(|| "inputize c")) + } +} diff --git a/src/parafold/mod.rs b/src/parafold/mod.rs index c480faff6..a936a4a14 100644 --- a/src/parafold/mod.rs +++ b/src/parafold/mod.rs @@ -1,13 +1,14 @@ mod circuit; -#[allow(dead_code)] + mod cycle_fold; -#[allow(dead_code)] + mod nifs; -#[allow(dead_code)] + mod nivc; + #[allow(dead_code)] mod prover; -#[allow(dead_code)] + mod transcript; // pub struct ProvingKey { diff --git a/src/parafold/nifs/circuit.rs b/src/parafold/nifs/circuit.rs index 6d11a84ef..21704a9af 100644 --- a/src/parafold/nifs/circuit.rs +++ b/src/parafold/nifs/circuit.rs @@ -74,92 +74,92 @@ impl AllocatedRelaxedR1CSInstance { }) } - /// Optimized merge over the primary curve, where the same `r` is used across many accumulators. - pub fn merge_many( - mut cs: CS, - accs_L: Vec, - accs_R: Vec, - acc_sm: &mut AllocatedScalarMulAccumulator, - transcript: &mut AllocatedTranscript, - ) -> Result, SynthesisError> - where - CS: ConstraintSystem, - { - assert_eq!(accs_L.len(), accs_R.len()); - - // Add all cross-term commitments to the transcript. - let Ts = (0..accs_L.len()) - .map(|i| transcript.read_commitment_primary(cs.namespace(|| format!("transcript T[{i}]")))) - .collect::, _>>()?; - - // Get common challenge - let r = transcript.squeeze(cs.namespace(|| "squeeze r"))?; - let r_bits = r.to_bits_le(cs.namespace(|| "r_bits"))?; - - // Merge all accumulators - let accs_next = zip_eq(accs_L, accs_R) - .zip_eq(Ts) - .map(|((acc_L, acc_R), T)| { - let Self { - pp: pp_L, - u: u_L, - X: X_L, - W: W_L, - E: E_L, - .. - } = acc_L; - let Self { - pp: pp_R, - u: u_R, - X: X_R, - W: W_R, - E: E_R, - .. - } = acc_R; - - cs.enforce( - || "pp_L = pp_R", - |lc| lc, - |lc| lc, - |lc| lc + pp_L.get_variable() - pp_R.get_variable(), - ); - - let u_next = mul_add(cs.namespace(|| "u_new"), &u_L, &u_R, &r)?; - let X_next = mul_add(cs.namespace(|| "X_new[{i}]"), &X_L, &X_R, &r)?; - let W_next = acc_sm.scalar_mul( - cs.namespace(|| "W_next"), - W_L.clone(), - W_R.clone(), - r_bits.clone(), - transcript, - )?; - let E1_next = acc_sm.scalar_mul( - cs.namespace(|| "E1_next"), - T.clone(), - E_R.clone(), - r_bits.clone(), - transcript, - )?; - let E_next = acc_sm.scalar_mul( - cs.namespace(|| "E_next"), - E_L.clone(), - E1_next.clone(), - r_bits.clone(), - transcript, - )?; - - Ok::(Self { - pp: pp_L, - u: u_next, - X: X_next, - W: W_next, - E: E_next, - }) - }) - .collect::, _>>()?; - - Ok(accs_next) - } + // /// Optimized merge over the primary curve, where the same `r` is used across many accumulators. + // pub fn merge_many( + // mut cs: CS, + // accs_L: Vec, + // accs_R: Vec, + // acc_sm: &mut AllocatedScalarMulAccumulator, + // transcript: &mut AllocatedTranscript, + // ) -> Result, SynthesisError> + // where + // CS: ConstraintSystem, + // { + // assert_eq!(accs_L.len(), accs_R.len()); + // + // // Add all cross-term commitments to the transcript. + // let Ts = (0..accs_L.len()) + // .map(|i| transcript.read_commitment_primary(cs.namespace(|| format!("transcript T[{i}]")))) + // .collect::, _>>()?; + // + // // Get common challenge + // let r = transcript.squeeze(cs.namespace(|| "squeeze r"))?; + // let r_bits = r.to_bits_le(cs.namespace(|| "r_bits"))?; + // + // // Merge all accumulators + // let accs_next = zip_eq(accs_L, accs_R) + // .zip_eq(Ts) + // .map(|((acc_L, acc_R), T)| { + // let Self { + // pp: pp_L, + // u: u_L, + // X: X_L, + // W: W_L, + // E: E_L, + // .. + // } = acc_L; + // let Self { + // pp: pp_R, + // u: u_R, + // X: X_R, + // W: W_R, + // E: E_R, + // .. + // } = acc_R; + // + // cs.enforce( + // || "pp_L = pp_R", + // |lc| lc, + // |lc| lc, + // |lc| lc + pp_L.get_variable() - pp_R.get_variable(), + // ); + // + // let u_next = mul_add(cs.namespace(|| "u_new"), &u_L, &u_R, &r)?; + // let X_next = mul_add(cs.namespace(|| "X_new[{i}]"), &X_L, &X_R, &r)?; + // let W_next = acc_sm.scalar_mul( + // cs.namespace(|| "W_next"), + // W_L.clone(), + // W_R.clone(), + // r_bits.clone(), + // transcript, + // )?; + // let E1_next = acc_sm.scalar_mul( + // cs.namespace(|| "E1_next"), + // T.clone(), + // E_R.clone(), + // r_bits.clone(), + // transcript, + // )?; + // let E_next = acc_sm.scalar_mul( + // cs.namespace(|| "E_next"), + // E_L.clone(), + // E1_next.clone(), + // r_bits.clone(), + // transcript, + // )?; + // + // Ok::(Self { + // pp: pp_L, + // u: u_next, + // X: X_next, + // W: W_next, + // E: E_next, + // }) + // }) + // .collect::, _>>()?; + // + // Ok(accs_next) + // } /// Compute the hash of the accumulator over the primary curve. pub fn hash(&self, mut cs: CS) -> Result, SynthesisError> @@ -183,8 +183,8 @@ impl AllocatedRelaxedR1CSInstance { let pp = AllocatedNum::alloc_infallible(cs.namespace(|| "alloc pp"), || instance.pp); let u = AllocatedNum::alloc_infallible(cs.namespace(|| "alloc u"), || instance.u); let X = AllocatedNum::alloc_infallible(cs.namespace(|| "alloc X"), || instance.X); - let W = AllocatedPrimaryCommitment::alloc(cs.namespace(|| "alloc W"), instance.W); - let E = AllocatedPrimaryCommitment::alloc(cs.namespace(|| "alloc E"), instance.E); + let W = AllocatedPrimaryCommitment::alloc(cs.namespace(|| "alloc W"), &instance.W); + let E = AllocatedPrimaryCommitment::alloc(cs.namespace(|| "alloc E"), &instance.E); Self { pp, u, X, W, E } } @@ -205,7 +205,7 @@ where let b_val = b.get_value().ok_or(SynthesisError::AssignmentMissing)?; let r_val = r.get_value().ok_or(SynthesisError::AssignmentMissing)?; - Ok(a_val + r_val + b_val) + Ok(a_val + r_val * b_val) })?; cs.enforce( diff --git a/src/parafold/nivc/circuit.rs b/src/parafold/nivc/circuit.rs index 16142e9f1..bd9c2c6d9 100644 --- a/src/parafold/nivc/circuit.rs +++ b/src/parafold/nivc/circuit.rs @@ -3,7 +3,11 @@ use bellpepper_core::num::AllocatedNum; use bellpepper_core::{ConstraintSystem, LinearCombination, SynthesisError}; use ff::Field; use itertools::{chain, zip_eq}; -use neptune::circuit2::{Elt, PoseidonCircuit2}; +use neptune::circuit2::Elt; +use neptune::sponge::api::{IOPattern, SpongeAPI, SpongeOp}; +use neptune::sponge::circuit::SpongeCircuit; +use neptune::sponge::vanilla::Mode::Simplex; +use neptune::sponge::vanilla::SpongeTrait; use crate::gadgets::utils::{alloc_num_equals, alloc_zero, conditionally_select}; use crate::parafold::cycle_fold::circuit::AllocatedScalarMulAccumulator; @@ -249,8 +253,9 @@ impl AllocatedNIVCState { let accs_hash = state .accs_hash .into_iter() - .map(|acc_hash| { - AllocatedNum::alloc_infallible(cs.namespace(|| "alloc acc_hash"), || acc_hash) + .enumerate() + .map(|(i, acc_hash)| { + AllocatedNum::alloc_infallible(cs.namespace(|| format!("alloc acc_hash {i}")), || acc_hash) }) .collect::>(); @@ -272,8 +277,16 @@ impl AllocatedNIVCState { CS: ConstraintSystem, { let elements = self.as_preimage().into_iter().collect::>(); - let constants = NIVCPoseidonConstants::::new_constant_length(elements.len()); - PoseidonCircuit2::new(elements, &constants).hash_to_allocated(cs.namespace(|| "state hash")) + let num_absorbs = elements.len() as u32; + let constants = NIVCPoseidonConstants::::new(); + + let pattern = IOPattern(vec![SpongeOp::Absorb(num_absorbs), SpongeOp::Squeeze(1u32)]); + let acc = &mut cs.namespace(|| "squeeze"); + let mut sponge = SpongeCircuit::new_with_constants(&constants, Simplex); + sponge.start(pattern, None, acc); + SpongeAPI::absorb(&mut sponge, num_absorbs, &elements, acc); + let state_out = SpongeAPI::squeeze(&mut sponge, 1, acc); + state_out[0].ensure_allocated(acc, true) } fn update_accs( @@ -347,9 +360,10 @@ impl AllocatedNIVCState { // Update hashes of accumulators in state zip_eq(accs_hash.into_iter(), accs_hash_selector.iter()) - .map(|(acc_hash, bit)| { + .enumerate() + .map(|(i, (acc_hash, bit))| { conditionally_select( - cs.namespace(|| "accs_hash_curr"), + cs.namespace(|| format!("accs_hash_curr {i}")), &acc_curr_hash, &acc_hash, bit, diff --git a/src/parafold/nivc/mod.rs b/src/parafold/nivc/mod.rs index 6df9d2b6c..1027dfa80 100644 --- a/src/parafold/nivc/mod.rs +++ b/src/parafold/nivc/mod.rs @@ -1,10 +1,14 @@ use bellpepper_core::num::AllocatedNum; +use ff::Field; use neptune::generic_array::typenum::U24; use neptune::poseidon::PoseidonConstants; use crate::parafold::cycle_fold::nifs::prover::RelaxedSecondaryR1CSInstance; +use crate::parafold::cycle_fold::prover::ScalarMulAccumulator; +use crate::parafold::nifs::prover::RelaxedR1CS; use crate::parafold::nifs::RelaxedR1CSInstance; -use crate::parafold::transcript::TranscriptElement; +use crate::parafold::transcript::prover::Transcript; +use crate::parafold::transcript::{TranscriptConstants, TranscriptElement}; use crate::traits::{CurveCycleEquipped, Engine}; pub mod circuit; @@ -53,9 +57,67 @@ pub struct NIVCUpdateProof { index_prev: Option, } -#[derive(Debug, Clone)] -pub struct NIVCMergeProof { - transcript_buffer: Vec, - accs_L: Vec>, - accs_R: Vec>, +impl NIVCUpdateProof { + pub fn dummy( + ro_consts: TranscriptConstants, + arity: usize, + num_circuit: usize, + ) -> Self { + let state_instance = NIVCStateInstance::::dummy(arity, num_circuit); + + let state_hash = E::Scalar::ZERO; + + let mut transcript = Transcript::new(ro_consts.clone(), [state_hash]); + + let mut acc_sm = ScalarMulAccumulator::dummy(); + RelaxedR1CS::simulate_fold(&mut acc_sm, &mut transcript); + let _ = acc_sm.simulate_finalize(&mut transcript); + + let (_, transcript_buffer) = transcript.seal(); + Self { + transcript_buffer, + state: state_instance, + acc_prev: RelaxedR1CSInstance::default(), + index_prev: None, + } + } +} + +// #[derive(Debug, Clone)] +// pub struct NIVCMergeProof { +// transcript_buffer: Vec, +// accs_L: Vec>, +// accs_R: Vec>, +// } + +#[cfg(test)] +mod tests { + use crate::parafold::nivc::circuit::AllocatedNIVCState; + use crate::provider::Bn256EngineKZG as E; + use crate::traits::Engine; + use bellpepper_core::test_cs::TestConstraintSystem; + use bellpepper_core::ConstraintSystem; + + use super::*; + + type Scalar = ::Scalar; + + type CS = TestConstraintSystem; + + #[test] + fn test_from_proof() { + let mut cs = CS::new(); + + let ro_consts = TranscriptConstants::::new(); + let dummy_proof = NIVCUpdateProof::::dummy(ro_consts.clone(), 4, 4); + + let _state = + AllocatedNIVCState::from_proof(cs.namespace(|| "from proof"), &ro_consts, dummy_proof) + .unwrap(); + + if !cs.is_satisfied() { + println!("{:?}", cs.which_is_unsatisfied()); + } + assert!(cs.is_satisfied()); + } } diff --git a/src/parafold/nivc/prover.rs b/src/parafold/nivc/prover.rs index ca2d7a62c..538661123 100644 --- a/src/parafold/nivc/prover.rs +++ b/src/parafold/nivc/prover.rs @@ -1,8 +1,12 @@ use ff::Field; use itertools::chain; -use neptune::Poseidon; +use neptune::sponge::api::{IOPattern, SpongeAPI, SpongeOp}; +use neptune::sponge::vanilla::Mode::Simplex; +use neptune::sponge::vanilla::{Sponge, SpongeTrait}; -use crate::parafold::cycle_fold::nifs::prover::RelaxedSecondaryR1CS; +use crate::parafold::cycle_fold::nifs::prover::{ + RelaxedSecondaryR1CS, RelaxedSecondaryR1CSInstance, +}; use crate::parafold::cycle_fold::prover::ScalarMulAccumulator; use crate::parafold::nifs::prover::RelaxedR1CS; use crate::parafold::nifs::RelaxedR1CSInstance; @@ -62,9 +66,9 @@ impl NIVCState { let mut transcript = Transcript::new(ro_consts.clone(), [state_hash]); - let mut acc_sm = ScalarMulAccumulator::new(acc_cf); + let mut acc_sm = ScalarMulAccumulator::dummy(); RelaxedR1CS::simulate_fold(&mut acc_sm, &mut transcript); - let acc_cf = acc_sm.simulate_finalize(&mut transcript); + let _ = acc_sm.simulate_finalize(&mut transcript); let (transcript_state, transcript_buffer) = transcript.seal(); @@ -225,6 +229,15 @@ impl NIVCState { } impl NIVCStateInstance { + pub fn dummy(arity: usize, num_circuit: usize) -> Self { + Self { + transcript_state: Default::default(), + io: NIVCIO::dummy(arity), + accs_hash: vec![Default::default(); num_circuit], + acc_cf: RelaxedSecondaryR1CSInstance::dummy(), + } + } + pub fn hash(&self) -> E::Scalar { let elements = self .as_preimage() @@ -232,8 +245,16 @@ impl NIVCStateInstance { .map(|x| x.to_field()) .flatten() .collect::>(); + let num_absorbs = elements.len() as u32; let constants = NIVCPoseidonConstants::::new_constant_length(elements.len()); - Poseidon::new_with_preimage(&elements, &constants).hash() + + let acc = &mut (); + let mut sponge = Sponge::new_with_constants(&constants, Simplex); + let parameter = IOPattern(vec![SpongeOp::Absorb(num_absorbs), SpongeOp::Squeeze(1u32)]); + sponge.start(parameter, None, acc); + SpongeAPI::absorb(&mut sponge, num_absorbs, &elements, acc); + let hash = SpongeAPI::squeeze(&mut sponge, 1, acc); + hash[0] } pub fn as_preimage(&self) -> impl IntoIterator> + '_ { @@ -260,6 +281,15 @@ impl NIVCIO { } } + pub fn dummy(arity: usize) -> Self { + Self { + pc_in: Default::default(), + z_in: vec![Default::default(); arity], + pc_out: Default::default(), + z_out: vec![Default::default(); arity], + } + } + pub fn update(&mut self, io_next: Self) { assert_eq!(self.pc_in, io_next.pc_in); assert_eq!(self.z_in, io_next.z_in); diff --git a/src/parafold/transcript/circuit.rs b/src/parafold/transcript/circuit.rs index 02a99e94f..922bc6c5c 100644 --- a/src/parafold/transcript/circuit.rs +++ b/src/parafold/transcript/circuit.rs @@ -8,7 +8,6 @@ use neptune::sponge::circuit::SpongeCircuit; use neptune::sponge::vanilla::Mode::Simplex; use neptune::sponge::vanilla::SpongeTrait; -use crate::parafold::cycle_fold::gadgets::emulated::AllocatedBase; use crate::parafold::cycle_fold::gadgets::secondary_commitment::AllocatedSecondaryCommitment; use crate::parafold::cycle_fold::AllocatedPrimaryCommitment; use crate::parafold::transcript::{TranscriptConstants, TranscriptElement}; @@ -42,6 +41,7 @@ impl AllocatedTranscript { } /// Reads a single field element from the transcript + #[allow(unused)] pub fn read_scalar(&mut self, mut cs: CS) -> Result, SynthesisError> where CS: ConstraintSystem, @@ -61,39 +61,6 @@ impl AllocatedTranscript { Ok(val) } - pub fn read_scalar_vec( - &mut self, - mut cs: CS, - len: usize, - ) -> Result>, SynthesisError> - where - CS: ConstraintSystem, - { - (0..len) - .map(|i| self.read_scalar(cs.namespace(|| i.to_string()))) - .collect() - } - - /// Reads a single field element from the transcript - pub fn read_base(&mut self, mut cs: CS) -> Result, SynthesisError> - where - CS: ConstraintSystem, - { - let element = self - .buffer - .next() - .ok_or(SynthesisError::AssignmentMissing)?; - - let allocated_base = match element { - TranscriptElement::Base(base) => AllocatedBase::alloc(cs.namespace(|| "alloc base"), base), - _ => return Err(SynthesisError::AssignmentMissing), - }; - - self.state.extend(allocated_base.as_preimage()); - - Ok(allocated_base) - } - pub fn read_commitment_primary( &mut self, mut cs: CS, @@ -108,7 +75,7 @@ impl AllocatedTranscript { let allocated_hashed_commitment = match element { TranscriptElement::CommitmentPrimary(commitment) => { - AllocatedPrimaryCommitment::alloc(cs.namespace(|| "alloc commitment primary"), commitment) + AllocatedPrimaryCommitment::alloc(cs.namespace(|| "alloc commitment primary"), &commitment) } _ => return Err(SynthesisError::AssignmentMissing), }; @@ -164,9 +131,8 @@ impl AllocatedTranscript { SpongeAPI::absorb(&mut sponge, num_absorbs, &elements, acc); let state_out = SpongeAPI::squeeze(&mut sponge, 1, acc); - sponge.finish(acc).unwrap(); - state_out[0].ensure_allocated(acc, false)? + state_out[0].ensure_allocated(acc, true)? }; self.prev = Some(hash.clone()); diff --git a/src/parafold/transcript/mod.rs b/src/parafold/transcript/mod.rs index 39a800bff..5b69e3062 100644 --- a/src/parafold/transcript/mod.rs +++ b/src/parafold/transcript/mod.rs @@ -1,7 +1,7 @@ -use ff::{PrimeField, PrimeFieldBits}; use generic_array::typenum::U24; use neptune::poseidon::PoseidonConstants; +use crate::parafold::cycle_fold::gadgets::emulated::BaseParams; use crate::parafold::cycle_fold::hash_commitment; use crate::traits::commitment::CommitmentTrait; use crate::traits::CurveCycleEquipped; @@ -15,9 +15,14 @@ pub type TranscriptConstants = PoseidonConstants; #[derive(Clone, Debug)] pub enum TranscriptElement { + /// Serialized as self Scalar(E::Scalar), + /// Serialized as `[Scalar(limb_0), Scalar(limb_1), ...]`, + /// where the limbs are given by the [BaseParams] definition. Base(E::Base), + /// Serialized as `Base(hash)`, where `hash = Poseidon(self.x, self.y)` CommitmentPrimary(Commitment), + /// Serialized as `[Scalar(self.x), Scalar(self.y)]` CommitmentSecondary(Commitment), } @@ -25,41 +30,16 @@ impl TranscriptElement { pub fn to_field(&self) -> impl IntoIterator { // TODO: figure out if we can avoid Vec match self { - TranscriptElement::Scalar(scalar) => vec![*scalar], - TranscriptElement::Base(base) => base - .to_le_bits() - .chunks(128) - .map(|bits| { - bits - .iter() - .enumerate() - .fold(0_u128, |mut byte, (index, bit)| { - if *bit { - let mask = 1_u128 << index; - byte &= mask; - } - byte - }) - }) - .map(|limb| E::Scalar::from_u128(limb)) - .collect::>(), - TranscriptElement::CommitmentPrimary(c) => { - let hash = hash_commitment::(*c); + Self::Scalar(scalar) => vec![*scalar], + Self::Base(base) => BaseParams::::base_to_limb(*base), + Self::CommitmentPrimary(c) => { + let hash = hash_commitment::(c); Self::Base(hash).to_field() } - TranscriptElement::CommitmentSecondary(c) => { + Self::CommitmentSecondary(c) => { let (x, y, _) = c.to_coordinates(); [x, y].to_vec() } } } - - pub fn size(&self) -> usize { - match self { - TranscriptElement::Scalar(_) => 1, - TranscriptElement::Base(_) => 2, - TranscriptElement::CommitmentPrimary(_) => 2, - TranscriptElement::CommitmentSecondary(_) => 2, - } - } } diff --git a/src/parafold/transcript/prover.rs b/src/parafold/transcript/prover.rs index 01212770e..2c2909faa 100644 --- a/src/parafold/transcript/prover.rs +++ b/src/parafold/transcript/prover.rs @@ -40,37 +40,17 @@ impl Transcript { self.buffer.push(element); } - /// Adds a sequence of elements to the transcript, storing them for future deserialization by - /// the corresponding verifier transcript. - pub fn absorb_many(&mut self, elements: impl IntoIterator>) { - for element in elements { - self.absorb(element); - } - } - - // pub fn absorb_commitment_primary>(&mut self, c: Commitment) { - // let limbs = commitment_to_hash_limbs::(c); - // self.absorb_many(limbs); - // } - // - // pub fn absorb_commitment_secondary>(&mut self, c: Commitment) { - // let (x, y, _) = c.to_coordinates(); - // self.absorb_many([x, y]); - // } - pub fn squeeze(&mut self) -> E::Scalar { - let mut sponge = Sponge::new_with_constants(&self.constants, Simplex); - let elements = chain!(self.prev.clone(), self.round_state.drain(..)).collect::>(); let num_absorbs = elements.len() as u32; let hash = { let acc = &mut (); + let mut sponge = Sponge::new_with_constants(&self.constants, Simplex); let parameter = IOPattern(vec![SpongeOp::Absorb(num_absorbs), SpongeOp::Squeeze(1u32)]); sponge.start(parameter, None, acc); SpongeAPI::absorb(&mut sponge, num_absorbs, &elements, acc); let hash = SpongeAPI::squeeze(&mut sponge, 1, acc); - sponge.finish(acc).unwrap(); hash[0] };