diff --git a/src/gadgets/ecc/point.rs b/src/gadgets/ecc/point.rs index 0f00c1d4..984c3638 100644 --- a/src/gadgets/ecc/point.rs +++ b/src/gadgets/ecc/point.rs @@ -9,6 +9,13 @@ pub struct AssignedPoint { pub(crate) y: AssignedValue, } +impl From> for (AssignedValue, AssignedValue) { + fn from(p: AssignedPoint) -> (AssignedValue, AssignedValue) { + let AssignedPoint { x, y } = p; + (x, y) + } +} + impl AssignedPoint { pub fn coordinates(&self) -> (&AssignedValue, &AssignedValue) { (&self.x, &self.y) diff --git a/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs b/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs index 3f0e41e0..575f8805 100644 --- a/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs +++ b/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs @@ -113,7 +113,7 @@ where }; let (primary_plonk_structure, initial_primary_trace) = { - let mut mock_sfc = StepFoldingCircuit:: { + let mut mock_sfc = StepFoldingCircuit:: { sc: primary_sfc, input: sfc::Input::::new_initial::( &PlonkStructure { @@ -123,6 +123,7 @@ where &support_plonk_structure, &initial_support_trace.u, ), + _p: PhantomData, }; let mock_instances = mock_sfc.initial_instances(); @@ -142,13 +143,14 @@ where .try_collect_plonk_structure() .unwrap(); - let sfc = StepFoldingCircuit:: { + let sfc = StepFoldingCircuit:: { sc: primary_sfc, input: sfc::Input::::new_initial::( &mock_S, &support_plonk_structure, &initial_support_trace.u, ), + _p: PhantomData, }; // TODO #369 Use expected out marker, instead of zero diff --git a/src/ivc/cyclefold/sfc/input/assigned.rs b/src/ivc/cyclefold/sfc/input/assigned.rs index ccc2f122..342f5a1c 100644 --- a/src/ivc/cyclefold/sfc/input/assigned.rs +++ b/src/ivc/cyclefold/sfc/input/assigned.rs @@ -9,7 +9,7 @@ use crate::{ main_gate::{self, AdviceCyclicAssignor, AssignedValue, MainGate, RegionCtx, WrapValue}, }; -pub type MainGateConfig = main_gate::MainGateConfig<{ super::super::T_MAIN_GATE }>; +pub type MainGateConfig = main_gate::MainGateConfig<{ super::super::MAIN_GATE_T }>; pub type BigUint = Vec; @@ -76,7 +76,7 @@ impl ProtoGalaxyAccumulatorInstance { }) } - fn conditional_select( + pub fn conditional_select( region: &mut RegionCtx<'_, F>, mg: &MainGate, lhs: &Self, @@ -272,10 +272,11 @@ impl PairedPlonkInstance { } } +#[derive(Clone)] pub struct SangriaAccumulatorInstance { pub(crate) ins: PairedPlonkInstance, pub(crate) E_commitment: (AssignedValue, AssignedValue), - pub(crate) u: BigUint>, + pub(crate) u: AssignedValue, } impl SangriaAccumulatorInstance { @@ -302,10 +303,20 @@ impl SangriaAccumulatorInstance { original.E_commitment.1, )?, ), - u: assigner.assign_all_advice(region, || "u", original.u.limbs().iter().cloned())?, + u: assigner.assign_next_advice(region, || "u", original.u)?, }) } + pub fn conditional_select( + _region: &mut RegionCtx<'_, F>, + _mg: &MainGate, + _lhs: &Self, + _rhs: &Self, + _cond: &AssignedValue, + ) -> Result { + todo!() + } + fn iter_wrap_values(&self) -> impl '_ + Iterator> { let Self { ins, @@ -316,7 +327,7 @@ impl SangriaAccumulatorInstance { ins.iter_wrap_values().chain( [E_commitment.0.clone(), E_commitment.1.clone()] .into_iter() - .chain(u.iter().cloned()) + .chain(iter::once(u.clone())) .map(|v| WrapValue::Assigned(v)), ) } @@ -329,7 +340,7 @@ pub struct PairedTrace { // The size from one to three // Depdend on `W_commitments_len` pub incoming: Box<[PairedPlonkInstance]>, - proof: SangriaCrossTermCommits, + pub proof: SangriaCrossTermCommits, } impl PairedTrace { @@ -377,7 +388,7 @@ impl PairedTrace { }) } - fn iter_wrap_values(&self) -> impl '_ + Iterator> { + pub fn iter_wrap_values(&self) -> impl '_ + Iterator> { let Self { input_accumulator, incoming, diff --git a/src/ivc/cyclefold/sfc/input/mod.rs b/src/ivc/cyclefold/sfc/input/mod.rs index 13fc0d82..f217f7a8 100644 --- a/src/ivc/cyclefold/sfc/input/mod.rs +++ b/src/ivc/cyclefold/sfc/input/mod.rs @@ -157,7 +157,7 @@ impl SelfTrace { pub struct SangriaAccumulatorInstance { pub(crate) ins: PairedPlonkInstance, pub(crate) E_commitment: (F, F), - pub(crate) u: BigUint, + pub(crate) u: F, } impl> AbsorbInRO for SangriaAccumulatorInstance { @@ -168,7 +168,10 @@ impl> AbsorbInRO for SangriaAccumulatorInst u, } = self; - ro.absorb(ins).absorb_field(*ex).absorb_field(*ey).absorb(u); + ro.absorb(ins) + .absorb_field(*ex) + .absorb_field(*ey) + .absorb_field(*u); } } @@ -246,7 +249,7 @@ impl PairedTrace { input_accumulator: SangriaAccumulatorInstance { ins: ins.clone(), E_commitment: (F::ZERO, F::ZERO), - u: BigUint::zero(DEFAULT_LIMB_WIDTH), + u: F::ZERO, }, incoming: vec![ins.clone(); W_commitments_len].into_boxed_slice(), proof: vec![ @@ -353,7 +356,7 @@ impl Input { challenges: vec![random_big_uint(&mut gen); 1], }, E_commitment: (gen.next().unwrap(), gen.next().unwrap()), - u: random_big_uint(&mut gen), + u: gen.next().unwrap(), }, incoming: vec![ PairedPlonkInstance { diff --git a/src/ivc/cyclefold/sfc/mod.rs b/src/ivc/cyclefold/sfc/mod.rs index 09da5083..08f0ff7c 100644 --- a/src/ivc/cyclefold/sfc/mod.rs +++ b/src/ivc/cyclefold/sfc/mod.rs @@ -1,4 +1,4 @@ -use std::num::NonZeroUsize; +use std::{marker::PhantomData, num::NonZeroUsize}; use itertools::Itertools; use tracing::error; @@ -21,47 +21,55 @@ use crate::{ mod input; pub use input::Input; +pub mod sangria_adapter; + use crate::halo2_proofs::halo2curves::ff::{FromUniformBytes, PrimeField, PrimeFieldBits}; -const T_MAIN_GATE: usize = 5; +const MAIN_GATE_T: usize = 5; /// 'SCC' here is 'Step Circuit Config' #[derive(Debug, Clone)] pub struct Config { sc: SCC, - mg: MainGateConfig, + mg: MainGateConfig, } pub struct StepFoldingCircuit< 'sc, const ARITY: usize, - C: CurveAffine, - SC: StepCircuit, + CMain: CurveAffine, + CSup: CurveAffine, + SC: StepCircuit, > { pub sc: &'sc SC, - pub input: Input, + pub input: Input, + pub _p: PhantomData, } -impl> - StepFoldingCircuit<'_, ARITY, C, SC> +impl< + const ARITY: usize, + CMain: CurveAffine, + CSup: CurveAffine, + SC: StepCircuit, + > StepFoldingCircuit<'_, ARITY, CMain, CSup, SC> where - C::ScalarExt: PrimeFieldBits + FromUniformBytes<64>, + CMain::ScalarExt: PrimeFieldBits + FromUniformBytes<64>, { /// For the initial iteration, we will give the same accumulators that we take from the input - pub fn initial_instances(&self) -> Vec> { - let marker = cyclefold::ro() - .absorb(&self.input) - .output(NonZeroUsize::new(::NUM_BITS as usize).unwrap()); + pub fn initial_instances(&self) -> Vec> { + let marker = cyclefold::ro().absorb(&self.input).output( + NonZeroUsize::new(::NUM_BITS as usize).unwrap(), + ); let mut instances = self.sc.instances(); instances.insert(0, vec![marker, marker]); instances } - pub fn instances(&self, expected_out: C::ScalarExt) -> Vec> { - let input_marker = cyclefold::ro() - .absorb(&self.input) - .output(NonZeroUsize::new(::NUM_BITS as usize).unwrap()); + pub fn instances(&self, expected_out: CMain::ScalarExt) -> Vec> { + let input_marker = cyclefold::ro().absorb(&self.input).output( + NonZeroUsize::new(::NUM_BITS as usize).unwrap(), + ); let mut instances = self.sc.instances(); instances.insert(0, vec![input_marker, expected_out]); @@ -69,10 +77,14 @@ where } } -impl> Circuit - for StepFoldingCircuit<'_, ARITY, C, SC> +impl< + const ARITY: usize, + CMain: CurveAffine, + CSup: CurveAffine, + SC: StepCircuit, + > Circuit for StepFoldingCircuit<'_, ARITY, CMain, CSup, SC> where - C::ScalarExt: PrimeFieldBits + FromUniformBytes<64>, + CMain::ScalarExt: PrimeFieldBits + FromUniformBytes<64>, { type Config = Config; type FloorPlanner = SimpleFloorPlanner; @@ -81,10 +93,11 @@ where Self { sc: self.sc, input: self.input.get_without_witness(), + _p: PhantomData, } } - fn configure(meta: &mut ConstraintSystem) -> Self::Config { + fn configure(meta: &mut ConstraintSystem) -> Self::Config { Self::Config { sc: SC::configure(meta), mg: MainGate::configure(meta), @@ -94,7 +107,7 @@ where fn synthesize( &self, config: Self::Config, - mut layouter: impl Layouter, + mut layouter: impl Layouter, ) -> Result<(), Halo2PlonkError> { let input = layouter.assign_region( || "sfc input", @@ -113,8 +126,8 @@ where Halo2PlonkError::Synthesis })?; - let _self_acc_out: input::assigned::ProtoGalaxyAccumulatorInstance = layouter - .assign_region( + let self_acc_out: input::assigned::ProtoGalaxyAccumulatorInstance = + layouter.assign_region( || "sfc protogalaxy", |region| { let mut region = RegionCtx::new(region, 0); @@ -137,6 +150,18 @@ where }, )?; + let paired_acc_out: input::assigned::SangriaAccumulatorInstance = + layouter.assign_region( + || "sfc sangria", + |region| { + sangria_adapter::fold::( + &mut RegionCtx::new(region, 0), + config.mg.clone(), + &input.paired_trace, + ) + }, + )?; + layouter.assign_region( || "sfc out", |region| { @@ -156,6 +181,24 @@ where .try_into() .unwrap(); + let _self_trace_output = + input::assigned::ProtoGalaxyAccumulatorInstance::conditional_select( + &mut region, + &mg, + &input.self_trace.input_accumulator, + &self_acc_out, + &is_zero_step, + )?; + + let _paired_trace_output = + input::assigned::SangriaAccumulatorInstance::conditional_select( + &mut region, + &mg, + &input.paired_trace.input_accumulator, + &paired_acc_out, + &is_zero_step, + ); + Ok(()) }, )?; diff --git a/src/ivc/cyclefold/sfc/sangria_adapter.rs b/src/ivc/cyclefold/sfc/sangria_adapter.rs new file mode 100644 index 00000000..89de4c10 --- /dev/null +++ b/src/ivc/cyclefold/sfc/sangria_adapter.rs @@ -0,0 +1,174 @@ +use halo2_proofs::halo2curves::{ + ff::{FromUniformBytes, PrimeField, PrimeFieldBits}, + CurveAffine, +}; +use itertools::Itertools; +use num_traits::Num; + +use super::{input, MAIN_GATE_T}; +use crate::{ + constants::NUM_CHALLENGE_BITS, + gadgets::{ + ecc::{AssignedPoint, EccChip}, + nonnative::{self, bn::big_uint_mul_mod_chip::BigUintMulModChip}, + }, + halo2_proofs::plonk::Error as Halo2PlonkError, + ivc::{ + cyclefold::{ro_chip, DEFAULT_LIMBS_COUNT_LIMIT, DEFAULT_LIMB_WIDTH}, + fold_relaxed_plonk_instance_chip::{self, BigUintView, FoldRelaxedPlonkInstanceChip}, + }, + main_gate::{MainGate, MainGateConfig, RegionCtx}, + poseidon::ROCircuitTrait, +}; + +fn bn_chip(main_gate_config: MainGateConfig) -> BigUintMulModChip { + BigUintMulModChip::new( + main_gate_config.into_smaller_size().unwrap(), + DEFAULT_LIMB_WIDTH, + DEFAULT_LIMBS_COUNT_LIMIT, + ) +} + +fn ecc_chip( + main_gate_config: MainGateConfig, +) -> EccChip> { + EccChip::new(main_gate_config.into_smaller_size().unwrap()) +} + +use nonnative::bn::big_uint::{self, BigUint}; + +fn module_as_bn() -> Result, big_uint::Error> { + BigUint::::from_biguint( + &num_bigint::BigUint::from_str_radix( + ::MODULUS.trim_start_matches("0x"), + 16, + ) + .unwrap(), + DEFAULT_LIMB_WIDTH, + DEFAULT_LIMBS_COUNT_LIMIT, + ) +} + +pub fn fold>( + region: &mut RegionCtx, + config: MainGateConfig, + input: &input::assigned::PairedTrace, +) -> Result, Halo2PlonkError> +where + CMain::ScalarExt: FromUniformBytes<64> + PrimeFieldBits, +{ + let bn_chip = bn_chip(config.clone()); + let ecc_chip = ecc_chip::(config.clone()); + let mg = MainGate::new(config.clone()); + + let r = ro_chip(config.clone()) + .absorb_iter(input.iter_wrap_values()) + .squeeze(region)?; + let r_bits = mg.le_num_to_bits(region, r.clone(), NUM_CHALLENGE_BITS)?; + let r_as_bn = bn_chip.from_assigned_cell_to_limbs(region, &r).unwrap(); + + let m_bn = module_as_bn::().unwrap(); + + let mut acc = input.input_accumulator.clone(); + + for input::assigned::PairedPlonkInstance { + W_commitments: input_W_commitments, + challenges: input_challenges, + instances: input_instances, + } in input.incoming.iter() + { + let input::assigned::SangriaAccumulatorInstance { + ins: + input::assigned::PairedPlonkInstance { + W_commitments: acc_W_commitments, + instances: acc_instances, + challenges: acc_challenges, + }, + E_commitment: acc_E_commitment, + u: acc_u, + } = &mut acc; + + *acc_W_commitments = FoldRelaxedPlonkInstanceChip::::fold_W( + region, + &config, + &acc_W_commitments + .iter() + .cloned() + .map(|(x, y)| AssignedPoint { x, y }) + .collect::>(), + &input_W_commitments + .iter() + .cloned() + .map(|(x, y)| AssignedPoint { x, y }) + .collect::>(), + &r_bits, + )? + .into_iter() + .map(|p| (p.x, p.y)) + .collect(); + + acc_instances + .iter_mut() + .zip_eq(input_instances) + .try_for_each( + |(acc_instances, input_instances)| -> Result<(), Halo2PlonkError> { + acc_instances + .iter_mut() + .zip_eq(input_instances) + .try_for_each( + |(acc_instance, input_instance)| -> Result<(), Halo2PlonkError> { + *acc_instance = fold_relaxed_plonk_instance_chip::fold_via_biguint( + region, + &bn_chip, + input_instance, + acc_instance.to_vec(), + &m_bn, + &r_as_bn, + DEFAULT_LIMB_WIDTH, + )?; + + Ok(()) + }, + )?; + + Ok(()) + }, + )?; + + *acc_challenges = FoldRelaxedPlonkInstanceChip::::fold_challenges( + region, + &bn_chip, + input_challenges.to_vec(), + acc_challenges.to_vec(), + &r_as_bn, + &m_bn, + DEFAULT_LIMB_WIDTH, + )?; + + *acc_E_commitment = fold_relaxed_plonk_instance_chip::fold_E( + region, + &bn_chip, + &ecc_chip, + AssignedPoint { + x: acc_E_commitment.0.clone(), + y: acc_E_commitment.1.clone(), + }, + &input + .proof + .iter() + .cloned() + .map(|(x, y)| AssignedPoint { x, y }) + .collect::>(), + BigUintView { + as_bn_limbs: r_as_bn.clone(), + as_bits: r_bits.clone(), + }, + &m_bn, + )? + .into(); + + *acc_u = mg.add(region, acc_u, &r)?; + } + + Ok(acc) +} diff --git a/src/ivc/sangria/fold_relaxed_plonk_instance_chip.rs b/src/ivc/sangria/fold_relaxed_plonk_instance_chip.rs index c02c9da6..ec5948b5 100644 --- a/src/ivc/sangria/fold_relaxed_plonk_instance_chip.rs +++ b/src/ivc/sangria/fold_relaxed_plonk_instance_chip.rs @@ -436,7 +436,7 @@ where /// ```markdown /// new_folded_W[i] = folded_W[i] + input_W[i] * r /// ``` - fn fold_W( + pub fn fold_W( region: &mut RegionCtx, config: &MainGateConfig, folded_W: &[AssignedPoint], @@ -460,133 +460,6 @@ where .collect() } - /// Fold [`RelaxedPlonkInstance::E_commitments`] & [`CrossTermCommits`] - /// - /// # Description - /// - /// This function is responsible for combining the current `folded_W` accumulator with - /// `cross_term_commits`. This is achieved through a scalar multiplication followed by - /// an elliptic curve addition. The scalar multiplication is defined by a random - /// scalar `r` in power of cross term commit index. - /// - /// # Implementation Details - /// - /// 1. **Multiplication & Conversion to bits**: Form a vector of degrees `r` and their representations as bits - /// 2. **Scalar Multiplication**: Each element of `cross_term_commits` is multiplied by power of random scalar - /// `r` (challenge) in bits representation. This is executed using the [`EccChip`] for elliptic curve operations. - /// 3. **Accumulation**: The result of the scalar multiplication is then added to the corresponding component in - /// the current `folded_E` accumulator. This is executed using the [`EccChip`] for elliptic curve operations. - /// - /// ```markdown - /// new_folded_E = folded_E + Sum [ cross_term_commits[i] * (r ^ i) ] - /// ``` - fn fold_E( - &self, - region: &mut RegionCtx, - folded_E: AssignedPoint, - cross_term_commits: &[AssignedPoint], - r: BigUintView, - m_bn: &BigUint, - ) -> Result, Error> { - debug!("Start calculate r^i from {r:?}"); - - let powers_of_r = iter::successors(Some(Ok(r.clone())), |val| { - Some(Ok(val.as_ref().ok()?).and_then(|r_pow_i| { - let BigUintView { - as_bn_limbs, - as_bits: _, - } = r_pow_i; - - let next = self - .bn_chip - .mult_mod(region, as_bn_limbs, &r.as_bn_limbs, m_bn)? - .remainder; - - debug!("Next r^i from {next:?}"); - - Result::<_, Error>::Ok(BigUintView { - as_bits: self.bn_chip.to_le_bits(region, &next)?, - as_bn_limbs: next, - }) - })) - }) - .take(cross_term_commits.len()) - .collect::, _>>()?; - - let ecc = EccChip::>::new(self.config.clone()); - // TODO Check what with all commits - let rT = cross_term_commits - .iter() - .zip(powers_of_r.into_iter()) - .map(|(commit, r_pow_i)| ecc.scalar_mul(region, commit, &r_pow_i)) - .collect::, _>>()?; - - Ok(rT - .into_iter() - .try_fold(folded_E, |folded_E, rT_i| ecc.add(region, &folded_E, &rT_i))?) - } - - /// Fold `input` with `folded` in bn form - /// - /// # Implementation Details - /// - /// 1. Multiplies a part of the PLONK instance (`$input`) by a randomized value (`r_as_bn`), - /// and then takes the remainder modulo a specified modulus (`m_bn`). - /// 2. Sums this multiplication result with a pre-assigned part of the instance (`$folded`). - /// 3. Reduces the sum modulo the modulus (`m_bn`) to get the final folded value. - /// - /// ```markdown - /// new_folded = folded + (input * r mod m) mod m - /// ``` - /// - /// # Notes - /// - /// We call this function in the chip if we need to perform the fold on a `Scalar` field. - fn fold_via_biguint( - region: &mut RegionCtx, - bn_chip: &BigUintMulModChip, - input: &[AssignedValue], - folded: Vec>, - m_bn: &BigUint, - r_as_bn: &[AssignedValue], - limb_width: NonZeroUsize, - ) -> Result>, Error> { - debug!( - "fold: via bn input: input = {:?} folded = {:?}, r = {:?}", - CellsValuesView::from(input), - CellsValuesView::from(folded.as_slice()), - CellsValuesView::from(r_as_bn) - ); - // Multiply the part of the instance by the randomized value - let part_mult_r = bn_chip - .mult_mod(region, input, r_as_bn, m_bn) - .inspect_err(|err| error!("while mult: input * r mod m: {err:?}"))? - .remainder; - debug!( - "fold: mult mod: {:?}", - CellsValuesView::from(part_mult_r.as_slice()) - ); - - // Sum the multiplication result with the assigned part - let part_mult_r_sum_part = bn_chip - .assign_sum( - region, - &OverflowingBigUint::new(folded, limb_width), - &part_mult_r, - )? - .res; - - debug!( - "fold: assign_sum {:?}", - CellsValuesView::from(part_mult_r_sum_part.cells.as_slice()) - ); - - // Reduce the sum modulo the modulus - Ok(bn_chip - .red_mod(region, part_mult_r_sum_part, m_bn)? - .remainder) - } - /// Fold consistency markers /// /// # Description @@ -612,7 +485,7 @@ where let [input_X0, input_X1] = input_consistency_marker; let [folded_X0, folded_X1] = folded_consistency_marker; - let new_folded_X0 = Self::fold_via_biguint( + let new_folded_X0 = fold_via_biguint( region, bn_chip, &input_X0, folded_X0, m_bn, r_as_bn, limb_width, ) .inspect_err(|err| error!("Error while fold X0: {err:?}"))?; @@ -622,7 +495,7 @@ where CellsValuesView::from(new_folded_X0.as_slice()) ); - let new_folded_X1 = Self::fold_via_biguint( + let new_folded_X1 = fold_via_biguint( region, bn_chip, &input_X1, folded_X1, m_bn, r_as_bn, limb_width, ) .inspect_err(|err| error!("Error while fold X1: {err:?}"))?; @@ -651,6 +524,46 @@ where ) } + /// Fold [`RelaxedPlonkInstance::E_commitments`] & [`CrossTermCommits`] + /// + /// # Description + /// + /// This function is responsible for combining the current `folded_W` accumulator with + /// `cross_term_commits`. This is achieved through a scalar multiplication followed by + /// an elliptic curve addition. The scalar multiplication is defined by a random + /// scalar `r` in power of cross term commit index. + /// + /// # Implementation Details + /// + /// 1. **Multiplication & Conversion to bits**: Form a vector of degrees `r` and their representations as bits + /// 2. **Scalar Multiplication**: Each element of `cross_term_commits` is multiplied by power of random scalar + /// `r` (challenge) in bits representation. This is executed using the [`EccChip`] for elliptic curve operations. + /// 3. **Accumulation**: The result of the scalar multiplication is then added to the corresponding component in + /// the current `folded_E` accumulator. This is executed using the [`EccChip`] for elliptic curve operations. + /// + /// ```markdown + /// new_folded_E = folded_E + Sum [ cross_term_commits[i] * (r ^ i) ] + /// ``` + pub fn fold_E( + &self, + region: &mut RegionCtx, + folded_E: AssignedPoint, + cross_term_commits: &[AssignedPoint], + r: BigUintView, + m_bn: &BigUint, + ) -> Result, Error> { + debug!("Start calculate r^i from {r:?}"); + fold_E( + region, + &self.bn_chip, + &EccChip::>::new(self.config.clone()), + folded_E, + cross_term_commits, + r, + m_bn, + ) + } + /// Fold [`RelaxedPlonkInstance::challenges`] & [`PlonkInstance::challenges`] /// /// # Description @@ -664,7 +577,7 @@ where /// ``` /// /// Please check [`FoldRelaxedPlonkInstanceChip::fold_via_biguint`] for more details - fn fold_challenges( + pub fn fold_challenges( region: &mut RegionCtx, bn_chip: &BigUintMulModChip, input_challenges: Vec>>, @@ -677,7 +590,7 @@ where .into_iter() .zip_eq(input_challenges) .map(|(folded_challenge, input_challange)| { - Self::fold_via_biguint( + fold_via_biguint( region, bn_chip, &input_challange, @@ -1025,10 +938,139 @@ where } } +/// Fold [`RelaxedPlonkInstance::E_commitments`] & [`CrossTermCommits`] +/// +/// # Description +/// +/// This function is responsible for combining the current `folded_W` accumulator with +/// `cross_term_commits`. This is achieved through a scalar multiplication followed by +/// an elliptic curve addition. The scalar multiplication is defined by a random +/// scalar `r` in power of cross term commit index. +/// +/// # Implementation Details +/// +/// 1. **Multiplication & Conversion to bits**: Form a vector of degrees `r` and their representations as bits +/// 2. **Scalar Multiplication**: Each element of `cross_term_commits` is multiplied by power of random scalar +/// `r` (challenge) in bits representation. This is executed using the [`EccChip`] for elliptic curve operations. +/// 3. **Accumulation**: The result of the scalar multiplication is then added to the corresponding component in +/// the current `folded_E` accumulator. This is executed using the [`EccChip`] for elliptic curve operations. +/// +/// ```markdown +/// new_folded_E = folded_E + Sum [ cross_term_commits[i] * (r ^ i) ] +/// ``` +pub fn fold_E( + region: &mut RegionCtx, + bn_chip: &BigUintMulModChip, + ecc_chip: &EccChip>, + folded_E: AssignedPoint, + cross_term_commits: &[AssignedPoint], + r: BigUintView, + m_bn: &BigUint, +) -> Result, Error> +where + C::Base: PrimeFieldBits, +{ + debug!("Start calculate r^i from {r:?}"); + + let powers_of_r = iter::successors(Some(Ok(r.clone())), |val| { + Some(Ok(val.as_ref().ok()?).and_then(|r_pow_i| { + let BigUintView { + as_bn_limbs, + as_bits: _, + } = r_pow_i; + + let next = bn_chip + .mult_mod(region, as_bn_limbs, &r.as_bn_limbs, m_bn)? + .remainder; + + debug!("Next r^i from {next:?}"); + + Result::<_, Error>::Ok(BigUintView { + as_bits: bn_chip.to_le_bits(region, &next)?, + as_bn_limbs: next, + }) + })) + }) + .take(cross_term_commits.len()) + .collect::, _>>()?; + + // TODO Check what with all commits + let rT = cross_term_commits + .iter() + .zip(powers_of_r.into_iter()) + .map(|(commit, r_pow_i)| ecc_chip.scalar_mul(region, commit, &r_pow_i)) + .collect::, _>>()?; + + Ok(rT.into_iter().try_fold(folded_E, |folded_E, rT_i| { + ecc_chip.add(region, &folded_E, &rT_i) + })?) +} + +/// Fold `input` with `folded` in bn form +/// +/// # Implementation Details +/// +/// 1. Multiplies a part of the PLONK instance (`$input`) by a randomized value (`r_as_bn`), +/// and then takes the remainder modulo a specified modulus (`m_bn`). +/// 2. Sums this multiplication result with a pre-assigned part of the instance (`$folded`). +/// 3. Reduces the sum modulo the modulus (`m_bn`) to get the final folded value. +/// +/// ```markdown +/// new_folded = folded + (input * r mod m) mod m +/// ``` +/// +/// # Notes +/// +/// We call this function in the chip if we need to perform the fold on a `Scalar` field. +pub fn fold_via_biguint( + region: &mut RegionCtx, + bn_chip: &BigUintMulModChip, + input: &[AssignedValue], + folded: Vec>, + m_bn: &BigUint, + r_as_bn: &[AssignedValue], + limb_width: NonZeroUsize, +) -> Result>, Error> { + debug!( + "fold: via bn input: input = {:?} folded = {:?}, r = {:?}", + CellsValuesView::from(input), + CellsValuesView::from(folded.as_slice()), + CellsValuesView::from(r_as_bn) + ); + // Multiply the part of the instance by the randomized value + let part_mult_r = bn_chip + .mult_mod(region, input, r_as_bn, m_bn) + .inspect_err(|err| error!("while mult: input * r mod m: {err:?}"))? + .remainder; + debug!( + "fold: mult mod: {:?}", + CellsValuesView::from(part_mult_r.as_slice()) + ); + + // Sum the multiplication result with the assigned part + let part_mult_r_sum_part = bn_chip + .assign_sum( + region, + &OverflowingBigUint::new(folded, limb_width), + &part_mult_r, + )? + .res; + + debug!( + "fold: assign_sum {:?}", + CellsValuesView::from(part_mult_r_sum_part.cells.as_slice()) + ); + + // Reduce the sum modulo the modulus + Ok(bn_chip + .red_mod(region, part_mult_r_sum_part, m_bn)? + .remainder) +} + #[derive(Debug, Clone)] -struct BigUintView { - as_bn_limbs: Vec>, - as_bits: Vec>, +pub struct BigUintView { + pub as_bn_limbs: Vec>, + pub as_bits: Vec>, } impl ops::Deref for BigUintView { type Target = [AssignedValue];