Skip to content

Commit

Permalink
test: add test vectors for R1CS to detect circuit breakage
Browse files Browse the repository at this point in the history
  • Loading branch information
redshiftzero committed Nov 19, 2024
1 parent 8065dba commit 3ad1257
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 3 deletions.
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");
}

0 comments on commit 3ad1257

Please sign in to comment.