arnaucube
arnaucube/NovaPublic Self-hosted repository
Nova/src/lib.rs
src/lib.rsRaw
   1//! This library implements Nova, a high-speed recursive SNARK.
   2#![deny(
   3  warnings,
   4  unused,
   5  future_incompatible,
   6  nonstandard_style,
   7  rust_2018_idioms,
   8  missing_docs
   9)]
  10#![allow(non_snake_case)]
  11#![allow(clippy::type_complexity)]
  12#![forbid(unsafe_code)]
  13
  14// private modules
  15mod bellperson;
  16mod circuit;
  17mod constants;
  18mod nifs;
  19mod r1cs;
  20
  21// public modules
  22pub mod errors;
  23pub mod gadgets;
  24pub mod provider;
  25pub mod spartan;
  26pub mod traits;
  27
  28use crate::bellperson::{
  29  r1cs::{NovaShape, NovaWitness},
  30  shape_cs::ShapeCS,
  31  solver::SatisfyingAssignment,
  32};
  33use ::bellperson::{Circuit, ConstraintSystem};
  34use circuit::{NovaAugmentedCircuit, NovaAugmentedCircuitInputs, NovaAugmentedCircuitParams};
  35use constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS};
  36use core::marker::PhantomData;
  37use errors::NovaError;
  38use ff::Field;
  39use gadgets::utils::scalar_as_base;
  40use nifs::NIFS;
  41use r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness};
  42use serde::{Deserialize, Serialize};
  43use traits::{
  44  circuit::StepCircuit,
  45  commitment::{CommitmentEngineTrait, CommitmentTrait},
  46  snark::RelaxedR1CSSNARKTrait,
  47  AbsorbInROTrait, Group, ROConstants, ROConstantsCircuit, ROConstantsTrait, ROTrait,
  48};
  49
  50/// A type that holds public parameters of Nova
  51#[derive(Serialize, Deserialize)]
  52#[serde(bound = "")]
  53pub struct PublicParams<G1, G2, C1, C2>
  54where
  55  G1: Group<Base = <G2 as Group>::Scalar>,
  56  G2: Group<Base = <G1 as Group>::Scalar>,
  57  C1: StepCircuit<G1::Scalar>,
  58  C2: StepCircuit<G2::Scalar>,
  59{
  60  F_arity_primary: usize,
  61  F_arity_secondary: usize,
  62  ro_consts_primary: ROConstants<G1>,
  63  ro_consts_circuit_primary: ROConstantsCircuit<G2>,
  64  ck_primary: CommitmentKey<G1>,
  65  r1cs_shape_primary: R1CSShape<G1>,
  66  ro_consts_secondary: ROConstants<G2>,
  67  ro_consts_circuit_secondary: ROConstantsCircuit<G1>,
  68  ck_secondary: CommitmentKey<G2>,
  69  r1cs_shape_secondary: R1CSShape<G2>,
  70  augmented_circuit_params_primary: NovaAugmentedCircuitParams,
  71  augmented_circuit_params_secondary: NovaAugmentedCircuitParams,
  72  _p_c1: PhantomData<C1>,
  73  _p_c2: PhantomData<C2>,
  74}
  75
  76impl<G1, G2, C1, C2> PublicParams<G1, G2, C1, C2>
  77where
  78  G1: Group<Base = <G2 as Group>::Scalar>,
  79  G2: Group<Base = <G1 as Group>::Scalar>,
  80  C1: StepCircuit<G1::Scalar>,
  81  C2: StepCircuit<G2::Scalar>,
  82{
  83  /// Create a new `PublicParams`
  84  pub fn setup(c_primary: C1, c_secondary: C2) -> Self {
  85    let augmented_circuit_params_primary =
  86      NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true);
  87    let augmented_circuit_params_secondary =
  88      NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
  89
  90    let ro_consts_primary: ROConstants<G1> = ROConstants::<G1>::new();
  91    let ro_consts_secondary: ROConstants<G2> = ROConstants::<G2>::new();
  92
  93    let F_arity_primary = c_primary.arity();
  94    let F_arity_secondary = c_secondary.arity();
  95
  96    // ro_consts_circuit_primart are parameterized by G2 because the type alias uses G2::Base = G1::Scalar
  97    let ro_consts_circuit_primary: ROConstantsCircuit<G2> = ROConstantsCircuit::<G2>::new();
  98    let ro_consts_circuit_secondary: ROConstantsCircuit<G1> = ROConstantsCircuit::<G1>::new();
  99
 100    // Initialize ck for the primary
 101    let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
 102      augmented_circuit_params_primary.clone(),
 103      None,
 104      c_primary,
 105      ro_consts_circuit_primary.clone(),
 106    );
 107    let mut cs: ShapeCS<G1> = ShapeCS::new();
 108    let _ = circuit_primary.synthesize(&mut cs);
 109    let (r1cs_shape_primary, ck_primary) = (cs.r1cs_shape(), cs.commitment_key());
 110
 111    // Initialize ck for the secondary
 112    let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
 113      augmented_circuit_params_secondary.clone(),
 114      None,
 115      c_secondary,
 116      ro_consts_circuit_secondary.clone(),
 117    );
 118    let mut cs: ShapeCS<G2> = ShapeCS::new();
 119    let _ = circuit_secondary.synthesize(&mut cs);
 120    let (r1cs_shape_secondary, ck_secondary) = (cs.r1cs_shape(), cs.commitment_key());
 121
 122    Self {
 123      F_arity_primary,
 124      F_arity_secondary,
 125      ro_consts_primary,
 126      ro_consts_circuit_primary,
 127      ck_primary,
 128      r1cs_shape_primary,
 129      ro_consts_secondary,
 130      ro_consts_circuit_secondary,
 131      ck_secondary,
 132      r1cs_shape_secondary,
 133      augmented_circuit_params_primary,
 134      augmented_circuit_params_secondary,
 135      _p_c1: Default::default(),
 136      _p_c2: Default::default(),
 137    }
 138  }
 139
 140  /// Returns the number of constraints in the primary and secondary circuits
 141  pub fn num_constraints(&self) -> (usize, usize) {
 142    (
 143      self.r1cs_shape_primary.num_cons,
 144      self.r1cs_shape_secondary.num_cons,
 145    )
 146  }
 147
 148  /// Returns the number of variables in the primary and secondary circuits
 149  pub fn num_variables(&self) -> (usize, usize) {
 150    (
 151      self.r1cs_shape_primary.num_vars,
 152      self.r1cs_shape_secondary.num_vars,
 153    )
 154  }
 155}
 156
 157/// A SNARK that proves the correct execution of an incremental computation
 158#[derive(Clone, Debug, Serialize, Deserialize)]
 159#[serde(bound = "")]
 160pub struct RecursiveSNARK<G1, G2, C1, C2>
 161where
 162  G1: Group<Base = <G2 as Group>::Scalar>,
 163  G2: Group<Base = <G1 as Group>::Scalar>,
 164  C1: StepCircuit<G1::Scalar>,
 165  C2: StepCircuit<G2::Scalar>,
 166{
 167  r_W_primary: RelaxedR1CSWitness<G1>,
 168  r_U_primary: RelaxedR1CSInstance<G1>,
 169  l_w_primary: R1CSWitness<G1>,
 170  l_u_primary: R1CSInstance<G1>,
 171  r_W_secondary: RelaxedR1CSWitness<G2>,
 172  r_U_secondary: RelaxedR1CSInstance<G2>,
 173  l_w_secondary: R1CSWitness<G2>,
 174  l_u_secondary: R1CSInstance<G2>,
 175  i: usize,
 176  zi_primary: Vec<G1::Scalar>,
 177  zi_secondary: Vec<G2::Scalar>,
 178  _p_c1: PhantomData<C1>,
 179  _p_c2: PhantomData<C2>,
 180}
 181
 182impl<G1, G2, C1, C2> RecursiveSNARK<G1, G2, C1, C2>
 183where
 184  G1: Group<Base = <G2 as Group>::Scalar>,
 185  G2: Group<Base = <G1 as Group>::Scalar>,
 186  C1: StepCircuit<G1::Scalar>,
 187  C2: StepCircuit<G2::Scalar>,
 188{
 189  /// Create a new `RecursiveSNARK` (or updates the provided `RecursiveSNARK`)
 190  /// by executing a step of the incremental computation
 191  pub fn prove_step(
 192    pp: &PublicParams<G1, G2, C1, C2>,
 193    recursive_snark: Option<Self>,
 194    c_primary: C1,
 195    c_secondary: C2,
 196    z0_primary: Vec<G1::Scalar>,
 197    z0_secondary: Vec<G2::Scalar>,
 198  ) -> Result<Self, NovaError> {
 199    if z0_primary.len() != pp.F_arity_primary || z0_secondary.len() != pp.F_arity_secondary {
 200      return Err(NovaError::InvalidInitialInputLength);
 201    }
 202
 203    match recursive_snark {
 204      None => {
 205        // base case for the primary
 206        let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
 207        let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
 208          pp.r1cs_shape_secondary.get_digest(),
 209          G1::Scalar::zero(),
 210          z0_primary.clone(),
 211          None,
 212          None,
 213          None,
 214          None,
 215        );
 216
 217        let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
 218          pp.augmented_circuit_params_primary.clone(),
 219          Some(inputs_primary),
 220          c_primary.clone(),
 221          pp.ro_consts_circuit_primary.clone(),
 222        );
 223        let _ = circuit_primary.synthesize(&mut cs_primary);
 224        let (u_primary, w_primary) = cs_primary
 225          .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary)
 226          .map_err(|_e| NovaError::UnSat)?;
 227
 228        // base case for the secondary
 229        let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
 230        let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
 231          pp.r1cs_shape_primary.get_digest(),
 232          G2::Scalar::zero(),
 233          z0_secondary.clone(),
 234          None,
 235          None,
 236          Some(u_primary.clone()),
 237          None,
 238        );
 239        let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
 240          pp.augmented_circuit_params_secondary.clone(),
 241          Some(inputs_secondary),
 242          c_secondary.clone(),
 243          pp.ro_consts_circuit_secondary.clone(),
 244        );
 245        let _ = circuit_secondary.synthesize(&mut cs_secondary);
 246        let (u_secondary, w_secondary) = cs_secondary
 247          .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary)
 248          .map_err(|_e| NovaError::UnSat)?;
 249
 250        // IVC proof for the primary circuit
 251        let l_w_primary = w_primary;
 252        let l_u_primary = u_primary;
 253        let r_W_primary =
 254          RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &l_w_primary);
 255        let r_U_primary = RelaxedR1CSInstance::from_r1cs_instance(
 256          &pp.ck_primary,
 257          &pp.r1cs_shape_primary,
 258          &l_u_primary,
 259        );
 260
 261        // IVC proof of the secondary circuit
 262        let l_w_secondary = w_secondary;
 263        let l_u_secondary = u_secondary;
 264        let r_W_secondary = RelaxedR1CSWitness::<G2>::default(&pp.r1cs_shape_secondary);
 265        let r_U_secondary =
 266          RelaxedR1CSInstance::<G2>::default(&pp.ck_secondary, &pp.r1cs_shape_secondary);
 267
 268        // Outputs of the two circuits thus far
 269        let zi_primary = c_primary.output(&z0_primary);
 270        let zi_secondary = c_secondary.output(&z0_secondary);
 271
 272        if z0_primary.len() != pp.F_arity_primary || z0_secondary.len() != pp.F_arity_secondary {
 273          return Err(NovaError::InvalidStepOutputLength);
 274        }
 275
 276        Ok(Self {
 277          r_W_primary,
 278          r_U_primary,
 279          l_w_primary,
 280          l_u_primary,
 281          r_W_secondary,
 282          r_U_secondary,
 283          l_w_secondary,
 284          l_u_secondary,
 285          i: 1_usize,
 286          zi_primary,
 287          zi_secondary,
 288          _p_c1: Default::default(),
 289          _p_c2: Default::default(),
 290        })
 291      }
 292      Some(r_snark) => {
 293        // fold the secondary circuit's instance
 294        let (nifs_secondary, (r_U_secondary, r_W_secondary)) = NIFS::prove(
 295          &pp.ck_secondary,
 296          &pp.ro_consts_secondary,
 297          &pp.r1cs_shape_secondary,
 298          &r_snark.r_U_secondary,
 299          &r_snark.r_W_secondary,
 300          &r_snark.l_u_secondary,
 301          &r_snark.l_w_secondary,
 302        )?;
 303
 304        let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
 305        let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
 306          pp.r1cs_shape_secondary.get_digest(),
 307          G1::Scalar::from(r_snark.i as u64),
 308          z0_primary,
 309          Some(r_snark.zi_primary.clone()),
 310          Some(r_snark.r_U_secondary.clone()),
 311          Some(r_snark.l_u_secondary.clone()),
 312          Some(Commitment::<G2>::decompress(&nifs_secondary.comm_T)?),
 313        );
 314
 315        let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
 316          pp.augmented_circuit_params_primary.clone(),
 317          Some(inputs_primary),
 318          c_primary.clone(),
 319          pp.ro_consts_circuit_primary.clone(),
 320        );
 321        let _ = circuit_primary.synthesize(&mut cs_primary);
 322
 323        let (l_u_primary, l_w_primary) = cs_primary
 324          .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary)
 325          .map_err(|_e| NovaError::UnSat)?;
 326
 327        // fold the primary circuit's instance
 328        let (nifs_primary, (r_U_primary, r_W_primary)) = NIFS::prove(
 329          &pp.ck_primary,
 330          &pp.ro_consts_primary,
 331          &pp.r1cs_shape_primary,
 332          &r_snark.r_U_primary,
 333          &r_snark.r_W_primary,
 334          &l_u_primary,
 335          &l_w_primary,
 336        )?;
 337
 338        let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
 339        let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
 340          pp.r1cs_shape_primary.get_digest(),
 341          G2::Scalar::from(r_snark.i as u64),
 342          z0_secondary,
 343          Some(r_snark.zi_secondary.clone()),
 344          Some(r_snark.r_U_primary.clone()),
 345          Some(l_u_primary.clone()),
 346          Some(Commitment::<G1>::decompress(&nifs_primary.comm_T)?),
 347        );
 348
 349        let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
 350          pp.augmented_circuit_params_secondary.clone(),
 351          Some(inputs_secondary),
 352          c_secondary.clone(),
 353          pp.ro_consts_circuit_secondary.clone(),
 354        );
 355        let _ = circuit_secondary.synthesize(&mut cs_secondary);
 356
 357        let (l_u_secondary, l_w_secondary) = cs_secondary
 358          .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary)
 359          .map_err(|_e| NovaError::UnSat)?;
 360
 361        // update the running instances and witnesses
 362        let zi_primary = c_primary.output(&r_snark.zi_primary);
 363        let zi_secondary = c_secondary.output(&r_snark.zi_secondary);
 364
 365        Ok(Self {
 366          r_W_primary,
 367          r_U_primary,
 368          l_w_primary,
 369          l_u_primary,
 370          r_W_secondary,
 371          r_U_secondary,
 372          l_w_secondary,
 373          l_u_secondary,
 374          i: r_snark.i + 1,
 375          zi_primary,
 376          zi_secondary,
 377          _p_c1: Default::default(),
 378          _p_c2: Default::default(),
 379        })
 380      }
 381    }
 382  }
 383
 384  /// Verify the correctness of the `RecursiveSNARK`
 385  pub fn verify(
 386    &self,
 387    pp: &PublicParams<G1, G2, C1, C2>,
 388    num_steps: usize,
 389    z0_primary: Vec<G1::Scalar>,
 390    z0_secondary: Vec<G2::Scalar>,
 391  ) -> Result<(Vec<G1::Scalar>, Vec<G2::Scalar>), NovaError> {
 392    // number of steps cannot be zero
 393    if num_steps == 0 {
 394      return Err(NovaError::ProofVerifyError);
 395    }
 396
 397    // check if the provided proof has executed num_steps
 398    if self.i != num_steps {
 399      return Err(NovaError::ProofVerifyError);
 400    }
 401
 402    // check if the (relaxed) R1CS instances have two public outputs
 403    if self.l_u_primary.X.len() != 2
 404      || self.l_u_secondary.X.len() != 2
 405      || self.r_U_primary.X.len() != 2
 406      || self.r_U_secondary.X.len() != 2
 407    {
 408      return Err(NovaError::ProofVerifyError);
 409    }
 410
 411    // check if the output hashes in R1CS instances point to the right running instances
 412    let (hash_primary, hash_secondary) = {
 413      let mut hasher = <G2 as Group>::RO::new(
 414        pp.ro_consts_secondary.clone(),
 415        NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_primary,
 416      );
 417      hasher.absorb(scalar_as_base::<G2>(pp.r1cs_shape_secondary.get_digest()));
 418      hasher.absorb(G1::Scalar::from(num_steps as u64));
 419      for e in &z0_primary {
 420        hasher.absorb(*e);
 421      }
 422      for e in &self.zi_primary {
 423        hasher.absorb(*e);
 424      }
 425      self.r_U_secondary.absorb_in_ro(&mut hasher);
 426
 427      let mut hasher2 = <G1 as Group>::RO::new(
 428        pp.ro_consts_primary.clone(),
 429        NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_secondary,
 430      );
 431      hasher2.absorb(scalar_as_base::<G1>(pp.r1cs_shape_primary.get_digest()));
 432      hasher2.absorb(G2::Scalar::from(num_steps as u64));
 433      for e in &z0_secondary {
 434        hasher2.absorb(*e);
 435      }
 436      for e in &self.zi_secondary {
 437        hasher2.absorb(*e);
 438      }
 439      self.r_U_primary.absorb_in_ro(&mut hasher2);
 440
 441      (
 442        hasher.squeeze(NUM_HASH_BITS),
 443        hasher2.squeeze(NUM_HASH_BITS),
 444      )
 445    };
 446
 447    if hash_primary != scalar_as_base::<G1>(self.l_u_primary.X[1])
 448      || hash_secondary != scalar_as_base::<G2>(self.l_u_secondary.X[1])
 449    {
 450      return Err(NovaError::ProofVerifyError);
 451    }
 452
 453    // check the satisfiability of the provided instances
 454    let ((res_r_primary, res_l_primary), (res_r_secondary, res_l_secondary)) = rayon::join(
 455      || {
 456        rayon::join(
 457          || {
 458            pp.r1cs_shape_primary.is_sat_relaxed(
 459              &pp.ck_primary,
 460              &self.r_U_primary,
 461              &self.r_W_primary,
 462            )
 463          },
 464          || {
 465            pp.r1cs_shape_primary
 466              .is_sat(&pp.ck_primary, &self.l_u_primary, &self.l_w_primary)
 467          },
 468        )
 469      },
 470      || {
 471        rayon::join(
 472          || {
 473            pp.r1cs_shape_secondary.is_sat_relaxed(
 474              &pp.ck_secondary,
 475              &self.r_U_secondary,
 476              &self.r_W_secondary,
 477            )
 478          },
 479          || {
 480            pp.r1cs_shape_secondary.is_sat(
 481              &pp.ck_secondary,
 482              &self.l_u_secondary,
 483              &self.l_w_secondary,
 484            )
 485          },
 486        )
 487      },
 488    );
 489
 490    // check the returned res objects
 491    res_r_primary?;
 492    res_l_primary?;
 493    res_r_secondary?;
 494    res_l_secondary?;
 495
 496    Ok((self.zi_primary.clone(), self.zi_secondary.clone()))
 497  }
 498}
 499
 500/// A type that holds the prover key for `CompressedSNARK`
 501#[derive(Clone, Debug, Serialize, Deserialize)]
 502#[serde(bound = "")]
 503pub struct ProverKey<G1, G2, C1, C2, S1, S2>
 504where
 505  G1: Group<Base = <G2 as Group>::Scalar>,
 506  G2: Group<Base = <G1 as Group>::Scalar>,
 507  C1: StepCircuit<G1::Scalar>,
 508  C2: StepCircuit<G2::Scalar>,
 509  S1: RelaxedR1CSSNARKTrait<G1>,
 510  S2: RelaxedR1CSSNARKTrait<G2>,
 511{
 512  pk_primary: S1::ProverKey,
 513  pk_secondary: S2::ProverKey,
 514  _p_c1: PhantomData<C1>,
 515  _p_c2: PhantomData<C2>,
 516}
 517
 518/// A type that holds the verifier key for `CompressedSNARK`
 519#[derive(Clone, Serialize, Deserialize)]
 520#[serde(bound = "")]
 521pub struct VerifierKey<G1, G2, C1, C2, S1, S2>
 522where
 523  G1: Group<Base = <G2 as Group>::Scalar>,
 524  G2: Group<Base = <G1 as Group>::Scalar>,
 525  C1: StepCircuit<G1::Scalar>,
 526  C2: StepCircuit<G2::Scalar>,
 527  S1: RelaxedR1CSSNARKTrait<G1>,
 528  S2: RelaxedR1CSSNARKTrait<G2>,
 529{
 530  F_arity_primary: usize,
 531  F_arity_secondary: usize,
 532  ro_consts_primary: ROConstants<G1>,
 533  ro_consts_secondary: ROConstants<G2>,
 534  r1cs_shape_primary_digest: G1::Scalar,
 535  r1cs_shape_secondary_digest: G2::Scalar,
 536  vk_primary: S1::VerifierKey,
 537  vk_secondary: S2::VerifierKey,
 538  _p_c1: PhantomData<C1>,
 539  _p_c2: PhantomData<C2>,
 540}
 541
 542/// A SNARK that proves the knowledge of a valid `RecursiveSNARK`
 543#[derive(Clone, Serialize, Deserialize)]
 544#[serde(bound = "")]
 545pub struct CompressedSNARK<G1, G2, C1, C2, S1, S2>
 546where
 547  G1: Group<Base = <G2 as Group>::Scalar>,
 548  G2: Group<Base = <G1 as Group>::Scalar>,
 549  C1: StepCircuit<G1::Scalar>,
 550  C2: StepCircuit<G2::Scalar>,
 551  S1: RelaxedR1CSSNARKTrait<G1>,
 552  S2: RelaxedR1CSSNARKTrait<G2>,
 553{
 554  r_U_primary: RelaxedR1CSInstance<G1>,
 555  l_u_primary: R1CSInstance<G1>,
 556  nifs_primary: NIFS<G1>,
 557  f_W_snark_primary: S1,
 558
 559  r_U_secondary: RelaxedR1CSInstance<G2>,
 560  l_u_secondary: R1CSInstance<G2>,
 561  nifs_secondary: NIFS<G2>,
 562  f_W_snark_secondary: S2,
 563
 564  zn_primary: Vec<G1::Scalar>,
 565  zn_secondary: Vec<G2::Scalar>,
 566
 567  _p_c1: PhantomData<C1>,
 568  _p_c2: PhantomData<C2>,
 569}
 570
 571impl<G1, G2, C1, C2, S1, S2> CompressedSNARK<G1, G2, C1, C2, S1, S2>
 572where
 573  G1: Group<Base = <G2 as Group>::Scalar>,
 574  G2: Group<Base = <G1 as Group>::Scalar>,
 575  C1: StepCircuit<G1::Scalar>,
 576  C2: StepCircuit<G2::Scalar>,
 577  S1: RelaxedR1CSSNARKTrait<G1>,
 578  S2: RelaxedR1CSSNARKTrait<G2>,
 579{
 580  /// Creates prover and verifier keys for `CompressedSNARK`
 581  pub fn setup(
 582    pp: &PublicParams<G1, G2, C1, C2>,
 583  ) -> (
 584    ProverKey<G1, G2, C1, C2, S1, S2>,
 585    VerifierKey<G1, G2, C1, C2, S1, S2>,
 586  ) {
 587    let (pk_primary, vk_primary) = S1::setup(&pp.ck_primary, &pp.r1cs_shape_primary);
 588    let (pk_secondary, vk_secondary) = S2::setup(&pp.ck_secondary, &pp.r1cs_shape_secondary);
 589
 590    let pk = ProverKey {
 591      pk_primary,
 592      pk_secondary,
 593      _p_c1: Default::default(),
 594      _p_c2: Default::default(),
 595    };
 596
 597    let vk = VerifierKey {
 598      F_arity_primary: pp.F_arity_primary,
 599      F_arity_secondary: pp.F_arity_secondary,
 600      ro_consts_primary: pp.ro_consts_primary.clone(),
 601      ro_consts_secondary: pp.ro_consts_secondary.clone(),
 602      r1cs_shape_primary_digest: pp.r1cs_shape_primary.get_digest(),
 603      r1cs_shape_secondary_digest: pp.r1cs_shape_secondary.get_digest(),
 604      vk_primary,
 605      vk_secondary,
 606      _p_c1: Default::default(),
 607      _p_c2: Default::default(),
 608    };
 609
 610    (pk, vk)
 611  }
 612
 613  /// Create a new `CompressedSNARK`
 614  pub fn prove(
 615    pp: &PublicParams<G1, G2, C1, C2>,
 616    pk: &ProverKey<G1, G2, C1, C2, S1, S2>,
 617    recursive_snark: &RecursiveSNARK<G1, G2, C1, C2>,
 618  ) -> Result<Self, NovaError> {
 619    let (res_primary, res_secondary) = rayon::join(
 620      // fold the primary circuit's instance
 621      || {
 622        NIFS::prove(
 623          &pp.ck_primary,
 624          &pp.ro_consts_primary,
 625          &pp.r1cs_shape_primary,
 626          &recursive_snark.r_U_primary,
 627          &recursive_snark.r_W_primary,
 628          &recursive_snark.l_u_primary,
 629          &recursive_snark.l_w_primary,
 630        )
 631      },
 632      || {
 633        // fold the secondary circuit's instance
 634        NIFS::prove(
 635          &pp.ck_secondary,
 636          &pp.ro_consts_secondary,
 637          &pp.r1cs_shape_secondary,
 638          &recursive_snark.r_U_secondary,
 639          &recursive_snark.r_W_secondary,
 640          &recursive_snark.l_u_secondary,
 641          &recursive_snark.l_w_secondary,
 642        )
 643      },
 644    );
 645
 646    let (nifs_primary, (f_U_primary, f_W_primary)) = res_primary?;
 647    let (nifs_secondary, (f_U_secondary, f_W_secondary)) = res_secondary?;
 648
 649    // create SNARKs proving the knowledge of f_W_primary and f_W_secondary
 650    let (f_W_snark_primary, f_W_snark_secondary) = rayon::join(
 651      || S1::prove(&pp.ck_primary, &pk.pk_primary, &f_U_primary, &f_W_primary),
 652      || {
 653        S2::prove(
 654          &pp.ck_secondary,
 655          &pk.pk_secondary,
 656          &f_U_secondary,
 657          &f_W_secondary,
 658        )
 659      },
 660    );
 661
 662    Ok(Self {
 663      r_U_primary: recursive_snark.r_U_primary.clone(),
 664      l_u_primary: recursive_snark.l_u_primary.clone(),
 665      nifs_primary,
 666      f_W_snark_primary: f_W_snark_primary?,
 667
 668      r_U_secondary: recursive_snark.r_U_secondary.clone(),
 669      l_u_secondary: recursive_snark.l_u_secondary.clone(),
 670      nifs_secondary,
 671      f_W_snark_secondary: f_W_snark_secondary?,
 672
 673      zn_primary: recursive_snark.zi_primary.clone(),
 674      zn_secondary: recursive_snark.zi_secondary.clone(),
 675
 676      _p_c1: Default::default(),
 677      _p_c2: Default::default(),
 678    })
 679  }
 680
 681  /// Verify the correctness of the `CompressedSNARK`
 682  pub fn verify(
 683    &self,
 684    vk: &VerifierKey<G1, G2, C1, C2, S1, S2>,
 685    num_steps: usize,
 686    z0_primary: Vec<G1::Scalar>,
 687    z0_secondary: Vec<G2::Scalar>,
 688  ) -> Result<(Vec<G1::Scalar>, Vec<G2::Scalar>), NovaError> {
 689    // number of steps cannot be zero
 690    if num_steps == 0 {
 691      return Err(NovaError::ProofVerifyError);
 692    }
 693
 694    // check if the (relaxed) R1CS instances have two public outputs
 695    if self.l_u_primary.X.len() != 2
 696      || self.l_u_secondary.X.len() != 2
 697      || self.r_U_primary.X.len() != 2
 698      || self.r_U_secondary.X.len() != 2
 699    {
 700      return Err(NovaError::ProofVerifyError);
 701    }
 702
 703    // check if the output hashes in R1CS instances point to the right running instances
 704    let (hash_primary, hash_secondary) = {
 705      let mut hasher = <G2 as Group>::RO::new(
 706        vk.ro_consts_secondary.clone(),
 707        NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_primary,
 708      );
 709      hasher.absorb(scalar_as_base::<G2>(vk.r1cs_shape_secondary_digest));
 710      hasher.absorb(G1::Scalar::from(num_steps as u64));
 711      for e in z0_primary {
 712        hasher.absorb(e);
 713      }
 714      for e in &self.zn_primary {
 715        hasher.absorb(*e);
 716      }
 717      self.r_U_secondary.absorb_in_ro(&mut hasher);
 718
 719      let mut hasher2 = <G1 as Group>::RO::new(
 720        vk.ro_consts_primary.clone(),
 721        NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_secondary,
 722      );
 723      hasher2.absorb(scalar_as_base::<G1>(vk.r1cs_shape_primary_digest));
 724      hasher2.absorb(G2::Scalar::from(num_steps as u64));
 725      for e in z0_secondary {
 726        hasher2.absorb(e);
 727      }
 728      for e in &self.zn_secondary {
 729        hasher2.absorb(*e);
 730      }
 731      self.r_U_primary.absorb_in_ro(&mut hasher2);
 732
 733      (
 734        hasher.squeeze(NUM_HASH_BITS),
 735        hasher2.squeeze(NUM_HASH_BITS),
 736      )
 737    };
 738
 739    if hash_primary != scalar_as_base::<G1>(self.l_u_primary.X[1])
 740      || hash_secondary != scalar_as_base::<G2>(self.l_u_secondary.X[1])
 741    {
 742      return Err(NovaError::ProofVerifyError);
 743    }
 744
 745    // fold the running instance and last instance to get a folded instance
 746    let f_U_primary = self.nifs_primary.verify(
 747      &vk.ro_consts_primary,
 748      &vk.r1cs_shape_primary_digest,
 749      &self.r_U_primary,
 750      &self.l_u_primary,
 751    )?;
 752    let f_U_secondary = self.nifs_secondary.verify(
 753      &vk.ro_consts_secondary,
 754      &vk.r1cs_shape_secondary_digest,
 755      &self.r_U_secondary,
 756      &self.l_u_secondary,
 757    )?;
 758
 759    // check the satisfiability of the folded instances using SNARKs proving the knowledge of their satisfying witnesses
 760    let (res_primary, res_secondary) = rayon::join(
 761      || self.f_W_snark_primary.verify(&vk.vk_primary, &f_U_primary),
 762      || {
 763        self
 764          .f_W_snark_secondary
 765          .verify(&vk.vk_secondary, &f_U_secondary)
 766      },
 767    );
 768
 769    res_primary?;
 770    res_secondary?;
 771
 772    Ok((self.zn_primary.clone(), self.zn_secondary.clone()))
 773  }
 774}
 775
 776type CommitmentKey<G> = <<G as traits::Group>::CE as CommitmentEngineTrait<G>>::CommitmentKey;
 777type Commitment<G> = <<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment;
 778type CompressedCommitment<G> = <<<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment as CommitmentTrait<G>>::CompressedCommitment;
 779type CE<G> = <G as Group>::CE;
 780
 781#[cfg(test)]
 782mod tests {
 783  use super::*;
 784  type G1 = pasta_curves::pallas::Point;
 785  type G2 = pasta_curves::vesta::Point;
 786  type EE1 = provider::ipa_pc::EvaluationEngine<G1>;
 787  type EE2 = provider::ipa_pc::EvaluationEngine<G2>;
 788  type S1 = spartan::RelaxedR1CSSNARK<G1, EE1>;
 789  type S2 = spartan::RelaxedR1CSSNARK<G2, EE2>;
 790  use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError};
 791  use core::marker::PhantomData;
 792  use ff::PrimeField;
 793  use traits::circuit::TrivialTestCircuit;
 794
 795  #[derive(Clone, Debug, Default)]
 796  struct CubicCircuit<F: PrimeField> {
 797    _p: PhantomData<F>,
 798  }
 799
 800  impl<F> StepCircuit<F> for CubicCircuit<F>
 801  where
 802    F: PrimeField,
 803  {
 804    fn arity(&self) -> usize {
 805      1
 806    }
 807
 808    fn synthesize<CS: ConstraintSystem<F>>(
 809      &self,
 810      cs: &mut CS,
 811      z: &[AllocatedNum<F>],
 812    ) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
 813      // Consider a cubic equation: `x^3 + x + 5 = y`, where `x` and `y` are respectively the input and output.
 814      let x = &z[0];
 815      let x_sq = x.square(cs.namespace(|| "x_sq"))?;
 816      let x_cu = x_sq.mul(cs.namespace(|| "x_cu"), x)?;
 817      let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
 818        Ok(x_cu.get_value().unwrap() + x.get_value().unwrap() + F::from(5u64))
 819      })?;
 820
 821      cs.enforce(
 822        || "y = x^3 + x + 5",
 823        |lc| {
 824          lc + x_cu.get_variable()
 825            + x.get_variable()
 826            + CS::one()
 827            + CS::one()
 828            + CS::one()
 829            + CS::one()
 830            + CS::one()
 831        },
 832        |lc| lc + CS::one(),
 833        |lc| lc + y.get_variable(),
 834      );
 835
 836      Ok(vec![y])
 837    }
 838
 839    fn output(&self, z: &[F]) -> Vec<F> {
 840      vec![z[0] * z[0] * z[0] + z[0] + F::from(5u64)]
 841    }
 842  }
 843
 844  #[test]
 845  fn test_ivc_trivial() {
 846    // produce public parameters
 847    let pp = PublicParams::<
 848      G1,
 849      G2,
 850      TrivialTestCircuit<<G1 as Group>::Scalar>,
 851      TrivialTestCircuit<<G2 as Group>::Scalar>,
 852    >::setup(TrivialTestCircuit::default(), TrivialTestCircuit::default());
 853
 854    let num_steps = 1;
 855
 856    // produce a recursive SNARK
 857    let res = RecursiveSNARK::prove_step(
 858      &pp,
 859      None,
 860      TrivialTestCircuit::default(),
 861      TrivialTestCircuit::default(),
 862      vec![<G1 as Group>::Scalar::zero()],
 863      vec![<G2 as Group>::Scalar::zero()],
 864    );
 865    assert!(res.is_ok());
 866    let recursive_snark = res.unwrap();
 867
 868    // verify the recursive SNARK
 869    let res = recursive_snark.verify(
 870      &pp,
 871      num_steps,
 872      vec![<G1 as Group>::Scalar::zero()],
 873      vec![<G2 as Group>::Scalar::zero()],
 874    );
 875    assert!(res.is_ok());
 876  }
 877
 878  #[test]
 879  fn test_ivc_nontrivial() {
 880    let circuit_primary = TrivialTestCircuit::default();
 881    let circuit_secondary = CubicCircuit::default();
 882
 883    // produce public parameters
 884    let pp = PublicParams::<
 885      G1,
 886      G2,
 887      TrivialTestCircuit<<G1 as Group>::Scalar>,
 888      CubicCircuit<<G2 as Group>::Scalar>,
 889    >::setup(circuit_primary.clone(), circuit_secondary.clone());
 890
 891    let num_steps = 3;
 892
 893    // produce a recursive SNARK
 894    let mut recursive_snark: Option<
 895      RecursiveSNARK<
 896        G1,
 897        G2,
 898        TrivialTestCircuit<<G1 as Group>::Scalar>,
 899        CubicCircuit<<G2 as Group>::Scalar>,
 900      >,
 901    > = None;
 902
 903    for i in 0..num_steps {
 904      let res = RecursiveSNARK::prove_step(
 905        &pp,
 906        recursive_snark,
 907        circuit_primary.clone(),
 908        circuit_secondary.clone(),
 909        vec![<G1 as Group>::Scalar::one()],
 910        vec![<G2 as Group>::Scalar::zero()],
 911      );
 912      assert!(res.is_ok());
 913      let recursive_snark_unwrapped = res.unwrap();
 914
 915      // verify the recursive snark at each step of recursion
 916      let res = recursive_snark_unwrapped.verify(
 917        &pp,
 918        i + 1,
 919        vec![<G1 as Group>::Scalar::one()],
 920        vec![<G2 as Group>::Scalar::zero()],
 921      );
 922      assert!(res.is_ok());
 923
 924      // set the running variable for the next iteration
 925      recursive_snark = Some(recursive_snark_unwrapped);
 926    }
 927
 928    assert!(recursive_snark.is_some());
 929    let recursive_snark = recursive_snark.unwrap();
 930
 931    // verify the recursive SNARK
 932    let res = recursive_snark.verify(
 933      &pp,
 934      num_steps,
 935      vec![<G1 as Group>::Scalar::one()],
 936      vec![<G2 as Group>::Scalar::zero()],
 937    );
 938    assert!(res.is_ok());
 939
 940    let (zn_primary, zn_secondary) = res.unwrap();
 941
 942    // sanity: check the claimed output with a direct computation of the same
 943    assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::one()]);
 944    let mut zn_secondary_direct = vec![<G2 as Group>::Scalar::zero()];
 945    for _i in 0..num_steps {
 946      zn_secondary_direct = CubicCircuit::default().output(&zn_secondary_direct);
 947    }
 948    assert_eq!(zn_secondary, zn_secondary_direct);
 949    assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);
 950  }
 951
 952  #[test]
 953  fn test_ivc_nontrivial_with_compression() {
 954    let circuit_primary = TrivialTestCircuit::default();
 955    let circuit_secondary = CubicCircuit::default();
 956
 957    // produce public parameters
 958    let pp = PublicParams::<
 959      G1,
 960      G2,
 961      TrivialTestCircuit<<G1 as Group>::Scalar>,
 962      CubicCircuit<<G2 as Group>::Scalar>,
 963    >::setup(circuit_primary.clone(), circuit_secondary.clone());
 964
 965    let num_steps = 3;
 966
 967    // produce a recursive SNARK
 968    let mut recursive_snark: Option<
 969      RecursiveSNARK<
 970        G1,
 971        G2,
 972        TrivialTestCircuit<<G1 as Group>::Scalar>,
 973        CubicCircuit<<G2 as Group>::Scalar>,
 974      >,
 975    > = None;
 976
 977    for _i in 0..num_steps {
 978      let res = RecursiveSNARK::prove_step(
 979        &pp,
 980        recursive_snark,
 981        circuit_primary.clone(),
 982        circuit_secondary.clone(),
 983        vec![<G1 as Group>::Scalar::one()],
 984        vec![<G2 as Group>::Scalar::zero()],
 985      );
 986      assert!(res.is_ok());
 987      recursive_snark = Some(res.unwrap());
 988    }
 989
 990    assert!(recursive_snark.is_some());
 991    let recursive_snark = recursive_snark.unwrap();
 992
 993    // verify the recursive SNARK
 994    let res = recursive_snark.verify(
 995      &pp,
 996      num_steps,
 997      vec![<G1 as Group>::Scalar::one()],
 998      vec![<G2 as Group>::Scalar::zero()],
 999    );
1000    assert!(res.is_ok());
1001
1002    let (zn_primary, zn_secondary) = res.unwrap();
1003
1004    // sanity: check the claimed output with a direct computation of the same
1005    assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::one()]);
1006    let mut zn_secondary_direct = vec![<G2 as Group>::Scalar::zero()];
1007    for _i in 0..num_steps {
1008      zn_secondary_direct = CubicCircuit::default().output(&zn_secondary_direct);
1009    }
1010    assert_eq!(zn_secondary, zn_secondary_direct);
1011    assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);
1012
1013    // produce the prover and verifier keys for compressed snark
1014    let (pk, vk) = CompressedSNARK::<_, _, _, _, S1, S2>::setup(&pp);
1015
1016    // produce a compressed SNARK
1017    let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &pk, &recursive_snark);
1018    assert!(res.is_ok());
1019    let compressed_snark = res.unwrap();
1020
1021    // verify the compressed SNARK
1022    let res = compressed_snark.verify(
1023      &vk,
1024      num_steps,
1025      vec![<G1 as Group>::Scalar::one()],
1026      vec![<G2 as Group>::Scalar::zero()],
1027    );
1028    assert!(res.is_ok());
1029  }
1030
1031  #[test]
1032  fn test_ivc_nondet_with_compression() {
1033    // y is a non-deterministic advice representing the fifth root of the input at a step.
1034    #[derive(Clone, Debug)]
1035    struct FifthRootCheckingCircuit<F: PrimeField> {
1036      y: F,
1037    }
1038
1039    impl<F> FifthRootCheckingCircuit<F>
1040    where
1041      F: PrimeField,
1042    {
1043      fn new(num_steps: usize) -> (Vec<F>, Vec<Self>) {
1044        let mut powers = Vec::new();
1045        let rng = &mut rand::rngs::OsRng;
1046        let mut seed = F::random(rng);
1047        for _i in 0..num_steps + 1 {
1048          let mut power = seed;
1049          power = power.square();
1050          power = power.square();
1051          power *= seed;
1052
1053          powers.push(Self { y: power });
1054
1055          seed = power;
1056        }
1057
1058        // reverse the powers to get roots
1059        let roots = powers.into_iter().rev().collect::<Vec<Self>>();
1060        (vec![roots[0].y], roots[1..].to_vec())
1061      }
1062    }
1063
1064    impl<F> StepCircuit<F> for FifthRootCheckingCircuit<F>
1065    where
1066      F: PrimeField,
1067    {
1068      fn arity(&self) -> usize {
1069        1
1070      }
1071
1072      fn synthesize<CS: ConstraintSystem<F>>(
1073        &self,
1074        cs: &mut CS,
1075        z: &[AllocatedNum<F>],
1076      ) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
1077        let x = &z[0];
1078
1079        // we allocate a variable and set it to the provided non-derministic advice.
1080        let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(self.y))?;
1081
1082        // We now check if y = x^{1/5} by checking if y^5 = x
1083        let y_sq = y.square(cs.namespace(|| "y_sq"))?;
1084        let y_quad = y_sq.square(cs.namespace(|| "y_quad"))?;
1085        let y_pow_5 = y_quad.mul(cs.namespace(|| "y_fifth"), &y)?;
1086
1087        cs.enforce(
1088          || "y^5 = x",
1089          |lc| lc + y_pow_5.get_variable(),
1090          |lc| lc + CS::one(),
1091          |lc| lc + x.get_variable(),
1092        );
1093
1094        Ok(vec![y])
1095      }
1096
1097      fn output(&self, z: &[F]) -> Vec<F> {
1098        // sanity check
1099        let x = z[0];
1100        let y_pow_5 = {
1101          let y = self.y;
1102          let y_sq = y.square();
1103          let y_quad = y_sq.square();
1104          y_quad * self.y
1105        };
1106        assert_eq!(x, y_pow_5);
1107
1108        // return non-deterministic advice
1109        // as the output of the step
1110        vec![self.y]
1111      }
1112    }
1113
1114    let circuit_primary = FifthRootCheckingCircuit {
1115      y: <G1 as Group>::Scalar::zero(),
1116    };
1117
1118    let circuit_secondary = TrivialTestCircuit::default();
1119
1120    // produce public parameters
1121    let pp = PublicParams::<
1122      G1,
1123      G2,
1124      FifthRootCheckingCircuit<<G1 as Group>::Scalar>,
1125      TrivialTestCircuit<<G2 as Group>::Scalar>,
1126    >::setup(circuit_primary, circuit_secondary.clone());
1127
1128    let num_steps = 3;
1129
1130    // produce non-deterministic advice
1131    let (z0_primary, roots) = FifthRootCheckingCircuit::new(num_steps);
1132    let z0_secondary = vec![<G2 as Group>::Scalar::zero()];
1133
1134    // produce a recursive SNARK
1135    let mut recursive_snark: Option<
1136      RecursiveSNARK<
1137        G1,
1138        G2,
1139        FifthRootCheckingCircuit<<G1 as Group>::Scalar>,
1140        TrivialTestCircuit<<G2 as Group>::Scalar>,
1141      >,
1142    > = None;
1143
1144    for circuit_primary in roots.iter().take(num_steps) {
1145      let res = RecursiveSNARK::prove_step(
1146        &pp,
1147        recursive_snark,
1148        circuit_primary.clone(),
1149        circuit_secondary.clone(),
1150        z0_primary.clone(),
1151        z0_secondary.clone(),
1152      );
1153      assert!(res.is_ok());
1154      recursive_snark = Some(res.unwrap());
1155    }
1156
1157    assert!(recursive_snark.is_some());
1158    let recursive_snark = recursive_snark.unwrap();
1159
1160    // verify the recursive SNARK
1161    let res = recursive_snark.verify(&pp, num_steps, z0_primary.clone(), z0_secondary.clone());
1162    assert!(res.is_ok());
1163
1164    // produce the prover and verifier keys for compressed snark
1165    let (pk, vk) = CompressedSNARK::<_, _, _, _, S1, S2>::setup(&pp);
1166
1167    // produce a compressed SNARK
1168    let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &pk, &recursive_snark);
1169    assert!(res.is_ok());
1170    let compressed_snark = res.unwrap();
1171
1172    // verify the compressed SNARK
1173    let res = compressed_snark.verify(&vk, num_steps, z0_primary, z0_secondary);
1174    assert!(res.is_ok());
1175  }
1176
1177  #[test]
1178  fn test_ivc_base() {
1179    // produce public parameters
1180    let pp = PublicParams::<
1181      G1,
1182      G2,
1183      TrivialTestCircuit<<G1 as Group>::Scalar>,
1184      CubicCircuit<<G2 as Group>::Scalar>,
1185    >::setup(TrivialTestCircuit::default(), CubicCircuit::default());
1186
1187    let num_steps = 1;
1188
1189    // produce a recursive SNARK
1190    let res = RecursiveSNARK::prove_step(
1191      &pp,
1192      None,
1193      TrivialTestCircuit::default(),
1194      CubicCircuit::default(),
1195      vec![<G1 as Group>::Scalar::one()],
1196      vec![<G2 as Group>::Scalar::zero()],
1197    );
1198    assert!(res.is_ok());
1199    let recursive_snark = res.unwrap();
1200
1201    // verify the recursive SNARK
1202    let res = recursive_snark.verify(
1203      &pp,
1204      num_steps,
1205      vec![<G1 as Group>::Scalar::one()],
1206      vec![<G2 as Group>::Scalar::zero()],
1207    );
1208    assert!(res.is_ok());
1209
1210    let (zn_primary, zn_secondary) = res.unwrap();
1211
1212    assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::one()]);
1213    assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(5u64)]);
1214  }
1215}