From 72770d422ae37d6389ab96197cec0e454919e813 Mon Sep 17 00:00:00 2001 From: cyphersnake Date: Wed, 8 Jan 2025 15:52:40 +0100 Subject: [PATCH] feat(nifs): support generic number of sangria c-markers **Motivation** Within the sangria folding scheme we have two consistency markers that fold via multiplication by `r`. For use within cyclefold (#373) we need 9 of them, so we make support for an arbitrary markers len. Also the second problem was the availability of an accumulator for the step-circuit instance in nifs::sangria of the columns. Without changes - it would be necessary to do hash counting for an empty set within Cyclefold IVC as part of the sagnria check of the accumulator. **Overview** The first problem was solved by adding generics to all types The second problem was solved by wrapping the accumulator.step_circuit_hash_acc in `Option`, which always zeroes for step-circuit instances, if they not present. To make it more expressive as an `Option`, the custom type `SCInstancesHashAcc` was used - Also, to cure conflicts, I temporarily removed the ivc::cyclefold implementation - Also modules related to sangria were moved to the ivc::sangria submodule. --- .../mod.rs | 109 +---------- .../public_params.rs | 4 +- src/ivc/cyclefold/sfc/mod.rs | 40 +---- src/ivc/mod.rs | 9 +- .../consistency_markers_computation.rs | 19 +- .../fold_relaxed_plonk_instance_chip.rs | 130 ++++++++------ .../incrementally_verifiable_computation.rs | 9 +- .../instances_accumulator_computation.rs | 0 src/ivc/sangria/mod.rs | 3 + src/ivc/{ => sangria}/public_params.rs | 12 +- src/ivc/sangria/step_folding_circuit.rs | 9 +- src/nifs/sangria/accumulator.rs | 170 ++++++++++++++---- src/nifs/sangria/mod.rs | 100 +++++++---- src/nifs/sangria/tests.rs | 2 +- 14 files changed, 329 insertions(+), 287 deletions(-) rename src/ivc/{ => sangria}/consistency_markers_computation.rs (94%) rename src/ivc/{ => sangria}/instances_accumulator_computation.rs (100%) rename src/ivc/{ => sangria}/public_params.rs (97%) diff --git a/src/ivc/cyclefold/incrementally_verifiable_computation/mod.rs b/src/ivc/cyclefold/incrementally_verifiable_computation/mod.rs index 265e2bfb..0d269e64 100644 --- a/src/ivc/cyclefold/incrementally_verifiable_computation/mod.rs +++ b/src/ivc/cyclefold/incrementally_verifiable_computation/mod.rs @@ -1,3 +1,5 @@ +#![allow(unused_imports)] + use std::{marker::PhantomData, num::NonZeroUsize}; use public_params::PublicParams; @@ -50,109 +52,10 @@ where CSup::Scalar: PrimeFieldBits + FromUniformBytes<64>, { pub fn new( - pp: &PublicParams, - sc: &SC, - z_0: [CMain::ScalarExt; ARITY], + _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(), - ); - - // At zero step cyclefold ivc - output protogalaxy-accumulator is input - // protogalaxy-accumulator. Bug proof still should be valid. - 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(); - - // At zero step cyclefold ivc - output sangria-accumulator is input - // sangria-accumulator. Bug proofs still should be valid. - // - // At this block we fold three same support-circuit initial traces (from pp) but result of - // this folding will be not used in next step, because of zero step - let paired_incoming = { - let mut proofs = Vec::with_capacity(initial_self_acc.W_commitment_len()); - - let mut paired_acc_ptr = nifs::sangria::accumulator::RelaxedPlonkTrace::from_regular( - pp.support_initial_trace.clone(), - SupportCircuit::::MIN_K_TABLE_SIZE as usize, - ); - - for _ in 0..initial_self_acc.W_commitment_len() { - let (new_acc, paired_proof) = - SangriaFS::::prove( - &pp.support_ck, - &nifs::sangria::ProverParam { - S: pp.support_S.clone(), - pp_digest: pp.csup_pp_digest(), - }, - &mut ro(), - paired_acc_ptr, - &[pp.support_initial_trace.clone()], - ) - .unwrap(); - - proofs.push((pp.support_initial_trace.u.clone(), paired_proof)); - - paired_acc_ptr = new_acc; - } - - proofs - }; - - let primary_sfc = StepFoldingCircuit::<'_, ARITY, CMain, CSup, SC> { - sc, - input: sfc::InputBuilder { - step: 0, - pp_digest: pp.csup_pp_digest(), - 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 primary_initial_instances = primary_sfc.initial_instances(); - let primary_witness = CircuitRunner::new( - pp.primary_k_table_size, - primary_sfc, - primary_initial_instances.clone(), - ) - .try_collect_witness() - .unwrap(); - - let primary_post_initial_trace = ProtoGalaxy::::generate_plonk_trace( - &pp.primary_ck, - &primary_initial_instances, - &primary_witness, - &pp.protogalaxy_prover_params(), - &mut ro(), - ) - .unwrap(); - - Self { - step: NonZeroUsize::new(1).unwrap(), - primary_trace: primary_post_initial_trace, - _p: PhantomData, - } + todo!("temporarily removed for the purposes of a simple merge into main") } } diff --git a/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs b/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs index f136caaf..61078769 100644 --- a/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs +++ b/src/ivc/cyclefold/incrementally_verifiable_computation/public_params.rs @@ -110,7 +110,7 @@ where &support_cr.try_collect_witness().unwrap(), &nifs::sangria::ProverParam { S: support_cr.try_collect_plonk_structure().unwrap(), - pp_digest: CSup::identity(), + pp_digest: (CSup::Base::ZERO, CSup::Base::ZERO), }, &mut ro(), ) @@ -249,7 +249,7 @@ where pub fn sangria_prover_params(&self) -> nifs::sangria::ProverParam { nifs::sangria::ProverParam { S: self.support_S.clone(), - pp_digest: self.csup_pp_digest(), + pp_digest: self.csup_pp_digest_coordinates(), } } } diff --git a/src/ivc/cyclefold/sfc/mod.rs b/src/ivc/cyclefold/sfc/mod.rs index cda95292..8f5d47e9 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, InputBuilder}; +pub use input::Input; pub mod sangria_adapter; @@ -253,41 +253,3 @@ where 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/mod.rs b/src/ivc/mod.rs index 8892a1c0..cda44ec7 100644 --- a/src/ivc/mod.rs +++ b/src/ivc/mod.rs @@ -2,17 +2,14 @@ pub mod step_circuit; pub mod sangria; pub use sangria::{ - fold_relaxed_plonk_instance_chip, incrementally_verifiable_computation, step_folding_circuit, + fold_relaxed_plonk_instance_chip, incrementally_verifiable_computation, + public_params::{CircuitPublicParamsInput, PublicParams}, + step_folding_circuit, }; pub mod protogalaxy; pub mod cyclefold; -mod consistency_markers_computation; -pub mod instances_accumulator_computation; -mod public_params; - pub use halo2_proofs::circuit::SimpleFloorPlanner; pub use incrementally_verifiable_computation::*; -pub use public_params::{CircuitPublicParamsInput, PublicParams}; diff --git a/src/ivc/consistency_markers_computation.rs b/src/ivc/sangria/consistency_markers_computation.rs similarity index 94% rename from src/ivc/consistency_markers_computation.rs rename to src/ivc/sangria/consistency_markers_computation.rs index fe105c1c..3de457aa 100644 --- a/src/ivc/consistency_markers_computation.rs +++ b/src/ivc/sangria/consistency_markers_computation.rs @@ -10,7 +10,7 @@ use crate::{ gadgets::{ecc::AssignedPoint, nonnative::bn::big_uint::BigUint}, halo2curves::CurveAffine, main_gate::{AssignedValue, MainGate, MainGateConfig, RegionCtx, WrapValue}, - nifs::sangria::accumulator::RelaxedPlonkInstance, + nifs::sangria::accumulator::{RelaxedPlonkInstance, SCInstancesHashAcc}, poseidon::{AbsorbInRO, ROCircuitTrait, ROTrait}, util::{self, ScalarToBase}, }; @@ -94,7 +94,8 @@ where pub(crate) consistency_markers: Vec>, pub(crate) challenges: Vec>, pub(crate) u: &'l C::ScalarExt, - pub(crate) step_circuit_instances_hash_accumulator: &'l C::ScalarExt, + pub(crate) step_circuit_instances_hash_accumulator: + SCInstancesHashAcc<&'l C::ScalarExt>, } impl> AbsorbInRO @@ -116,8 +117,11 @@ where .copied(), ) .absorb_field(C::scalar_to_base(self.u).unwrap()) - .absorb_field( - C::scalar_to_base(self.step_circuit_instances_hash_accumulator).unwrap(), + .absorb( + &self + .step_circuit_instances_hash_accumulator + .as_ref() + .map(|v| C::scalar_to_base(v).unwrap()), ); } } @@ -156,7 +160,8 @@ where .unwrap() }) .collect(), - step_circuit_instances_hash_accumulator, + step_circuit_instances_hash_accumulator: step_circuit_instances_hash_accumulator + .as_ref(), u, }; @@ -227,7 +232,9 @@ mod tests { challenges: vec![Scalar::from_u128(0x123456); 10], E_commitment: CommitmentKey::::default_value(), u: Scalar::from_u128(u128::MAX), - step_circuit_instances_hash_accumulator: Scalar::from_u128(0xaaaaaaaaaaaaa), + step_circuit_instances_hash_accumulator: SCInstancesHashAcc::Hash(Scalar::from_u128( + 0xaaaaaaaaaaaaa, + )), }; let off_circuit_hash: Base = ConsistencyMarkerComputation::< diff --git a/src/ivc/sangria/fold_relaxed_plonk_instance_chip.rs b/src/ivc/sangria/fold_relaxed_plonk_instance_chip.rs index b5a1db52..19cab9d3 100644 --- a/src/ivc/sangria/fold_relaxed_plonk_instance_chip.rs +++ b/src/ivc/sangria/fold_relaxed_plonk_instance_chip.rs @@ -45,12 +45,10 @@ use std::{iter, num::NonZeroUsize, ops}; -use halo2_proofs::circuit::AssignedCell; use itertools::Itertools; use num_traits::Num; use tracing::*; -use super::super::instances_accumulator_computation; use crate::{ constants::NUM_CHALLENGE_BITS, ff::{Field, FromUniformBytes, PrimeField, PrimeFieldBits}, @@ -61,19 +59,23 @@ use crate::{ big_uint_mul_mod_chip::{self, BigUintMulModChip, OverflowingBigUint}, }, }, + halo2_proofs::circuit::AssignedCell, halo2curves::CurveAffine, + ivc::sangria::instances_accumulator_computation, main_gate::{ AdviceCyclicAssignor, AssignedBit, AssignedValue, MainGate, MainGateConfig, RegionCtx, WrapValue, }, nifs::sangria::{ - accumulator::{FoldablePlonkInstance, RelaxedPlonkInstance}, - GetConsistencyMarkers, GetStepCircuitInstances, + accumulator::{FoldablePlonkInstance, RelaxedPlonkInstance, SCInstancesHashAcc}, + GetConsistencyMarkers, GetStepCircuitInstances, CONSISTENCY_MARKERS_COUNT, }, poseidon::ROCircuitTrait, util::{self, CellsValuesView}, }; +/// `MARKERS_LEN` - the first column of instance is folded separately, the length of this column is +/// regulated by this parameter pub(crate) struct FoldRelaxedPlonkInstanceChip< const T: usize, C: CurveAffine, @@ -114,7 +116,7 @@ pub(crate) struct AssignedRelaxedPlonkInstance, + pub folded_step_circuit_instances_hash_accumulator: SCInstancesHashAcc>, } impl AssignedRelaxedPlonkInstance { @@ -186,12 +188,15 @@ impl AssignedRelaxedPlonkInstance { + SCInstancesHashAcc::Hash(gate.conditional_select(region, lhs, rhs, &condition)?) + } + SCInstancesHashAcc::None => SCInstancesHashAcc::None, + }; Ok(Self { folded_W, @@ -307,13 +312,14 @@ impl AssignedRelaxedPlonkInstance SCInstancesHashAcc::None, + SCInstancesHashAcc::Hash(hash) => SCInstancesHashAcc::Hash({ + util::fe_to_fe_safe(&unwrap_result_option!(hash.value().unwrap().copied())) + .expect("fields same bytes len") + }), + }, })) } } @@ -559,16 +565,22 @@ where ctx: &mut RegionCtx, config: MainGateConfig, input_instances: &[AssignedValue], - folded_instances: &AssignedValue, - ) -> Result, Error> { - Ok( - instances_accumulator_computation::absorb_in_assign_sc_instances_accumulator( - ctx, - config.into_smaller_size().unwrap(), - folded_instances, - input_instances, - )?, - ) + folded_instances: &SCInstancesHashAcc>, + ) -> Result>, Error> { + Ok(match folded_instances { + SCInstancesHashAcc::None => { + assert!(input_instances.is_empty()); + SCInstancesHashAcc::None + } + SCInstancesHashAcc::Hash(folded_instances) => SCInstancesHashAcc::Hash( + instances_accumulator_computation::absorb_in_assign_sc_instances_accumulator( + ctx, + config.into_smaller_size().unwrap(), + folded_instances, + input_instances, + )?, + ), + }) } /// Fold [`RelaxedPlonkInstance::challenges`] & [`PlonkInstance::challenges`] @@ -622,6 +634,8 @@ where let gate = MainGate::new(self.config.clone()); let r_value = gate.le_bits_to_num(region, &r)?; + debug!("sangria on-circuir r: {:?}", r_value.value()); + let r = BigUintView { as_bn_limbs: self .bn_chip @@ -769,10 +783,15 @@ where let assigned_u = assign_diff_field!(&self.relaxed.u, || "relaxed u")?; - let assigned_step_circuit_instances = assign_diff_field!( - &self.relaxed.step_circuit_instances_hash_accumulator, - || "relaxed u" - )?; + let assigned_step_circuit_instances = + match self.relaxed.step_circuit_instances_hash_accumulator { + SCInstancesHashAcc::None => SCInstancesHashAcc::None, + SCInstancesHashAcc::Hash(hash) => { + SCInstancesHashAcc::Hash(assign_diff_field!(&hash, || { + "step_circuit_instances_hash_accumulator" + })?) + } + }; Ok(AssignedRelaxedPlonkInstance { folded_W: assigned_W, @@ -792,7 +811,7 @@ where &self, region: &mut RegionCtx, public_params_hash: &C, - input_plonk: &FoldablePlonkInstance, + input_plonk: &FoldablePlonkInstance, cross_term_commits: &[C], mut ro_circuit: impl ROCircuitTrait, ) -> Result<(AssignedWitness, Vec>), Error> { @@ -851,7 +870,6 @@ where .iter() .map(|W| assign_and_absorb_point!(W)) .collect::, _>>()?; - let assigned_E = assign_and_absorb_point!(&self.relaxed.E_commitment)?; let consistency_markers = self.relaxed.get_consistency_markers(); let assigned_consistency_markers = consistency_markers @@ -876,10 +894,17 @@ where let assigned_u = assign_and_absorb_diff_field!(&self.relaxed.u, || "relaxed u")?; - let assigned_step_circuit_instances = assign_and_absorb_diff_field!( - &self.relaxed.step_circuit_instances_hash_accumulator, - || { "step_circuit_instances" } - )?; + let assigned_E = assign_and_absorb_point!(&self.relaxed.E_commitment)?; + + let assigned_step_circuit_instances = + match self.relaxed.step_circuit_instances_hash_accumulator { + SCInstancesHashAcc::None => SCInstancesHashAcc::None, + SCInstancesHashAcc::Hash(hash) => { + SCInstancesHashAcc::Hash(assign_and_absorb_diff_field!(&hash, || { + "step_circuit_instances" + })?) + } + }; let assigned_relaxed = AssignedRelaxedPlonkInstance { folded_W: assigned_W, @@ -1140,7 +1165,7 @@ fn assign_next_advice_from_diff_field>( Ok(assignor.assign_next_advice(region, annotation, val)?) } -pub struct FoldResult { +pub struct FoldResult { pub(crate) assigned_input: AssignedWitness, pub(crate) assigned_result_of_fold: AssignedRelaxedPlonkInstance, } @@ -1158,7 +1183,6 @@ mod tests { use super::*; use crate::{ commitment::CommitmentKey, - constants::MAX_BITS, ff::Field, halo2curves::{bn256::G1Affine as C1, CurveAffine}, nifs::sangria::{self, VanillaFS, CONSISTENCY_MARKERS_COUNT}, @@ -1173,7 +1197,8 @@ mod tests { const T: usize = 6; const NUM_WITNESS: usize = 5; - const NUM_INSTANCES: usize = CONSISTENCY_MARKERS_COUNT; + const NUM_MARKERS: usize = CONSISTENCY_MARKERS_COUNT; + const NUM_INSTANCES: usize = 10; const NUM_CHALLENGES: usize = 5; /// When the number of fold rounds increases, `K` must be increased too /// as the number of required rows in the table grows. @@ -1256,14 +1281,14 @@ mod tests { .collect(), instances: iter::once( iter::repeat_with(|| ScalarExt::random(&mut rnd)) - .take(NUM_INSTANCES) + .take(NUM_MARKERS) .collect_vec(), ) .chain( iter::repeat_with(|| ScalarExt::random(&mut rnd)) .chunks(10) .into_iter() - .take(10) + .take(NUM_INSTANCES) .map(|ch| ch.into_iter().collect_vec()) .collect_vec(), ) @@ -1294,7 +1319,7 @@ mod tests { let mut layouter = SingleChipLayouter::new(&mut ws, vec![]).unwrap(); - let spec = Spec::::new(10, 10); + let spec = Spec::::new(10, 10); for _round in 0..=NUM_OF_FOLD_ROUNDS { let plonk = generate_random_plonk_instance(&mut rnd); @@ -1368,7 +1393,7 @@ mod tests { let mut layouter = SingleChipLayouter::new(&mut ws, vec![]).unwrap(); - let mut plonk = RelaxedPlonkInstance::::new(0, NUM_WITNESS); + let mut plonk = RelaxedPlonkInstance::::new(0, NUM_WITNESS, 0); for _round in 0..=NUM_OF_FOLD_ROUNDS { let input_W = random_curve_vec(&mut rnd); @@ -1387,7 +1412,7 @@ mod tests { Value::known(C1::scalar_to_base(&r).unwrap()), )?; - let r = gate.le_num_to_bits(&mut ctx, assigned_r, MAX_BITS)?; + let r = gate.le_num_to_bits(&mut ctx, assigned_r, NUM_CHALLENGE_BITS)?; Ok(FoldRelaxedPlonkInstanceChip::< T, @@ -1448,11 +1473,11 @@ mod tests { let mut layouter = SingleChipLayouter::new(&mut ws, vec![]).unwrap(); - let mut plonk = RelaxedPlonkInstance::::new(0, 0); + let mut plonk = RelaxedPlonkInstance::::new(0, 0, 0); let chip = FoldRelaxedPlonkInstanceChip::::new( - RelaxedPlonkInstance::new(0, 0), + RelaxedPlonkInstance::new(0, 0, 0), LIMB_WIDTH, LIMBS_COUNT, config.clone(), @@ -1534,7 +1559,7 @@ mod tests { let mut layouter = SingleChipLayouter::new(&mut ws, vec![]).unwrap(); - let mut relaxed_plonk = RelaxedPlonkInstance::::new(0, 0); + let mut relaxed_plonk = RelaxedPlonkInstance::::new(0, 0, 0); let bn_chip = BigUintMulModChip::::new( config @@ -1680,7 +1705,7 @@ mod tests { let mut layouter = SingleChipLayouter::new(&mut ws, vec![]).unwrap(); - let mut relaxed_plonk = RelaxedPlonkInstance::::new(NUM_CHALLENGES, 0); + let mut relaxed_plonk = RelaxedPlonkInstance::::new(NUM_CHALLENGES, 0, 0); let bn_chip = BigUintMulModChip::::new( config @@ -1816,6 +1841,8 @@ mod tests { #[traced_test] #[test] fn fold_all() { + const T: usize = 6; + let Fixture { mut ws, config, @@ -1826,11 +1853,11 @@ mod tests { let spec = Spec::::new(10, 10); - let mut relaxed = RelaxedPlonkInstance::new(NUM_CHALLENGES, NUM_WITNESS); + let mut relaxed = RelaxedPlonkInstance::new(NUM_CHALLENGES, NUM_WITNESS, NUM_INSTANCES); + let pp_hash = C1::random(&mut rnd); for _round in 0..=NUM_OF_FOLD_ROUNDS { let input_plonk = generate_random_plonk_instance(&mut rnd); - let pp_hash = C1::random(&mut rnd); let cross_term_commits = random_curve_vec(&mut rnd); let on_circuit_relaxed = layouter @@ -1871,6 +1898,7 @@ mod tests { &input_plonk, &cross_term_commits, ); + debug!("sangria off-circuir r: {off_circuit_r:?}"); relaxed = relaxed.fold(&input_plonk, &cross_term_commits, &off_circuit_r); @@ -1889,6 +1917,8 @@ mod tests { let mut ro = PoseidonHash::new(spec.clone()); + let pp_hash = pp_hash.coordinates().map(|c| (*c.x(), *c.y())).unwrap(); + VanillaFS::generate_challenge(&pp_hash, &mut ro, relaxed, input, cross_term_commits) .unwrap() } diff --git a/src/ivc/sangria/incrementally_verifiable_computation.rs b/src/ivc/sangria/incrementally_verifiable_computation.rs index d2c979ac..089b1ce8 100644 --- a/src/ivc/sangria/incrementally_verifiable_computation.rs +++ b/src/ivc/sangria/incrementally_verifiable_computation.rs @@ -11,8 +11,10 @@ use crate::{ group::prime::PrimeCurveAffine, halo2curves::CurveAffine, ivc::{ - consistency_markers_computation::ConsistencyMarkerComputation, - public_params::PublicParams, + sangria::{ + consistency_markers_computation::ConsistencyMarkerComputation, + public_params::PublicParams, + }, step_folding_circuit::{StepFoldingCircuit, StepInputs}, }, main_gate::MainGateConfig, @@ -126,7 +128,7 @@ where step: usize, secondary_nifs_pp: nifs::sangria::ProverParam, primary_nifs_pp: nifs::sangria::ProverParam, - secondary_trace: [FoldablePlonkTrace; 1], + secondary_trace: [FoldablePlonkTrace; 1], debug_mode: bool, } @@ -445,6 +447,7 @@ where self.secondary.relaxed_trace.clone(), &self.secondary_trace, )?; + self.secondary .pub_instances .push(self.secondary_trace[0].u.instances.clone()); diff --git a/src/ivc/instances_accumulator_computation.rs b/src/ivc/sangria/instances_accumulator_computation.rs similarity index 100% rename from src/ivc/instances_accumulator_computation.rs rename to src/ivc/sangria/instances_accumulator_computation.rs diff --git a/src/ivc/sangria/mod.rs b/src/ivc/sangria/mod.rs index 71a4c8cf..8be65dc1 100644 --- a/src/ivc/sangria/mod.rs +++ b/src/ivc/sangria/mod.rs @@ -1,3 +1,6 @@ +pub mod consistency_markers_computation; pub mod fold_relaxed_plonk_instance_chip; pub mod incrementally_verifiable_computation; +pub mod instances_accumulator_computation; +pub mod public_params; pub mod step_folding_circuit; diff --git a/src/ivc/public_params.rs b/src/ivc/sangria/public_params.rs similarity index 97% rename from src/ivc/public_params.rs rename to src/ivc/sangria/public_params.rs index c834d4fc..9c251fea 100644 --- a/src/ivc/public_params.rs +++ b/src/ivc/sangria/public_params.rs @@ -4,7 +4,10 @@ use halo2_proofs::plonk; use serde::Serialize; use tracing::*; -use super::{step_folding_circuit::StepParams, StepCircuit}; +use super::{ + super::{step_folding_circuit::StepParams, StepCircuit}, + consistency_markers_computation::ConsistencyMarkerComputation, +}; use crate::{ commitment::CommitmentKey, constants::NUM_HASH_BITS, @@ -14,7 +17,6 @@ use crate::{ halo2curves::CurveAffine, ivc::{ self, - consistency_markers_computation::ConsistencyMarkerComputation, step_folding_circuit::{StepFoldingCircuit, StepInputs}, }, main_gate::MainGateConfig, @@ -149,7 +151,7 @@ pub struct PublicParams< _p: PhantomData<(SC1, SC2)>, #[serde(skip_serializing)] - secondary_initial_plonk_trace: FoldablePlonkTrace, + secondary_initial_plonk_trace: FoldablePlonkTrace, #[serde(skip_serializing)] digest_1: C1, @@ -384,7 +386,9 @@ where Ok(self_) } - pub fn secondary_initial_plonk_trace(&self) -> &FoldablePlonkTrace { + pub fn secondary_initial_plonk_trace( + &self, + ) -> &FoldablePlonkTrace { &self.secondary_initial_plonk_trace } diff --git a/src/ivc/sangria/step_folding_circuit.rs b/src/ivc/sangria/step_folding_circuit.rs index 4f1e797e..2bda683a 100644 --- a/src/ivc/sangria/step_folding_circuit.rs +++ b/src/ivc/sangria/step_folding_circuit.rs @@ -13,10 +13,10 @@ use crate::{ }, halo2curves::CurveAffine, ivc::{ - consistency_markers_computation::AssignedConsistencyMarkersComputation, fold_relaxed_plonk_instance_chip::{ AssignedRelaxedPlonkInstance, FoldRelaxedPlonkInstanceChip, FoldResult, }, + sangria::consistency_markers_computation::AssignedConsistencyMarkersComputation, StepCircuit, }, main_gate::{AdviceCyclicAssignor, MainGate, MainGateConfig, RegionCtx}, @@ -173,7 +173,11 @@ where public_params_hash: C::identity(), z_0: [C::Base::ZERO; ARITY], z_i: [C::Base::ZERO; ARITY], - U: RelaxedPlonkInstance::new(num_challenges, round_sizes.len()), + U: RelaxedPlonkInstance::new( + num_challenges, + round_sizes.len(), + step_circuit_instances.len(), + ), u: FoldablePlonkInstance::new(PlonkInstance::new( paired_num_io, num_challenges, @@ -312,6 +316,7 @@ where U: RelaxedPlonkInstance::new( self.input.U.challenges.len(), self.input.U.W_commitments.len(), + self.input.step_circuit_instances.len(), ), u, step_circuit_instances: self diff --git a/src/nifs/sangria/accumulator.rs b/src/nifs/sangria/accumulator.rs index ba902777..402b6a60 100644 --- a/src/nifs/sangria/accumulator.rs +++ b/src/nifs/sangria/accumulator.rs @@ -11,11 +11,12 @@ use itertools::Itertools; use rayon::prelude::*; use tracing::{debug, instrument, warn}; -use super::{GetConsistencyMarkers, GetStepCircuitInstances}; +use super::{GetConsistencyMarkers, GetStepCircuitInstances, CONSISTENCY_MARKERS_COUNT}; use crate::{ commitment::CommitmentKey, ff::{Field, PrimeField}, - ivc::instances_accumulator_computation, + ivc::sangria::instances_accumulator_computation, + main_gate::{AssignedValue, WrapValue}, plonk::{ self, GetChallenges, GetWitness, PlonkInstance, PlonkStructure, PlonkTrace, PlonkWitness, }, @@ -23,8 +24,74 @@ use crate::{ util::ScalarToBase, }; +/// Accumulator for step-circuit instance columns +/// +/// Since the first column is folded directly via `r` and the rest (step-circuit instances columns) +/// are accumulated via hash, in case of absence of any columns except the first one, we don't need +/// additional costs for accumulation of other instance columns. +/// +/// This will make it easier to use sangria IVC code in other IVCs, instead of calculating a chain +/// of hashes from empty sets. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SCInstancesHashAcc { + /// If StepCircuit does not possess sc_instances will be None + None, + Hash(F), +} + +impl> AbsorbInRO for SCInstancesHashAcc { + fn absorb_into(&self, ro: &mut RO) { + match self { + Self::None => { + ro.absorb_field(F::ZERO); + } + Self::Hash(hash) => { + ro.absorb_field(*hash); + } + } + } +} + +impl<'l, F: PrimeField> From<&'l SCInstancesHashAcc>> for WrapValue { + fn from(val: &'l SCInstancesHashAcc>) -> Self { + match &val { + SCInstancesHashAcc::None => WrapValue::Zero, + SCInstancesHashAcc::Hash(hash) => WrapValue::Assigned(hash.clone()), + } + } +} + +impl SCInstancesHashAcc { + pub fn as_ref(&self) -> SCInstancesHashAcc<&F> { + match self { + SCInstancesHashAcc::None => SCInstancesHashAcc::None, + SCInstancesHashAcc::Hash(h) => SCInstancesHashAcc::Hash(h), + } + } + + pub fn zip<'l>(lhs: &'l Self, rhs: &'l Self) -> SCInstancesHashAcc<(&'l F, &'l F)> { + match (lhs, rhs) { + (Self::Hash(l), Self::Hash(r)) => SCInstancesHashAcc::Hash((l, r)), + _ => SCInstancesHashAcc::None, + } + } + pub fn map(self, f: impl FnOnce(F) -> O) -> SCInstancesHashAcc { + match self { + SCInstancesHashAcc::None => SCInstancesHashAcc::None, + SCInstancesHashAcc::Hash(h) => SCInstancesHashAcc::Hash(f(h)), + } + } +} + +/// Accumulated version of Plonk Instance +/// +/// `MARKERS_LEN` - the first column of instance is folded separately, the length of this column is +/// regulated by this parameter #[derive(Clone, Debug, PartialEq, Eq)] -pub struct RelaxedPlonkInstance { +pub struct RelaxedPlonkInstance< + C: CurveAffine, + const MARKERS_LEN: usize = CONSISTENCY_MARKERS_COUNT, +> { /// `W_commitments = round_sizes.len()`, see [`PlonkStructure::round_sizes`] pub(crate) W_commitments: Vec, /// First instance column [`crate::ivc::step_folding_circuit::StepFoldingCircuit`] reserved for @@ -51,20 +118,26 @@ pub struct RelaxedPlonkInstance { /// /// Unlike consistency markers, instance columns belonging to the step circuit itself are not /// folded, but are accumulated using the hash function. - pub(crate) step_circuit_instances_hash_accumulator: C::ScalarExt, + pub(crate) step_circuit_instances_hash_accumulator: SCInstancesHashAcc, } -impl From> +impl From> for RelaxedPlonkInstance where C::Base: PrimeFieldBits + FromUniformBytes<64>, { - fn from(inner: FoldablePlonkInstance) -> Self { - let step_circuit_instances_hash_accumulator = - instances_accumulator_computation::absorb_in_sc_instances_accumulator::( - &C::ScalarExt::ZERO, - inner.get_step_circuit_instances(), - ); + fn from(inner: FoldablePlonkInstance) -> Self { + let step_circuit_instances = inner.get_step_circuit_instances(); + let step_circuit_instances_hash_accumulator = if step_circuit_instances.is_empty() { + SCInstancesHashAcc::None + } else { + SCInstancesHashAcc::Hash( + instances_accumulator_computation::absorb_in_sc_instances_accumulator::( + &C::ScalarExt::ZERO, + step_circuit_instances, + ), + ) + }; let consistency_markers = inner.get_consistency_markers(); let FoldablePlonkInstance(PlonkInstance { @@ -88,9 +161,13 @@ impl RelaxedPlonkInstance, { - pub fn new(num_challenges: usize, num_witness: usize) -> Self { - let step_circuit_instances_hash_accumulator = - instances_accumulator_computation::get_initial_sc_instances_accumulator::(); + pub fn new(num_challenges: usize, num_witness: usize, num_sc_instances: usize) -> Self { + let step_circuit_instances_hash_accumulator = match num_sc_instances { + 0 => SCInstancesHashAcc::None, + _any => SCInstancesHashAcc::Hash( + instances_accumulator_computation::get_initial_sc_instances_accumulator::(), + ), + }; Self { consistency_markers: array::from_fn(|_| C::ScalarExt::ZERO), @@ -125,7 +202,7 @@ where #[instrument(name = "fold_plonk_instance", skip_all)] pub fn fold( &self, - U2: &FoldablePlonkInstance, + U2: &FoldablePlonkInstance, cross_term_commits: &[C], r: &C::ScalarExt, ) -> Self { @@ -168,11 +245,15 @@ where .map(|(tk, power_of_r)| best_multiexp(&[power_of_r], &[*tk]).into()) .fold(self.E_commitment, |acc, x| (acc + x).into()); - let step_circuit_instances_hash_accumulator = - instances_accumulator_computation::absorb_in_sc_instances_accumulator::( - &self.step_circuit_instances_hash_accumulator, - U2.get_step_circuit_instances(), - ); + let step_circuit_instances_hash_accumulator = self + .step_circuit_instances_hash_accumulator + .as_ref() + .map(|acc| { + instances_accumulator_computation::absorb_in_sc_instances_accumulator::( + acc, + U2.get_step_circuit_instances(), + ) + }); RelaxedPlonkInstance { W_commitments, @@ -191,14 +272,14 @@ where // TODO #31 docs #[derive(Debug, Clone)] -pub struct RelaxedPlonkTrace { +pub struct RelaxedPlonkTrace { pub U: RelaxedPlonkInstance, pub W: RelaxedPlonkWitness, } impl RelaxedPlonkTrace { pub fn from_regular( - tr: FoldablePlonkTrace, + tr: FoldablePlonkTrace, k_table_size: usize, ) -> RelaxedPlonkTrace where @@ -219,7 +300,11 @@ where { pub fn new(args: RelaxedPlonkTraceArgs) -> Self { Self { - U: RelaxedPlonkInstance::new(args.num_challenges, args.num_witness), + U: RelaxedPlonkInstance::new( + args.num_challenges, + args.num_witness, + args.num_io.len() - 1, // instances + ), W: RelaxedPlonkWitness::new(args.k_table_size, &args.round_sizes), } } @@ -272,14 +357,18 @@ impl, const MARKERS_LEN: usize> AbsorbInRO< } = self; ro.absorb_point_iter(W_commitments.iter()) - .absorb_point(E_commitment) .absorb_field_iter( consistency_markers .iter() .chain(challenges.iter()) .chain(iter::once(u)) - .chain(iter::once(step_circuit_instances_hash_accumulator)) .map(|m| C::scalar_to_base(m).unwrap()), + ) + .absorb_point(E_commitment) + .absorb( + &step_circuit_instances_hash_accumulator + .as_ref() + .map(|acc| C::scalar_to_base(acc).unwrap()), ); } } @@ -346,21 +435,25 @@ impl RelaxedPlonkWitness { /// column has exactly two elements. /// /// # Consistency Markers -/// - Ensures that `instances.first().len() == 2` for `PlonkInstance`. +/// - Ensures that `instances.first().len() == MARKERS_LEN` for `PlonkInstance`. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct FoldablePlonkInstance(PlonkInstance); +pub struct FoldablePlonkInstance< + C: CurveAffine, + const MARKERS_LEN: usize = CONSISTENCY_MARKERS_COUNT, +>(PlonkInstance); -impl FoldablePlonkInstance { +impl FoldablePlonkInstance { /// Creates a new `FoldablePlonkInstance` from a `PlonkInstance`. /// /// # Consistency Markers - /// - Ensures that `instances.first().len() == 2` for `FoldablePlonkInstance`. + /// - Ensures that `instances.first().len() == MARKERS_LEN` for `FoldablePlonkInstance`. pub fn new(pl: PlonkInstance) -> Option { - matches!(pl.instances.first(), Some(instance) if instance.len() == 2).then_some(Self(pl)) + matches!(pl.instances.first(), Some(instance) if instance.len() == MARKERS_LEN) + .then_some(Self(pl)) } } -impl Deref for FoldablePlonkInstance { +impl Deref for FoldablePlonkInstance { type Target = PlonkInstance; fn deref(&self) -> &Self::Target { @@ -368,7 +461,7 @@ impl Deref for FoldablePlonkInstance { } } -impl DerefMut for FoldablePlonkInstance { +impl DerefMut for FoldablePlonkInstance { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } @@ -380,18 +473,21 @@ impl DerefMut for FoldablePlonkInstance { /// # Consistency Markers /// - Contains a `FoldablePlonkInstance` and a `PlonkWitness`. #[derive(Debug, Clone)] -pub struct FoldablePlonkTrace { +pub struct FoldablePlonkTrace +{ /// The foldable PLONK instance, ensuring the first instance column has exactly two elements. /// /// # Consistency Markers /// - Holds a `FoldablePlonkInstance` to enforce first-instance column length. - pub u: FoldablePlonkInstance, + pub u: FoldablePlonkInstance, pub w: PlonkWitness, } -impl From> for PlonkTrace { +impl From> + for PlonkTrace +{ /// Converts a `FoldablePlonkTrace` into a `PlonkTrace`. - fn from(value: FoldablePlonkTrace) -> Self { + fn from(value: FoldablePlonkTrace) -> Self { PlonkTrace { u: value.u.0, w: value.w, @@ -399,7 +495,7 @@ impl From> for PlonkTrace { } } -impl FoldablePlonkTrace { +impl FoldablePlonkTrace { /// Creates a new `FoldablePlonkTrace` from a `PlonkTrace`. /// /// # Consistency Markers diff --git a/src/nifs/sangria/mod.rs b/src/nifs/sangria/mod.rs index 0d9c34c7..c70372f6 100644 --- a/src/nifs/sangria/mod.rs +++ b/src/nifs/sangria/mod.rs @@ -6,7 +6,9 @@ use rayon::prelude::*; use some_to_err::ErrOr; use tracing::*; -pub use self::accumulator::{FoldablePlonkInstance, FoldablePlonkTrace, RelaxedPlonkInstance}; +pub use self::accumulator::{ + FoldablePlonkInstance, FoldablePlonkTrace, RelaxedPlonkInstance, RelaxedPlonkTrace, +}; pub use crate::plonk::PlonkInstance; use crate::{ commitment::{self, CommitmentKey}, @@ -18,8 +20,8 @@ use crate::{ halo2curves::ff::{FromUniformBytes, PrimeField, PrimeFieldBits}, plonk::Error as Halo2Error, }, - ivc::{instances_accumulator_computation, Instances}, - nifs::sangria::accumulator::{RelaxedPlonkTrace, RelaxedPlonkWitness}, + ivc::{sangria::instances_accumulator_computation, Instances}, + nifs::sangria::accumulator::RelaxedPlonkWitness, plonk::{ self, eval::{Error as EvalError, GetDataForEval, PlonkEvalDomain}, @@ -56,16 +58,19 @@ pub type CrossTermCommits = Vec; /// - `cross_term_commits = [Comm(T_1),...,Comm(T_{d-1})]` /// /// Please refer to: [notes](https://hackmd.io/@chaosma/BJvWmnw_h#31-NIFS) +/// +/// `MARKERS_LEN` - the first column of instance is folded separately, the length of this column is +/// regulated by this parameter // TODO Replace links to either the documentation right here, or the official Snarkify resource #[derive(Clone, Debug)] -pub struct VanillaFS { +pub struct VanillaFS { _marker: PhantomData, } pub struct ProverParam { pub(crate) S: PlonkStructure, /// digest of public parameter of IVC circuit - pub pp_digest: C, + pub pp_digest: (C::Base, C::Base), } impl VanillaFS @@ -155,17 +160,21 @@ where /// Absorb all fields into RandomOracle `RO` & generate challenge based on that #[instrument(skip_all)] pub(crate) fn generate_challenge( - pp_digest: &C, + pp_digest: &(C::Base, C::Base), ro_acc: &mut impl ROTrait, U1: &RelaxedPlonkInstance, U2: &PlonkInstance, cross_term_commits: &[C], ) -> Result<::ScalarExt, Error> { + let _span = info_span!("sangria_cha").entered(); + Ok(ro_acc - .absorb_point(pp_digest) + .absorb_field(pp_digest.0) + .absorb_field(pp_digest.1) .absorb(U1) .absorb(U2) .absorb_point_iter(cross_term_commits.iter()) + .inspect(|buf| debug!("buf before: {buf:?}")) .squeeze::(NUM_CHALLENGE_BITS)) } } @@ -186,7 +195,15 @@ pub enum Error { NoConsistencyMarkers, } -pub type VerifierParam = C; +pub struct VerifierParam { + pp_digest: (C::Base, C::Base), +} + +impl From<(C::Base, C::Base)> for VerifierParam { + fn from(pp_digest: (C::Base, C::Base)) -> Self { + Self { pp_digest } + } +} impl VanillaFS where @@ -196,7 +213,12 @@ where pp_digest: C, S: PlonkStructure, ) -> Result<(ProverParam, VerifierParam), Error> { - Ok((ProverParam { S, pp_digest }, pp_digest)) + let pp_digest = { + let c = pp_digest.coordinates().unwrap(); + (*c.x(), *c.y()) + }; + + Ok((ProverParam { S, pp_digest }, VerifierParam { pp_digest })) } #[instrument(skip_all)] @@ -206,7 +228,7 @@ where witness: &[Vec], pp: &ProverParam, ro_nark: &mut impl ROTrait, - ) -> Result, Error> { + ) -> Result, Error> { pp.S.run_sps_protocol(ck, instances, witness, ro_nark) .map(FoldablePlonkTrace::new)? .ok_or(Error::NoConsistencyMarkers) @@ -233,7 +255,7 @@ where pp: &ProverParam, ro_acc: &mut impl ROTrait, accumulator: RelaxedPlonkTrace, - incoming: &[FoldablePlonkTrace; 1], + incoming: &[FoldablePlonkTrace; 1], ) -> Result<(RelaxedPlonkTrace, CrossTermCommits), Error> { let incoming = &incoming[0]; @@ -246,6 +268,7 @@ where Self::commit_cross_terms(ck, &pp.S, U1, W1, U2, W2)?; let r = VanillaFS::generate_challenge(&pp.pp_digest, ro_acc, U1, U2, &cross_term_commits)?; + debug!("sangria_cha: {r:?}"); let U = U1.fold(U2, &cross_term_commits, &r); let W = W1.fold(W2, &cross_terms, &r); @@ -282,7 +305,7 @@ where U2.sps_verify(ro_nark)?; - let r = VanillaFS::generate_challenge(vp, ro_acc, U1, U2, cross_term_commits)?; + let r = VanillaFS::generate_challenge(&vp.pp_digest, ro_acc, U1, U2, cross_term_commits)?; Ok(U1.fold(U2, cross_term_commits, &r)) } @@ -304,13 +327,13 @@ pub enum VerifyError { InstanceMismatch, } -impl VanillaFS +impl VanillaFS where C::Base: PrimeFieldBits + FromUniformBytes<64>, { pub fn is_sat_accumulation( S: &PlonkStructure, - acc: &RelaxedPlonkTrace, + acc: &RelaxedPlonkTrace, ) -> Result<(), VerifyError> { let RelaxedPlonkTrace { U, W } = acc; @@ -361,7 +384,7 @@ where pub fn is_sat_permutation( S: &PlonkStructure, - acc: &RelaxedPlonkTrace, + acc: &RelaxedPlonkTrace, ) -> Result<(), VerifyError> { /// Under this collapsing scheme, `instance` columns other than consistency markers are not /// foldeded, but accumulated using hash. Therefore, they need to be cut out for @@ -381,8 +404,8 @@ where /// While checking permutations, we need to line up all instance columns one after the other, but /// since we cut out all instance columns except the null column (consistency_marker) for the /// `Relaxed*` version we need to augment them based on [`PlonkStructure::num_io`]. - fn iter_flat_instances_with_padding<'b, C: CurveAffine>( - U: &'b RelaxedPlonkInstance, + fn iter_flat_instances_with_padding<'b, C: CurveAffine, const MARKERS_LEN: usize>( + U: &'b RelaxedPlonkInstance, S: &'b PlonkStructure, ) -> impl 'b + Iterator { U.consistency_markers.iter().copied().chain( @@ -431,7 +454,7 @@ where pub fn is_sat_witness_commit( ck: &CommitmentKey, - acc: &RelaxedPlonkTrace, + acc: &RelaxedPlonkTrace, ) -> Result<(), VerifyError> { let RelaxedPlonkTrace { U, W } = acc; @@ -451,23 +474,31 @@ where } pub fn is_sat_pub_instances( - acc: &RelaxedPlonkTrace, + acc: &RelaxedPlonkTrace, pub_instances: &[Vec::ScalarExt>>], ) -> Result<(), VerifyError> { - pub_instances - .iter() - .fold( - instances_accumulator_computation::get_initial_sc_instances_accumulator::(), - |acc, instances| { - instances_accumulator_computation::absorb_in_sc_instances_accumulator::( - &acc, - instances.get_step_circuit_instances(), + match acc.U.step_circuit_instances_hash_accumulator { + accumulator::SCInstancesHashAcc::None => { + assert!(pub_instances.iter().all(|instances| instances.get_step_circuit_instances().is_empty())); + Ok(()) + } + accumulator::SCInstancesHashAcc::Hash(step_circuit_instances_hash_accumulator) => { + pub_instances + .iter() + .fold( + instances_accumulator_computation::get_initial_sc_instances_accumulator::(), + |acc, instances| { + instances_accumulator_computation::absorb_in_sc_instances_accumulator::( + &acc, + instances.get_step_circuit_instances(), + ) + }, ) - }, - ) - .ne(&acc.U.step_circuit_instances_hash_accumulator) - .then_some(VerifyError::InstanceMismatch) - .err_or(()) + .ne(&step_circuit_instances_hash_accumulator) + .then_some(VerifyError::InstanceMismatch) + .err_or(()) + } + } } /// Comprehensive satisfaction check for an accumulator. @@ -478,7 +509,7 @@ where pub fn is_sat( ck: &CommitmentKey, S: &PlonkStructure, - acc: &RelaxedPlonkTrace, + acc: &RelaxedPlonkTrace, pub_instances: &[Vec>], ) -> Result<(), Vec> { let mut errors = vec![]; @@ -508,6 +539,7 @@ where } /// Number of consistency markers in instance column +/// This number is relevant for sangria IVC pub const CONSISTENCY_MARKERS_COUNT: usize = 2; /// As part of the vanilla folding scheme, we use the values in the zero instance of the column for @@ -523,7 +555,7 @@ pub trait GetConsistencyMarkers { } impl GetConsistencyMarkers - for FoldablePlonkInstance + for FoldablePlonkInstance { fn get_consistency_markers(&self) -> [C::ScalarExt; MARKERS] { match self.instances.first() { diff --git a/src/nifs/sangria/tests.rs b/src/nifs/sangria/tests.rs index bb290dd1..ed6ed469 100644 --- a/src/nifs/sangria/tests.rs +++ b/src/nifs/sangria/tests.rs @@ -166,7 +166,7 @@ where const R_P: usize = 3; let mut f_tr = RelaxedPlonkTrace { - U: RelaxedPlonkInstance::new(S.num_challenges, S.round_sizes.len()), + U: RelaxedPlonkInstance::new(S.num_challenges, S.round_sizes.len(), S.num_io.len() - 1), W: RelaxedPlonkWitness::new(S.k, &S.round_sizes), };