From ddd13b6f09724d728f51fcc18bbeb3586b0b2a08 Mon Sep 17 00:00:00 2001 From: cyphersnake Date: Mon, 16 Dec 2024 15:41:15 +0100 Subject: [PATCH] feat(ivc): impl cyclefold zero step **Motivation** Part of #373 and testing of #369 **Overview** The first part of the implementation of the zero step cyclefold __Temporarily commented out part of cyclefold step folding circuit concerning pairing check__ --- .../mod.rs | 136 +++++- .../public_params.rs | 81 ++-- src/ivc/cyclefold/mod.rs | 6 +- src/ivc/cyclefold/sfc/input/assigned.rs | 153 ++++--- src/ivc/cyclefold/sfc/input/mod.rs | 407 ++++++++++++++++-- src/ivc/cyclefold/sfc/mod.rs | 94 ++-- src/ivc/cyclefold/sfc/sangria_adapter.rs | 118 ++--- src/ivc/protogalaxy/mod.rs | 32 +- 8 files changed, 805 insertions(+), 222 deletions(-) diff --git a/src/ivc/cyclefold/incrementally_verifiable_computation/mod.rs b/src/ivc/cyclefold/incrementally_verifiable_computation/mod.rs index 2af9401b..41d51442 100644 --- a/src/ivc/cyclefold/incrementally_verifiable_computation/mod.rs +++ b/src/ivc/cyclefold/incrementally_verifiable_computation/mod.rs @@ -1,23 +1,151 @@ use std::marker::PhantomData; +use public_params::PublicParams; +use tracing::info_span; + +use super::{ + ro, + support_circuit::{self, SupportCircuit}, +}; use crate::{ halo2_proofs::halo2curves::{ - ff::{FromUniformBytes, PrimeFieldBits}, + ff::{Field, FromUniformBytes, PrimeFieldBits}, group::prime::PrimeCurveAffine, CurveAffine, }, - ivc::StepCircuit, + ivc::{ + cyclefold::sfc::{self, StepFoldingCircuit}, + StepCircuit, + }, + nifs::{ + self, + protogalaxy::{AccumulatorArgs, ProtoGalaxy}, + sangria::{FoldablePlonkTrace, VanillaFS}, + }, + table::CircuitRunner, }; mod public_params; -pub struct IVC +pub struct IVC where C1: CurveAffine::Scalar>, C2: CurveAffine::Scalar>, - SC: StepCircuit, + SC: StepCircuit, C1::Scalar: PrimeFieldBits + FromUniformBytes<64>, C2::Scalar: PrimeFieldBits + FromUniformBytes<64>, { _p: PhantomData<(C1, C2, SC)>, } + +impl IVC +where + CMain: CurveAffine::Scalar>, + CSup: CurveAffine::Scalar>, + SC: StepCircuit, + CMain::Scalar: PrimeFieldBits + FromUniformBytes<64>, + CSup::Scalar: PrimeFieldBits + FromUniformBytes<64>, +{ + pub fn new( + pp: &PublicParams, + sc: &SC, + z_0: [CMain::ScalarExt; ARITY], + ) -> Self { + let _primary_span = info_span!("primary").entered(); + + let initial_self_acc = ProtoGalaxy::::new_accumulator( + AccumulatorArgs::from(&pp.primary_S), + &nifs::protogalaxy::ProverParam { + S: pp.primary_S.clone(), + pp_digest: pp.cmain_pp_digest(), + }, + &mut ro(), + ); + + let (_new_acc, self_proof) = ProtoGalaxy::prove( + &pp.primary_ck, + &nifs::protogalaxy::ProverParam { + S: pp.primary_S.clone(), + pp_digest: pp.cmain_pp_digest(), + }, + &mut ro(), + initial_self_acc.clone(), + &[pp.primary_initial_trace.clone()], + ) + .unwrap(); + + let mut acc_ptr = nifs::sangria::accumulator::RelaxedPlonkTrace::from_regular( + pp.support_initial_trace.clone(), + SupportCircuit::::MIN_K_TABLE_SIZE as usize, + ); + let mut paired_incoming = vec![]; + + for _ in 0..initial_self_acc.W_commitment_len() { + let (new_acc, paired_proof) = + VanillaFS::::prove( + &pp.support_ck, + &nifs::sangria::ProverParam { + S: pp.support_S.clone(), + pp_digest: pp.csup_pp_digest(), + }, + &mut ro(), + acc_ptr, + &[pp.support_initial_trace.clone()], + ) + .unwrap(); + + paired_incoming.push((pp.support_initial_trace.u.clone(), paired_proof)); + + acc_ptr = new_acc; + } + + let _primary_sfc = StepFoldingCircuit::<'_, ARITY, CMain, CSup, SC> { + sc, + input: sfc::InputBuilder { + pp_digest: pp.csup_pp_digest(), + step: 0, + self_incoming: &pp.primary_initial_trace.u, + self_proof, + paired_acc: &pp.support_initial_trace.u.clone().into(), + paired_incoming: paired_incoming.as_slice(), + self_acc: &initial_self_acc.into(), + z_i: z_0, + z_0, + } + .build(), + _p: PhantomData, + }; + + let _initial_support_trace: FoldablePlonkTrace = { + let _support_span = info_span!("support").entered(); + + let support_circuit_instances: Vec> = support_circuit::InstanceInput { + p0: CMain::identity(), + l0: CMain::Base::ZERO, + p1: CMain::identity(), + l1: CMain::Base::ZERO, + } + .into_instance(); + + let support_cr = CircuitRunner::::new( + SupportCircuit::::MIN_K_TABLE_SIZE, + SupportCircuit::::default(), + support_circuit_instances.clone(), + ); + + VanillaFS::::generate_plonk_trace( + &pp.support_ck, + &support_circuit_instances, + &support_cr.try_collect_witness().unwrap(), + &nifs::sangria::ProverParam { + S: support_cr.try_collect_plonk_structure().unwrap(), + pp_digest: CSup::identity(), + }, + &mut ro(), + ) + .unwrap() + }; + + todo!() + } +} diff --git a/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs b/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs index e1f5f4f7..951ef1c1 100644 --- a/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs +++ b/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs @@ -24,22 +24,22 @@ use crate::{ table::CircuitRunner, }; -pub struct PublicParams +pub struct PublicParams where - C1: CurveAffine::Scalar>, - C2: CurveAffine::Scalar>, - SC: StepCircuit, - C1::Scalar: PrimeFieldBits + FromUniformBytes<64>, - C2::Scalar: PrimeFieldBits + FromUniformBytes<64>, + CMain: CurveAffine::Scalar>, + CSup: CurveAffine::Scalar>, + SC: StepCircuit, + CMain::Scalar: PrimeFieldBits + FromUniformBytes<64>, + CSup::Scalar: PrimeFieldBits + FromUniformBytes<64>, { - ck1: CommitmentKey, - primary_S: PlonkStructure, - primary_k_table_size: u32, - initial_primary_trace: PlonkTrace, + pub primary_ck: CommitmentKey, + pub primary_S: PlonkStructure, + pub primary_k_table_size: u32, + pub primary_initial_trace: PlonkTrace, - ck2: CommitmentKey, - secondary_S: PlonkStructure, - initial_secondary_trace: FoldablePlonkTrace, + pub support_ck: CommitmentKey, + pub support_S: PlonkStructure, + pub support_initial_trace: FoldablePlonkTrace, _p: PhantomData, } @@ -61,18 +61,14 @@ where CMain::Scalar: PrimeFieldBits + FromUniformBytes<64>, CSup::Scalar: PrimeFieldBits + FromUniformBytes<64>, { - /// StepFoldingCircuit { - /// step == 0 => init(acc, incoming) => output_trace, - /// step != 0 => fold(acc, incoming) => output_trace, - /// } pub fn new( - primary_sfc: &SC, + primary_sc: &SC, ck1: CommitmentKey, ck2: CommitmentKey, k_table_size: u32, ) -> Self { // Trace in C1::Base or C2::Scalar - let (support_plonk_structure, initial_support_trace): ( + let (support_S, support_initial_trace): ( PlonkStructure, FoldablePlonkTrace, ) = { @@ -111,16 +107,16 @@ where ) }; - let (primary_plonk_structure, initial_primary_trace) = { + let (primary_S, primary_initial_trace) = { let mut mock_sfc = StepFoldingCircuit:: { - sc: primary_sfc, + sc: primary_sc, input: sfc::Input::::new_initial::( &PlonkStructure { k: k_table_size as usize, ..Default::default() }, - &support_plonk_structure, - &initial_support_trace.u, + &support_S, + &support_initial_trace.u, ), _p: PhantomData, }; @@ -134,8 +130,8 @@ where num_io: mock_instances.iter().map(|col| col.len()).collect(), ..Default::default() }, - &support_plonk_structure, - &initial_support_trace.u, + &support_S, + &support_initial_trace.u, ); let mock_S = CircuitRunner::new(k_table_size, mock_sfc, mock_instances) @@ -143,16 +139,15 @@ where .unwrap(); let sfc = StepFoldingCircuit:: { - sc: primary_sfc, + sc: primary_sc, input: sfc::Input::::new_initial::( &mock_S, - &support_plonk_structure, - &initial_support_trace.u, + &support_S, + &support_initial_trace.u, ), _p: PhantomData, }; - // TODO #369 Use expected out marker, instead of zero let primary_instances = sfc.initial_instances(); let primary_cr = CircuitRunner::new(k_table_size, sfc, primary_instances.clone()); @@ -173,17 +168,33 @@ where }; Self { - ck1, - ck2, + primary_ck: ck1, + support_ck: ck2, primary_k_table_size: k_table_size, - initial_primary_trace, - initial_secondary_trace: initial_support_trace, + primary_initial_trace, + support_initial_trace, - primary_S: primary_plonk_structure, - secondary_S: support_plonk_structure, + primary_S, + support_S, _p: PhantomData, } } + + pub fn cmain_pp_digest(&self) -> CMain { + todo!() + } + + pub fn csup_pp_digest(&self) -> CSup { + todo!() + } + + pub fn cmain_pp_digest_coordinates(&self) -> (CMain::Scalar, CMain::Scalar) { + todo!() + } + + pub fn csup_pp_digest_coordinates(&self) -> (CMain::Base, CMain::Base) { + todo!() + } } diff --git a/src/ivc/cyclefold/mod.rs b/src/ivc/cyclefold/mod.rs index 27ce2382..b605a00a 100644 --- a/src/ivc/cyclefold/mod.rs +++ b/src/ivc/cyclefold/mod.rs @@ -20,11 +20,11 @@ pub const RATE: usize = T - 1; pub const R_F: usize = 10; pub const R_P: usize = 10; -/// Safety: because 32 != 0 -pub const DEFAULT_LIMB_WIDTH: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(32) }; +/// Safety: because 64 != 0 +pub const DEFAULT_LIMB_WIDTH: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(64) }; /// Safety: because 10 != 0 -pub const DEFAULT_LIMBS_COUNT_LIMIT: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(10) }; +pub const DEFAULT_LIMBS_COUNT_LIMIT: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(20) }; pub fn ro_const>() -> Spec { Spec::::new(R_F, R_P) diff --git a/src/ivc/cyclefold/sfc/input/assigned.rs b/src/ivc/cyclefold/sfc/input/assigned.rs index e1ef8ec0..9b990c4d 100644 --- a/src/ivc/cyclefold/sfc/input/assigned.rs +++ b/src/ivc/cyclefold/sfc/input/assigned.rs @@ -1,6 +1,9 @@ use std::iter; -use halo2_proofs::halo2curves::ff::{FromUniformBytes, PrimeField, PrimeFieldBits}; +use halo2_proofs::{ + circuit::Value, + halo2curves::ff::{FromUniformBytes, PrimeField, PrimeFieldBits}, +}; use itertools::Itertools; use tracing::error; @@ -52,6 +55,8 @@ impl NativePlonkInstance { let challenges = assigner.assign_all_advice(region, || "challenges", challenges.iter().cloned())?; + region.next(); + Ok(Self { W_commitments, instances, @@ -73,13 +78,17 @@ impl ProtoGalaxyAccumulatorInstance { let ins = NativePlonkInstance::assign_advice_from_native(region, ins, main_gate_config)?; let mut assigner = main_gate_config.advice_cycle_assigner(); - Ok(Self { + let self_ = Self { ins, betas: assigner .assign_all_advice(region, || "betas", betas.iter().cloned())? .into_boxed_slice(), e: assigner.assign_next_advice(region, || "e", *e)?, - }) + }; + + region.next(); + + Ok(self_) } pub fn conditional_select( @@ -125,14 +134,18 @@ impl ProtogalaxyProof { let mut assigner = main_gate_config.advice_cycle_assigner(); let super::nifs::protogalaxy::Proof { poly_F, poly_K } = original; - Ok(Self { + let self_ = Self { poly_F: assigner .assign_all_advice(region, || "poly_F", poly_F.iter().cloned())? .into(), poly_K: assigner .assign_all_advice(region, || "poly_K", poly_K.iter().cloned())? .into(), - }) + }; + + region.next(); + + Ok(self_) } fn iter_wrap_values(&self) -> impl '_ + Iterator> { @@ -251,6 +264,8 @@ impl PairedPlonkInstance { }) .collect::, Halo2PlonkError>>()?; + region.next(); + Ok(Self { W_commitments, instances, @@ -360,7 +375,7 @@ impl SangriaAccumulatorInstance { let mut assigner = main_gate_config.advice_cycle_assigner(); - Ok(Self { + let self_ = Self { ins, E_commitment: ( assigner.assign_next_advice( @@ -375,7 +390,11 @@ impl SangriaAccumulatorInstance { )?, ), u: assigner.assign_next_advice(region, || "u", original.u)?, - }) + }; + + region.next(); + + Ok(self_) } pub fn conditional_select( @@ -430,12 +449,55 @@ impl SangriaAccumulatorInstance { pub type SangriaCrossTermCommits = Vec<(AssignedValue, AssignedValue)>; +pub struct PairedIncoming { + pub instance: PairedPlonkInstance, + pub proof: SangriaCrossTermCommits, +} + +impl PairedIncoming { + fn assign_advice_from( + region: &mut RegionCtx<'_, F>, + original: &super::PairedIncoming, + main_gate_config: &MainGateConfig, + ) -> Result { + let super::PairedIncoming { instance, proof } = original; + + let instance = PairedPlonkInstance::assign_advice_from(region, instance, main_gate_config)?; + + let mut assigner = main_gate_config.advice_cycle_assigner(); + + let proof = proof + .iter() + .copied() + .map(|(commit_x, commit_y)| -> Result<_, Halo2PlonkError> { + Ok(( + assigner.assign_next_advice(region, || "commit.x", commit_x)?, + assigner.assign_next_advice(region, || "commit.y", commit_y)?, + )) + }) + .collect::, _>>()?; + + region.next(); + + Ok(Self { instance, proof }) + } + + pub fn iter_wrap_values(&self) -> impl '_ + Iterator> { + let Self { instance, proof } = self; + instance + .iter_wrap_values() + .chain(proof.iter().flat_map(|(a, b)| { + iter::once(WrapValue::Assigned(a.clone())) + .chain(std::iter::once(WrapValue::Assigned(b.clone()))) + })) + } +} + pub struct PairedTrace { pub input_accumulator: SangriaAccumulatorInstance, // The size from one to three // Depdend on `W_commitments_len` - pub incoming: Box<[PairedPlonkInstance]>, - pub proof: SangriaCrossTermCommits, + pub incoming: Box<[PairedIncoming]>, } impl PairedTrace { @@ -454,32 +516,14 @@ impl PairedTrace { .incoming .iter() .map(|paired_plonk_instance| { - PairedPlonkInstance::assign_advice_from( - region, - paired_plonk_instance, - main_gate_config, - ) + PairedIncoming::assign_advice_from(region, paired_plonk_instance, main_gate_config) }) .collect::, Halo2PlonkError>>()? .into_boxed_slice(); - let mut assigner = main_gate_config.advice_cycle_assigner(); - - let proof = original - .proof - .iter() - .map(|(a, b)| { - Ok(( - assigner.assign_next_advice(region, || "proof_a", *a)?, - assigner.assign_next_advice(region, || "proof_b", *b)?, - )) - }) - .collect::, Halo2PlonkError>>()?; - Ok(Self { input_accumulator, incoming, - proof, }) } @@ -487,33 +531,13 @@ impl PairedTrace { let Self { input_accumulator, incoming, - proof, } = self; - input_accumulator - .iter_wrap_values() - .chain( - incoming - .iter() - .flat_map(|instance| instance.iter_wrap_values()), - ) - .chain(proof.iter().flat_map(|(a, b)| { - iter::once(WrapValue::Assigned(a.clone())) - .chain(std::iter::once(WrapValue::Assigned(b.clone()))) - })) - } - - pub fn get_self_W_commitment_from_paired(&self) -> Vec>> { - self.incoming - .iter() - .map(|tr| { - let (W, _other) = tr.instances[0].split_at(2); - BigUintPoint { - x: W[0].clone(), - y: W[1].clone(), - } - }) - .collect::>() + input_accumulator.iter_wrap_values().chain( + incoming + .iter() + .flat_map(|instance| instance.iter_wrap_values()), + ) } } @@ -555,6 +579,8 @@ impl Input { let z_i = assigner.assign_all_advice(region, || "z_i", original.z_i.iter().cloned())?; + region.next(); + Ok(Self { pp_digest: (pp_digest_0, pp_digest_1), self_trace, @@ -633,15 +659,23 @@ impl Input { DEFAULT_LIMBS_COUNT_LIMIT, ); + let mg = MainGate::new(main_gate_config.clone()); + let is_zero_term = mg.is_zero_term(region, self.step.clone())?; + + let zero = region.assign_advice(|| "", main_gate_config.state[0], Value::known(F::ZERO))?; + region.next(); + + let expected_l0 = mg.conditional_select(region, &zero, &poly_L_values[0], &is_zero_term)?; let expected_l0 = bn_chip - .from_assigned_cell_to_limbs(region, &poly_L_values[0]) + .from_assigned_cell_to_limbs(region, &expected_l0) .map_err(|err| { error!("while make from L0 biguint form: {err:?}"); Halo2PlonkError::Synthesis })?; + let expected_l1 = mg.conditional_select(region, &zero, &poly_L_values[1], &is_zero_term)?; let expected_l1 = bn_chip - .from_assigned_cell_to_limbs(region, &poly_L_values[1]) + .from_assigned_cell_to_limbs(region, &expected_l1) .map_err(|err| { error!("while make from L1 biguint form: {err:?}"); Halo2PlonkError::Synthesis @@ -653,8 +687,13 @@ impl Input { self.paired_trace.incoming.iter(), new_acc.ins.W_commitments.iter_mut(), )) { - let [expected_x, expected_y, x0, y0, l0, x1, y1, l1] = - trace.instances[0].clone().try_into().unwrap(); + let [expected_x, expected_y, x0, y0, l0, x1, y1, l1] = trace.instance + .instances + .first() + .expect("`SupportCircuit` always has instances.len() == 1 and it should always be used for sfc") + .clone() + .try_into() + .unwrap(); l0.iter() .zip_eq(expected_l0.iter()) diff --git a/src/ivc/cyclefold/sfc/input/mod.rs b/src/ivc/cyclefold/sfc/input/mod.rs index f217f7a8..809a4c57 100644 --- a/src/ivc/cyclefold/sfc/input/mod.rs +++ b/src/ivc/cyclefold/sfc/input/mod.rs @@ -1,4 +1,4 @@ -use std::array; +use std::{array, ops::Deref}; use super::super::{DEFAULT_LIMBS_COUNT_LIMIT, DEFAULT_LIMB_WIDTH}; pub use crate::ivc::protogalaxy::verify_chip::BigUintPoint; @@ -8,6 +8,7 @@ use crate::{ nifs, plonk, polynomial::univariate::UnivariatePoly, poseidon::{AbsorbInRO, ROTrait}, + util, }; pub mod assigned; @@ -19,6 +20,25 @@ pub struct NativePlonkInstance { pub(crate) challenges: Vec, } +impl NativePlonkInstance { + pub fn new>(acc: &plonk::PlonkInstance) -> Self { + let plonk::PlonkInstance { + W_commitments, + instances, + challenges, + } = acc; + + Self { + W_commitments: W_commitments + .iter() + .map(|commitment| BigUintPoint::new(commitment).unwrap()) + .collect(), + instances: instances.to_vec(), + challenges: challenges.to_vec(), + } + } +} + impl> AbsorbInRO for NativePlonkInstance { fn absorb_into(&self, ro: &mut RO) { let Self { @@ -39,6 +59,54 @@ pub struct PairedPlonkInstance { pub(crate) challenges: Vec>, } +impl PairedPlonkInstance { + pub fn new>( + acc: &nifs::sangria::FoldablePlonkInstance, + ) -> Self { + let nifs::sangria::PlonkInstance { + W_commitments, + instances, + challenges, + } = acc.deref(); + Self { + W_commitments: W_commitments + .iter() + .map(|commit| { + let c = commit.coordinates().unwrap(); + (*c.x(), *c.y()) + }) + .collect(), + instances: instances + .iter() + .map(|instance| { + instance + .iter() + .map(|value| { + BigUint::from_different_field( + value, + DEFAULT_LIMB_WIDTH, + DEFAULT_LIMBS_COUNT_LIMIT, + ) + .unwrap() + }) + .collect() + }) + .collect(), + challenges: challenges + .iter() + .map(|cha| { + BigUint::from_different_field( + cha, + DEFAULT_LIMB_WIDTH, + DEFAULT_LIMBS_COUNT_LIMIT, + ) + .unwrap() + }) + .collect(), + } + } +} + impl> AbsorbInRO for PairedPlonkInstance { fn absorb_into(&self, ro: &mut RO) { let Self { @@ -75,6 +143,22 @@ pub struct ProtoGalaxyAccumulatorInstance { pub(crate) e: F, } +impl ProtoGalaxyAccumulatorInstance { + pub fn new>( + acc: &nifs::protogalaxy::AccumulatorInstance, + ) -> Self { + let nifs::protogalaxy::AccumulatorInstance { ins, betas, e } = acc; + + let ins = NativePlonkInstance::new(ins); + + Self { + ins, + betas: betas.clone(), + e: *e, + } + } +} + impl> AbsorbInRO for ProtoGalaxyAccumulatorInstance { fn absorb_into(&self, ro: &mut RO) { let Self { ins, betas, e } = self; @@ -160,6 +244,61 @@ pub struct SangriaAccumulatorInstance { pub(crate) u: F, } +impl SangriaAccumulatorInstance { + pub fn new>( + acc: &nifs::sangria::RelaxedPlonkInstance, + ) -> Self { + let nifs::sangria::RelaxedPlonkInstance { + W_commitments, + consistency_markers, + challenges, + E_commitment, + u, + step_circuit_instances_hash_accumulator: _, + } = acc; + + Self { + ins: PairedPlonkInstance { + W_commitments: W_commitments + .iter() + .map(|commitment| { + let c = commitment.coordinates().unwrap(); + (*c.x(), *c.y()) + }) + .collect(), + // TODO #369 Make markers + instances: vec![consistency_markers + .iter() + .map(|instance| { + BigUint::from_different_field( + instance, + DEFAULT_LIMB_WIDTH, + DEFAULT_LIMBS_COUNT_LIMIT, + ) + .unwrap() + }) + .collect()], + challenges: challenges + .iter() + .map(|cha| { + BigUint::from_different_field( + cha, + DEFAULT_LIMB_WIDTH, + DEFAULT_LIMBS_COUNT_LIMIT, + ) + .unwrap() + }) + .collect(), + }, + E_commitment: { + let c = E_commitment.coordinates().unwrap(); + (*c.x(), *c.y()) + }, + u: util::fe_to_fe(u).unwrap(), + } + } +} + impl> AbsorbInRO for SangriaAccumulatorInstance { fn absorb_into(&self, ro: &mut RO) { let Self { @@ -175,13 +314,45 @@ impl> AbsorbInRO for SangriaAccumulatorInst } } +#[derive(Debug, Clone)] +pub struct PairedIncoming { + instance: PairedPlonkInstance, + proof: nifs::sangria::CrossTermCommits<(F, F)>, +} + +impl PairedIncoming { + pub fn new>( + instance: &nifs::sangria::FoldablePlonkInstance, + proof: &nifs::sangria::CrossTermCommits, + ) -> Self { + let proof = proof + .iter() + .map(|commit| { + let c = commit.coordinates().unwrap(); + (*c.x(), *c.y()) + }) + .collect::>(); + let instance = PairedPlonkInstance::new(instance); + + Self { instance, proof } + } +} + +impl> AbsorbInRO for PairedIncoming { + fn absorb_into(&self, ro: &mut RO) { + let Self { instance, proof } = self; + let proof_iter = proof.iter().flat_map(|(a, b)| [a, b]).copied(); + + ro.absorb(instance).absorb_field_iter(proof_iter); + } +} + #[derive(Debug, Clone)] pub struct PairedTrace { pub input_accumulator: SangriaAccumulatorInstance, // The size from one to three // Depdend on `W_commitments_len` - pub incoming: Box<[PairedPlonkInstance]>, - proof: nifs::sangria::CrossTermCommits<(F, F)>, + pub incoming: Box<[PairedIncoming]>, } impl> AbsorbInRO for PairedTrace { @@ -189,14 +360,9 @@ impl> AbsorbInRO for PairedTrace { let Self { input_accumulator, incoming, - proof, } = self; - let proof_iter = proof.iter().flat_map(|(a, b)| [a, b]).copied(); - - ro.absorb(input_accumulator) - .absorb_iter(incoming.iter()) - .absorb_field_iter(proof_iter); + ro.absorb(input_accumulator).absorb_iter(incoming.iter()); } } @@ -245,19 +411,22 @@ impl PairedTrace { .collect(), }; - Self { - input_accumulator: SangriaAccumulatorInstance { - ins: ins.clone(), - E_commitment: (F::ZERO, F::ZERO), - u: F::ZERO, - }, - incoming: vec![ins.clone(); W_commitments_len].into_boxed_slice(), + let pairing = PairedIncoming { + instance: ins.clone(), proof: vec![ (F::ZERO, F::ZERO); paired_plonk_structure .get_degree_for_folding() .saturating_sub(1) ], + }; + Self { + input_accumulator: SangriaAccumulatorInstance { + ins: ins.clone(), + E_commitment: (F::ZERO, F::ZERO), + u: F::ZERO, + }, + incoming: vec![pairing; W_commitments_len].into_boxed_slice(), } } } @@ -295,8 +464,8 @@ impl Input { // Helper closure to create a random BigUint fn random_big_uint(gen: &mut impl Iterator) -> BigUint { - BigUint::from_limbs( - gen.by_ref().take(DEFAULT_LIMBS_COUNT_LIMIT.get()), + BigUint::from_f( + &gen.next().unwrap(), DEFAULT_LIMB_WIDTH, DEFAULT_LIMBS_COUNT_LIMIT, ) @@ -349,25 +518,24 @@ impl Input { input_accumulator: SangriaAccumulatorInstance { ins: PairedPlonkInstance { W_commitments: vec![(gen.next().unwrap(), gen.next().unwrap()); 1], - instances: vec![ - vec![random_big_uint(&mut gen); 10]; // 5 instances each with 10 BigUint - 1 - ], + instances: vec![vec![random_big_uint(&mut gen); 8]; 1], challenges: vec![random_big_uint(&mut gen); 1], }, E_commitment: (gen.next().unwrap(), gen.next().unwrap()), u: gen.next().unwrap(), }, incoming: vec![ - PairedPlonkInstance { - W_commitments: vec![(gen.next().unwrap(), gen.next().unwrap()); 1], - instances: vec![vec![random_big_uint(&mut gen); 1]; 2], - challenges: vec![random_big_uint(&mut gen); 2], + PairedIncoming { + instance: PairedPlonkInstance { + W_commitments: vec![(gen.next().unwrap(), gen.next().unwrap()); 1], + instances: vec![vec![random_big_uint(&mut gen); 8]; 1], + challenges: vec![random_big_uint(&mut gen); 1], + }, + proof: vec![(gen.next().unwrap(), gen.next().unwrap()); 1], }; 1 ] .into_boxed_slice(), - proof: vec![(gen.next().unwrap(), gen.next().unwrap()); 1], }; // Initialize `z_0` and `z_i` arrays with random field elements. @@ -408,7 +576,121 @@ impl> AbsorbInRO for In impl Input { pub(super) fn get_without_witness(&self) -> Self { - todo!() + // Zero out `pp_digest`. + let pp_digest = (F::ZERO, F::ZERO); + + // Zero out `self_trace`. + let self_trace = SelfTrace { + input_accumulator: ProtoGalaxyAccumulatorInstance { + ins: NativePlonkInstance { + W_commitments: vec![ + BigUintPoint::identity(); + self.self_trace.input_accumulator.ins.W_commitments.len() + ], + instances: self + .self_trace + .input_accumulator + .ins + .instances + .iter() + .map(|v| vec![F::ZERO; v.len()]) + .collect(), + challenges: vec![ + F::ZERO; + self.self_trace.input_accumulator.ins.challenges.len() + ], + }, + betas: vec![F::ZERO; self.self_trace.input_accumulator.betas.len()] + .into_boxed_slice(), + e: F::ZERO, + }, + incoming: NativePlonkInstance { + W_commitments: vec![ + BigUintPoint::identity(); + self.self_trace.incoming.W_commitments.len() + ], + instances: self + .self_trace + .incoming + .instances + .iter() + .map(|v| vec![F::ZERO; v.len()]) + .collect(), + challenges: vec![F::ZERO; self.self_trace.incoming.challenges.len()], + }, + proof: nifs::protogalaxy::Proof { + poly_F: UnivariatePoly::new_zeroed(self.self_trace.proof.poly_F.len()), + poly_K: UnivariatePoly::new_zeroed(self.self_trace.proof.poly_K.len()), + }, + }; + + // Zero out `paired_trace`. + let paired_trace = PairedTrace { + input_accumulator: SangriaAccumulatorInstance { + ins: PairedPlonkInstance { + W_commitments: vec![ + (F::ZERO, F::ZERO); + self.paired_trace.input_accumulator.ins.W_commitments.len() + ], + instances: self + .paired_trace + .input_accumulator + .ins + .instances + .iter() + .map(|v| vec![BigUint::zero(DEFAULT_LIMB_WIDTH); v.len()]) + .collect(), + challenges: vec![ + BigUint::zero(DEFAULT_LIMB_WIDTH); + self.paired_trace.input_accumulator.ins.challenges.len() + ], + }, + E_commitment: (F::ZERO, F::ZERO), + u: F::ZERO, + }, + incoming: self + .paired_trace + .incoming + .iter() + .map(|incoming| PairedIncoming { + instance: PairedPlonkInstance { + W_commitments: vec![ + (F::ZERO, F::ZERO); + incoming.instance.W_commitments.len() + ], + instances: incoming + .instance + .instances + .iter() + .map(|v| vec![BigUint::zero(DEFAULT_LIMB_WIDTH); v.len()]) + .collect(), + challenges: vec![ + BigUint::zero(DEFAULT_LIMB_WIDTH); + incoming.instance.challenges.len() + ], + }, + proof: vec![(F::ZERO, F::ZERO); incoming.proof.len()], + }) + .collect::>() + .into_boxed_slice(), + }; + + // Zero out `step`. + let step = 0; + + // Zero out `z_0` and `z_i`. + let z_0 = array::from_fn(|_| F::ZERO); + let z_i = array::from_fn(|_| F::ZERO); + + // Construct the new zeroed Input instance. + Self { + pp_digest, + self_trace, + paired_trace, + step, + z_0, + z_i, + } } /// This method creates an input to initialize an empty accumulators and incoming traces of the @@ -433,14 +715,67 @@ impl Input { z_i: array::from_fn(|_| F::ZERO), } } +} - pub fn new, CSup: CurveAffine>( - _pp_digest: CSup, - _native_acc: &nifs::protogalaxy::AccumulatorInstance, - _native_incoming: &plonk::PlonkInstance, - _paired_acc: &nifs::sangria::FoldablePlonkInstance, - _paired_incoming: &nifs::sangria::FoldablePlonkInstance, - ) -> Self { - todo!() +pub struct InputBuilder< + 'link, + CMain: CurveAffine, + CSup: CurveAffine, + const ARITY: usize, +> { + pub pp_digest: CSup, + pub step: usize, + + pub self_acc: &'link nifs::protogalaxy::AccumulatorInstance, + pub self_incoming: &'link plonk::PlonkInstance, + pub self_proof: nifs::protogalaxy::Proof, + + pub paired_acc: &'link nifs::sangria::RelaxedPlonkInstance, + pub paired_incoming: &'link [( + nifs::sangria::FoldablePlonkInstance, + nifs::sangria::CrossTermCommits, + )], + + pub z_0: [CMain::Scalar; ARITY], + pub z_i: [CMain::Scalar; ARITY], +} + +impl, CSup: CurveAffine, const ARITY: usize> + InputBuilder<'_, CMain, CSup, ARITY> +{ + pub fn build(self) -> Input { + let Self { + pp_digest, + step, + self_acc, + self_incoming, + self_proof, + paired_acc, + paired_incoming, + z_0, + z_i, + } = self; + + Input { + pp_digest: { + let c = pp_digest.coordinates().unwrap(); + (*c.x(), *c.y()) + }, + step, + z_0, + z_i, + self_trace: SelfTrace { + input_accumulator: ProtoGalaxyAccumulatorInstance::new(self_acc), + incoming: NativePlonkInstance::new(self_incoming), + proof: self_proof, + }, + paired_trace: PairedTrace { + input_accumulator: SangriaAccumulatorInstance::new(paired_acc), + incoming: paired_incoming + .iter() + .map(|(instance, proof)| PairedIncoming::new(instance, proof)) + .collect(), + }, + } } } diff --git a/src/ivc/cyclefold/sfc/mod.rs b/src/ivc/cyclefold/sfc/mod.rs index 7856b2e5..cda95292 100644 --- a/src/ivc/cyclefold/sfc/mod.rs +++ b/src/ivc/cyclefold/sfc/mod.rs @@ -23,7 +23,7 @@ use crate::{ }; mod input; -pub use input::Input; +pub use input::{Input, InputBuilder}; pub mod sangria_adapter; @@ -146,8 +146,8 @@ where let mut region = RegionCtx::new(region, 0); let VerifyResult { - mut result_acc, - poly_L_values, + result_acc, + poly_L_values: _, } = protogalaxy::verify_chip::verify( &mut region, config.mg.clone(), @@ -164,30 +164,34 @@ where Halo2PlonkError::Synthesis })?; - input.pairing_check( - &mut region, - &config.mg, - &poly_L_values, - &mut result_acc, - )?; + //input.pairing_check( + // &mut region, + // &config.mg, + // &poly_L_values, + // &mut result_acc, + //)?; Ok(result_acc) }, )?; 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, - ) - }, - )?; - - let consistency_marker_output = layouter.assign_region( + layouter + .assign_region( + || "sfc sangria", + |region| { + sangria_adapter::fold::( + &mut RegionCtx::new(region, 0), + config.mg.clone(), + &input.paired_trace, + ) + }, + ) + .inspect_err(|err| { + error!("while sfc sangria: {err:?}"); + })?; + + let _consistency_marker_output = layouter.assign_region( || "sfc out", |region| { let mut region = RegionCtx::new(region, 0); @@ -240,12 +244,50 @@ where }, )?; - layouter.constrain_instance( - consistency_marker_output.cell(), - config.consistency_marker, - 0, - )?; + //layouter.constrain_instance( + // consistency_marker_output.cell(), + // config.consistency_marker, + // 0, + //)?; Ok(()) } } + +#[cfg(test)] +mod tests { + use tracing_test::traced_test; + + use super::*; + use crate::{halo2_proofs::dev::MockProver, ivc::step_circuit::trivial, prelude::bn256}; + + type CMain = bn256::C1Affine; + type CSup = bn256::C2Affine; + + type Base = ::Base; + type Scalar = ::ScalarExt; + + const ARITY: usize = 2; + + #[traced_test] + #[test] + fn e2e_zero_step() { + let mut input = Input::<2, Scalar>::random(&mut rand::thread_rng()); + input.step = 0; + + let sc = trivial::Circuit::default(); + + let sfc = StepFoldingCircuit::> { + sc: &sc, + input, + _p: PhantomData, + }; + + let instances = sfc.initial_instances(); + + MockProver::run(17, &sfc, instances) + .unwrap() + .verify() + .unwrap(); + } +} diff --git a/src/ivc/cyclefold/sfc/sangria_adapter.rs b/src/ivc/cyclefold/sfc/sangria_adapter.rs index b2851094..c3748ac9 100644 --- a/src/ivc/cyclefold/sfc/sangria_adapter.rs +++ b/src/ivc/cyclefold/sfc/sangria_adapter.rs @@ -1,23 +1,27 @@ -use halo2_proofs::halo2curves::{ - ff::{FromUniformBytes, PrimeField, PrimeFieldBits}, - CurveAffine, -}; use itertools::Itertools; use num_traits::Num; +use tracing::error; use super::{input, MAIN_GATE_T}; use crate::{ - constants::NUM_CHALLENGE_BITS, + constants::MAX_BITS, gadgets::{ ecc::{AssignedPoint, EccChip}, nonnative::{self, bn::big_uint_mul_mod_chip::BigUintMulModChip}, }, - halo2_proofs::plonk::Error as Halo2PlonkError, + halo2_proofs::{ + halo2curves::{ + ff::{FromUniformBytes, PrimeField, PrimeFieldBits}, + CurveAffine, + }, + plonk::Error as Halo2PlonkError, + }, ivc::{ - cyclefold::{ro_chip, support_circuit, DEFAULT_LIMBS_COUNT_LIMIT, DEFAULT_LIMB_WIDTH}, + cyclefold::{ro_chip, DEFAULT_LIMBS_COUNT_LIMIT, DEFAULT_LIMB_WIDTH}, fold_relaxed_plonk_instance_chip::{self, BigUintView, FoldRelaxedPlonkInstanceChip}, }, main_gate::{MainGate, MainGateConfig, RegionCtx}, + nifs::sangria, poseidon::ROCircuitTrait, }; @@ -64,17 +68,21 @@ where 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_bits = mg.le_num_to_bits(region, r.clone(), MAX_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, + for input::assigned::PairedIncoming { + instance: + input::assigned::PairedPlonkInstance { + W_commitments: input_W_commitments, + challenges: input_challenges, + instances: input_instances, + }, + proof, } in input.incoming.iter() { let input::assigned::SangriaAccumulatorInstance { @@ -91,7 +99,7 @@ where *acc_W_commitments = FoldRelaxedPlonkInstanceChip::< MAIN_GATE_T, CSup, - { support_circuit::INSTANCES_LEN }, + { sangria::CONSISTENCY_MARKERS_COUNT }, >::fold_W( region, &config, @@ -106,7 +114,10 @@ where .map(|(x, y)| AssignedPoint { x, y }) .collect::>(), &r_bits, - )? + ) + .inspect_err(|err| { + error!("while fold W: {err:?}"); + })? .into_iter() .map(|p| (p.x, p.y)) .collect(); @@ -115,43 +126,50 @@ where .iter_mut() .zip_eq(input_instances) .try_for_each( - |(acc_instances, input_instances)| -> Result<(), Halo2PlonkError> { - acc_instances + |(acc_instance, input_instance)| -> Result<(), Halo2PlonkError> { + acc_instance .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(()) - }, - )?; + .zip_eq(input_instance) + .try_for_each(|(acc_cell, input_cell)| -> Result<(), Halo2PlonkError> { + *acc_cell = fold_relaxed_plonk_instance_chip::fold_via_biguint( + region, + &bn_chip, + input_cell, + acc_cell.to_vec(), + &m_bn, + &r_as_bn, + DEFAULT_LIMB_WIDTH, + )?; + + Ok(()) + })?; Ok(()) }, - )?; + ) + .inspect_err(|err| { + error!("while fold instances: {err:?}"); + })?; - *acc_challenges = FoldRelaxedPlonkInstanceChip::< - MAIN_GATE_T, - CSup, - { support_circuit::INSTANCES_LEN }, - >::fold_challenges( - region, - &bn_chip, - input_challenges.to_vec(), - acc_challenges.to_vec(), - &r_as_bn, - &m_bn, - DEFAULT_LIMB_WIDTH, - )?; + acc_challenges + .iter_mut() + .zip_eq(input_challenges.iter()) + .try_for_each(|(acc_cha, inp_cha)| -> Result<(), Halo2PlonkError> { + *acc_cha = fold_relaxed_plonk_instance_chip::fold_via_biguint( + region, + &bn_chip, + inp_cha, + acc_cha.to_vec(), + &m_bn, + &r_as_bn, + DEFAULT_LIMB_WIDTH, + )?; + + Ok(()) + }) + .inspect_err(|err| { + error!("while fold challenges: {err:?}"); + })?; *acc_E_commitment = fold_relaxed_plonk_instance_chip::fold_E( region, @@ -161,8 +179,7 @@ where x: acc_E_commitment.0.clone(), y: acc_E_commitment.1.clone(), }, - &input - .proof + &proof .iter() .cloned() .map(|(x, y)| AssignedPoint { x, y }) @@ -172,7 +189,10 @@ where as_bits: r_bits.clone(), }, &m_bn, - )? + ) + .inspect_err(|err| { + error!("while fold E: {err:?}"); + })? .into(); *acc_u = mg.add(region, acc_u, &r)?; diff --git a/src/ivc/protogalaxy/mod.rs b/src/ivc/protogalaxy/mod.rs index d5e0c674..bcf89999 100644 --- a/src/ivc/protogalaxy/mod.rs +++ b/src/ivc/protogalaxy/mod.rs @@ -160,10 +160,14 @@ pub mod verify_chip { ) -> Result>, Halo2PlonkError> { let mut assigner = main_gate_config.advice_cycle_assigner(); - Ok(BigUintPoint { + let p = BigUintPoint { x: assigner.assign_all_advice(region, || "x", self.x.into_iter())?, y: assigner.assign_all_advice(region, || "y", self.y.into_iter())?, - }) + }; + + region.next(); + + Ok(p) } } @@ -502,11 +506,11 @@ pub mod verify_chip { let prev_col = &main_gate_config.input; let result_col = &main_gate_config.out; - challenge_powers.get_or_eval(region, main_gate, self.len().saturating_sub(1))?; + challenge_powers.get_or_eval(region, main_gate, self.len().saturating_add(1))?; self.0 .iter() - .zip_eq(challenge_powers.iter()) + .zip_eq(challenge_powers.iter().take(self.0.len())) .chunks(2) .into_iter() .try_fold(Option::>::None, |prev, chunks| { @@ -526,13 +530,13 @@ pub mod verify_chip { let assigned_coeffs = coeffs .iter() - .zip_eq(coeffs_col) + .zip(coeffs_col) .map(|(coeff, col)| region.assign_advice_from(|| "coeff", col, *coeff)) .collect::, _>>()?; let assigned_cha = cha_in_power .iter() - .zip_eq(cha_col) + .zip(cha_col) .map(|(cha_in_power, col)| { region.assign_advice_from(|| "cha", col, *cha_in_power) }) @@ -540,7 +544,7 @@ pub mod verify_chip { let output = assigned_coeffs .iter() - .zip_eq(assigned_cha.iter()) + .zip(assigned_cha.iter()) .fold(assigned_prev.value().copied(), |res, (coeff, cha)| { res + (coeff.value().copied() * cha.value()) }); @@ -730,7 +734,8 @@ pub mod verify_chip { calculate_exponentiation_sequence(region, main_gate, cha.delta, cha.betas.len()) .map_err(|err| Error::Deltas { err })?; - cha.betas + let bs = cha + .betas .iter() .zip_eq(deltas) .map(|(beta, delta_power)| { @@ -738,7 +743,11 @@ pub mod verify_chip { main_gate.add(region, beta, &alpha_mul_delta) }) .collect::, Halo2PlonkError>>() - .map_err(|err| Error::BetasStroke { err }) + .map_err(|err| Error::BetasStroke { err })?; + + region.next(); + + Ok(bs) } /// Evaluate the values of the Lagrange polynomial for a cyclic subgroup of length `n` (`2.pow(log_n)`) at @@ -777,6 +786,7 @@ pub mod verify_chip { let X = cha.value(); + region.next(); let X_sub_value = main_gate.add_with_const(region, &X, -value)?; let (is_zero_X_sub_value, X_sub_value_inverted) = @@ -1019,11 +1029,10 @@ pub mod verify_chip { ) .map_err(|err| Error::WhileE { err })?; - let poly_L_values = (0..) + let poly_L_values = (0..(L + 1)) .map(|index| { eval_lagrange_poly::(region, &main_gate, index, &mut gamma_powers) }) - .take(L + 1) .collect::, _>>() .map_err(|err| Error::Fold { err })?; @@ -1371,7 +1380,6 @@ pub mod verify_chip { ) -> Result<(), Halo2PlonkError> { let cha = Fr::from_u128(123); - dbg!(::S); let lagrange_domain = PolyContext::::get_lagrange_domain::(); debug!("lagrange_domain: {lagrange_domain}");