diff --git a/src/lib.rs b/src/lib.rs index b94403a84..21869f48f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -415,17 +415,17 @@ where let buffer_primary = ResourceBuffer { l_w: None, l_u: None, - ABC_Z_1: R1CSResult::default(r1cs_primary), - ABC_Z_2: R1CSResult::default(r1cs_primary), - T: r1cs::default_T(r1cs_primary), + ABC_Z_1: R1CSResult::default(r1cs_primary.num_cons), + ABC_Z_2: R1CSResult::default(r1cs_primary.num_cons), + T: r1cs::default_T::(r1cs_primary.num_cons), }; let buffer_secondary = ResourceBuffer { l_w: None, l_u: None, - ABC_Z_1: R1CSResult::default(r1cs_secondary), - ABC_Z_2: R1CSResult::default(r1cs_secondary), - T: r1cs::default_T(r1cs_secondary), + ABC_Z_1: R1CSResult::default(r1cs_secondary.num_cons), + ABC_Z_2: R1CSResult::default(r1cs_secondary.num_cons), + T: r1cs::default_T::(r1cs_secondary.num_cons), }; Ok(Self { diff --git a/src/r1cs/mod.rs b/src/r1cs/mod.rs index 91e933f46..e2772ce86 100644 --- a/src/r1cs/mod.rs +++ b/src/r1cs/mod.rs @@ -577,11 +577,11 @@ impl R1CSShape { impl R1CSResult { /// Produces a default `R1CSResult` given an `R1CSShape` - pub fn default(S: &R1CSShape) -> Self { + pub fn default(num_cons: usize) -> Self { Self { - AZ: vec![E::Scalar::ZERO; S.num_cons], - BZ: vec![E::Scalar::ZERO; S.num_cons], - CZ: vec![E::Scalar::ZERO; S.num_cons], + AZ: vec![E::Scalar::ZERO; num_cons], + BZ: vec![E::Scalar::ZERO; num_cons], + CZ: vec![E::Scalar::ZERO; num_cons], } } } @@ -817,8 +817,8 @@ impl AbsorbInROTrait for RelaxedR1CSInstance { } /// Empty buffer for `commit_T_into` -pub fn default_T(shape: &R1CSShape) -> Vec { - Vec::with_capacity(shape.num_cons) +pub fn default_T(num_cons: usize) -> Vec { + Vec::with_capacity(num_cons) } #[cfg(test)] diff --git a/src/supernova/mod.rs b/src/supernova/mod.rs index 7aca96222..6489b21a8 100644 --- a/src/supernova/mod.rs +++ b/src/supernova/mod.rs @@ -9,7 +9,7 @@ use crate::{ digest::{DigestComputer, SimpleDigestible}, errors::NovaError, r1cs::{ - commitment_key_size, CommitmentKeyHint, R1CSInstance, R1CSShape, R1CSWitness, + self, commitment_key_size, CommitmentKeyHint, R1CSInstance, R1CSResult, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness, }, scalar_as_base, @@ -393,6 +393,21 @@ where } } +/// A resource buffer for SuperNova's [`RecursiveSNARK`] for storing scratch values that are computed by `prove_step`, +/// which allows the reuse of memory allocations and avoids unnecessary new allocations in the critical section. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct ResourceBuffer { + l_w: Option>, + l_u: Option>, + + ABC_Z_1: R1CSResult, + ABC_Z_2: R1CSResult, + + /// buffer for `commit_T` + T: Vec, +} + /// A SNARK that proves the correct execution of an non-uniform incremental computation #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "")] @@ -416,6 +431,11 @@ where proven_circuit_index: usize, program_counter: E1::Scalar, + /// Buffer for memory needed by the primary fold-step + buffer_primary: ResourceBuffer, + /// Buffer for memory needed by the secondary fold-step + buffer_secondary: ResourceBuffer, + // Relaxed instances for the primary circuits // Entries are `None` if the circuit has not been executed yet r_W_primary: Vec>>, @@ -454,6 +474,8 @@ where let num_augmented_circuits = non_uniform_circuit.num_circuits(); let circuit_index = non_uniform_circuit.initial_circuit_index(); + let r1cs_secondary = &pp.circuit_shape_secondary.r1cs_shape; + // check the length of the secondary initial input if z0_secondary.len() != pp.circuit_shape_secondary.F_arity { return Err(SuperNovaError::NovaError( @@ -541,7 +563,7 @@ where return Err(NovaError::InvalidStepOutputLength.into()); } let (u_secondary, w_secondary) = cs_secondary - .r1cs_instance_and_witness(&pp.circuit_shape_secondary.r1cs_shape, &pp.ck_secondary) + .r1cs_instance_and_witness(r1cs_secondary, &pp.ck_secondary) .map_err(|_| SuperNovaError::NovaError(NovaError::UnSat))?; // IVC proof for the primary circuit @@ -561,9 +583,8 @@ where let l_u_secondary = u_secondary; // Initialize relaxed instance/witness pair for the secondary circuit proofs - let r_W_secondary = RelaxedR1CSWitness::::default(&pp.circuit_shape_secondary.r1cs_shape); - let r_U_secondary = - RelaxedR1CSInstance::default(&pp.ck_secondary, &pp.circuit_shape_secondary.r1cs_shape); + let r_W_secondary: RelaxedR1CSWitness = RelaxedR1CSWitness::::default(r1cs_secondary); + let r_U_secondary = RelaxedR1CSInstance::default(&pp.ck_secondary, r1cs_secondary); // Outputs of the two circuits and next program counter thus far. let zi_primary = zi_primary @@ -593,6 +614,31 @@ where let r_U_primary_initial_list = (0..num_augmented_circuits) .map(|i| (i == circuit_index).then(|| r_U_primary.clone())) .collect::>>>(); + + // find the largest length r1cs shape for the buffer size + let max_num_cons = pp + .circuit_shapes + .iter() + .map(|circuit| circuit.r1cs_shape.num_cons) + .max() + .unwrap(); + + let buffer_primary = ResourceBuffer { + l_w: None, + l_u: None, + ABC_Z_1: R1CSResult::default(max_num_cons), + ABC_Z_2: R1CSResult::default(max_num_cons), + T: r1cs::default_T::(max_num_cons), + }; + + let buffer_secondary = ResourceBuffer { + l_w: None, + l_u: None, + ABC_Z_1: R1CSResult::default(r1cs_secondary.num_cons), + ABC_Z_2: R1CSResult::default(r1cs_secondary.num_cons), + T: r1cs::default_T::(r1cs_secondary.num_cons), + }; + Ok(Self { pp_digest: pp.digest(), num_augmented_circuits, @@ -603,6 +649,9 @@ where proven_circuit_index: circuit_index, program_counter: zi_primary_pc_next, + buffer_primary, + buffer_secondary, + r_W_primary: r_W_primary_initial_list, r_U_primary: r_U_primary_initial_list, z0_secondary: z0_secondary.to_vec(), @@ -629,31 +678,36 @@ where return Ok(()); } + // save the inputs before proceeding to the `i+1`th step + let r_U_primary_i = self.r_U_primary.clone(); + // Create single-entry accumulator list for the secondary circuit to hand to SuperNovaAugmentedCircuitInputs + let r_U_secondary_i = vec![Some(self.r_U_secondary.clone())]; + let l_u_secondary_i = self.l_u_secondary.clone(); + let circuit_index = c_primary.circuit_index(); assert_eq!(self.program_counter, E1::Scalar::from(circuit_index as u64)); // fold the secondary circuit's instance - let (nifs_secondary, (r_U_secondary_folded, r_W_secondary_folded)) = NIFS::prove( + let nifs_secondary = NIFS::prove_mut( &pp.ck_secondary, &pp.ro_consts_secondary, &scalar_as_base::(self.pp_digest), &pp.circuit_shape_secondary.r1cs_shape, - &self.r_U_secondary, - &self.r_W_secondary, + &mut self.r_U_secondary, + &mut self.r_W_secondary, &self.l_u_secondary, &self.l_w_secondary, + &mut self.buffer_secondary.T, + &mut self.buffer_secondary.ABC_Z_1, + &mut self.buffer_secondary.ABC_Z_2, ) .map_err(SuperNovaError::NovaError)?; - // clone and updated running instance on respective circuit_index - let r_U_secondary_next = r_U_secondary_folded; - let r_W_secondary_next = r_W_secondary_folded; - - // Create single-entry accumulator list for the secondary circuit to hand to SuperNovaAugmentedCircuitInputs - let r_U_secondary = vec![Some(self.r_U_secondary.clone())]; - - let mut cs_primary = SatisfyingAssignment::::new(); - let T = + let mut cs_primary = SatisfyingAssignment::::with_capacity( + pp[circuit_index].r1cs_shape.num_io + 1, + pp[circuit_index].r1cs_shape.num_vars, + ); + let T: <::CE as CommitmentEngineTrait>::Commitment = Commitment::::decompress(&nifs_secondary.comm_T).map_err(SuperNovaError::NovaError)?; let inputs_primary: SuperNovaAugmentedCircuitInputs<'_, E2> = SuperNovaAugmentedCircuitInputs::new( @@ -661,8 +715,8 @@ where E1::Scalar::from(self.i as u64), &self.z0_primary, Some(&self.zi_primary), - Some(&r_U_secondary), - Some(&self.l_u_secondary), + Some(&r_U_secondary_i), + Some(&l_u_secondary_i), Some(&T), Some(self.program_counter), E1::Scalar::ZERO, @@ -689,37 +743,43 @@ where .r1cs_instance_and_witness(&pp[circuit_index].r1cs_shape, &pp.ck_primary) .map_err(SuperNovaError::NovaError)?; - // Split into `if let`/`else` statement - // to avoid `returns a value referencing data owned by closure` error on `&RelaxedR1CSInstance::default` and `RelaxedR1CSWitness::default` - let (nifs_primary, (r_U_primary_folded, r_W_primary_folded)) = match ( - self.r_U_primary.get(circuit_index), - self.r_W_primary.get(circuit_index), + let (r_U_primary, r_W_primary) = if let (Some(Some(r_U_primary)), Some(Some(r_W_primary))) = ( + self.r_U_primary.get_mut(circuit_index), + self.r_W_primary.get_mut(circuit_index), ) { - (Some(Some(r_U_primary)), Some(Some(r_W_primary))) => NIFS::prove( - &pp.ck_primary, - &pp.ro_consts_primary, - &self.pp_digest, - &pp[circuit_index].r1cs_shape, - r_U_primary, - r_W_primary, - &l_u_primary, - &l_w_primary, - ) - .map_err(SuperNovaError::NovaError)?, - _ => NIFS::prove( + (r_U_primary, r_W_primary) + } else { + self.r_U_primary[circuit_index] = Some(RelaxedR1CSInstance::default( &pp.ck_primary, - &pp.ro_consts_primary, - &self.pp_digest, &pp[circuit_index].r1cs_shape, - &RelaxedR1CSInstance::default(&pp.ck_primary, &pp[circuit_index].r1cs_shape), - &RelaxedR1CSWitness::default(&pp[circuit_index].r1cs_shape), - &l_u_primary, - &l_w_primary, + )); + self.r_W_primary[circuit_index] = + Some(RelaxedR1CSWitness::default(&pp[circuit_index].r1cs_shape)); + ( + self.r_U_primary[circuit_index].as_mut().unwrap(), + self.r_W_primary[circuit_index].as_mut().unwrap(), ) - .map_err(SuperNovaError::NovaError)?, }; - let mut cs_secondary = SatisfyingAssignment::::new(); + let nifs_primary = NIFS::prove_mut( + &pp.ck_primary, + &pp.ro_consts_primary, + &self.pp_digest, + &pp[circuit_index].r1cs_shape, + r_U_primary, + r_W_primary, + &l_u_primary, + &l_w_primary, + &mut self.buffer_primary.T, + &mut self.buffer_primary.ABC_Z_1, + &mut self.buffer_primary.ABC_Z_2, + ) + .map_err(SuperNovaError::NovaError)?; + + let mut cs_secondary = SatisfyingAssignment::::with_capacity( + pp.circuit_shape_secondary.r1cs_shape.num_io + 1, + pp.circuit_shape_secondary.r1cs_shape.num_vars, + ); let binding = Commitment::::decompress(&nifs_primary.comm_T).map_err(SuperNovaError::NovaError)?; let inputs_secondary: SuperNovaAugmentedCircuitInputs<'_, E1> = @@ -728,7 +788,7 @@ where E2::Scalar::from(self.i as u64), &self.z0_secondary, Some(&self.zi_secondary), - Some(&self.r_U_primary), + Some(&r_U_primary_i), Some(&l_u_primary), Some(&binding), None, // pc is always None for secondary circuit @@ -782,11 +842,6 @@ where )); } - // clone and updated running instance on respective circuit_index - self.r_U_primary[circuit_index] = Some(r_U_primary_folded); - self.r_W_primary[circuit_index] = Some(r_W_primary_folded); - self.r_W_secondary = r_W_secondary_next; - self.r_U_secondary = r_U_secondary_next; self.l_w_secondary = l_w_secondary_next; self.l_u_secondary = l_u_secondary_next; self.i += 1;