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

test: add test vectors for R1CS to detect circuit breakage #66

Merged
merged 1 commit into from
Nov 20, 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 @@ -29,3 +29,13 @@ This will generate a report at `target/criterion/report/index.html`.
Performance benchmarked on commit 9750f5ff01d11f158f111a1a75401901049e5575 on
a 2023 Macbook Pro M2 (12 core CPU) with 32 GB memory using our 4-to-1 optimized
poseidon hash takes 47.3µs, or ~21,141 hashes/second.

## 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
```
4 changes: 3 additions & 1 deletion poseidon-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ ark-relations = { version="0.4", default-features = false }
ark-snark = { version = "0.4", default-features = false }
ark-ec = { version = "0.4", default_features = false }
ark-std = { version = "0.4", default-features = false }
ark-serialize = { version = "0.4", default_features = false }
ark-serialize = { version = "0.4", default_features = false }
anyhow = "1"
once_cell = "1"
Binary file added poseidon-tests/test_vectors/preimage_pk.bin
Binary file not shown.
Binary file added poseidon-tests/test_vectors/preimage_vk.param
Binary file not shown.
51 changes: 49 additions & 2 deletions poseidon-tests/tests/poseidon377_r1cs.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use std::{fs, io::BufWriter, path::PathBuf};

use ark_groth16::{r1cs_to_qap::LibsnarkReduction, Groth16, ProvingKey, VerifyingKey};
use ark_r1cs_std::prelude::{AllocVar, EqGadget};
use ark_relations::r1cs::{ConstraintSynthesizer, ToConstraintField};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_snark::SNARK;
use decaf377::{
r1cs::{CountConstraints, FqVar},
Bls12_377, Fq,
};
use once_cell::sync::Lazy;
use proptest::prelude::*;
use rand_core::OsRng;

Expand All @@ -20,6 +24,18 @@ const DOMAIN_SEP: Fq = Fq::from_montgomery_limbs([
/// The maximum fixed-width Poseidon hash exposed to downstream users of this crate.
const MAX_WIDTH_POSEIDON_HASH: usize = 7;

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

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

#[derive(Clone)]
struct PreimageCircuit {
// Witnesses
Expand Down Expand Up @@ -164,7 +180,8 @@ proptest! {
#![proptest_config(ProptestConfig::with_cases(1))]
#[test]
fn groth16_hash_proof_happy_path(v1 in fq_strategy(), v2 in fq_strategy(), v3 in fq_strategy(), v4 in fq_strategy(), v5 in fq_strategy(), v6 in fq_strategy(), v7 in fq_strategy()) {
let (pk, vk) = PreimageCircuit::generate_test_parameters();
let pk = PREIMAGE_PK.clone();
let vk = PREIMAGE_VK.clone();
let mut rng = OsRng;

let preimages = [v1, v2, v3, v4, v5, v6, v7];
Expand Down Expand Up @@ -197,7 +214,8 @@ proptest! {
#![proptest_config(ProptestConfig::with_cases(5))]
#[test]
fn groth16_hash_proof_unhappy_path(v1 in fq_strategy(), v2 in fq_strategy(), v3 in fq_strategy(), v4 in fq_strategy(), v5 in fq_strategy(), v6 in fq_strategy(), v7 in fq_strategy()) {
let (pk, vk) = PreimageCircuit::generate_test_parameters();
let pk = PREIMAGE_PK.clone();
let vk = PREIMAGE_VK.clone();
let mut rng = OsRng;

let preimages = [v1, v2, v3, v4, v5, v6, v7];
Expand Down Expand Up @@ -225,3 +243,32 @@ fn groth16_hash_proof_unhappy_path(v1 in fq_strategy(), v2 in fq_strategy(), v3
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) = PreimageCircuit::generate_test_parameters();
write_params(&PathBuf::from("test_vectors"), "preimage", &pk, &vk)
.expect("can write test vectors");
}
Loading