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 sha3::{Digest, Sha3_256};
  44use traits::{
  45  circuit::StepCircuit,
  46  commitment::{CommitmentEngineTrait, CommitmentTrait},
  47  snark::RelaxedR1CSSNARKTrait,
  48  AbsorbInROTrait, Group, ROConstants, ROConstantsCircuit, ROConstantsTrait, ROTrait,
  49};
  50
  51/// A type that holds public parameters of Nova
  52#[derive(Serialize, Deserialize)]
  53#[serde(bound = "")]
  54pub struct PublicParams<G1, G2, C1, C2>
  55where
  56  G1: Group<Base = <G2 as Group>::Scalar>,
  57  G2: Group<Base = <G1 as Group>::Scalar>,
  58  C1: StepCircuit<G1::Scalar>,
  59  C2: StepCircuit<G2::Scalar>,
  60{
  61  F_arity_primary: usize,
  62  F_arity_secondary: usize,
  63  ro_consts_primary: ROConstants<G1>,
  64  ro_consts_circuit_primary: ROConstantsCircuit<G2>,
  65  ck_primary: CommitmentKey<G1>,
  66  r1cs_shape_primary: R1CSShape<G1>,
  67  ro_consts_secondary: ROConstants<G2>,
  68  ro_consts_circuit_secondary: ROConstantsCircuit<G1>,
  69  ck_secondary: CommitmentKey<G2>,
  70  r1cs_shape_secondary: R1CSShape<G2>,
  71  augmented_circuit_params_primary: NovaAugmentedCircuitParams,
  72  augmented_circuit_params_secondary: NovaAugmentedCircuitParams,
  73  digest: G1::Scalar, // digest of everything else with this field set to G1::Scalar::ZERO
  74  _p_c1: PhantomData<C1>,
  75  _p_c2: PhantomData<C2>,
  76}
  77
  78impl<G1, G2, C1, C2> PublicParams<G1, G2, C1, C2>
  79where
  80  G1: Group<Base = <G2 as Group>::Scalar>,
  81  G2: Group<Base = <G1 as Group>::Scalar>,
  82  C1: StepCircuit<G1::Scalar>,
  83  C2: StepCircuit<G2::Scalar>,
  84{
  85  /// Create a new `PublicParams`
  86  pub fn setup(c_primary: C1, c_secondary: C2) -> Self {
  87    let augmented_circuit_params_primary =
  88      NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true);
  89    let augmented_circuit_params_secondary =
  90      NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
  91
  92    let ro_consts_primary: ROConstants<G1> = ROConstants::<G1>::new();
  93    let ro_consts_secondary: ROConstants<G2> = ROConstants::<G2>::new();
  94
  95    let F_arity_primary = c_primary.arity();
  96    let F_arity_secondary = c_secondary.arity();
  97
  98    // ro_consts_circuit_primary are parameterized by G2 because the type alias uses G2::Base = G1::Scalar
  99    let ro_consts_circuit_primary: ROConstantsCircuit<G2> = ROConstantsCircuit::<G2>::new();
 100    let ro_consts_circuit_secondary: ROConstantsCircuit<G1> = ROConstantsCircuit::<G1>::new();
 101
 102    // Initialize ck for the primary
 103    let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
 104      augmented_circuit_params_primary.clone(),
 105      None,
 106      c_primary,
 107      ro_consts_circuit_primary.clone(),
 108    );
 109    let mut cs: ShapeCS<G1> = ShapeCS::new();
 110    let _ = circuit_primary.synthesize(&mut cs);
 111    let (r1cs_shape_primary, ck_primary) = cs.r1cs_shape();
 112
 113    // Initialize ck for the secondary
 114    let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
 115      augmented_circuit_params_secondary.clone(),
 116      None,
 117      c_secondary,
 118      ro_consts_circuit_secondary.clone(),
 119    );
 120    let mut cs: ShapeCS<G2> = ShapeCS::new();
 121    let _ = circuit_secondary.synthesize(&mut cs);
 122    let (r1cs_shape_secondary, ck_secondary) = cs.r1cs_shape();
 123
 124    let mut pp = Self {
 125      F_arity_primary,
 126      F_arity_secondary,
 127      ro_consts_primary,
 128      ro_consts_circuit_primary,
 129      ck_primary,
 130      r1cs_shape_primary,
 131      ro_consts_secondary,
 132      ro_consts_circuit_secondary,
 133      ck_secondary,
 134      r1cs_shape_secondary,
 135      augmented_circuit_params_primary,
 136      augmented_circuit_params_secondary,
 137      digest: G1::Scalar::ZERO,
 138      _p_c1: Default::default(),
 139      _p_c2: Default::default(),
 140    };
 141
 142    // set the digest in pp
 143    pp.digest = compute_digest::<G1, PublicParams<G1, G2, C1, C2>>(&pp);
 144
 145    pp
 146  }
 147
 148  /// Returns the number of constraints in the primary and secondary circuits
 149  pub fn num_constraints(&self) -> (usize, usize) {
 150    (
 151      self.r1cs_shape_primary.num_cons,
 152      self.r1cs_shape_secondary.num_cons,
 153    )
 154  }
 155
 156  /// Returns the number of variables in the primary and secondary circuits
 157  pub fn num_variables(&self) -> (usize, usize) {
 158    (
 159      self.r1cs_shape_primary.num_vars,
 160      self.r1cs_shape_secondary.num_vars,
 161    )
 162  }
 163}
 164
 165/// A SNARK that proves the correct execution of an incremental computation
 166#[derive(Clone, Debug, Serialize, Deserialize)]
 167#[serde(bound = "")]
 168pub struct RecursiveSNARK<G1, G2, C1, C2>
 169where
 170  G1: Group<Base = <G2 as Group>::Scalar>,
 171  G2: Group<Base = <G1 as Group>::Scalar>,
 172  C1: StepCircuit<G1::Scalar>,
 173  C2: StepCircuit<G2::Scalar>,
 174{
 175  r_W_primary: RelaxedR1CSWitness<G1>,
 176  r_U_primary: RelaxedR1CSInstance<G1>,
 177  r_W_secondary: RelaxedR1CSWitness<G2>,
 178  r_U_secondary: RelaxedR1CSInstance<G2>,
 179  l_w_secondary: R1CSWitness<G2>,
 180  l_u_secondary: R1CSInstance<G2>,
 181  i: usize,
 182  zi_primary: Vec<G1::Scalar>,
 183  zi_secondary: Vec<G2::Scalar>,
 184  _p_c1: PhantomData<C1>,
 185  _p_c2: PhantomData<C2>,
 186}
 187
 188impl<G1, G2, C1, C2> RecursiveSNARK<G1, G2, C1, C2>
 189where
 190  G1: Group<Base = <G2 as Group>::Scalar>,
 191  G2: Group<Base = <G1 as Group>::Scalar>,
 192  C1: StepCircuit<G1::Scalar>,
 193  C2: StepCircuit<G2::Scalar>,
 194{
 195  /// Create new instance of recursive SNARK
 196  pub fn new(
 197    pp: &PublicParams<G1, G2, C1, C2>,
 198    c_primary: &C1,
 199    c_secondary: &C2,
 200    z0_primary: Vec<G1::Scalar>,
 201    z0_secondary: Vec<G2::Scalar>,
 202  ) -> Self {
 203    // Expected outputs of the two circuits
 204    let zi_primary = c_primary.output(&z0_primary);
 205    let zi_secondary = c_secondary.output(&z0_secondary);
 206
 207    // base case for the primary
 208    let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
 209    let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
 210      scalar_as_base::<G1>(pp.digest),
 211      G1::Scalar::ZERO,
 212      z0_primary,
 213      None,
 214      None,
 215      None,
 216      None,
 217    );
 218
 219    let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
 220      pp.augmented_circuit_params_primary.clone(),
 221      Some(inputs_primary),
 222      c_primary.clone(),
 223      pp.ro_consts_circuit_primary.clone(),
 224    );
 225    let _ = circuit_primary.synthesize(&mut cs_primary);
 226    let (u_primary, w_primary) = cs_primary
 227      .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary)
 228      .map_err(|_e| NovaError::UnSat)
 229      .expect("Nova error unsat");
 230
 231    // base case for the secondary
 232    let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
 233    let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
 234      pp.digest,
 235      G2::Scalar::ZERO,
 236      z0_secondary,
 237      None,
 238      None,
 239      Some(u_primary.clone()),
 240      None,
 241    );
 242    let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
 243      pp.augmented_circuit_params_secondary.clone(),
 244      Some(inputs_secondary),
 245      c_secondary.clone(),
 246      pp.ro_consts_circuit_secondary.clone(),
 247    );
 248    let _ = circuit_secondary.synthesize(&mut cs_secondary);
 249    let (u_secondary, w_secondary) = cs_secondary
 250      .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary)
 251      .map_err(|_e| NovaError::UnSat)
 252      .expect("Nova error unsat");
 253
 254    // IVC proof for the primary circuit
 255    let l_w_primary = w_primary;
 256    let l_u_primary = u_primary;
 257    let r_W_primary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &l_w_primary);
 258    let r_U_primary =
 259      RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, &pp.r1cs_shape_primary, &l_u_primary);
 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    if zi_primary.len() != pp.F_arity_primary || zi_secondary.len() != pp.F_arity_secondary {
 269      panic!("Invalid step length");
 270    }
 271
 272    Self {
 273      r_W_primary,
 274      r_U_primary,
 275      r_W_secondary,
 276      r_U_secondary,
 277      l_w_secondary,
 278      l_u_secondary,
 279      i: 0,
 280      zi_primary,
 281      zi_secondary,
 282      _p_c1: Default::default(),
 283      _p_c2: Default::default(),
 284    }
 285  }
 286
 287  /// Create a new `RecursiveSNARK` (or updates the provided `RecursiveSNARK`)
 288  /// by executing a step of the incremental computation
 289  pub fn prove_step(
 290    &mut self,
 291    pp: &PublicParams<G1, G2, C1, C2>,
 292    c_primary: &C1,
 293    c_secondary: &C2,
 294    z0_primary: Vec<G1::Scalar>,
 295    z0_secondary: Vec<G2::Scalar>,
 296  ) -> Result<(), NovaError> {
 297    if z0_primary.len() != pp.F_arity_primary || z0_secondary.len() != pp.F_arity_secondary {
 298      return Err(NovaError::InvalidInitialInputLength);
 299    }
 300
 301    // Frist step was already done in the constructor
 302    if self.i == 0 {
 303      self.i = 1;
 304      return Ok(());
 305    }
 306
 307    // fold the secondary circuit's instance
 308    let (nifs_secondary, (r_U_secondary, r_W_secondary)) = NIFS::prove(
 309      &pp.ck_secondary,
 310      &pp.ro_consts_secondary,
 311      &scalar_as_base::<G1>(pp.digest),
 312      &pp.r1cs_shape_secondary,
 313      &self.r_U_secondary,
 314      &self.r_W_secondary,
 315      &self.l_u_secondary,
 316      &self.l_w_secondary,
 317    )
 318    .expect("Unable to fold secondary");
 319
 320    let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
 321    let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
 322      scalar_as_base::<G1>(pp.digest),
 323      G1::Scalar::from(self.i as u64),
 324      z0_primary,
 325      Some(self.zi_primary.clone()),
 326      Some(self.r_U_secondary.clone()),
 327      Some(self.l_u_secondary.clone()),
 328      Some(Commitment::<G2>::decompress(&nifs_secondary.comm_T)?),
 329    );
 330
 331    let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
 332      pp.augmented_circuit_params_primary.clone(),
 333      Some(inputs_primary),
 334      c_primary.clone(),
 335      pp.ro_consts_circuit_primary.clone(),
 336    );
 337    let _ = circuit_primary.synthesize(&mut cs_primary);
 338
 339    let (l_u_primary, l_w_primary) = cs_primary
 340      .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary)
 341      .map_err(|_e| NovaError::UnSat)
 342      .expect("Nova error unsat");
 343
 344    // fold the primary circuit's instance
 345    let (nifs_primary, (r_U_primary, r_W_primary)) = NIFS::prove(
 346      &pp.ck_primary,
 347      &pp.ro_consts_primary,
 348      &pp.digest,
 349      &pp.r1cs_shape_primary,
 350      &self.r_U_primary,
 351      &self.r_W_primary,
 352      &l_u_primary,
 353      &l_w_primary,
 354    )
 355    .expect("Unable to fold primary");
 356
 357    let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
 358    let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
 359      pp.digest,
 360      G2::Scalar::from(self.i as u64),
 361      z0_secondary,
 362      Some(self.zi_secondary.clone()),
 363      Some(self.r_U_primary.clone()),
 364      Some(l_u_primary),
 365      Some(Commitment::<G1>::decompress(&nifs_primary.comm_T)?),
 366    );
 367
 368    let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
 369      pp.augmented_circuit_params_secondary.clone(),
 370      Some(inputs_secondary),
 371      c_secondary.clone(),
 372      pp.ro_consts_circuit_secondary.clone(),
 373    );
 374    let _ = circuit_secondary.synthesize(&mut cs_secondary);
 375
 376    let (l_u_secondary, l_w_secondary) = cs_secondary
 377      .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary)
 378      .map_err(|_e| NovaError::UnSat)?;
 379
 380    // update the running instances and witnesses
 381    self.zi_primary = c_primary.output(&self.zi_primary);
 382    self.zi_secondary = c_secondary.output(&self.zi_secondary);
 383
 384    self.l_u_secondary = l_u_secondary;
 385    self.l_w_secondary = l_w_secondary;
 386
 387    self.r_U_primary = r_U_primary;
 388    self.r_W_primary = r_W_primary;
 389
 390    self.i += 1;
 391
 392    self.r_U_secondary = r_U_secondary;
 393    self.r_W_secondary = r_W_secondary;
 394
 395    Ok(())
 396  }
 397
 398  /// Verify the correctness of the `RecursiveSNARK`
 399  pub fn verify(
 400    &self,
 401    pp: &PublicParams<G1, G2, C1, C2>,
 402    num_steps: usize,
 403    z0_primary: &[G1::Scalar],
 404    z0_secondary: &[G2::Scalar],
 405  ) -> Result<(Vec<G1::Scalar>, Vec<G2::Scalar>), NovaError> {
 406    // number of steps cannot be zero
 407    if num_steps == 0 {
 408      return Err(NovaError::ProofVerifyError);
 409    }
 410
 411    // check if the provided proof has executed num_steps
 412    if self.i != num_steps {
 413      return Err(NovaError::ProofVerifyError);
 414    }
 415
 416    // check if the (relaxed) R1CS instances have two public outputs
 417    if self.l_u_secondary.X.len() != 2
 418      || self.r_U_primary.X.len() != 2
 419      || self.r_U_secondary.X.len() != 2
 420    {
 421      return Err(NovaError::ProofVerifyError);
 422    }
 423
 424    // check if the output hashes in R1CS instances point to the right running instances
 425    let (hash_primary, hash_secondary) = {
 426      let mut hasher = <G2 as Group>::RO::new(
 427        pp.ro_consts_secondary.clone(),
 428        NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_primary,
 429      );
 430      hasher.absorb(pp.digest);
 431      hasher.absorb(G1::Scalar::from(num_steps as u64));
 432      for e in z0_primary {
 433        hasher.absorb(*e);
 434      }
 435      for e in &self.zi_primary {
 436        hasher.absorb(*e);
 437      }
 438      self.r_U_secondary.absorb_in_ro(&mut hasher);
 439
 440      let mut hasher2 = <G1 as Group>::RO::new(
 441        pp.ro_consts_primary.clone(),
 442        NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_secondary,
 443      );
 444      hasher2.absorb(scalar_as_base::<G1>(pp.digest));
 445      hasher2.absorb(G2::Scalar::from(num_steps as u64));
 446      for e in z0_secondary {
 447        hasher2.absorb(*e);
 448      }
 449      for e in &self.zi_secondary {
 450        hasher2.absorb(*e);
 451      }
 452      self.r_U_primary.absorb_in_ro(&mut hasher2);
 453
 454      (
 455        hasher.squeeze(NUM_HASH_BITS),
 456        hasher2.squeeze(NUM_HASH_BITS),
 457      )
 458    };
 459
 460    if hash_primary != self.l_u_secondary.X[0]
 461      || hash_secondary != scalar_as_base::<G2>(self.l_u_secondary.X[1])
 462    {
 463      return Err(NovaError::ProofVerifyError);
 464    }
 465
 466    // check the satisfiability of the provided instances
 467    let (res_r_primary, (res_r_secondary, res_l_secondary)) = rayon::join(
 468      || {
 469        pp.r1cs_shape_primary
 470          .is_sat_relaxed(&pp.ck_primary, &self.r_U_primary, &self.r_W_primary)
 471      },
 472      || {
 473        rayon::join(
 474          || {
 475            pp.r1cs_shape_secondary.is_sat_relaxed(
 476              &pp.ck_secondary,
 477              &self.r_U_secondary,
 478              &self.r_W_secondary,
 479            )
 480          },
 481          || {
 482            pp.r1cs_shape_secondary.is_sat(
 483              &pp.ck_secondary,
 484              &self.l_u_secondary,
 485              &self.l_w_secondary,
 486            )
 487          },
 488        )
 489      },
 490    );
 491
 492    // check the returned res objects
 493    res_r_primary?;
 494    res_r_secondary?;
 495    res_l_secondary?;
 496
 497    Ok((self.zi_primary.clone(), self.zi_secondary.clone()))
 498  }
 499}
 500
 501/// A type that holds the prover key for `CompressedSNARK`
 502#[derive(Clone, Debug, Serialize, Deserialize)]
 503#[serde(bound = "")]
 504pub struct ProverKey<G1, G2, C1, C2, S1, S2>
 505where
 506  G1: Group<Base = <G2 as Group>::Scalar>,
 507  G2: Group<Base = <G1 as Group>::Scalar>,
 508  C1: StepCircuit<G1::Scalar>,
 509  C2: StepCircuit<G2::Scalar>,
 510  S1: RelaxedR1CSSNARKTrait<G1>,
 511  S2: RelaxedR1CSSNARKTrait<G2>,
 512{
 513  pk_primary: S1::ProverKey,
 514  pk_secondary: S2::ProverKey,
 515  _p_c1: PhantomData<C1>,
 516  _p_c2: PhantomData<C2>,
 517}
 518
 519/// A type that holds the verifier key for `CompressedSNARK`
 520#[derive(Clone, Serialize, Deserialize)]
 521#[serde(bound = "")]
 522pub struct VerifierKey<G1, G2, C1, C2, S1, S2>
 523where
 524  G1: Group<Base = <G2 as Group>::Scalar>,
 525  G2: Group<Base = <G1 as Group>::Scalar>,
 526  C1: StepCircuit<G1::Scalar>,
 527  C2: StepCircuit<G2::Scalar>,
 528  S1: RelaxedR1CSSNARKTrait<G1>,
 529  S2: RelaxedR1CSSNARKTrait<G2>,
 530{
 531  F_arity_primary: usize,
 532  F_arity_secondary: usize,
 533  ro_consts_primary: ROConstants<G1>,
 534  ro_consts_secondary: ROConstants<G2>,
 535  digest: G1::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  r_W_snark_primary: S1,
 556
 557  r_U_secondary: RelaxedR1CSInstance<G2>,
 558  l_u_secondary: R1CSInstance<G2>,
 559  nifs_secondary: NIFS<G2>,
 560  f_W_snark_secondary: S2,
 561
 562  zn_primary: Vec<G1::Scalar>,
 563  zn_secondary: Vec<G2::Scalar>,
 564
 565  _p_c1: PhantomData<C1>,
 566  _p_c2: PhantomData<C2>,
 567}
 568
 569impl<G1, G2, C1, C2, S1, S2> CompressedSNARK<G1, G2, C1, C2, S1, S2>
 570where
 571  G1: Group<Base = <G2 as Group>::Scalar>,
 572  G2: Group<Base = <G1 as Group>::Scalar>,
 573  C1: StepCircuit<G1::Scalar>,
 574  C2: StepCircuit<G2::Scalar>,
 575  S1: RelaxedR1CSSNARKTrait<G1>,
 576  S2: RelaxedR1CSSNARKTrait<G2>,
 577{
 578  /// Creates prover and verifier keys for `CompressedSNARK`
 579  pub fn setup(
 580    pp: &PublicParams<G1, G2, C1, C2>,
 581  ) -> Result<
 582    (
 583      ProverKey<G1, G2, C1, C2, S1, S2>,
 584      VerifierKey<G1, G2, C1, C2, S1, S2>,
 585    ),
 586    NovaError,
 587  > {
 588    let (pk_primary, vk_primary) = S1::setup(&pp.ck_primary, &pp.r1cs_shape_primary)?;
 589    let (pk_secondary, vk_secondary) = S2::setup(&pp.ck_secondary, &pp.r1cs_shape_secondary)?;
 590
 591    let pk = ProverKey {
 592      pk_primary,
 593      pk_secondary,
 594      _p_c1: Default::default(),
 595      _p_c2: Default::default(),
 596    };
 597
 598    let vk = VerifierKey {
 599      F_arity_primary: pp.F_arity_primary,
 600      F_arity_secondary: pp.F_arity_secondary,
 601      ro_consts_primary: pp.ro_consts_primary.clone(),
 602      ro_consts_secondary: pp.ro_consts_secondary.clone(),
 603      digest: pp.digest,
 604      vk_primary,
 605      vk_secondary,
 606      _p_c1: Default::default(),
 607      _p_c2: Default::default(),
 608    };
 609
 610    Ok((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    // fold the secondary circuit's instance
 620    let res_secondary = NIFS::prove(
 621      &pp.ck_secondary,
 622      &pp.ro_consts_secondary,
 623      &scalar_as_base::<G1>(pp.digest),
 624      &pp.r1cs_shape_secondary,
 625      &recursive_snark.r_U_secondary,
 626      &recursive_snark.r_W_secondary,
 627      &recursive_snark.l_u_secondary,
 628      &recursive_snark.l_w_secondary,
 629    );
 630
 631    let (nifs_secondary, (f_U_secondary, f_W_secondary)) = res_secondary?;
 632
 633    // create SNARKs proving the knowledge of f_W_primary and f_W_secondary
 634    let (r_W_snark_primary, f_W_snark_secondary) = rayon::join(
 635      || {
 636        S1::prove(
 637          &pp.ck_primary,
 638          &pk.pk_primary,
 639          &recursive_snark.r_U_primary,
 640          &recursive_snark.r_W_primary,
 641        )
 642      },
 643      || {
 644        S2::prove(
 645          &pp.ck_secondary,
 646          &pk.pk_secondary,
 647          &f_U_secondary,
 648          &f_W_secondary,
 649        )
 650      },
 651    );
 652
 653    Ok(Self {
 654      r_U_primary: recursive_snark.r_U_primary.clone(),
 655      r_W_snark_primary: r_W_snark_primary?,
 656
 657      r_U_secondary: recursive_snark.r_U_secondary.clone(),
 658      l_u_secondary: recursive_snark.l_u_secondary.clone(),
 659      nifs_secondary,
 660      f_W_snark_secondary: f_W_snark_secondary?,
 661
 662      zn_primary: recursive_snark.zi_primary.clone(),
 663      zn_secondary: recursive_snark.zi_secondary.clone(),
 664
 665      _p_c1: Default::default(),
 666      _p_c2: Default::default(),
 667    })
 668  }
 669
 670  /// Verify the correctness of the `CompressedSNARK`
 671  pub fn verify(
 672    &self,
 673    vk: &VerifierKey<G1, G2, C1, C2, S1, S2>,
 674    num_steps: usize,
 675    z0_primary: Vec<G1::Scalar>,
 676    z0_secondary: Vec<G2::Scalar>,
 677  ) -> Result<(Vec<G1::Scalar>, Vec<G2::Scalar>), NovaError> {
 678    // number of steps cannot be zero
 679    if num_steps == 0 {
 680      return Err(NovaError::ProofVerifyError);
 681    }
 682
 683    // check if the (relaxed) R1CS instances have two public outputs
 684    if self.l_u_secondary.X.len() != 2
 685      || self.r_U_primary.X.len() != 2
 686      || self.r_U_secondary.X.len() != 2
 687    {
 688      return Err(NovaError::ProofVerifyError);
 689    }
 690
 691    // check if the output hashes in R1CS instances point to the right running instances
 692    let (hash_primary, hash_secondary) = {
 693      let mut hasher = <G2 as Group>::RO::new(
 694        vk.ro_consts_secondary.clone(),
 695        NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_primary,
 696      );
 697      hasher.absorb(vk.digest);
 698      hasher.absorb(G1::Scalar::from(num_steps as u64));
 699      for e in z0_primary {
 700        hasher.absorb(e);
 701      }
 702      for e in &self.zn_primary {
 703        hasher.absorb(*e);
 704      }
 705      self.r_U_secondary.absorb_in_ro(&mut hasher);
 706
 707      let mut hasher2 = <G1 as Group>::RO::new(
 708        vk.ro_consts_primary.clone(),
 709        NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_secondary,
 710      );
 711      hasher2.absorb(scalar_as_base::<G1>(vk.digest));
 712      hasher2.absorb(G2::Scalar::from(num_steps as u64));
 713      for e in z0_secondary {
 714        hasher2.absorb(e);
 715      }
 716      for e in &self.zn_secondary {
 717        hasher2.absorb(*e);
 718      }
 719      self.r_U_primary.absorb_in_ro(&mut hasher2);
 720
 721      (
 722        hasher.squeeze(NUM_HASH_BITS),
 723        hasher2.squeeze(NUM_HASH_BITS),
 724      )
 725    };
 726
 727    if hash_primary != self.l_u_secondary.X[0]
 728      || hash_secondary != scalar_as_base::<G2>(self.l_u_secondary.X[1])
 729    {
 730      return Err(NovaError::ProofVerifyError);
 731    }
 732
 733    // fold the running instance and last instance to get a folded instance
 734    let f_U_secondary = self.nifs_secondary.verify(
 735      &vk.ro_consts_secondary,
 736      &scalar_as_base::<G1>(vk.digest),
 737      &self.r_U_secondary,
 738      &self.l_u_secondary,
 739    )?;
 740
 741    // check the satisfiability of the folded instances using SNARKs proving the knowledge of their satisfying witnesses
 742    let (res_primary, res_secondary) = rayon::join(
 743      || {
 744        self
 745          .r_W_snark_primary
 746          .verify(&vk.vk_primary, &self.r_U_primary)
 747      },
 748      || {
 749        self
 750          .f_W_snark_secondary
 751          .verify(&vk.vk_secondary, &f_U_secondary)
 752      },
 753    );
 754
 755    res_primary?;
 756    res_secondary?;
 757
 758    Ok((self.zn_primary.clone(), self.zn_secondary.clone()))
 759  }
 760}
 761
 762type CommitmentKey<G> = <<G as traits::Group>::CE as CommitmentEngineTrait<G>>::CommitmentKey;
 763type Commitment<G> = <<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment;
 764type CompressedCommitment<G> = <<<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment as CommitmentTrait<G>>::CompressedCommitment;
 765type CE<G> = <G as Group>::CE;
 766
 767fn compute_digest<G: Group, T: Serialize>(o: &T) -> G::Scalar {
 768  // obtain a vector of bytes representing public parameters
 769  let bytes = bincode::serialize(o).unwrap();
 770  // convert pp_bytes into a short digest
 771  let mut hasher = Sha3_256::new();
 772  hasher.input(&bytes);
 773  let digest = hasher.result();
 774
 775  // truncate the digest to NUM_HASH_BITS bits
 776  let bv = (0..NUM_HASH_BITS).map(|i| {
 777    let (byte_pos, bit_pos) = (i / 8, i % 8);
 778    let bit = (digest[byte_pos] >> bit_pos) & 1;
 779    bit == 1
 780  });
 781
 782  // turn the bit vector into a scalar
 783  let mut digest = G::Scalar::ZERO;
 784  let mut coeff = G::Scalar::ONE;
 785  for bit in bv {
 786    if bit {
 787      digest += coeff;
 788    }
 789    coeff += coeff;
 790  }
 791  digest
 792}
 793
 794#[cfg(test)]
 795mod tests {
 796  use crate::provider::pedersen::CommitmentKeyExtTrait;
 797
 798  use super::*;
 799  type EE1<G1> = provider::ipa_pc::EvaluationEngine<G1>;
 800  type EE2<G2> = provider::ipa_pc::EvaluationEngine<G2>;
 801  type S1<G1> = spartan::RelaxedR1CSSNARK<G1, EE1<G1>>;
 802  type S2<G2> = spartan::RelaxedR1CSSNARK<G2, EE2<G2>>;
 803  type S1Prime<G1> = spartan::pp::RelaxedR1CSSNARK<G1, EE1<G1>>;
 804  type S2Prime<G2> = spartan::pp::RelaxedR1CSSNARK<G2, EE2<G2>>;
 805
 806  use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError};
 807  use core::marker::PhantomData;
 808  use ff::PrimeField;
 809  use traits::circuit::TrivialTestCircuit;
 810
 811  #[derive(Clone, Debug, Default)]
 812  struct CubicCircuit<F: PrimeField> {
 813    _p: PhantomData<F>,
 814  }
 815
 816  impl<F> StepCircuit<F> for CubicCircuit<F>
 817  where
 818    F: PrimeField,
 819  {
 820    fn arity(&self) -> usize {
 821      1
 822    }
 823
 824    fn synthesize<CS: ConstraintSystem<F>>(
 825      &self,
 826      cs: &mut CS,
 827      z: &[AllocatedNum<F>],
 828    ) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
 829      // Consider a cubic equation: `x^3 + x + 5 = y`, where `x` and `y` are respectively the input and output.
 830      let x = &z[0];
 831      let x_sq = x.square(cs.namespace(|| "x_sq"))?;
 832      let x_cu = x_sq.mul(cs.namespace(|| "x_cu"), x)?;
 833      let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
 834        Ok(x_cu.get_value().unwrap() + x.get_value().unwrap() + F::from(5u64))
 835      })?;
 836
 837      cs.enforce(
 838        || "y = x^3 + x + 5",
 839        |lc| {
 840          lc + x_cu.get_variable()
 841            + x.get_variable()
 842            + CS::one()
 843            + CS::one()
 844            + CS::one()
 845            + CS::one()
 846            + CS::one()
 847        },
 848        |lc| lc + CS::one(),
 849        |lc| lc + y.get_variable(),
 850      );
 851
 852      Ok(vec![y])
 853    }
 854
 855    fn output(&self, z: &[F]) -> Vec<F> {
 856      vec![z[0] * z[0] * z[0] + z[0] + F::from(5u64)]
 857    }
 858  }
 859
 860  fn test_pp_digest_with<G1, G2, T1, T2>(circuit1: T1, circuit2: T2, expected: &str)
 861  where
 862    G1: Group<Base = <G2 as Group>::Scalar>,
 863    G2: Group<Base = <G1 as Group>::Scalar>,
 864    T1: StepCircuit<G1::Scalar>,
 865    T2: StepCircuit<G2::Scalar>,
 866  {
 867    let pp = PublicParams::<G1, G2, T1, T2>::setup(circuit1, circuit2);
 868
 869    let digest_str = pp
 870      .digest
 871      .to_repr()
 872      .as_ref()
 873      .iter()
 874      .map(|b| format!("{:02x}", b))
 875      .collect::<String>();
 876    assert_eq!(digest_str, expected);
 877  }
 878
 879  #[test]
 880  fn test_pp_digest() {
 881    type G1 = pasta_curves::pallas::Point;
 882    type G2 = pasta_curves::vesta::Point;
 883    let trivial_circuit1 = TrivialTestCircuit::<<G1 as Group>::Scalar>::default();
 884    let trivial_circuit2 = TrivialTestCircuit::<<G2 as Group>::Scalar>::default();
 885    let cubic_circuit1 = CubicCircuit::<<G1 as Group>::Scalar>::default();
 886
 887    test_pp_digest_with::<G1, G2, _, _>(
 888      trivial_circuit1,
 889      trivial_circuit2.clone(),
 890      "39a4ea9dd384346fdeb6b5857c7be56fa035153b616d55311f3191dfbceea603",
 891    );
 892
 893    test_pp_digest_with::<G1, G2, _, _>(
 894      cubic_circuit1,
 895      trivial_circuit2,
 896      "3f7b25f589f2da5ab26254beba98faa54f6442ebf5fa5860caf7b08b576cab00",
 897    );
 898  }
 899
 900  fn test_ivc_trivial_with<G1, G2>()
 901  where
 902    G1: Group<Base = <G2 as Group>::Scalar>,
 903    G2: Group<Base = <G1 as Group>::Scalar>,
 904  {
 905    let test_circuit1 = TrivialTestCircuit::<<G1 as Group>::Scalar>::default();
 906    let test_circuit2 = TrivialTestCircuit::<<G2 as Group>::Scalar>::default();
 907
 908    // produce public parameters
 909    let pp = PublicParams::<
 910      G1,
 911      G2,
 912      TrivialTestCircuit<<G1 as Group>::Scalar>,
 913      TrivialTestCircuit<<G2 as Group>::Scalar>,
 914    >::setup(test_circuit1.clone(), test_circuit2.clone());
 915
 916    let num_steps = 1;
 917
 918    // produce a recursive SNARK
 919    let mut recursive_snark = RecursiveSNARK::new(
 920      &pp,
 921      &test_circuit1,
 922      &test_circuit2,
 923      vec![<G1 as Group>::Scalar::ZERO],
 924      vec![<G2 as Group>::Scalar::ZERO],
 925    );
 926
 927    let res = recursive_snark.prove_step(
 928      &pp,
 929      &test_circuit1,
 930      &test_circuit2,
 931      vec![<G1 as Group>::Scalar::ZERO],
 932      vec![<G2 as Group>::Scalar::ZERO],
 933    );
 934
 935    assert!(res.is_ok());
 936
 937    // verify the recursive SNARK
 938    let res = recursive_snark.verify(
 939      &pp,
 940      num_steps,
 941      &[<G1 as Group>::Scalar::ZERO],
 942      &[<G2 as Group>::Scalar::ZERO],
 943    );
 944    assert!(res.is_ok());
 945  }
 946
 947  #[test]
 948  fn test_ivc_trivial() {
 949    type G1 = pasta_curves::pallas::Point;
 950    type G2 = pasta_curves::vesta::Point;
 951    test_ivc_trivial_with::<G1, G2>();
 952  }
 953
 954  fn test_ivc_nontrivial_with<G1, G2>()
 955  where
 956    G1: Group<Base = <G2 as Group>::Scalar>,
 957    G2: Group<Base = <G1 as Group>::Scalar>,
 958  {
 959    let circuit_primary = TrivialTestCircuit::default();
 960    let circuit_secondary = CubicCircuit::default();
 961
 962    // produce public parameters
 963    let pp = PublicParams::<
 964      G1,
 965      G2,
 966      TrivialTestCircuit<<G1 as Group>::Scalar>,
 967      CubicCircuit<<G2 as Group>::Scalar>,
 968    >::setup(circuit_primary.clone(), circuit_secondary.clone());
 969
 970    let num_steps = 3;
 971
 972    // produce a recursive SNARK
 973    let mut recursive_snark = RecursiveSNARK::<
 974      G1,
 975      G2,
 976      TrivialTestCircuit<<G1 as Group>::Scalar>,
 977      CubicCircuit<<G2 as Group>::Scalar>,
 978    >::new(
 979      &pp,
 980      &circuit_primary,
 981      &circuit_secondary,
 982      vec![<G1 as Group>::Scalar::ONE],
 983      vec![<G2 as Group>::Scalar::ZERO],
 984    );
 985
 986    for i in 0..num_steps {
 987      let res = recursive_snark.prove_step(
 988        &pp,
 989        &circuit_primary,
 990        &circuit_secondary,
 991        vec![<G1 as Group>::Scalar::ONE],
 992        vec![<G2 as Group>::Scalar::ZERO],
 993      );
 994      assert!(res.is_ok());
 995
 996      // verify the recursive snark at each step of recursion
 997      let res = recursive_snark.verify(
 998        &pp,
 999        i + 1,
1000        &[<G1 as Group>::Scalar::ONE],
1001        &[<G2 as Group>::Scalar::ZERO],
1002      );
1003      assert!(res.is_ok());
1004    }
1005
1006    // verify the recursive SNARK
1007    let res = recursive_snark.verify(
1008      &pp,
1009      num_steps,
1010      &[<G1 as Group>::Scalar::ONE],
1011      &[<G2 as Group>::Scalar::ZERO],
1012    );
1013    assert!(res.is_ok());
1014
1015    let (zn_primary, zn_secondary) = res.unwrap();
1016
1017    // sanity: check the claimed output with a direct computation of the same
1018    assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::ONE]);
1019    let mut zn_secondary_direct = vec![<G2 as Group>::Scalar::ZERO];
1020    for _i in 0..num_steps {
1021      zn_secondary_direct = circuit_secondary.clone().output(&zn_secondary_direct);
1022    }
1023    assert_eq!(zn_secondary, zn_secondary_direct);
1024    assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);
1025  }
1026
1027  #[test]
1028  fn test_ivc_nontrivial() {
1029    type G1 = pasta_curves::pallas::Point;
1030    type G2 = pasta_curves::vesta::Point;
1031
1032    test_ivc_nontrivial_with::<G1, G2>();
1033  }
1034
1035  fn test_ivc_nontrivial_with_compression_with<G1, G2>()
1036  where
1037    G1: Group<Base = <G2 as Group>::Scalar>,
1038    G2: Group<Base = <G1 as Group>::Scalar>,
1039    // this is due to the reliance on CommitmentKeyExtTrait as a bound in ipa_pc
1040    <G1::CE as CommitmentEngineTrait<G1>>::CommitmentKey:
1041      CommitmentKeyExtTrait<G1, CE = <G1 as Group>::CE>,
1042    <G2::CE as CommitmentEngineTrait<G2>>::CommitmentKey:
1043      CommitmentKeyExtTrait<G2, CE = <G2 as Group>::CE>,
1044  {
1045    let circuit_primary = TrivialTestCircuit::default();
1046    let circuit_secondary = CubicCircuit::default();
1047
1048    // produce public parameters
1049    let pp = PublicParams::<
1050      G1,
1051      G2,
1052      TrivialTestCircuit<<G1 as Group>::Scalar>,
1053      CubicCircuit<<G2 as Group>::Scalar>,
1054    >::setup(circuit_primary.clone(), circuit_secondary.clone());
1055
1056    let num_steps = 3;
1057
1058    // produce a recursive SNARK
1059    let mut recursive_snark = RecursiveSNARK::<
1060      G1,
1061      G2,
1062      TrivialTestCircuit<<G1 as Group>::Scalar>,
1063      CubicCircuit<<G2 as Group>::Scalar>,
1064    >::new(
1065      &pp,
1066      &circuit_primary,
1067      &circuit_secondary,
1068      vec![<G1 as Group>::Scalar::ONE],
1069      vec![<G2 as Group>::Scalar::ZERO],
1070    );
1071
1072    for _i in 0..num_steps {
1073      let res = recursive_snark.prove_step(
1074        &pp,
1075        &circuit_primary,
1076        &circuit_secondary,
1077        vec![<G1 as Group>::Scalar::ONE],
1078        vec![<G2 as Group>::Scalar::ZERO],
1079      );
1080      assert!(res.is_ok());
1081    }
1082
1083    // verify the recursive SNARK
1084    let res = recursive_snark.verify(
1085      &pp,
1086      num_steps,
1087      &[<G1 as Group>::Scalar::ONE],
1088      &[<G2 as Group>::Scalar::ZERO],
1089    );
1090    assert!(res.is_ok());
1091
1092    let (zn_primary, zn_secondary) = res.unwrap();
1093
1094    // sanity: check the claimed output with a direct computation of the same
1095    assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::ONE]);
1096    let mut zn_secondary_direct = vec![<G2 as Group>::Scalar::ZERO];
1097    for _i in 0..num_steps {
1098      zn_secondary_direct = circuit_secondary.clone().output(&zn_secondary_direct);
1099    }
1100    assert_eq!(zn_secondary, zn_secondary_direct);
1101    assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);
1102
1103    // produce the prover and verifier keys for compressed snark
1104    let (pk, vk) = CompressedSNARK::<_, _, _, _, S1<G1>, S2<G2>>::setup(&pp).unwrap();
1105
1106    // produce a compressed SNARK
1107    let res = CompressedSNARK::<_, _, _, _, S1<G1>, S2<G2>>::prove(&pp, &pk, &recursive_snark);
1108    assert!(res.is_ok());
1109    let compressed_snark = res.unwrap();
1110
1111    // verify the compressed SNARK
1112    let res = compressed_snark.verify(
1113      &vk,
1114      num_steps,
1115      vec![<G1 as Group>::Scalar::ONE],
1116      vec![<G2 as Group>::Scalar::ZERO],
1117    );
1118    assert!(res.is_ok());
1119  }
1120
1121  #[test]
1122  fn test_ivc_nontrivial_with_compression() {
1123    type G1 = pasta_curves::pallas::Point;
1124    type G2 = pasta_curves::vesta::Point;
1125
1126    test_ivc_nontrivial_with_compression_with::<G1, G2>();
1127  }
1128
1129  fn test_ivc_nontrivial_with_spark_compression_with<G1, G2>()
1130  where
1131    G1: Group<Base = <G2 as Group>::Scalar>,
1132    G2: Group<Base = <G1 as Group>::Scalar>,
1133    // this is due to the reliance on CommitmentKeyExtTrait as a bound in ipa_pc
1134    <G1::CE as CommitmentEngineTrait<G1>>::CommitmentKey:
1135      CommitmentKeyExtTrait<G1, CE = <G1 as Group>::CE>,
1136    <G2::CE as CommitmentEngineTrait<G2>>::CommitmentKey:
1137      CommitmentKeyExtTrait<G2, CE = <G2 as Group>::CE>,
1138  {
1139    let circuit_primary = TrivialTestCircuit::default();
1140    let circuit_secondary = CubicCircuit::default();
1141
1142    // produce public parameters
1143    let pp = PublicParams::<
1144      G1,
1145      G2,
1146      TrivialTestCircuit<<G1 as Group>::Scalar>,
1147      CubicCircuit<<G2 as Group>::Scalar>,
1148    >::setup(circuit_primary.clone(), circuit_secondary.clone());
1149
1150    let num_steps = 3;
1151
1152    // produce a recursive SNARK
1153    let mut recursive_snark = RecursiveSNARK::<
1154      G1,
1155      G2,
1156      TrivialTestCircuit<<G1 as Group>::Scalar>,
1157      CubicCircuit<<G2 as Group>::Scalar>,
1158    >::new(
1159      &pp,
1160      &circuit_primary,
1161      &circuit_secondary,
1162      vec![<G1 as Group>::Scalar::ONE],
1163      vec![<G2 as Group>::Scalar::ZERO],
1164    );
1165
1166    for _i in 0..num_steps {
1167      let res = recursive_snark.prove_step(
1168        &pp,
1169        &circuit_primary,
1170        &circuit_secondary,
1171        vec![<G1 as Group>::Scalar::ONE],
1172        vec![<G2 as Group>::Scalar::ZERO],
1173      );
1174      assert!(res.is_ok());
1175    }
1176
1177    // verify the recursive SNARK
1178    let res = recursive_snark.verify(
1179      &pp,
1180      num_steps,
1181      &[<G1 as Group>::Scalar::ONE],
1182      &[<G2 as Group>::Scalar::ZERO],
1183    );
1184    assert!(res.is_ok());
1185
1186    let (zn_primary, zn_secondary) = res.unwrap();
1187
1188    // sanity: check the claimed output with a direct computation of the same
1189    assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::ONE]);
1190    let mut zn_secondary_direct = vec![<G2 as Group>::Scalar::ZERO];
1191    for _i in 0..num_steps {
1192      zn_secondary_direct = CubicCircuit::default().output(&zn_secondary_direct);
1193    }
1194    assert_eq!(zn_secondary, zn_secondary_direct);
1195    assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);
1196
1197    // run the compressed snark with Spark compiler
1198
1199    // produce the prover and verifier keys for compressed snark
1200    let (pk, vk) = CompressedSNARK::<_, _, _, _, S1Prime<G1>, S2Prime<G2>>::setup(&pp).unwrap();
1201
1202    // produce a compressed SNARK
1203    let res =
1204      CompressedSNARK::<_, _, _, _, S1Prime<G1>, S2Prime<G2>>::prove(&pp, &pk, &recursive_snark);
1205    assert!(res.is_ok());
1206    let compressed_snark = res.unwrap();
1207
1208    // verify the compressed SNARK
1209    let res = compressed_snark.verify(
1210      &vk,
1211      num_steps,
1212      vec![<G1 as Group>::Scalar::ONE],
1213      vec![<G2 as Group>::Scalar::ZERO],
1214    );
1215    assert!(res.is_ok());
1216  }
1217
1218  #[test]
1219  fn test_ivc_nontrivial_with_spark_compression() {
1220    type G1 = pasta_curves::pallas::Point;
1221    type G2 = pasta_curves::vesta::Point;
1222
1223    test_ivc_nontrivial_with_spark_compression_with::<G1, G2>();
1224  }
1225
1226  fn test_ivc_nondet_with_compression_with<G1, G2>()
1227  where
1228    G1: Group<Base = <G2 as Group>::Scalar>,
1229    G2: Group<Base = <G1 as Group>::Scalar>,
1230    // this is due to the reliance on CommitmentKeyExtTrait as a bound in ipa_pc
1231    <G1::CE as CommitmentEngineTrait<G1>>::CommitmentKey:
1232      CommitmentKeyExtTrait<G1, CE = <G1 as Group>::CE>,
1233    <G2::CE as CommitmentEngineTrait<G2>>::CommitmentKey:
1234      CommitmentKeyExtTrait<G2, CE = <G2 as Group>::CE>,
1235  {
1236    // y is a non-deterministic advice representing the fifth root of the input at a step.
1237    #[derive(Clone, Debug)]
1238    struct FifthRootCheckingCircuit<F: PrimeField> {
1239      y: F,
1240    }
1241
1242    impl<F> FifthRootCheckingCircuit<F>
1243    where
1244      F: PrimeField,
1245    {
1246      fn new(num_steps: usize) -> (Vec<F>, Vec<Self>) {
1247        let mut powers = Vec::new();
1248        let rng = &mut rand::rngs::OsRng;
1249        let mut seed = F::random(rng);
1250        for _i in 0..num_steps + 1 {
1251          seed *= seed.clone().square().square();
1252
1253          powers.push(Self { y: seed });
1254        }
1255
1256        // reverse the powers to get roots
1257        let roots = powers.into_iter().rev().collect::<Vec<Self>>();
1258        (vec![roots[0].y], roots[1..].to_vec())
1259      }
1260    }
1261
1262    impl<F> StepCircuit<F> for FifthRootCheckingCircuit<F>
1263    where
1264      F: PrimeField,
1265    {
1266      fn arity(&self) -> usize {
1267        1
1268      }
1269
1270      fn synthesize<CS: ConstraintSystem<F>>(
1271        &self,
1272        cs: &mut CS,
1273        z: &[AllocatedNum<F>],
1274      ) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
1275        let x = &z[0];
1276
1277        // we allocate a variable and set it to the provided non-deterministic advice.
1278        let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(self.y))?;
1279
1280        // We now check if y = x^{1/5} by checking if y^5 = x
1281        let y_sq = y.square(cs.namespace(|| "y_sq"))?;
1282        let y_quad = y_sq.square(cs.namespace(|| "y_quad"))?;
1283        let y_pow_5 = y_quad.mul(cs.namespace(|| "y_fifth"), &y)?;
1284
1285        cs.enforce(
1286          || "y^5 = x",
1287          |lc| lc + y_pow_5.get_variable(),
1288          |lc| lc + CS::one(),
1289          |lc| lc + x.get_variable(),
1290        );
1291
1292        Ok(vec![y])
1293      }
1294
1295      fn output(&self, z: &[F]) -> Vec<F> {
1296        // sanity check
1297        let x = z[0];
1298        let y_pow_5 = self.y * self.y.clone().square().square();
1299        assert_eq!(x, y_pow_5);
1300
1301        // return non-deterministic advice
1302        // as the output of the step
1303        vec![self.y]
1304      }
1305    }
1306
1307    let circuit_primary = FifthRootCheckingCircuit {
1308      y: <G1 as Group>::Scalar::ZERO,
1309    };
1310
1311    let circuit_secondary = TrivialTestCircuit::default();
1312
1313    // produce public parameters
1314    let pp = PublicParams::<
1315      G1,
1316      G2,
1317      FifthRootCheckingCircuit<<G1 as Group>::Scalar>,
1318      TrivialTestCircuit<<G2 as Group>::Scalar>,
1319    >::setup(circuit_primary, circuit_secondary.clone());
1320
1321    let num_steps = 3;
1322
1323    // produce non-deterministic advice
1324    let (z0_primary, roots) = FifthRootCheckingCircuit::new(num_steps);
1325    let z0_secondary = vec![<G2 as Group>::Scalar::ZERO];
1326
1327    // produce a recursive SNARK
1328    let mut recursive_snark: RecursiveSNARK<
1329      G1,
1330      G2,
1331      FifthRootCheckingCircuit<<G1 as Group>::Scalar>,
1332      TrivialTestCircuit<<G2 as Group>::Scalar>,
1333    > = RecursiveSNARK::<
1334      G1,
1335      G2,
1336      FifthRootCheckingCircuit<<G1 as Group>::Scalar>,
1337      TrivialTestCircuit<<G2 as Group>::Scalar>,
1338    >::new(
1339      &pp,
1340      &roots[0],
1341      &circuit_secondary,
1342      z0_primary.clone(),
1343      z0_secondary.clone(),
1344    );
1345
1346    for circuit_primary in roots.iter().take(num_steps) {
1347      let res = recursive_snark.prove_step(
1348        &pp,
1349        circuit_primary,
1350        &circuit_secondary.clone(),
1351        z0_primary.clone(),
1352        z0_secondary.clone(),
1353      );
1354      assert!(res.is_ok());
1355    }
1356
1357    // verify the recursive SNARK
1358    let res = recursive_snark.verify(&pp, num_steps, &z0_primary, &z0_secondary);
1359    assert!(res.is_ok());
1360
1361    // produce the prover and verifier keys for compressed snark
1362    let (pk, vk) = CompressedSNARK::<_, _, _, _, S1<G1>, S2<G2>>::setup(&pp).unwrap();
1363
1364    // produce a compressed SNARK
1365    let res = CompressedSNARK::<_, _, _, _, S1<G1>, S2<G2>>::prove(&pp, &pk, &recursive_snark);
1366    assert!(res.is_ok());
1367    let compressed_snark = res.unwrap();
1368
1369    // verify the compressed SNARK
1370    let res = compressed_snark.verify(&vk, num_steps, z0_primary, z0_secondary);
1371    assert!(res.is_ok());
1372  }
1373
1374  #[test]
1375  fn test_ivc_nondet_with_compression() {
1376    type G1 = pasta_curves::pallas::Point;
1377    type G2 = pasta_curves::vesta::Point;
1378
1379    test_ivc_nondet_with_compression_with::<G1, G2>();
1380  }
1381
1382  fn test_ivc_base_with<G1, G2>()
1383  where
1384    G1: Group<Base = <G2 as Group>::Scalar>,
1385    G2: Group<Base = <G1 as Group>::Scalar>,
1386  {
1387    let test_circuit1 = TrivialTestCircuit::<<G1 as Group>::Scalar>::default();
1388    let test_circuit2 = CubicCircuit::<<G2 as Group>::Scalar>::default();
1389
1390    // produce public parameters
1391    let pp = PublicParams::<
1392      G1,
1393      G2,
1394      TrivialTestCircuit<<G1 as Group>::Scalar>,
1395      CubicCircuit<<G2 as Group>::Scalar>,
1396    >::setup(test_circuit1.clone(), test_circuit2.clone());
1397
1398    let num_steps = 1;
1399
1400    // produce a recursive SNARK
1401    let mut recursive_snark = RecursiveSNARK::<
1402      G1,
1403      G2,
1404      TrivialTestCircuit<<G1 as Group>::Scalar>,
1405      CubicCircuit<<G2 as Group>::Scalar>,
1406    >::new(
1407      &pp,
1408      &test_circuit1,
1409      &test_circuit2,
1410      vec![<G1 as Group>::Scalar::ONE],
1411      vec![<G2 as Group>::Scalar::ZERO],
1412    );
1413
1414    // produce a recursive SNARK
1415    let res = recursive_snark.prove_step(
1416      &pp,
1417      &test_circuit1,
1418      &test_circuit2,
1419      vec![<G1 as Group>::Scalar::ONE],
1420      vec![<G2 as Group>::Scalar::ZERO],
1421    );
1422
1423    assert!(res.is_ok());
1424
1425    // verify the recursive SNARK
1426    let res = recursive_snark.verify(
1427      &pp,
1428      num_steps,
1429      &[<G1 as Group>::Scalar::ONE],
1430      &[<G2 as Group>::Scalar::ZERO],
1431    );
1432    assert!(res.is_ok());
1433
1434    let (zn_primary, zn_secondary) = res.unwrap();
1435
1436    assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::ONE]);
1437    assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(5u64)]);
1438  }
1439
1440  #[test]
1441  fn test_ivc_base() {
1442    type G1 = pasta_curves::pallas::Point;
1443    type G2 = pasta_curves::vesta::Point;
1444
1445    test_ivc_base_with::<G1, G2>();
1446  }
1447}