Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add test vector utility for R1CS circuits #110

Merged
merged 3 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,13 @@ cargo bench
```

This will generate a report at `target/criterion/report/index.html`.

## Generate Test Vectors

There is a test utility that will generate the proving and verifying keys for
the R1CS tests. These keys are pre-generated to guard against breaking changes
to the circuits (e.g. when upgrading Arkworks dependencies).

```
cargo test generate_test_vectors -- --ignored
```
257 changes: 227 additions & 30 deletions tests/groth16_gadgets.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use ark_ff::UniformRand;
use std::{fs, io::BufWriter, path::PathBuf};

use ark_groth16::{r1cs_to_qap::LibsnarkReduction, Groth16, Proof, ProvingKey, VerifyingKey};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use once_cell::sync::Lazy;
use proptest::prelude::*;

use ark_r1cs_std::{
Expand All @@ -22,6 +25,85 @@ fn element_strategy() -> BoxedStrategy<Element> {
.boxed()
}

static DISCRETE_LOG_PK: Lazy<ProvingKey<Bls12_377>> = Lazy::new(|| {
let pk_bytes = include_bytes!("test_vectors/discrete_log_pk.bin");
ProvingKey::deserialize_uncompressed(&pk_bytes[..]).expect("can parse discrete log proving key")
});

static DISCRETE_LOG_VK: Lazy<VerifyingKey<Bls12_377>> = Lazy::new(|| {
let vk_bytes = include_bytes!("test_vectors/discrete_log_vk.param");
VerifyingKey::deserialize_uncompressed(&vk_bytes[..])
.expect("can parse discrete log verifying key")
});

static COMPRESSION_PK: Lazy<ProvingKey<Bls12_377>> = Lazy::new(|| {
let pk_bytes = include_bytes!("test_vectors/compression_pk.bin");
ProvingKey::deserialize_uncompressed(&pk_bytes[..]).expect("can parse compression proving key")
});

static COMPRESSION_VK: Lazy<VerifyingKey<Bls12_377>> = Lazy::new(|| {
let vk_bytes = include_bytes!("test_vectors/compression_vk.param");
VerifyingKey::deserialize_uncompressed(&vk_bytes[..])
.expect("can parse compression verifying key")
});

static DECOMPRESSION_PK: Lazy<ProvingKey<Bls12_377>> = Lazy::new(|| {
let pk_bytes = include_bytes!("test_vectors/decompression_pk.bin");
ProvingKey::deserialize_uncompressed(&pk_bytes[..])
.expect("can parse decompression proving key")
});

static DECOMPRESSION_VK: Lazy<VerifyingKey<Bls12_377>> = Lazy::new(|| {
let vk_bytes = include_bytes!("test_vectors/decompression_vk.param");
VerifyingKey::deserialize_uncompressed(&vk_bytes[..])
.expect("can parse decompression verifying key")
});

static ELLIGATOR_PK: Lazy<ProvingKey<Bls12_377>> = Lazy::new(|| {
let pk_bytes = include_bytes!("test_vectors/elligator_pk.bin");
ProvingKey::deserialize_uncompressed(&pk_bytes[..]).expect("can parse elligator proving key")
});

static ELLIGATOR_VK: Lazy<VerifyingKey<Bls12_377>> = Lazy::new(|| {
let vk_bytes = include_bytes!("test_vectors/elligator_vk.param");
VerifyingKey::deserialize_uncompressed(&vk_bytes[..])
.expect("can parse elligator verifying key")
});

static PUBLIC_ELEMENT_INPUT_PK: Lazy<ProvingKey<Bls12_377>> = Lazy::new(|| {
let pk_bytes = include_bytes!("test_vectors/public_element_input_pk.bin");
ProvingKey::deserialize_uncompressed(&pk_bytes[..])
.expect("can parse public element input proving key")
});

static PUBLIC_ELEMENT_INPUT_VK: Lazy<VerifyingKey<Bls12_377>> = Lazy::new(|| {
let vk_bytes = include_bytes!("test_vectors/public_element_input_vk.param");
VerifyingKey::deserialize_uncompressed(&vk_bytes[..])
.expect("can parse public element input verifying key")
});

static NEGATION_PK: Lazy<ProvingKey<Bls12_377>> = Lazy::new(|| {
let pk_bytes = include_bytes!("test_vectors/negation_pk.bin");
ProvingKey::deserialize_uncompressed(&pk_bytes[..]).expect("can parse negation proving key")
});

static NEGATION_VK: Lazy<VerifyingKey<Bls12_377>> = Lazy::new(|| {
let vk_bytes = include_bytes!("test_vectors/negation_vk.param");
VerifyingKey::deserialize_uncompressed(&vk_bytes[..]).expect("can parse negation verifying key")
});

static ADD_ASSIGN_ADD_PK: Lazy<ProvingKey<Bls12_377>> = Lazy::new(|| {
let pk_bytes = include_bytes!("test_vectors/add_assign_add_pk.bin");
ProvingKey::deserialize_uncompressed(&pk_bytes[..])
.expect("can parse add assign add proving key")
});

static ADD_ASSIGN_ADD_VK: Lazy<VerifyingKey<Bls12_377>> = Lazy::new(|| {
let vk_bytes = include_bytes!("test_vectors/add_assign_add_vk.param");
VerifyingKey::deserialize_uncompressed(&vk_bytes[..])
.expect("can parse add assign add verifying key")
});

#[derive(Clone)]
struct DiscreteLogCircuit {
// Witness
Expand Down Expand Up @@ -71,7 +153,9 @@ proptest! {
#![proptest_config(ProptestConfig::with_cases(5))]
#[test]
fn groth16_dl_proof_happy_path(scalar_arr in scalar_strategy_random()) {
let (pk, vk) = DiscreteLogCircuit::generate_test_parameters();
let pk = DISCRETE_LOG_PK.clone();
let vk = DISCRETE_LOG_VK.clone();

let mut rng = OsRng;

let scalar = scalar_arr;
Expand All @@ -97,7 +181,8 @@ proptest! {
#![proptest_config(ProptestConfig::with_cases(5))]
#[test]
fn groth16_dl_proof_unhappy_path(scalar_arr in scalar_strategy_random()) {
let (pk, vk) = DiscreteLogCircuit::generate_test_parameters();
let pk = DISCRETE_LOG_PK.clone();
let vk = DISCRETE_LOG_VK.clone();
let mut rng = OsRng;

let scalar = scalar_arr;
Expand Down Expand Up @@ -176,7 +261,8 @@ proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn groth16_compression_proof_happy_path(scalar in fr_strategy()) {
let (pk, vk) = CompressionCircuit::generate_test_parameters();
let pk = COMPRESSION_PK.clone();
let vk = COMPRESSION_VK.clone();
let mut rng = OsRng;

// Prover POV
Expand Down Expand Up @@ -206,7 +292,8 @@ proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn groth16_compression_proof_unhappy_path(scalar in fr_strategy()) {
let (pk, vk) = CompressionCircuit::generate_test_parameters();
let pk = COMPRESSION_PK.clone();
let vk = COMPRESSION_VK.clone();
let mut rng = OsRng;

// Prover POV
Expand Down Expand Up @@ -279,7 +366,8 @@ proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn groth16_decompression_proof_happy_path(scalar in fr_strategy()) {
let (pk, vk) = DecompressionCircuit::generate_test_parameters();
let pk = DECOMPRESSION_PK.clone();
let vk = DECOMPRESSION_VK.clone();
let mut rng = OsRng;

// Prover POV
Expand Down Expand Up @@ -309,7 +397,8 @@ proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn groth16_decompression_proof_unhappy_path(scalar in fr_strategy()) {
let (pk, vk) = DecompressionCircuit::generate_test_parameters();
let pk = DECOMPRESSION_PK.clone();
let vk = DECOMPRESSION_VK.clone();
let mut rng = OsRng;

// Prover POV
Expand Down Expand Up @@ -386,7 +475,8 @@ proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn groth16_elligator_proof_happy_path(field_element in fq_strategy()) {
let (pk, vk) = ElligatorCircuit::generate_test_parameters();
let pk = ELLIGATOR_PK.clone();
let vk = ELLIGATOR_VK.clone();
let mut rng = OsRng;

// Prover POV
Expand Down Expand Up @@ -415,7 +505,8 @@ proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn groth16_elligator_proof_unhappy_path(field_element in fq_strategy()) {
let (pk, vk) = ElligatorCircuit::generate_test_parameters();
let pk = ELLIGATOR_PK.clone();
let vk = ELLIGATOR_VK.clone();
let mut rng = OsRng;

// Prover POV
Expand Down Expand Up @@ -456,15 +547,24 @@ impl ConstraintSynthesizer<Fq> for PublicElementInput {
}
}

impl PublicElementInput {
fn generate_test_parameters() -> (ProvingKey<Bls12_377>, VerifyingKey<Bls12_377>) {
let circuit = PublicElementInput {
point: Element::GENERATOR,
};
let (pk, vk) =
Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(circuit, &mut OsRng)
.expect("can perform circuit specific setup");
(pk, vk)
}
}

proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn groth16_public_input(point in element_strategy()) {
let test_circuit = PublicElementInput {
point: Element::GENERATOR,
};
let (pk, vk) = Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(test_circuit, &mut OsRng)
.expect("can perform circuit specific setup");
let pk = PUBLIC_ELEMENT_INPUT_PK.clone();
let vk = PUBLIC_ELEMENT_INPUT_VK.clone();
let mut rng = OsRng;

// Prover POV
Expand Down Expand Up @@ -511,16 +611,26 @@ impl ConstraintSynthesizer<Fq> for NegationCircuit {
}
}

impl NegationCircuit {
fn generate_test_parameters() -> (ProvingKey<Bls12_377>, VerifyingKey<Bls12_377>) {
let point = Element::GENERATOR;
let circuit = NegationCircuit {
pos: Element::GENERATOR,
public_neg: point.negate(),
};
let (pk, vk) =
Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(circuit, &mut OsRng)
.expect("can perform circuit specific setup");
(pk, vk)
}
}

proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn groth16_negation(point in element_strategy()) {
let test_circuit = NegationCircuit {
pos: Element::GENERATOR,
public_neg: point.negate(),
};
let (pk, vk) = Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(test_circuit, &mut OsRng)
.expect("can perform circuit specific setup");
let pk = NEGATION_PK.clone();
let vk = NEGATION_VK.clone();
let mut rng = OsRng;

// Prover POV
Expand Down Expand Up @@ -581,20 +691,29 @@ impl ConstraintSynthesizer<Fq> for AddAssignAddCircuit {
}
}

impl AddAssignAddCircuit {
fn generate_test_parameters() -> (ProvingKey<Bls12_377>, VerifyingKey<Bls12_377>) {
let test_a = Element::GENERATOR;
let test_b = Element::GENERATOR * Fr::from(2u64);
let circuit = AddAssignAddCircuit {
a: test_a,
b: test_b,
c: test_a + test_b,
d: test_a - test_b,
};
let (pk, vk) =
Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(circuit, &mut OsRng)
.expect("can perform circuit specific setup");
(pk, vk)
}
}

proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn groth16_add_addassign(a in element_strategy(), b in element_strategy()) {
let test_a = Element::GENERATOR;
let test_b = Element::GENERATOR * Fr::from(2u64);
let test_circuit = AddAssignAddCircuit {
a: test_a,
b: test_b,
c: test_a + test_b,
d: test_a - test_b,
};
let (pk, vk) = Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(test_circuit, &mut OsRng)
.expect("can perform circuit specific setup");
let pk = ADD_ASSIGN_ADD_PK.clone();
let vk = ADD_ASSIGN_ADD_VK.clone();
let mut rng = OsRng;

// Prover POV
Expand All @@ -619,3 +738,81 @@ fn groth16_add_addassign(a in element_strategy(), b in element_strategy()) {
assert!(proof_result);
}
}

fn write_params(
target_dir: &PathBuf,
name: &str,
pk: &ProvingKey<Bls12_377>,
vk: &VerifyingKey<Bls12_377>,
) -> anyhow::Result<()> {
let pk_location = target_dir.join(format!("{}_pk.bin", name));
let vk_location = target_dir.join(format!("{}_vk.param", name));

let pk_file = fs::File::create(&pk_location)?;
let vk_file = fs::File::create(&vk_location)?;

let pk_writer = BufWriter::new(pk_file);
let vk_writer = BufWriter::new(vk_file);

ProvingKey::serialize_uncompressed(pk, pk_writer).expect("can serialize ProvingKey");
VerifyingKey::serialize_uncompressed(vk, vk_writer).expect("can serialize VerifyingKey");

Ok(())
}

#[ignore]
#[test]
fn generate_test_vectors() {
let (pk, vk) = DiscreteLogCircuit::generate_test_parameters();
write_params(
&PathBuf::from("tests/test_vectors"),
"discrete_log",
&pk,
&vk,
)
.expect("can write test vectors");

let (pk, vk) = CompressionCircuit::generate_test_parameters();
write_params(
&PathBuf::from("tests/test_vectors"),
"compression",
&pk,
&vk,
)
.expect("can write test vectors");

let (pk, vk) = DecompressionCircuit::generate_test_parameters();
write_params(
&PathBuf::from("tests/test_vectors"),
"decompression",
&pk,
&vk,
)
.expect("can write test vectors");

let (pk, vk) = ElligatorCircuit::generate_test_parameters();
write_params(&PathBuf::from("tests/test_vectors"), "elligator", &pk, &vk)
.expect("can write test vectors");

let (pk, vk) = PublicElementInput::generate_test_parameters();
write_params(
&PathBuf::from("tests/test_vectors"),
"public_element_input",
&pk,
&vk,
)
.expect("can write test vectors");

let (pk, vk) = NegationCircuit::generate_test_parameters();
write_params(&PathBuf::from("tests/test_vectors"), "negation", &pk, &vk)
.expect("can write test vectors");

let (pk, vk) = AddAssignAddCircuit::generate_test_parameters();
write_params(
&PathBuf::from("tests/test_vectors"),
"add_assign_add",
&pk,
&vk,
)
.expect("can write test vectors");
}
Binary file added tests/test_vectors/add_assign_add_pk.bin
Binary file not shown.
Binary file added tests/test_vectors/add_assign_add_vk.param
Binary file not shown.
Binary file added tests/test_vectors/compression_pk.bin
Binary file not shown.
Binary file added tests/test_vectors/compression_vk.param
Binary file not shown.
Binary file added tests/test_vectors/decompression_pk.bin
Binary file not shown.
Binary file added tests/test_vectors/decompression_vk.param
Binary file not shown.
Binary file added tests/test_vectors/discrete_log_pk.bin
Binary file not shown.
Binary file added tests/test_vectors/discrete_log_vk.param
Binary file not shown.
Binary file added tests/test_vectors/elligator_pk.bin
Binary file not shown.
Binary file added tests/test_vectors/elligator_vk.param
Binary file not shown.
Binary file added tests/test_vectors/negation_pk.bin
Binary file not shown.
Binary file added tests/test_vectors/negation_vk.param
Binary file not shown.
Binary file added tests/test_vectors/public_element_input_pk.bin
Binary file not shown.
Binary file added tests/test_vectors/public_element_input_vk.param
Binary file not shown.
Loading