nalinbhardwaj / Nova-Scotia

Middleware to compile Circom circuits to Nova prover
https://crates.io/crates/nova-scotia
MIT License
235 stars 50 forks source link

RecursiveSNARK::verify: Err(UnSat) following examples script #38

Closed fraVlaca closed 1 year ago

fraVlaca commented 1 year ago

Following examples found in NovaScotia and I get error UnSat, that following Nova docs means: returned if the supplied witness is not a satisfying witness to a given shape and instance.

The circuit is proved correctly but when it comes to verify it, this problems comes out with the witness verification. I have literally no idea on where the problem is as the circuit is proved correctly and should be verified correctly too, is there as step that I am missing with the primary or secondary curves inputs, for the secondary I am just passing 0 as I have no idea what it does (but can see that if I pass any other number I get ProofVerifyError, which tells me that the 0 might be on the right road)

This is my script code:


use std::{
    collections::HashMap,
    env::current_dir,
    io::Write,
    time::{Duration, Instant},
};
use babyjubjub_rs::Point;
use pasta_curves::group::ff::PrimeField;

use nova_scotia::{
    circom::reader::load_r1cs, create_public_params, create_recursive_circuit, FileLocation, F
};
use nova_snark::{
    traits::{circuit::TrivialTestCircuit, Group},
    CompressedSNARK, PublicParams, RecursiveSNARK,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
use poseidon_rs::{Fr, Poseidon};

#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
struct ProofInput {
    step_in: Vec<String>,
    newData: Vec<String>,
    nextUserBehavioralProfileCommitments: Vec<String>,
    r8x: String,
    r8y: String,
    s: String,
}

fn bench() -> (Duration, Duration) {
    type G1 = pasta_curves::pallas::Point;
    type G2 = pasta_curves::vesta::Point;
    let root = current_dir().unwrap();

    let circuit_file = root.join("../build/recursiveLogin.r1cs");

    let r1cs = load_r1cs::<G1, G2>(&FileLocation::PathBuf(circuit_file));
    let witness_generator_file =
        root.join("../build/recursiveLogin_js/recursiveLogin.wasm");

    // load serde json
    let users_data: Vec<ProofInput> =
        serde_json::from_str(include_str!("fetcher/loginsInputs.json")).unwrap();
    let iteration_count: usize = users_data.len();

    let start_public_input = [
        F::<G1>::from_str_vartime(&users_data[0].step_in[0]).unwrap(),
        F::<G1>::from_str_vartime(&users_data[0].step_in[1]).unwrap(),
        F::<G1>::from_str_vartime(&users_data[0].step_in[2]).unwrap(),
        F::<G1>::from_str_vartime(&users_data[0].step_in[3]).unwrap()
    ];

    let mut private_inputs = Vec::new();

    for i in 0..users_data.len() {
        let mut private_input = HashMap::new();
        private_input.insert(
            "newData".to_string(),
            json!(
                users_data[i].newData
            ),
            //(0..lengthNewData).map(|j| F1::from_raw(&usersData[i].newData[j]).unwrap()).collect();
        );
        private_input.insert(
            "S".to_string(),
            json!(users_data[i].s)
        );
        private_input.insert(
            "R8x".to_string(),
            json!(users_data[i].r8x)
        );
        private_input.insert(
            "R8y".to_string(),
            json!(users_data[i].r8y)
        );
        let mut nextUserBehavioralProfileCommitments = Vec::new();

        nextUserBehavioralProfileCommitments.extend_from_slice(&users_data[i].nextUserBehavioralProfileCommitments);

        private_input.insert(
            "nextUserBehavioralProfileCommitments".to_string(),
            json!(
                nextUserBehavioralProfileCommitments
            ),
        );
        private_inputs.push(private_input);
    }

     println!("{:?} {:?}", start_public_input, private_inputs);

    let pp = create_public_params(r1cs.clone());

    println!(
        "Number of constraints per step (primary circuit): {}",
        pp.num_constraints().0
    );
    println!(
        "Number of constraints per step (secondary circuit): {}",
        pp.num_constraints().1
    );

    println!(
        "Number of variables per step (primary circuit): {}",
        pp.num_variables().0
    );
    println!(
        "Number of variables per step (secondary circuit): {}",
        pp.num_variables().1
    );

    println!("witness_generator_file {:?}", witness_generator_file);

    println!("Creating a RecursiveSNARK...");
    let start = Instant::now();
    let recursive_snark = create_recursive_circuit(
        FileLocation::PathBuf(witness_generator_file),
        r1cs,
        private_inputs,
        start_public_input.to_vec(),
        &pp,
    )
    .unwrap();
    let prover_time = start.elapsed();
    println!("RecursiveSNARK creation took {:?}", start.elapsed());

    let z0_secondary = [<G2 as Group>::Scalar::zero()];

    // verify the recursive SNARK
    println!("Verifying a RecursiveSNARK...");
    let start = Instant::now();
    let res = recursive_snark.verify(
        &pp,
        iteration_count,
        &start_public_input,
        &z0_secondary,
    );
    println!(
        "RecursiveSNARK::verify: {:?}, took {:?}",
        res,
        start.elapsed()
    );
    let verifier_time = start.elapsed();
    assert!(res.is_ok());

    // produce a compressed SNARK
    println!("Generating a CompressedSNARK using Spartan with IPA-PC...");
    let (pk, vk) = CompressedSNARK::<_, _, _, _, S1, S2>::setup(&pp).unwrap();

    // produce a compressed SNARK
    println!("Generating a CompressedSNARK using Spartan with IPA-PC...");
    let start = Instant::now();
    type EE1 = nova_snark::provider::ipa_pc::EvaluationEngine<G1>;
    type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine<G2>;
    type S1 = nova_snark::spartan::snark::RelaxedR1CSSNARK<G1, EE1>;
    type S2 = nova_snark::spartan::snark::RelaxedR1CSSNARK<G2, EE2>;
    let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &pk, &recursive_snark);
    println!(
        "CompressedSNARK::prove: {:?}, took {:?}",
        res.is_ok(),
        start.elapsed()
    );
    assert!(res.is_ok());
    let compressed_snark = res.unwrap();

    // verify the compressed SNARK
    println!("Verifying a CompressedSNARK...");
    let start = Instant::now();
    let res = compressed_snark.verify(
        &vk,
        iteration_count,
        start_public_input.clone().to_vec(),
        z0_secondary.to_vec(),
    );
    println!(
        "CompressedSNARK::verify: {:?}, took {:?}",
        res.is_ok(),
        start.elapsed()
    );
    assert!(res.is_ok());
    let serialized_compressed_snark = serde_json::to_string(&compressed_snark).unwrap();

    (prover_time, verifier_time)
}

fn main() {
    // create benchmark file
    let mut file = std::fs::File::create("src/benchmark.csv").unwrap();
    file.write_all(b"iteration_count,prover_time,verifier_time\n")
        .unwrap();
    let iteration_count = 100;

        // run bash script
        /* std::process::Command::new("bash")
            .arg("../scripts/circom/compile.sh")
            .arg(i.to_string())
            .output()
            .expect("failed to execute process"); */

    let (prover_time, verifier_time) = bench();
    file.write_all(format!("{},{:?},{:?}\n", iteration_count, prover_time, verifier_time).as_bytes())
        .unwrap();
}

I get

Number of constraints per step (primary circuit): 15892 Number of constraints per step (secondary circuit): 10347 Number of variables per step (primary circuit): 16659 Number of variables per step (secondary circuit): 10329 witness_generator_file "/Users/vlaca/Documents/work/innerworks/zero-knowledge/zk/nova/../build/recursiveLogin_js/recursiveLogin.wasm" Creating a RecursiveSNARK... RecursiveSNARK creation took 1.677693459s Verifying a RecursiveSNARK... RecursiveSNARK::verify: Err(UnSat), took 104.866ms thread 'main' panicked at 'assertion failed: res.is_ok()', src/main.rs:165:5

fraVlaca commented 1 year ago

Solved, I was compiling with the wrong curve using circom