You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1472 lines
44 KiB

3 years ago
3 years ago
3 years ago
Verifier circuit (#23) * ECC scalar multiplication (first draft) * fix clippy nits * start implementing the ro gadget: 1st design Poseidon + truncate * truncate to 128 bits * implement add + double in constraints * finish implementing constraints for ecc * cargo fmt * input of smul should be an array of bits * cleanup ro a bit. Make the challenge returned be a vec of allocated bits * switch to neptune 6.0 * start implementing high level circuit * incomplete version of the verifier circuit with many TODOS * optimize ecc ops. add i ==0 case to the circuit * fix 0/1 constants at the circuit * wrap CompressedGroupElement of Pallas and Vesta * cargo fmt * generate poseidon constants once instead of every time we call get_challenge * Implement RO-based poseidon to use outside of circuit. Reorganize the repo * add inner circuit to verification circuit * start adding folding of the io. there is an error in the first call to mult_mod * add test to check that bellperson-nonnative is compatible with nova * remove swap file * add another test that fails * add inputs to the circuits in tests * rename q to m in circuit.rs. add more tests in test_bellperson_non_native. change a in test_mult_mod to expose error * push test for equal_with_carried. fix the issue is src/r1cs.rs * cargo fmt + update the verifier circuit: add folding of X and update all hashes with X * make limb_width and n_limbs parameters * make params part of h1 * allocate the field order as constant. add check that z0 == zi when i == 0 * fix error in test_poseidon_ro * remove merge error * small fixes * small fixes to comments * clippy lints * small edits; rename tests * move inputize before from_num * _limbs --> _bn * _limbs --> _bn Co-authored-by: Ioanna <iontzialla@gmail.com>
2 years ago
3 years ago
3 years ago
3 years ago
Add Grumpkin cycle implementation (#181) * bn256+grumpkin from halo2curves * chore: Integrate halo2curves more extensively - Extend existing tests with additional test cases using the new curve types * fix: Assign correct orders to bn256 and grumpkin scalar fields - Swap scalar orders between grumpkin and bn256 in `impl_traits!` implementation * test: Finish improving test integration with halo2curves - Enhances test coverage for `pasta_curves` and `halo2curves` - Cleans up commented code in `test_ivc_nontrivial` and `test_ivc_nontrivial_with_compression` tests - Updates relevant test cases in `src/lib.rs` to include new curve tests * chore: Remove commented-out/uneeded code in bn254_grumpkin.rs * test: reproduce test_from_label for bn254_grumpkin - Implement the `from_label_serial` function in bn254_grumpkin provider - Add a test to compare parallel and serial implementations of `from_label` function * refactor: Clean up to_coordinate & summarize changes * refactor: rename bn254_grumpkin -> bn256_grumpkin * test: Expand testing for public params digest using bn256 and grumpkin * chore: Update halo2curves dependency in Cargo.toml - Updated the `halo2curves` dependency in `Cargo.toml` to the latest version `0.1.0` from a specific git branch. * refactor: Refactor multi-exponentiation methods across providers - Updated bn256_grumpkin.rs to use the cpu_best_multiexp function from pasta provider instead of its native function. - Modified visibility of cpu_best_multiexp function in pasta.rs from private to crate level. * chore: set up dependencies to import the correct getrandom feature on Wasm --------- Co-authored-by: Leo Alt <leo@ethereum.org>
1 year ago
Add Grumpkin cycle implementation (#181) * bn256+grumpkin from halo2curves * chore: Integrate halo2curves more extensively - Extend existing tests with additional test cases using the new curve types * fix: Assign correct orders to bn256 and grumpkin scalar fields - Swap scalar orders between grumpkin and bn256 in `impl_traits!` implementation * test: Finish improving test integration with halo2curves - Enhances test coverage for `pasta_curves` and `halo2curves` - Cleans up commented code in `test_ivc_nontrivial` and `test_ivc_nontrivial_with_compression` tests - Updates relevant test cases in `src/lib.rs` to include new curve tests * chore: Remove commented-out/uneeded code in bn254_grumpkin.rs * test: reproduce test_from_label for bn254_grumpkin - Implement the `from_label_serial` function in bn254_grumpkin provider - Add a test to compare parallel and serial implementations of `from_label` function * refactor: Clean up to_coordinate & summarize changes * refactor: rename bn254_grumpkin -> bn256_grumpkin * test: Expand testing for public params digest using bn256 and grumpkin * chore: Update halo2curves dependency in Cargo.toml - Updated the `halo2curves` dependency in `Cargo.toml` to the latest version `0.1.0` from a specific git branch. * refactor: Refactor multi-exponentiation methods across providers - Updated bn256_grumpkin.rs to use the cpu_best_multiexp function from pasta provider instead of its native function. - Modified visibility of cpu_best_multiexp function in pasta.rs from private to crate level. * chore: set up dependencies to import the correct getrandom feature on Wasm --------- Co-authored-by: Leo Alt <leo@ethereum.org>
1 year ago
[refactorings] Leftovers (pot-pourri?) (#184) * test: compute_path * refactor: path computation - Improve path concatenation by utilizing built-in `join` method * refactor: replace `PartialEq` with derived instance - Derive `PartialEq` for `SatisfyingAssignment` struct - Remove redundant manual implementation of `PartialEq` Cargo-expand generates: ``` #[automatically_derived] impl<G: ::core::cmp::PartialEq + Group> ::core::cmp::PartialEq for SatisfyingAssignment<G> where G::Scalar: PrimeField, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, { #[inline] fn eq(&self, other: &SatisfyingAssignment<G>) -> bool { self.a_aux_density == other.a_aux_density && self.b_input_density == other.b_input_density && self.b_aux_density == other.b_aux_density && self.a == other.a && self.b == other.b && self.c == other.c && self.input_assignment == other.input_assignment && self.aux_assignment == other.aux_assignment } } ``` * refactor: avoid default for PhantomData Unit type * refactor: replace fold with sum where applicable - Simplify code by replacing `fold` with `sum` in various instances * refactor: decompression method in sumcheck.rs * refactor: test functions to use slice instead of vector conversion * refactor: use more references in functions - Update parameter types to use references instead of owned values in various functions that do not need them - Replace cloning instances with references
1 year ago
Add Grumpkin cycle implementation (#181) * bn256+grumpkin from halo2curves * chore: Integrate halo2curves more extensively - Extend existing tests with additional test cases using the new curve types * fix: Assign correct orders to bn256 and grumpkin scalar fields - Swap scalar orders between grumpkin and bn256 in `impl_traits!` implementation * test: Finish improving test integration with halo2curves - Enhances test coverage for `pasta_curves` and `halo2curves` - Cleans up commented code in `test_ivc_nontrivial` and `test_ivc_nontrivial_with_compression` tests - Updates relevant test cases in `src/lib.rs` to include new curve tests * chore: Remove commented-out/uneeded code in bn254_grumpkin.rs * test: reproduce test_from_label for bn254_grumpkin - Implement the `from_label_serial` function in bn254_grumpkin provider - Add a test to compare parallel and serial implementations of `from_label` function * refactor: Clean up to_coordinate & summarize changes * refactor: rename bn254_grumpkin -> bn256_grumpkin * test: Expand testing for public params digest using bn256 and grumpkin * chore: Update halo2curves dependency in Cargo.toml - Updated the `halo2curves` dependency in `Cargo.toml` to the latest version `0.1.0` from a specific git branch. * refactor: Refactor multi-exponentiation methods across providers - Updated bn256_grumpkin.rs to use the cpu_best_multiexp function from pasta provider instead of its native function. - Modified visibility of cpu_best_multiexp function in pasta.rs from private to crate level. * chore: set up dependencies to import the correct getrandom feature on Wasm --------- Co-authored-by: Leo Alt <leo@ethereum.org>
1 year ago
[refactorings] Leftovers (pot-pourri?) (#184) * test: compute_path * refactor: path computation - Improve path concatenation by utilizing built-in `join` method * refactor: replace `PartialEq` with derived instance - Derive `PartialEq` for `SatisfyingAssignment` struct - Remove redundant manual implementation of `PartialEq` Cargo-expand generates: ``` #[automatically_derived] impl<G: ::core::cmp::PartialEq + Group> ::core::cmp::PartialEq for SatisfyingAssignment<G> where G::Scalar: PrimeField, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, { #[inline] fn eq(&self, other: &SatisfyingAssignment<G>) -> bool { self.a_aux_density == other.a_aux_density && self.b_input_density == other.b_input_density && self.b_aux_density == other.b_aux_density && self.a == other.a && self.b == other.b && self.c == other.c && self.input_assignment == other.input_assignment && self.aux_assignment == other.aux_assignment } } ``` * refactor: avoid default for PhantomData Unit type * refactor: replace fold with sum where applicable - Simplify code by replacing `fold` with `sum` in various instances * refactor: decompression method in sumcheck.rs * refactor: test functions to use slice instead of vector conversion * refactor: use more references in functions - Update parameter types to use references instead of owned values in various functions that do not need them - Replace cloning instances with references
1 year ago
[refactorings] Leftovers (pot-pourri?) (#184) * test: compute_path * refactor: path computation - Improve path concatenation by utilizing built-in `join` method * refactor: replace `PartialEq` with derived instance - Derive `PartialEq` for `SatisfyingAssignment` struct - Remove redundant manual implementation of `PartialEq` Cargo-expand generates: ``` #[automatically_derived] impl<G: ::core::cmp::PartialEq + Group> ::core::cmp::PartialEq for SatisfyingAssignment<G> where G::Scalar: PrimeField, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, { #[inline] fn eq(&self, other: &SatisfyingAssignment<G>) -> bool { self.a_aux_density == other.a_aux_density && self.b_input_density == other.b_input_density && self.b_aux_density == other.b_aux_density && self.a == other.a && self.b == other.b && self.c == other.c && self.input_assignment == other.input_assignment && self.aux_assignment == other.aux_assignment } } ``` * refactor: avoid default for PhantomData Unit type * refactor: replace fold with sum where applicable - Simplify code by replacing `fold` with `sum` in various instances * refactor: decompression method in sumcheck.rs * refactor: test functions to use slice instead of vector conversion * refactor: use more references in functions - Update parameter types to use references instead of owned values in various functions that do not need them - Replace cloning instances with references
1 year ago
Add Grumpkin cycle implementation (#181) * bn256+grumpkin from halo2curves * chore: Integrate halo2curves more extensively - Extend existing tests with additional test cases using the new curve types * fix: Assign correct orders to bn256 and grumpkin scalar fields - Swap scalar orders between grumpkin and bn256 in `impl_traits!` implementation * test: Finish improving test integration with halo2curves - Enhances test coverage for `pasta_curves` and `halo2curves` - Cleans up commented code in `test_ivc_nontrivial` and `test_ivc_nontrivial_with_compression` tests - Updates relevant test cases in `src/lib.rs` to include new curve tests * chore: Remove commented-out/uneeded code in bn254_grumpkin.rs * test: reproduce test_from_label for bn254_grumpkin - Implement the `from_label_serial` function in bn254_grumpkin provider - Add a test to compare parallel and serial implementations of `from_label` function * refactor: Clean up to_coordinate & summarize changes * refactor: rename bn254_grumpkin -> bn256_grumpkin * test: Expand testing for public params digest using bn256 and grumpkin * chore: Update halo2curves dependency in Cargo.toml - Updated the `halo2curves` dependency in `Cargo.toml` to the latest version `0.1.0` from a specific git branch. * refactor: Refactor multi-exponentiation methods across providers - Updated bn256_grumpkin.rs to use the cpu_best_multiexp function from pasta provider instead of its native function. - Modified visibility of cpu_best_multiexp function in pasta.rs from private to crate level. * chore: set up dependencies to import the correct getrandom feature on Wasm --------- Co-authored-by: Leo Alt <leo@ethereum.org>
1 year ago
[refactorings] Leftovers (pot-pourri?) (#184) * test: compute_path * refactor: path computation - Improve path concatenation by utilizing built-in `join` method * refactor: replace `PartialEq` with derived instance - Derive `PartialEq` for `SatisfyingAssignment` struct - Remove redundant manual implementation of `PartialEq` Cargo-expand generates: ``` #[automatically_derived] impl<G: ::core::cmp::PartialEq + Group> ::core::cmp::PartialEq for SatisfyingAssignment<G> where G::Scalar: PrimeField, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, { #[inline] fn eq(&self, other: &SatisfyingAssignment<G>) -> bool { self.a_aux_density == other.a_aux_density && self.b_input_density == other.b_input_density && self.b_aux_density == other.b_aux_density && self.a == other.a && self.b == other.b && self.c == other.c && self.input_assignment == other.input_assignment && self.aux_assignment == other.aux_assignment } } ``` * refactor: avoid default for PhantomData Unit type * refactor: replace fold with sum where applicable - Simplify code by replacing `fold` with `sum` in various instances * refactor: decompression method in sumcheck.rs * refactor: test functions to use slice instead of vector conversion * refactor: use more references in functions - Update parameter types to use references instead of owned values in various functions that do not need them - Replace cloning instances with references
1 year ago
Add Grumpkin cycle implementation (#181) * bn256+grumpkin from halo2curves * chore: Integrate halo2curves more extensively - Extend existing tests with additional test cases using the new curve types * fix: Assign correct orders to bn256 and grumpkin scalar fields - Swap scalar orders between grumpkin and bn256 in `impl_traits!` implementation * test: Finish improving test integration with halo2curves - Enhances test coverage for `pasta_curves` and `halo2curves` - Cleans up commented code in `test_ivc_nontrivial` and `test_ivc_nontrivial_with_compression` tests - Updates relevant test cases in `src/lib.rs` to include new curve tests * chore: Remove commented-out/uneeded code in bn254_grumpkin.rs * test: reproduce test_from_label for bn254_grumpkin - Implement the `from_label_serial` function in bn254_grumpkin provider - Add a test to compare parallel and serial implementations of `from_label` function * refactor: Clean up to_coordinate & summarize changes * refactor: rename bn254_grumpkin -> bn256_grumpkin * test: Expand testing for public params digest using bn256 and grumpkin * chore: Update halo2curves dependency in Cargo.toml - Updated the `halo2curves` dependency in `Cargo.toml` to the latest version `0.1.0` from a specific git branch. * refactor: Refactor multi-exponentiation methods across providers - Updated bn256_grumpkin.rs to use the cpu_best_multiexp function from pasta provider instead of its native function. - Modified visibility of cpu_best_multiexp function in pasta.rs from private to crate level. * chore: set up dependencies to import the correct getrandom feature on Wasm --------- Co-authored-by: Leo Alt <leo@ethereum.org>
1 year ago
[refactorings] Leftovers (pot-pourri?) (#184) * test: compute_path * refactor: path computation - Improve path concatenation by utilizing built-in `join` method * refactor: replace `PartialEq` with derived instance - Derive `PartialEq` for `SatisfyingAssignment` struct - Remove redundant manual implementation of `PartialEq` Cargo-expand generates: ``` #[automatically_derived] impl<G: ::core::cmp::PartialEq + Group> ::core::cmp::PartialEq for SatisfyingAssignment<G> where G::Scalar: PrimeField, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, { #[inline] fn eq(&self, other: &SatisfyingAssignment<G>) -> bool { self.a_aux_density == other.a_aux_density && self.b_input_density == other.b_input_density && self.b_aux_density == other.b_aux_density && self.a == other.a && self.b == other.b && self.c == other.c && self.input_assignment == other.input_assignment && self.aux_assignment == other.aux_assignment } } ``` * refactor: avoid default for PhantomData Unit type * refactor: replace fold with sum where applicable - Simplify code by replacing `fold` with `sum` in various instances * refactor: decompression method in sumcheck.rs * refactor: test functions to use slice instead of vector conversion * refactor: use more references in functions - Update parameter types to use references instead of owned values in various functions that do not need them - Replace cloning instances with references
1 year ago
Add Grumpkin cycle implementation (#181) * bn256+grumpkin from halo2curves * chore: Integrate halo2curves more extensively - Extend existing tests with additional test cases using the new curve types * fix: Assign correct orders to bn256 and grumpkin scalar fields - Swap scalar orders between grumpkin and bn256 in `impl_traits!` implementation * test: Finish improving test integration with halo2curves - Enhances test coverage for `pasta_curves` and `halo2curves` - Cleans up commented code in `test_ivc_nontrivial` and `test_ivc_nontrivial_with_compression` tests - Updates relevant test cases in `src/lib.rs` to include new curve tests * chore: Remove commented-out/uneeded code in bn254_grumpkin.rs * test: reproduce test_from_label for bn254_grumpkin - Implement the `from_label_serial` function in bn254_grumpkin provider - Add a test to compare parallel and serial implementations of `from_label` function * refactor: Clean up to_coordinate & summarize changes * refactor: rename bn254_grumpkin -> bn256_grumpkin * test: Expand testing for public params digest using bn256 and grumpkin * chore: Update halo2curves dependency in Cargo.toml - Updated the `halo2curves` dependency in `Cargo.toml` to the latest version `0.1.0` from a specific git branch. * refactor: Refactor multi-exponentiation methods across providers - Updated bn256_grumpkin.rs to use the cpu_best_multiexp function from pasta provider instead of its native function. - Modified visibility of cpu_best_multiexp function in pasta.rs from private to crate level. * chore: set up dependencies to import the correct getrandom feature on Wasm --------- Co-authored-by: Leo Alt <leo@ethereum.org>
1 year ago
Add Grumpkin cycle implementation (#181) * bn256+grumpkin from halo2curves * chore: Integrate halo2curves more extensively - Extend existing tests with additional test cases using the new curve types * fix: Assign correct orders to bn256 and grumpkin scalar fields - Swap scalar orders between grumpkin and bn256 in `impl_traits!` implementation * test: Finish improving test integration with halo2curves - Enhances test coverage for `pasta_curves` and `halo2curves` - Cleans up commented code in `test_ivc_nontrivial` and `test_ivc_nontrivial_with_compression` tests - Updates relevant test cases in `src/lib.rs` to include new curve tests * chore: Remove commented-out/uneeded code in bn254_grumpkin.rs * test: reproduce test_from_label for bn254_grumpkin - Implement the `from_label_serial` function in bn254_grumpkin provider - Add a test to compare parallel and serial implementations of `from_label` function * refactor: Clean up to_coordinate & summarize changes * refactor: rename bn254_grumpkin -> bn256_grumpkin * test: Expand testing for public params digest using bn256 and grumpkin * chore: Update halo2curves dependency in Cargo.toml - Updated the `halo2curves` dependency in `Cargo.toml` to the latest version `0.1.0` from a specific git branch. * refactor: Refactor multi-exponentiation methods across providers - Updated bn256_grumpkin.rs to use the cpu_best_multiexp function from pasta provider instead of its native function. - Modified visibility of cpu_best_multiexp function in pasta.rs from private to crate level. * chore: set up dependencies to import the correct getrandom feature on Wasm --------- Co-authored-by: Leo Alt <leo@ethereum.org>
1 year ago
[refactorings] Leftovers (pot-pourri?) (#184) * test: compute_path * refactor: path computation - Improve path concatenation by utilizing built-in `join` method * refactor: replace `PartialEq` with derived instance - Derive `PartialEq` for `SatisfyingAssignment` struct - Remove redundant manual implementation of `PartialEq` Cargo-expand generates: ``` #[automatically_derived] impl<G: ::core::cmp::PartialEq + Group> ::core::cmp::PartialEq for SatisfyingAssignment<G> where G::Scalar: PrimeField, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, G::Scalar: ::core::cmp::PartialEq, { #[inline] fn eq(&self, other: &SatisfyingAssignment<G>) -> bool { self.a_aux_density == other.a_aux_density && self.b_input_density == other.b_input_density && self.b_aux_density == other.b_aux_density && self.a == other.a && self.b == other.b && self.c == other.c && self.input_assignment == other.input_assignment && self.aux_assignment == other.aux_assignment } } ``` * refactor: avoid default for PhantomData Unit type * refactor: replace fold with sum where applicable - Simplify code by replacing `fold` with `sum` in various instances * refactor: decompression method in sumcheck.rs * refactor: test functions to use slice instead of vector conversion * refactor: use more references in functions - Update parameter types to use references instead of owned values in various functions that do not need them - Replace cloning instances with references
1 year ago
Add Grumpkin cycle implementation (#181) * bn256+grumpkin from halo2curves * chore: Integrate halo2curves more extensively - Extend existing tests with additional test cases using the new curve types * fix: Assign correct orders to bn256 and grumpkin scalar fields - Swap scalar orders between grumpkin and bn256 in `impl_traits!` implementation * test: Finish improving test integration with halo2curves - Enhances test coverage for `pasta_curves` and `halo2curves` - Cleans up commented code in `test_ivc_nontrivial` and `test_ivc_nontrivial_with_compression` tests - Updates relevant test cases in `src/lib.rs` to include new curve tests * chore: Remove commented-out/uneeded code in bn254_grumpkin.rs * test: reproduce test_from_label for bn254_grumpkin - Implement the `from_label_serial` function in bn254_grumpkin provider - Add a test to compare parallel and serial implementations of `from_label` function * refactor: Clean up to_coordinate & summarize changes * refactor: rename bn254_grumpkin -> bn256_grumpkin * test: Expand testing for public params digest using bn256 and grumpkin * chore: Update halo2curves dependency in Cargo.toml - Updated the `halo2curves` dependency in `Cargo.toml` to the latest version `0.1.0` from a specific git branch. * refactor: Refactor multi-exponentiation methods across providers - Updated bn256_grumpkin.rs to use the cpu_best_multiexp function from pasta provider instead of its native function. - Modified visibility of cpu_best_multiexp function in pasta.rs from private to crate level. * chore: set up dependencies to import the correct getrandom feature on Wasm --------- Co-authored-by: Leo Alt <leo@ethereum.org>
1 year ago
  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. // private modules
  14. mod bellperson;
  15. mod circuit;
  16. mod constants;
  17. mod nifs;
  18. mod r1cs;
  19. // public modules
  20. pub mod errors;
  21. pub mod gadgets;
  22. pub mod provider;
  23. pub mod spartan;
  24. pub mod traits;
  25. use crate::bellperson::{
  26. r1cs::{NovaShape, NovaWitness},
  27. shape_cs::ShapeCS,
  28. solver::SatisfyingAssignment,
  29. };
  30. use ::bellperson::{Circuit, ConstraintSystem};
  31. use circuit::{NovaAugmentedCircuit, NovaAugmentedCircuitInputs, NovaAugmentedCircuitParams};
  32. use constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS};
  33. use core::marker::PhantomData;
  34. use errors::NovaError;
  35. use ff::Field;
  36. use gadgets::utils::scalar_as_base;
  37. use nifs::NIFS;
  38. use r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness};
  39. use serde::{Deserialize, Serialize};
  40. use sha3::{Digest, Sha3_256};
  41. use traits::{
  42. circuit::StepCircuit,
  43. commitment::{CommitmentEngineTrait, CommitmentTrait},
  44. snark::RelaxedR1CSSNARKTrait,
  45. AbsorbInROTrait, Group, ROConstants, ROConstantsCircuit, ROConstantsTrait, ROTrait,
  46. };
  47. /// A type that holds public parameters of Nova
  48. #[derive(Serialize, Deserialize)]
  49. #[serde(bound = "")]
  50. pub struct PublicParams<G1, G2, C1, C2>
  51. where
  52. G1: Group<Base = <G2 as Group>::Scalar>,
  53. G2: Group<Base = <G1 as Group>::Scalar>,
  54. C1: StepCircuit<G1::Scalar>,
  55. C2: StepCircuit<G2::Scalar>,
  56. {
  57. F_arity_primary: usize,
  58. F_arity_secondary: usize,
  59. ro_consts_primary: ROConstants<G1>,
  60. ro_consts_circuit_primary: ROConstantsCircuit<G2>,
  61. ck_primary: CommitmentKey<G1>,
  62. r1cs_shape_primary: R1CSShape<G1>,
  63. ro_consts_secondary: ROConstants<G2>,
  64. ro_consts_circuit_secondary: ROConstantsCircuit<G1>,
  65. ck_secondary: CommitmentKey<G2>,
  66. r1cs_shape_secondary: R1CSShape<G2>,
  67. augmented_circuit_params_primary: NovaAugmentedCircuitParams,
  68. augmented_circuit_params_secondary: NovaAugmentedCircuitParams,
  69. digest: G1::Scalar, // digest of everything else with this field set to G1::Scalar::ZERO
  70. _p_c1: PhantomData<C1>,
  71. _p_c2: PhantomData<C2>,
  72. }
  73. impl<G1, G2, C1, C2> PublicParams<G1, G2, C1, C2>
  74. where
  75. G1: Group<Base = <G2 as Group>::Scalar>,
  76. G2: Group<Base = <G1 as Group>::Scalar>,
  77. C1: StepCircuit<G1::Scalar>,
  78. C2: StepCircuit<G2::Scalar>,
  79. {
  80. /// Create a new `PublicParams`
  81. pub fn setup(c_primary: C1, c_secondary: C2) -> Self {
  82. let augmented_circuit_params_primary =
  83. NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true);
  84. let augmented_circuit_params_secondary =
  85. NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
  86. let ro_consts_primary: ROConstants<G1> = ROConstants::<G1>::new();
  87. let ro_consts_secondary: ROConstants<G2> = ROConstants::<G2>::new();
  88. let F_arity_primary = c_primary.arity();
  89. let F_arity_secondary = c_secondary.arity();
  90. // ro_consts_circuit_primary are parameterized by G2 because the type alias uses G2::Base = G1::Scalar
  91. let ro_consts_circuit_primary: ROConstantsCircuit<G2> = ROConstantsCircuit::<G2>::new();
  92. let ro_consts_circuit_secondary: ROConstantsCircuit<G1> = ROConstantsCircuit::<G1>::new();
  93. // Initialize ck for the primary
  94. let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
  95. augmented_circuit_params_primary.clone(),
  96. None,
  97. c_primary,
  98. ro_consts_circuit_primary.clone(),
  99. );
  100. let mut cs: ShapeCS<G1> = ShapeCS::new();
  101. let _ = circuit_primary.synthesize(&mut cs);
  102. let (r1cs_shape_primary, ck_primary) = cs.r1cs_shape();
  103. // Initialize ck for the secondary
  104. let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
  105. augmented_circuit_params_secondary.clone(),
  106. None,
  107. c_secondary,
  108. ro_consts_circuit_secondary.clone(),
  109. );
  110. let mut cs: ShapeCS<G2> = ShapeCS::new();
  111. let _ = circuit_secondary.synthesize(&mut cs);
  112. let (r1cs_shape_secondary, ck_secondary) = cs.r1cs_shape();
  113. let mut pp = Self {
  114. F_arity_primary,
  115. F_arity_secondary,
  116. ro_consts_primary,
  117. ro_consts_circuit_primary,
  118. ck_primary,
  119. r1cs_shape_primary,
  120. ro_consts_secondary,
  121. ro_consts_circuit_secondary,
  122. ck_secondary,
  123. r1cs_shape_secondary,
  124. augmented_circuit_params_primary,
  125. augmented_circuit_params_secondary,
  126. digest: G1::Scalar::ZERO,
  127. _p_c1: Default::default(),
  128. _p_c2: Default::default(),
  129. };
  130. // set the digest in pp
  131. pp.digest = compute_digest::<G1, PublicParams<G1, G2, C1, C2>>(&pp);
  132. pp
  133. }
  134. /// Returns the number of constraints in the primary and secondary circuits
  135. pub fn num_constraints(&self) -> (usize, usize) {
  136. (
  137. self.r1cs_shape_primary.num_cons,
  138. self.r1cs_shape_secondary.num_cons,
  139. )
  140. }
  141. /// Returns the number of variables in the primary and secondary circuits
  142. pub fn num_variables(&self) -> (usize, usize) {
  143. (
  144. self.r1cs_shape_primary.num_vars,
  145. self.r1cs_shape_secondary.num_vars,
  146. )
  147. }
  148. }
  149. /// A SNARK that proves the correct execution of an incremental computation
  150. #[derive(Clone, Debug, Serialize, Deserialize)]
  151. #[serde(bound = "")]
  152. pub struct RecursiveSNARK<G1, G2, C1, C2>
  153. where
  154. G1: Group<Base = <G2 as Group>::Scalar>,
  155. G2: Group<Base = <G1 as Group>::Scalar>,
  156. C1: StepCircuit<G1::Scalar>,
  157. C2: StepCircuit<G2::Scalar>,
  158. {
  159. r_W_primary: RelaxedR1CSWitness<G1>,
  160. r_U_primary: RelaxedR1CSInstance<G1>,
  161. r_W_secondary: RelaxedR1CSWitness<G2>,
  162. r_U_secondary: RelaxedR1CSInstance<G2>,
  163. l_w_secondary: R1CSWitness<G2>,
  164. l_u_secondary: R1CSInstance<G2>,
  165. i: usize,
  166. zi_primary: Vec<G1::Scalar>,
  167. zi_secondary: Vec<G2::Scalar>,
  168. _p_c1: PhantomData<C1>,
  169. _p_c2: PhantomData<C2>,
  170. }
  171. impl<G1, G2, C1, C2> RecursiveSNARK<G1, G2, C1, C2>
  172. where
  173. G1: Group<Base = <G2 as Group>::Scalar>,
  174. G2: Group<Base = <G1 as Group>::Scalar>,
  175. C1: StepCircuit<G1::Scalar>,
  176. C2: StepCircuit<G2::Scalar>,
  177. {
  178. /// Create new instance of recursive SNARK
  179. pub fn new(
  180. pp: &PublicParams<G1, G2, C1, C2>,
  181. c_primary: &C1,
  182. c_secondary: &C2,
  183. z0_primary: Vec<G1::Scalar>,
  184. z0_secondary: Vec<G2::Scalar>,
  185. ) -> Self {
  186. // Expected outputs of the two circuits
  187. let zi_primary = c_primary.output(&z0_primary);
  188. let zi_secondary = c_secondary.output(&z0_secondary);
  189. // base case for the primary
  190. let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
  191. let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
  192. scalar_as_base::<G1>(pp.digest),
  193. G1::Scalar::ZERO,
  194. z0_primary,
  195. None,
  196. None,
  197. None,
  198. None,
  199. );
  200. let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
  201. pp.augmented_circuit_params_primary.clone(),
  202. Some(inputs_primary),
  203. c_primary.clone(),
  204. pp.ro_consts_circuit_primary.clone(),
  205. );
  206. let _ = circuit_primary.synthesize(&mut cs_primary);
  207. let (u_primary, w_primary) = cs_primary
  208. .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary)
  209. .map_err(|_e| NovaError::UnSat)
  210. .expect("Nova error unsat");
  211. // base case for the secondary
  212. let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
  213. let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
  214. pp.digest,
  215. G2::Scalar::ZERO,
  216. z0_secondary,
  217. None,
  218. None,
  219. Some(u_primary.clone()),
  220. None,
  221. );
  222. let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
  223. pp.augmented_circuit_params_secondary.clone(),
  224. Some(inputs_secondary),
  225. c_secondary.clone(),
  226. pp.ro_consts_circuit_secondary.clone(),
  227. );
  228. let _ = circuit_secondary.synthesize(&mut cs_secondary);
  229. let (u_secondary, w_secondary) = cs_secondary
  230. .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary)
  231. .map_err(|_e| NovaError::UnSat)
  232. .expect("Nova error unsat");
  233. // IVC proof for the primary circuit
  234. let l_w_primary = w_primary;
  235. let l_u_primary = u_primary;
  236. let r_W_primary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &l_w_primary);
  237. let r_U_primary =
  238. RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, &pp.r1cs_shape_primary, &l_u_primary);
  239. // IVC proof of the secondary circuit
  240. let l_w_secondary = w_secondary;
  241. let l_u_secondary = u_secondary;
  242. let r_W_secondary = RelaxedR1CSWitness::<G2>::default(&pp.r1cs_shape_secondary);
  243. let r_U_secondary =
  244. RelaxedR1CSInstance::<G2>::default(&pp.ck_secondary, &pp.r1cs_shape_secondary);
  245. if zi_primary.len() != pp.F_arity_primary || zi_secondary.len() != pp.F_arity_secondary {
  246. panic!("Invalid step length");
  247. }
  248. Self {
  249. r_W_primary,
  250. r_U_primary,
  251. r_W_secondary,
  252. r_U_secondary,
  253. l_w_secondary,
  254. l_u_secondary,
  255. i: 0,
  256. zi_primary,
  257. zi_secondary,
  258. _p_c1: Default::default(),
  259. _p_c2: Default::default(),
  260. }
  261. }
  262. /// Create a new `RecursiveSNARK` (or updates the provided `RecursiveSNARK`)
  263. /// by executing a step of the incremental computation
  264. pub fn prove_step(
  265. &mut self,
  266. pp: &PublicParams<G1, G2, C1, C2>,
  267. c_primary: &C1,
  268. c_secondary: &C2,
  269. z0_primary: Vec<G1::Scalar>,
  270. z0_secondary: Vec<G2::Scalar>,
  271. ) -> Result<(), NovaError> {
  272. if z0_primary.len() != pp.F_arity_primary || z0_secondary.len() != pp.F_arity_secondary {
  273. return Err(NovaError::InvalidInitialInputLength);
  274. }
  275. // Frist step was already done in the constructor
  276. if self.i == 0 {
  277. self.i = 1;
  278. return Ok(());
  279. }
  280. // fold the secondary circuit's instance
  281. let (nifs_secondary, (r_U_secondary, r_W_secondary)) = NIFS::prove(
  282. &pp.ck_secondary,
  283. &pp.ro_consts_secondary,
  284. &scalar_as_base::<G1>(pp.digest),
  285. &pp.r1cs_shape_secondary,
  286. &self.r_U_secondary,
  287. &self.r_W_secondary,
  288. &self.l_u_secondary,
  289. &self.l_w_secondary,
  290. )
  291. .expect("Unable to fold secondary");
  292. let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
  293. let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
  294. scalar_as_base::<G1>(pp.digest),
  295. G1::Scalar::from(self.i as u64),
  296. z0_primary,
  297. Some(self.zi_primary.clone()),
  298. Some(self.r_U_secondary.clone()),
  299. Some(self.l_u_secondary.clone()),
  300. Some(Commitment::<G2>::decompress(&nifs_secondary.comm_T)?),
  301. );
  302. let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
  303. pp.augmented_circuit_params_primary.clone(),
  304. Some(inputs_primary),
  305. c_primary.clone(),
  306. pp.ro_consts_circuit_primary.clone(),
  307. );
  308. let _ = circuit_primary.synthesize(&mut cs_primary);
  309. let (l_u_primary, l_w_primary) = cs_primary
  310. .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary)
  311. .map_err(|_e| NovaError::UnSat)
  312. .expect("Nova error unsat");
  313. // fold the primary circuit's instance
  314. let (nifs_primary, (r_U_primary, r_W_primary)) = NIFS::prove(
  315. &pp.ck_primary,
  316. &pp.ro_consts_primary,
  317. &pp.digest,
  318. &pp.r1cs_shape_primary,
  319. &self.r_U_primary,
  320. &self.r_W_primary,
  321. &l_u_primary,
  322. &l_w_primary,
  323. )
  324. .expect("Unable to fold primary");
  325. let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
  326. let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
  327. pp.digest,
  328. G2::Scalar::from(self.i as u64),
  329. z0_secondary,
  330. Some(self.zi_secondary.clone()),
  331. Some(self.r_U_primary.clone()),
  332. Some(l_u_primary),
  333. Some(Commitment::<G1>::decompress(&nifs_primary.comm_T)?),
  334. );
  335. let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
  336. pp.augmented_circuit_params_secondary.clone(),
  337. Some(inputs_secondary),
  338. c_secondary.clone(),
  339. pp.ro_consts_circuit_secondary.clone(),
  340. );
  341. let _ = circuit_secondary.synthesize(&mut cs_secondary);
  342. let (l_u_secondary, l_w_secondary) = cs_secondary
  343. .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary)
  344. .map_err(|_e| NovaError::UnSat)?;
  345. // update the running instances and witnesses
  346. self.zi_primary = c_primary.output(&self.zi_primary);
  347. self.zi_secondary = c_secondary.output(&self.zi_secondary);
  348. self.l_u_secondary = l_u_secondary;
  349. self.l_w_secondary = l_w_secondary;
  350. self.r_U_primary = r_U_primary;
  351. self.r_W_primary = r_W_primary;
  352. self.i += 1;
  353. self.r_U_secondary = r_U_secondary;
  354. self.r_W_secondary = r_W_secondary;
  355. Ok(())
  356. }
  357. /// Verify the correctness of the `RecursiveSNARK`
  358. pub fn verify(
  359. &self,
  360. pp: &PublicParams<G1, G2, C1, C2>,
  361. num_steps: usize,
  362. z0_primary: &[G1::Scalar],
  363. z0_secondary: &[G2::Scalar],
  364. ) -> Result<(Vec<G1::Scalar>, Vec<G2::Scalar>), NovaError> {
  365. // number of steps cannot be zero
  366. if num_steps == 0 {
  367. return Err(NovaError::ProofVerifyError);
  368. }
  369. // check if the provided proof has executed num_steps
  370. if self.i != num_steps {
  371. return Err(NovaError::ProofVerifyError);
  372. }
  373. // check if the (relaxed) R1CS instances have two public outputs
  374. if self.l_u_secondary.X.len() != 2
  375. || self.r_U_primary.X.len() != 2
  376. || self.r_U_secondary.X.len() != 2
  377. {
  378. return Err(NovaError::ProofVerifyError);
  379. }
  380. // check if the output hashes in R1CS instances point to the right running instances
  381. let (hash_primary, hash_secondary) = {
  382. let mut hasher = <G2 as Group>::RO::new(
  383. pp.ro_consts_secondary.clone(),
  384. NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_primary,
  385. );
  386. hasher.absorb(pp.digest);
  387. hasher.absorb(G1::Scalar::from(num_steps as u64));
  388. for e in z0_primary {
  389. hasher.absorb(*e);
  390. }
  391. for e in &self.zi_primary {
  392. hasher.absorb(*e);
  393. }
  394. self.r_U_secondary.absorb_in_ro(&mut hasher);
  395. let mut hasher2 = <G1 as Group>::RO::new(
  396. pp.ro_consts_primary.clone(),
  397. NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_secondary,
  398. );
  399. hasher2.absorb(scalar_as_base::<G1>(pp.digest));
  400. hasher2.absorb(G2::Scalar::from(num_steps as u64));
  401. for e in z0_secondary {
  402. hasher2.absorb(*e);
  403. }
  404. for e in &self.zi_secondary {
  405. hasher2.absorb(*e);
  406. }
  407. self.r_U_primary.absorb_in_ro(&mut hasher2);
  408. (
  409. hasher.squeeze(NUM_HASH_BITS),
  410. hasher2.squeeze(NUM_HASH_BITS),
  411. )
  412. };
  413. if hash_primary != self.l_u_secondary.X[0]
  414. || hash_secondary != scalar_as_base::<G2>(self.l_u_secondary.X[1])
  415. {
  416. return Err(NovaError::ProofVerifyError);
  417. }
  418. // check the satisfiability of the provided instances
  419. let (res_r_primary, (res_r_secondary, res_l_secondary)) = rayon::join(
  420. || {
  421. pp.r1cs_shape_primary
  422. .is_sat_relaxed(&pp.ck_primary, &self.r_U_primary, &self.r_W_primary)
  423. },
  424. || {
  425. rayon::join(
  426. || {
  427. pp.r1cs_shape_secondary.is_sat_relaxed(
  428. &pp.ck_secondary,
  429. &self.r_U_secondary,
  430. &self.r_W_secondary,
  431. )
  432. },
  433. || {
  434. pp.r1cs_shape_secondary.is_sat(
  435. &pp.ck_secondary,
  436. &self.l_u_secondary,
  437. &self.l_w_secondary,
  438. )
  439. },
  440. )
  441. },
  442. );
  443. // check the returned res objects
  444. res_r_primary?;
  445. res_r_secondary?;
  446. res_l_secondary?;
  447. Ok((self.zi_primary.clone(), self.zi_secondary.clone()))
  448. }
  449. }
  450. /// A type that holds the prover key for `CompressedSNARK`
  451. #[derive(Clone, Debug, Serialize, Deserialize)]
  452. #[serde(bound = "")]
  453. pub struct ProverKey<G1, G2, C1, C2, S1, S2>
  454. where
  455. G1: Group<Base = <G2 as Group>::Scalar>,
  456. G2: Group<Base = <G1 as Group>::Scalar>,
  457. C1: StepCircuit<G1::Scalar>,
  458. C2: StepCircuit<G2::Scalar>,
  459. S1: RelaxedR1CSSNARKTrait<G1>,
  460. S2: RelaxedR1CSSNARKTrait<G2>,
  461. {
  462. pk_primary: S1::ProverKey,
  463. pk_secondary: S2::ProverKey,
  464. _p_c1: PhantomData<C1>,
  465. _p_c2: PhantomData<C2>,
  466. }
  467. /// A type that holds the verifier key for `CompressedSNARK`
  468. #[derive(Clone, Serialize, Deserialize)]
  469. #[serde(bound = "")]
  470. pub struct VerifierKey<G1, G2, C1, C2, S1, S2>
  471. where
  472. G1: Group<Base = <G2 as Group>::Scalar>,
  473. G2: Group<Base = <G1 as Group>::Scalar>,
  474. C1: StepCircuit<G1::Scalar>,
  475. C2: StepCircuit<G2::Scalar>,
  476. S1: RelaxedR1CSSNARKTrait<G1>,
  477. S2: RelaxedR1CSSNARKTrait<G2>,
  478. {
  479. F_arity_primary: usize,
  480. F_arity_secondary: usize,
  481. ro_consts_primary: ROConstants<G1>,
  482. ro_consts_secondary: ROConstants<G2>,
  483. digest: G1::Scalar,
  484. vk_primary: S1::VerifierKey,
  485. vk_secondary: S2::VerifierKey,
  486. _p_c1: PhantomData<C1>,
  487. _p_c2: PhantomData<C2>,
  488. }
  489. /// A SNARK that proves the knowledge of a valid `RecursiveSNARK`
  490. #[derive(Clone, Serialize, Deserialize)]
  491. #[serde(bound = "")]
  492. pub struct CompressedSNARK<G1, G2, C1, C2, S1, S2>
  493. where
  494. G1: Group<Base = <G2 as Group>::Scalar>,
  495. G2: Group<Base = <G1 as Group>::Scalar>,
  496. C1: StepCircuit<G1::Scalar>,
  497. C2: StepCircuit<G2::Scalar>,
  498. S1: RelaxedR1CSSNARKTrait<G1>,
  499. S2: RelaxedR1CSSNARKTrait<G2>,
  500. {
  501. r_U_primary: RelaxedR1CSInstance<G1>,
  502. r_W_snark_primary: S1,
  503. r_U_secondary: RelaxedR1CSInstance<G2>,
  504. l_u_secondary: R1CSInstance<G2>,
  505. nifs_secondary: NIFS<G2>,
  506. f_W_snark_secondary: S2,
  507. zn_primary: Vec<G1::Scalar>,
  508. zn_secondary: Vec<G2::Scalar>,
  509. _p_c1: PhantomData<C1>,
  510. _p_c2: PhantomData<C2>,
  511. }
  512. impl<G1, G2, C1, C2, S1, S2> CompressedSNARK<G1, G2, C1, C2, S1, S2>
  513. where
  514. G1: Group<Base = <G2 as Group>::Scalar>,
  515. G2: Group<Base = <G1 as Group>::Scalar>,
  516. C1: StepCircuit<G1::Scalar>,
  517. C2: StepCircuit<G2::Scalar>,
  518. S1: RelaxedR1CSSNARKTrait<G1>,
  519. S2: RelaxedR1CSSNARKTrait<G2>,
  520. {
  521. /// Creates prover and verifier keys for `CompressedSNARK`
  522. pub fn setup(
  523. pp: &PublicParams<G1, G2, C1, C2>,
  524. ) -> Result<
  525. (
  526. ProverKey<G1, G2, C1, C2, S1, S2>,
  527. VerifierKey<G1, G2, C1, C2, S1, S2>,
  528. ),
  529. NovaError,
  530. > {
  531. let (pk_primary, vk_primary) = S1::setup(&pp.ck_primary, &pp.r1cs_shape_primary)?;
  532. let (pk_secondary, vk_secondary) = S2::setup(&pp.ck_secondary, &pp.r1cs_shape_secondary)?;
  533. let pk = ProverKey {
  534. pk_primary,
  535. pk_secondary,
  536. _p_c1: Default::default(),
  537. _p_c2: Default::default(),
  538. };
  539. let vk = VerifierKey {
  540. F_arity_primary: pp.F_arity_primary,
  541. F_arity_secondary: pp.F_arity_secondary,
  542. ro_consts_primary: pp.ro_consts_primary.clone(),
  543. ro_consts_secondary: pp.ro_consts_secondary.clone(),
  544. digest: pp.digest,
  545. vk_primary,
  546. vk_secondary,
  547. _p_c1: Default::default(),
  548. _p_c2: Default::default(),
  549. };
  550. Ok((pk, vk))
  551. }
  552. /// Create a new `CompressedSNARK`
  553. pub fn prove(
  554. pp: &PublicParams<G1, G2, C1, C2>,
  555. pk: &ProverKey<G1, G2, C1, C2, S1, S2>,
  556. recursive_snark: &RecursiveSNARK<G1, G2, C1, C2>,
  557. ) -> Result<Self, NovaError> {
  558. // fold the secondary circuit's instance
  559. let res_secondary = NIFS::prove(
  560. &pp.ck_secondary,
  561. &pp.ro_consts_secondary,
  562. &scalar_as_base::<G1>(pp.digest),
  563. &pp.r1cs_shape_secondary,
  564. &recursive_snark.r_U_secondary,
  565. &recursive_snark.r_W_secondary,
  566. &recursive_snark.l_u_secondary,
  567. &recursive_snark.l_w_secondary,
  568. );
  569. let (nifs_secondary, (f_U_secondary, f_W_secondary)) = res_secondary?;
  570. // create SNARKs proving the knowledge of f_W_primary and f_W_secondary
  571. let (r_W_snark_primary, f_W_snark_secondary) = rayon::join(
  572. || {
  573. S1::prove(
  574. &pp.ck_primary,
  575. &pk.pk_primary,
  576. &recursive_snark.r_U_primary,
  577. &recursive_snark.r_W_primary,
  578. )
  579. },
  580. || {
  581. S2::prove(
  582. &pp.ck_secondary,
  583. &pk.pk_secondary,
  584. &f_U_secondary,
  585. &f_W_secondary,
  586. )
  587. },
  588. );
  589. Ok(Self {
  590. r_U_primary: recursive_snark.r_U_primary.clone(),
  591. r_W_snark_primary: r_W_snark_primary?,
  592. r_U_secondary: recursive_snark.r_U_secondary.clone(),
  593. l_u_secondary: recursive_snark.l_u_secondary.clone(),
  594. nifs_secondary,
  595. f_W_snark_secondary: f_W_snark_secondary?,
  596. zn_primary: recursive_snark.zi_primary.clone(),
  597. zn_secondary: recursive_snark.zi_secondary.clone(),
  598. _p_c1: Default::default(),
  599. _p_c2: Default::default(),
  600. })
  601. }
  602. /// Verify the correctness of the `CompressedSNARK`
  603. pub fn verify(
  604. &self,
  605. vk: &VerifierKey<G1, G2, C1, C2, S1, S2>,
  606. num_steps: usize,
  607. z0_primary: Vec<G1::Scalar>,
  608. z0_secondary: Vec<G2::Scalar>,
  609. ) -> Result<(Vec<G1::Scalar>, Vec<G2::Scalar>), NovaError> {
  610. // number of steps cannot be zero
  611. if num_steps == 0 {
  612. return Err(NovaError::ProofVerifyError);
  613. }
  614. // check if the (relaxed) R1CS instances have two public outputs
  615. if self.l_u_secondary.X.len() != 2
  616. || self.r_U_primary.X.len() != 2
  617. || self.r_U_secondary.X.len() != 2
  618. {
  619. return Err(NovaError::ProofVerifyError);
  620. }
  621. // check if the output hashes in R1CS instances point to the right running instances
  622. let (hash_primary, hash_secondary) = {
  623. let mut hasher = <G2 as Group>::RO::new(
  624. vk.ro_consts_secondary.clone(),
  625. NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_primary,
  626. );
  627. hasher.absorb(vk.digest);
  628. hasher.absorb(G1::Scalar::from(num_steps as u64));
  629. for e in z0_primary {
  630. hasher.absorb(e);
  631. }
  632. for e in &self.zn_primary {
  633. hasher.absorb(*e);
  634. }
  635. self.r_U_secondary.absorb_in_ro(&mut hasher);
  636. let mut hasher2 = <G1 as Group>::RO::new(
  637. vk.ro_consts_primary.clone(),
  638. NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_secondary,
  639. );
  640. hasher2.absorb(scalar_as_base::<G1>(vk.digest));
  641. hasher2.absorb(G2::Scalar::from(num_steps as u64));
  642. for e in z0_secondary {
  643. hasher2.absorb(e);
  644. }
  645. for e in &self.zn_secondary {
  646. hasher2.absorb(*e);
  647. }
  648. self.r_U_primary.absorb_in_ro(&mut hasher2);
  649. (
  650. hasher.squeeze(NUM_HASH_BITS),
  651. hasher2.squeeze(NUM_HASH_BITS),
  652. )
  653. };
  654. if hash_primary != self.l_u_secondary.X[0]
  655. || hash_secondary != scalar_as_base::<G2>(self.l_u_secondary.X[1])
  656. {
  657. return Err(NovaError::ProofVerifyError);
  658. }
  659. // fold the running instance and last instance to get a folded instance
  660. let f_U_secondary = self.nifs_secondary.verify(
  661. &vk.ro_consts_secondary,
  662. &scalar_as_base::<G1>(vk.digest),
  663. &self.r_U_secondary,
  664. &self.l_u_secondary,
  665. )?;
  666. // check the satisfiability of the folded instances using SNARKs proving the knowledge of their satisfying witnesses
  667. let (res_primary, res_secondary) = rayon::join(
  668. || {
  669. self
  670. .r_W_snark_primary
  671. .verify(&vk.vk_primary, &self.r_U_primary)
  672. },
  673. || {
  674. self
  675. .f_W_snark_secondary
  676. .verify(&vk.vk_secondary, &f_U_secondary)
  677. },
  678. );
  679. res_primary?;
  680. res_secondary?;
  681. Ok((self.zn_primary.clone(), self.zn_secondary.clone()))
  682. }
  683. }
  684. type CommitmentKey<G> = <<G as traits::Group>::CE as CommitmentEngineTrait<G>>::CommitmentKey;
  685. type Commitment<G> = <<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment;
  686. type CompressedCommitment<G> = <<<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment as CommitmentTrait<G>>::CompressedCommitment;
  687. type CE<G> = <G as Group>::CE;
  688. fn compute_digest<G: Group, T: Serialize>(o: &T) -> G::Scalar {
  689. // obtain a vector of bytes representing public parameters
  690. let bytes = bincode::serialize(o).unwrap();
  691. // convert pp_bytes into a short digest
  692. let mut hasher = Sha3_256::new();
  693. hasher.update(&bytes);
  694. let digest = hasher.finalize();
  695. // truncate the digest to NUM_HASH_BITS bits
  696. let bv = (0..NUM_HASH_BITS).map(|i| {
  697. let (byte_pos, bit_pos) = (i / 8, i % 8);
  698. let bit = (digest[byte_pos] >> bit_pos) & 1;
  699. bit == 1
  700. });
  701. // turn the bit vector into a scalar
  702. let mut digest = G::Scalar::ZERO;
  703. let mut coeff = G::Scalar::ONE;
  704. for bit in bv {
  705. if bit {
  706. digest += coeff;
  707. }
  708. coeff += coeff;
  709. }
  710. digest
  711. }
  712. #[cfg(test)]
  713. mod tests {
  714. use crate::provider::bn256_grumpkin::{bn256, grumpkin};
  715. use crate::provider::pedersen::CommitmentKeyExtTrait;
  716. use super::*;
  717. type EE1<G1> = provider::ipa_pc::EvaluationEngine<G1>;
  718. type EE2<G2> = provider::ipa_pc::EvaluationEngine<G2>;
  719. type S1<G1> = spartan::snark::RelaxedR1CSSNARK<G1, EE1<G1>>;
  720. type S2<G2> = spartan::snark::RelaxedR1CSSNARK<G2, EE2<G2>>;
  721. type S1Prime<G1> = spartan::ppsnark::RelaxedR1CSSNARK<G1, EE1<G1>>;
  722. type S2Prime<G2> = spartan::ppsnark::RelaxedR1CSSNARK<G2, EE2<G2>>;
  723. use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError};
  724. use core::marker::PhantomData;
  725. use ff::PrimeField;
  726. use traits::circuit::TrivialTestCircuit;
  727. #[derive(Clone, Debug, Default)]
  728. struct CubicCircuit<F: PrimeField> {
  729. _p: PhantomData<F>,
  730. }
  731. impl<F> StepCircuit<F> for CubicCircuit<F>
  732. where
  733. F: PrimeField,
  734. {
  735. fn arity(&self) -> usize {
  736. 1
  737. }
  738. fn synthesize<CS: ConstraintSystem<F>>(
  739. &self,
  740. cs: &mut CS,
  741. z: &[AllocatedNum<F>],
  742. ) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
  743. // Consider a cubic equation: `x^3 + x + 5 = y`, where `x` and `y` are respectively the input and output.
  744. let x = &z[0];
  745. let x_sq = x.square(cs.namespace(|| "x_sq"))?;
  746. let x_cu = x_sq.mul(cs.namespace(|| "x_cu"), x)?;
  747. let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
  748. Ok(x_cu.get_value().unwrap() + x.get_value().unwrap() + F::from(5u64))
  749. })?;
  750. cs.enforce(
  751. || "y = x^3 + x + 5",
  752. |lc| {
  753. lc + x_cu.get_variable()
  754. + x.get_variable()
  755. + CS::one()
  756. + CS::one()
  757. + CS::one()
  758. + CS::one()
  759. + CS::one()
  760. },
  761. |lc| lc + CS::one(),
  762. |lc| lc + y.get_variable(),
  763. );
  764. Ok(vec![y])
  765. }
  766. fn output(&self, z: &[F]) -> Vec<F> {
  767. vec![z[0] * z[0] * z[0] + z[0] + F::from(5u64)]
  768. }
  769. }
  770. fn test_pp_digest_with<G1, G2, T1, T2>(circuit1: T1, circuit2: T2, expected: &str)
  771. where
  772. G1: Group<Base = <G2 as Group>::Scalar>,
  773. G2: Group<Base = <G1 as Group>::Scalar>,
  774. T1: StepCircuit<G1::Scalar>,
  775. T2: StepCircuit<G2::Scalar>,
  776. {
  777. let pp = PublicParams::<G1, G2, T1, T2>::setup(circuit1, circuit2);
  778. let digest_str = pp
  779. .digest
  780. .to_repr()
  781. .as_ref()
  782. .iter()
  783. .map(|b| format!("{b:02x}"))
  784. .collect::<String>();
  785. assert_eq!(digest_str, expected);
  786. }
  787. #[test]
  788. fn test_pp_digest() {
  789. type G1 = pasta_curves::pallas::Point;
  790. type G2 = pasta_curves::vesta::Point;
  791. let trivial_circuit1 = TrivialTestCircuit::<<G1 as Group>::Scalar>::default();
  792. let trivial_circuit2 = TrivialTestCircuit::<<G2 as Group>::Scalar>::default();
  793. let cubic_circuit1 = CubicCircuit::<<G1 as Group>::Scalar>::default();
  794. test_pp_digest_with::<G1, G2, _, _>(
  795. trivial_circuit1,
  796. trivial_circuit2.clone(),
  797. "39a4ea9dd384346fdeb6b5857c7be56fa035153b616d55311f3191dfbceea603",
  798. );
  799. test_pp_digest_with::<G1, G2, _, _>(
  800. cubic_circuit1,
  801. trivial_circuit2,
  802. "3f7b25f589f2da5ab26254beba98faa54f6442ebf5fa5860caf7b08b576cab00",
  803. );
  804. let trivial_circuit1_grumpkin =
  805. TrivialTestCircuit::<<bn256::Point as Group>::Scalar>::default();
  806. let trivial_circuit2_grumpkin =
  807. TrivialTestCircuit::<<grumpkin::Point as Group>::Scalar>::default();
  808. let cubic_circuit1_grumpkin = CubicCircuit::<<bn256::Point as Group>::Scalar>::default();
  809. test_pp_digest_with::<bn256::Point, grumpkin::Point, _, _>(
  810. trivial_circuit1_grumpkin,
  811. trivial_circuit2_grumpkin.clone(),
  812. "967acca1d6b4731cd65d4072c12bbaca9648f24d7bcc2877aee720e4265d4302",
  813. );
  814. test_pp_digest_with::<bn256::Point, grumpkin::Point, _, _>(
  815. cubic_circuit1_grumpkin,
  816. trivial_circuit2_grumpkin,
  817. "44629f26a78bf6c4e3077f940232050d1793d304fdba5e221d0cf66f76a37903",
  818. );
  819. }
  820. fn test_ivc_trivial_with<G1, G2>()
  821. where
  822. G1: Group<Base = <G2 as Group>::Scalar>,
  823. G2: Group<Base = <G1 as Group>::Scalar>,
  824. {
  825. let test_circuit1 = TrivialTestCircuit::<<G1 as Group>::Scalar>::default();
  826. let test_circuit2 = TrivialTestCircuit::<<G2 as Group>::Scalar>::default();
  827. // produce public parameters
  828. let pp = PublicParams::<
  829. G1,
  830. G2,
  831. TrivialTestCircuit<<G1 as Group>::Scalar>,
  832. TrivialTestCircuit<<G2 as Group>::Scalar>,
  833. >::setup(test_circuit1.clone(), test_circuit2.clone());
  834. let num_steps = 1;
  835. // produce a recursive SNARK
  836. let mut recursive_snark = RecursiveSNARK::new(
  837. &pp,
  838. &test_circuit1,
  839. &test_circuit2,
  840. vec![<G1 as Group>::Scalar::ZERO],
  841. vec![<G2 as Group>::Scalar::ZERO],
  842. );
  843. let res = recursive_snark.prove_step(
  844. &pp,
  845. &test_circuit1,
  846. &test_circuit2,
  847. vec![<G1 as Group>::Scalar::ZERO],
  848. vec![<G2 as Group>::Scalar::ZERO],
  849. );
  850. assert!(res.is_ok());
  851. // verify the recursive SNARK
  852. let res = recursive_snark.verify(
  853. &pp,
  854. num_steps,
  855. &[<G1 as Group>::Scalar::ZERO],
  856. &[<G2 as Group>::Scalar::ZERO],
  857. );
  858. assert!(res.is_ok());
  859. }
  860. #[test]
  861. fn test_ivc_trivial() {
  862. type G1 = pasta_curves::pallas::Point;
  863. type G2 = pasta_curves::vesta::Point;
  864. test_ivc_trivial_with::<G1, G2>();
  865. test_ivc_trivial_with::<bn256::Point, grumpkin::Point>();
  866. }
  867. fn test_ivc_nontrivial_with<G1, G2>()
  868. where
  869. G1: Group<Base = <G2 as Group>::Scalar>,
  870. G2: Group<Base = <G1 as Group>::Scalar>,
  871. {
  872. let circuit_primary = TrivialTestCircuit::default();
  873. let circuit_secondary = CubicCircuit::default();
  874. // produce public parameters
  875. let pp = PublicParams::<
  876. G1,
  877. G2,
  878. TrivialTestCircuit<<G1 as Group>::Scalar>,
  879. CubicCircuit<<G2 as Group>::Scalar>,
  880. >::setup(circuit_primary.clone(), circuit_secondary.clone());
  881. let num_steps = 3;
  882. // produce a recursive SNARK
  883. let mut recursive_snark = RecursiveSNARK::<
  884. G1,
  885. G2,
  886. TrivialTestCircuit<<G1 as Group>::Scalar>,
  887. CubicCircuit<<G2 as Group>::Scalar>,
  888. >::new(
  889. &pp,
  890. &circuit_primary,
  891. &circuit_secondary,
  892. vec![<G1 as Group>::Scalar::ONE],
  893. vec![<G2 as Group>::Scalar::ZERO],
  894. );
  895. for i in 0..num_steps {
  896. let res = recursive_snark.prove_step(
  897. &pp,
  898. &circuit_primary,
  899. &circuit_secondary,
  900. vec![<G1 as Group>::Scalar::ONE],
  901. vec![<G2 as Group>::Scalar::ZERO],
  902. );
  903. assert!(res.is_ok());
  904. // verify the recursive snark at each step of recursion
  905. let res = recursive_snark.verify(
  906. &pp,
  907. i + 1,
  908. &[<G1 as Group>::Scalar::ONE],
  909. &[<G2 as Group>::Scalar::ZERO],
  910. );
  911. assert!(res.is_ok());
  912. }
  913. // verify the recursive SNARK
  914. let res = recursive_snark.verify(
  915. &pp,
  916. num_steps,
  917. &[<G1 as Group>::Scalar::ONE],
  918. &[<G2 as Group>::Scalar::ZERO],
  919. );
  920. assert!(res.is_ok());
  921. let (zn_primary, zn_secondary) = res.unwrap();
  922. // sanity: check the claimed output with a direct computation of the same
  923. assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::ONE]);
  924. let mut zn_secondary_direct = vec![<G2 as Group>::Scalar::ZERO];
  925. for _i in 0..num_steps {
  926. zn_secondary_direct = circuit_secondary.clone().output(&zn_secondary_direct);
  927. }
  928. assert_eq!(zn_secondary, zn_secondary_direct);
  929. assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);
  930. }
  931. #[test]
  932. fn test_ivc_nontrivial() {
  933. type G1 = pasta_curves::pallas::Point;
  934. type G2 = pasta_curves::vesta::Point;
  935. test_ivc_nontrivial_with::<G1, G2>();
  936. test_ivc_nontrivial_with::<bn256::Point, grumpkin::Point>();
  937. }
  938. fn test_ivc_nontrivial_with_compression_with<G1, G2>()
  939. where
  940. G1: Group<Base = <G2 as Group>::Scalar>,
  941. G2: Group<Base = <G1 as Group>::Scalar>,
  942. // this is due to the reliance on CommitmentKeyExtTrait as a bound in ipa_pc
  943. <G1::CE as CommitmentEngineTrait<G1>>::CommitmentKey:
  944. CommitmentKeyExtTrait<G1, CE = <G1 as Group>::CE>,
  945. <G2::CE as CommitmentEngineTrait<G2>>::CommitmentKey:
  946. CommitmentKeyExtTrait<G2, CE = <G2 as Group>::CE>,
  947. {
  948. let circuit_primary = TrivialTestCircuit::default();
  949. let circuit_secondary = CubicCircuit::default();
  950. // produce public parameters
  951. let pp = PublicParams::<
  952. G1,
  953. G2,
  954. TrivialTestCircuit<<G1 as Group>::Scalar>,
  955. CubicCircuit<<G2 as Group>::Scalar>,
  956. >::setup(circuit_primary.clone(), circuit_secondary.clone());
  957. let num_steps = 3;
  958. // produce a recursive SNARK
  959. let mut recursive_snark = RecursiveSNARK::<
  960. G1,
  961. G2,
  962. TrivialTestCircuit<<G1 as Group>::Scalar>,
  963. CubicCircuit<<G2 as Group>::Scalar>,
  964. >::new(
  965. &pp,
  966. &circuit_primary,
  967. &circuit_secondary,
  968. vec![<G1 as Group>::Scalar::ONE],
  969. vec![<G2 as Group>::Scalar::ZERO],
  970. );
  971. for _i in 0..num_steps {
  972. let res = recursive_snark.prove_step(
  973. &pp,
  974. &circuit_primary,
  975. &circuit_secondary,
  976. vec![<G1 as Group>::Scalar::ONE],
  977. vec![<G2 as Group>::Scalar::ZERO],
  978. );
  979. assert!(res.is_ok());
  980. }
  981. // verify the recursive SNARK
  982. let res = recursive_snark.verify(
  983. &pp,
  984. num_steps,
  985. &[<G1 as Group>::Scalar::ONE],
  986. &[<G2 as Group>::Scalar::ZERO],
  987. );
  988. assert!(res.is_ok());
  989. let (zn_primary, zn_secondary) = res.unwrap();
  990. // sanity: check the claimed output with a direct computation of the same
  991. assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::ONE]);
  992. let mut zn_secondary_direct = vec![<G2 as Group>::Scalar::ZERO];
  993. for _i in 0..num_steps {
  994. zn_secondary_direct = circuit_secondary.clone().output(&zn_secondary_direct);
  995. }
  996. assert_eq!(zn_secondary, zn_secondary_direct);
  997. assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);
  998. // produce the prover and verifier keys for compressed snark
  999. let (pk, vk) = CompressedSNARK::<_, _, _, _, S1<G1>, S2<G2>>::setup(&pp).unwrap();
  1000. // produce a compressed SNARK
  1001. let res = CompressedSNARK::<_, _, _, _, S1<G1>, S2<G2>>::prove(&pp, &pk, &recursive_snark);
  1002. assert!(res.is_ok());
  1003. let compressed_snark = res.unwrap();
  1004. // verify the compressed SNARK
  1005. let res = compressed_snark.verify(
  1006. &vk,
  1007. num_steps,
  1008. vec![<G1 as Group>::Scalar::ONE],
  1009. vec![<G2 as Group>::Scalar::ZERO],
  1010. );
  1011. assert!(res.is_ok());
  1012. }
  1013. #[test]
  1014. fn test_ivc_nontrivial_with_compression() {
  1015. type G1 = pasta_curves::pallas::Point;
  1016. type G2 = pasta_curves::vesta::Point;
  1017. test_ivc_nontrivial_with_compression_with::<G1, G2>();
  1018. test_ivc_nontrivial_with_compression_with::<bn256::Point, grumpkin::Point>();
  1019. }
  1020. fn test_ivc_nontrivial_with_spark_compression_with<G1, G2>()
  1021. where
  1022. G1: Group<Base = <G2 as Group>::Scalar>,
  1023. G2: Group<Base = <G1 as Group>::Scalar>,
  1024. // this is due to the reliance on CommitmentKeyExtTrait as a bound in ipa_pc
  1025. <G1::CE as CommitmentEngineTrait<G1>>::CommitmentKey:
  1026. CommitmentKeyExtTrait<G1, CE = <G1 as Group>::CE>,
  1027. <G2::CE as CommitmentEngineTrait<G2>>::CommitmentKey:
  1028. CommitmentKeyExtTrait<G2, CE = <G2 as Group>::CE>,
  1029. {
  1030. let circuit_primary = TrivialTestCircuit::default();
  1031. let circuit_secondary = CubicCircuit::default();
  1032. // produce public parameters
  1033. let pp = PublicParams::<
  1034. G1,
  1035. G2,
  1036. TrivialTestCircuit<<G1 as Group>::Scalar>,
  1037. CubicCircuit<<G2 as Group>::Scalar>,
  1038. >::setup(circuit_primary.clone(), circuit_secondary.clone());
  1039. let num_steps = 3;
  1040. // produce a recursive SNARK
  1041. let mut recursive_snark = RecursiveSNARK::<
  1042. G1,
  1043. G2,
  1044. TrivialTestCircuit<<G1 as Group>::Scalar>,
  1045. CubicCircuit<<G2 as Group>::Scalar>,
  1046. >::new(
  1047. &pp,
  1048. &circuit_primary,
  1049. &circuit_secondary,
  1050. vec![<G1 as Group>::Scalar::ONE],
  1051. vec![<G2 as Group>::Scalar::ZERO],
  1052. );
  1053. for _i in 0..num_steps {
  1054. let res = recursive_snark.prove_step(
  1055. &pp,
  1056. &circuit_primary,
  1057. &circuit_secondary,
  1058. vec![<G1 as Group>::Scalar::ONE],
  1059. vec![<G2 as Group>::Scalar::ZERO],
  1060. );
  1061. assert!(res.is_ok());
  1062. }
  1063. // verify the recursive SNARK
  1064. let res = recursive_snark.verify(
  1065. &pp,
  1066. num_steps,
  1067. &[<G1 as Group>::Scalar::ONE],
  1068. &[<G2 as Group>::Scalar::ZERO],
  1069. );
  1070. assert!(res.is_ok());
  1071. let (zn_primary, zn_secondary) = res.unwrap();
  1072. // sanity: check the claimed output with a direct computation of the same
  1073. assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::ONE]);
  1074. let mut zn_secondary_direct = vec![<G2 as Group>::Scalar::ZERO];
  1075. for _i in 0..num_steps {
  1076. zn_secondary_direct = CubicCircuit::default().output(&zn_secondary_direct);
  1077. }
  1078. assert_eq!(zn_secondary, zn_secondary_direct);
  1079. assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);
  1080. // run the compressed snark with Spark compiler
  1081. // produce the prover and verifier keys for compressed snark
  1082. let (pk, vk) = CompressedSNARK::<_, _, _, _, S1Prime<G1>, S2Prime<G2>>::setup(&pp).unwrap();
  1083. // produce a compressed SNARK
  1084. let res =
  1085. CompressedSNARK::<_, _, _, _, S1Prime<G1>, S2Prime<G2>>::prove(&pp, &pk, &recursive_snark);
  1086. assert!(res.is_ok());
  1087. let compressed_snark = res.unwrap();
  1088. // verify the compressed SNARK
  1089. let res = compressed_snark.verify(
  1090. &vk,
  1091. num_steps,
  1092. vec![<G1 as Group>::Scalar::ONE],
  1093. vec![<G2 as Group>::Scalar::ZERO],
  1094. );
  1095. assert!(res.is_ok());
  1096. }
  1097. #[test]
  1098. fn test_ivc_nontrivial_with_spark_compression() {
  1099. type G1 = pasta_curves::pallas::Point;
  1100. type G2 = pasta_curves::vesta::Point;
  1101. test_ivc_nontrivial_with_spark_compression_with::<G1, G2>();
  1102. test_ivc_nontrivial_with_spark_compression_with::<bn256::Point, grumpkin::Point>();
  1103. }
  1104. fn test_ivc_nondet_with_compression_with<G1, G2>()
  1105. where
  1106. G1: Group<Base = <G2 as Group>::Scalar>,
  1107. G2: Group<Base = <G1 as Group>::Scalar>,
  1108. // this is due to the reliance on CommitmentKeyExtTrait as a bound in ipa_pc
  1109. <G1::CE as CommitmentEngineTrait<G1>>::CommitmentKey:
  1110. CommitmentKeyExtTrait<G1, CE = <G1 as Group>::CE>,
  1111. <G2::CE as CommitmentEngineTrait<G2>>::CommitmentKey:
  1112. CommitmentKeyExtTrait<G2, CE = <G2 as Group>::CE>,
  1113. {
  1114. // y is a non-deterministic advice representing the fifth root of the input at a step.
  1115. #[derive(Clone, Debug)]
  1116. struct FifthRootCheckingCircuit<F: PrimeField> {
  1117. y: F,
  1118. }
  1119. impl<F> FifthRootCheckingCircuit<F>
  1120. where
  1121. F: PrimeField,
  1122. {
  1123. fn new(num_steps: usize) -> (Vec<F>, Vec<Self>) {
  1124. let mut powers = Vec::new();
  1125. let rng = &mut rand::rngs::OsRng;
  1126. let mut seed = F::random(rng);
  1127. for _i in 0..num_steps + 1 {
  1128. seed *= seed.clone().square().square();
  1129. powers.push(Self { y: seed });
  1130. }
  1131. // reverse the powers to get roots
  1132. let roots = powers.into_iter().rev().collect::<Vec<Self>>();
  1133. (vec![roots[0].y], roots[1..].to_vec())
  1134. }
  1135. }
  1136. impl<F> StepCircuit<F> for FifthRootCheckingCircuit<F>
  1137. where
  1138. F: PrimeField,
  1139. {
  1140. fn arity(&self) -> usize {
  1141. 1
  1142. }
  1143. fn synthesize<CS: ConstraintSystem<F>>(
  1144. &self,
  1145. cs: &mut CS,
  1146. z: &[AllocatedNum<F>],
  1147. ) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
  1148. let x = &z[0];
  1149. // we allocate a variable and set it to the provided non-deterministic advice.
  1150. let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(self.y))?;
  1151. // We now check if y = x^{1/5} by checking if y^5 = x
  1152. let y_sq = y.square(cs.namespace(|| "y_sq"))?;
  1153. let y_quad = y_sq.square(cs.namespace(|| "y_quad"))?;
  1154. let y_pow_5 = y_quad.mul(cs.namespace(|| "y_fifth"), &y)?;
  1155. cs.enforce(
  1156. || "y^5 = x",
  1157. |lc| lc + y_pow_5.get_variable(),
  1158. |lc| lc + CS::one(),
  1159. |lc| lc + x.get_variable(),
  1160. );
  1161. Ok(vec![y])
  1162. }
  1163. fn output(&self, z: &[F]) -> Vec<F> {
  1164. // sanity check
  1165. let x = z[0];
  1166. let y_pow_5 = self.y * self.y.clone().square().square();
  1167. assert_eq!(x, y_pow_5);
  1168. // return non-deterministic advice
  1169. // as the output of the step
  1170. vec![self.y]
  1171. }
  1172. }
  1173. let circuit_primary = FifthRootCheckingCircuit {
  1174. y: <G1 as Group>::Scalar::ZERO,
  1175. };
  1176. let circuit_secondary = TrivialTestCircuit::default();
  1177. // produce public parameters
  1178. let pp = PublicParams::<
  1179. G1,
  1180. G2,
  1181. FifthRootCheckingCircuit<<G1 as Group>::Scalar>,
  1182. TrivialTestCircuit<<G2 as Group>::Scalar>,
  1183. >::setup(circuit_primary, circuit_secondary.clone());
  1184. let num_steps = 3;
  1185. // produce non-deterministic advice
  1186. let (z0_primary, roots) = FifthRootCheckingCircuit::new(num_steps);
  1187. let z0_secondary = vec![<G2 as Group>::Scalar::ZERO];
  1188. // produce a recursive SNARK
  1189. let mut recursive_snark: RecursiveSNARK<
  1190. G1,
  1191. G2,
  1192. FifthRootCheckingCircuit<<G1 as Group>::Scalar>,
  1193. TrivialTestCircuit<<G2 as Group>::Scalar>,
  1194. > = RecursiveSNARK::<
  1195. G1,
  1196. G2,
  1197. FifthRootCheckingCircuit<<G1 as Group>::Scalar>,
  1198. TrivialTestCircuit<<G2 as Group>::Scalar>,
  1199. >::new(
  1200. &pp,
  1201. &roots[0],
  1202. &circuit_secondary,
  1203. z0_primary.clone(),
  1204. z0_secondary.clone(),
  1205. );
  1206. for circuit_primary in roots.iter().take(num_steps) {
  1207. let res = recursive_snark.prove_step(
  1208. &pp,
  1209. circuit_primary,
  1210. &circuit_secondary.clone(),
  1211. z0_primary.clone(),
  1212. z0_secondary.clone(),
  1213. );
  1214. assert!(res.is_ok());
  1215. }
  1216. // verify the recursive SNARK
  1217. let res = recursive_snark.verify(&pp, num_steps, &z0_primary, &z0_secondary);
  1218. assert!(res.is_ok());
  1219. // produce the prover and verifier keys for compressed snark
  1220. let (pk, vk) = CompressedSNARK::<_, _, _, _, S1<G1>, S2<G2>>::setup(&pp).unwrap();
  1221. // produce a compressed SNARK
  1222. let res = CompressedSNARK::<_, _, _, _, S1<G1>, S2<G2>>::prove(&pp, &pk, &recursive_snark);
  1223. assert!(res.is_ok());
  1224. let compressed_snark = res.unwrap();
  1225. // verify the compressed SNARK
  1226. let res = compressed_snark.verify(&vk, num_steps, z0_primary, z0_secondary);
  1227. assert!(res.is_ok());
  1228. }
  1229. #[test]
  1230. fn test_ivc_nondet_with_compression() {
  1231. type G1 = pasta_curves::pallas::Point;
  1232. type G2 = pasta_curves::vesta::Point;
  1233. test_ivc_nondet_with_compression_with::<G1, G2>();
  1234. test_ivc_nondet_with_compression_with::<bn256::Point, grumpkin::Point>();
  1235. }
  1236. fn test_ivc_base_with<G1, G2>()
  1237. where
  1238. G1: Group<Base = <G2 as Group>::Scalar>,
  1239. G2: Group<Base = <G1 as Group>::Scalar>,
  1240. {
  1241. let test_circuit1 = TrivialTestCircuit::<<G1 as Group>::Scalar>::default();
  1242. let test_circuit2 = CubicCircuit::<<G2 as Group>::Scalar>::default();
  1243. // produce public parameters
  1244. let pp = PublicParams::<
  1245. G1,
  1246. G2,
  1247. TrivialTestCircuit<<G1 as Group>::Scalar>,
  1248. CubicCircuit<<G2 as Group>::Scalar>,
  1249. >::setup(test_circuit1.clone(), test_circuit2.clone());
  1250. let num_steps = 1;
  1251. // produce a recursive SNARK
  1252. let mut recursive_snark = RecursiveSNARK::<
  1253. G1,
  1254. G2,
  1255. TrivialTestCircuit<<G1 as Group>::Scalar>,
  1256. CubicCircuit<<G2 as Group>::Scalar>,
  1257. >::new(
  1258. &pp,
  1259. &test_circuit1,
  1260. &test_circuit2,
  1261. vec![<G1 as Group>::Scalar::ONE],
  1262. vec![<G2 as Group>::Scalar::ZERO],
  1263. );
  1264. // produce a recursive SNARK
  1265. let res = recursive_snark.prove_step(
  1266. &pp,
  1267. &test_circuit1,
  1268. &test_circuit2,
  1269. vec![<G1 as Group>::Scalar::ONE],
  1270. vec![<G2 as Group>::Scalar::ZERO],
  1271. );
  1272. assert!(res.is_ok());
  1273. // verify the recursive SNARK
  1274. let res = recursive_snark.verify(
  1275. &pp,
  1276. num_steps,
  1277. &[<G1 as Group>::Scalar::ONE],
  1278. &[<G2 as Group>::Scalar::ZERO],
  1279. );
  1280. assert!(res.is_ok());
  1281. let (zn_primary, zn_secondary) = res.unwrap();
  1282. assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::ONE]);
  1283. assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(5u64)]);
  1284. }
  1285. #[test]
  1286. fn test_ivc_base() {
  1287. type G1 = pasta_curves::pallas::Point;
  1288. type G2 = pasta_curves::vesta::Point;
  1289. test_ivc_base_with::<G1, G2>();
  1290. test_ivc_base_with::<bn256::Point, grumpkin::Point>();
  1291. }
  1292. }