use ark_bn254::{Bn254, Fr, G1Projective as G1}; use ark_crypto_primitives::sponge::poseidon::PoseidonConfig; use ark_grumpkin::Projective as G2; use experimental_frontends::{circom::CircomFCircuit, utils::VecF}; use sonobe::{ commitment::{kzg::KZG, pedersen::Pedersen}, folding::{hypernova::HyperNova, nova::Nova}, transcript::poseidon::poseidon_canonical_config, Error, FoldingScheme, MultiFolding, }; use tracing::info_span; use crate::circuit::STEP_INPUT_WIDTH; pub type NovaFolding = Nova, KZG<'static, Bn254>, Pedersen, false>; pub type HyperNovaFolding = HyperNova< G1, G2, CircomFCircuit, KZG<'static, Bn254>, Pedersen, M, N, false, >; pub struct StepInput { pub external_inputs: VecF, pub other_instances: Option, } pub trait FoldingSchemeExt: FoldingScheme> { const MULTISTEP_SIZE: usize; fn num_steps(num_inputs: usize) -> usize { assert_eq!(num_inputs % Self::MULTISTEP_SIZE, 0); num_inputs / Self::MULTISTEP_SIZE } fn prepreprocess( poseidon_config: PoseidonConfig, circuit: CircomFCircuit, ) -> Self::PreprocessorParam; fn transform_multi_input( &self, multi_input: Vec>, initial_state: Vec, rng: &mut impl rand::RngCore, ) -> StepInput; fn prove_multistep( &mut self, multi_input: Vec>, initial_state: Vec, rng: &mut impl rand::RngCore, ) -> Result<(), Error> { let step_input = info_span!("Input prep") .in_scope(|| self.transform_multi_input(multi_input, initial_state, rng)); info_span!("Proving").in_scope(|| { self.prove_step(rng, step_input.external_inputs, step_input.other_instances) }) } } impl FoldingSchemeExt for NovaFolding { const MULTISTEP_SIZE: usize = 1; fn prepreprocess( poseidon_config: PoseidonConfig, circuit: CircomFCircuit, ) -> Self::PreprocessorParam { Self::PreprocessorParam::new(poseidon_config, circuit) } fn transform_multi_input( &self, input: Vec>, _initial_state: Vec, _rng: &mut impl rand::RngCore, ) -> StepInput { assert_eq!(input.len(), 1); StepInput { external_inputs: VecF(input[0].clone()), other_instances: None, } } } impl FoldingSchemeExt for HyperNovaFolding { const MULTISTEP_SIZE: usize = M + N - 1; fn prepreprocess( poseidon_config: PoseidonConfig, circuit: CircomFCircuit, ) -> Self::PreprocessorParam { Self::PreprocessorParam::new(poseidon_config, circuit) } fn transform_multi_input( &self, multi_input: Vec>, initial_state: Vec, rng: &mut impl rand::RngCore, ) -> StepInput { let (running, rest) = multi_input.split_at(M - 1); let (incoming, [single]) = rest.split_at(N - 1) else { panic!("Invalid input chunk size"); }; let new_running = |instance| { self.new_running_instance(&mut *rng, initial_state.clone(), VecF(instance)) .expect("Failed to create running instance") }; let new_instances = |instances: Vec>, maker| instances.into_iter().map(maker).collect(); let lcccs = new_instances(running.to_vec(), new_running); let cccs = incoming .iter() .map(|instance| { self.new_incoming_instance(&mut *rng, initial_state.clone(), VecF(instance.clone())) .expect("Failed to create incoming instance") }) .collect(); StepInput { external_inputs: VecF(single.clone()), other_instances: Some((lcccs, cccs)), } } } pub fn prepare_folding( circuit: &CircomFCircuit, start_ivc_state: Vec, rng: &mut impl rand::RngCore, ) -> (FS, FS::VerifierParam) { let preprocess_params = FS::prepreprocess(poseidon_canonical_config::(), circuit.clone()); let params = FS::preprocess(&mut *rng, &preprocess_params).expect("Failed to preprocess folding scheme"); let folding = FS::init(¶ms, circuit.clone(), start_ivc_state).expect("Failed to init folding scheme"); (folding, params.1) } pub fn verify_folding(folding: &FS, folding_vp: FS::VerifierParam) { let ivc_proof = folding.ivc_proof(); FS::verify(folding_vp, ivc_proof).expect("Failed to verify folded proof"); }