mirror of
https://github.com/nalinbhardwaj/Nova-Scotia.git
synced 2026-01-08 21:38:07 -05:00
feat: continue_recursive_circuit api (#34)
* feat: `add_step` api * fix: error * fix: `continue_recursive_circuit` API * fix: typo
This commit is contained in:
@@ -1,14 +1,10 @@
|
|||||||
use std::{collections::HashMap, env::current_dir, time::Instant};
|
use std::{collections::HashMap, env::current_dir, time::Instant};
|
||||||
|
|
||||||
use nova_scotia::{
|
use nova_scotia::{
|
||||||
circom::reader::load_r1cs, create_public_params, create_recursive_circuit, FileLocation, F, S,
|
circom::reader::load_r1cs, continue_recursive_circuit, create_public_params,
|
||||||
|
create_recursive_circuit, FileLocation, F, S,
|
||||||
};
|
};
|
||||||
use nova_snark::{
|
use nova_snark::{provider, CompressedSNARK, PublicParams};
|
||||||
provider,
|
|
||||||
traits::{circuit::StepCircuit, Group},
|
|
||||||
CompressedSNARK, PublicParams,
|
|
||||||
};
|
|
||||||
use pasta_curves::group::ff::Field;
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
fn run_test(circuit_filepath: String, witness_gen_filepath: String) {
|
fn run_test(circuit_filepath: String, witness_gen_filepath: String) {
|
||||||
@@ -58,9 +54,9 @@ fn run_test(circuit_filepath: String, witness_gen_filepath: String) {
|
|||||||
|
|
||||||
println!("Creating a RecursiveSNARK...");
|
println!("Creating a RecursiveSNARK...");
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let recursive_snark = create_recursive_circuit(
|
let mut recursive_snark = create_recursive_circuit(
|
||||||
FileLocation::PathBuf(witness_generator_file),
|
FileLocation::PathBuf(witness_generator_file.clone()),
|
||||||
r1cs,
|
r1cs.clone(),
|
||||||
private_inputs,
|
private_inputs,
|
||||||
start_public_input.to_vec(),
|
start_public_input.to_vec(),
|
||||||
&pp,
|
&pp,
|
||||||
@@ -82,6 +78,11 @@ fn run_test(circuit_filepath: String, witness_gen_filepath: String) {
|
|||||||
);
|
);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
|
|
||||||
|
let z_last = res.unwrap().0;
|
||||||
|
|
||||||
|
assert_eq!(z_last[0], F::<G1>::from(20));
|
||||||
|
assert_eq!(z_last[1], F::<G1>::from(70));
|
||||||
|
|
||||||
// produce a compressed SNARK
|
// produce a compressed SNARK
|
||||||
println!("Generating a CompressedSNARK using Spartan with IPA-PC...");
|
println!("Generating a CompressedSNARK using Spartan with IPA-PC...");
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
@@ -110,6 +111,48 @@ fn run_test(circuit_filepath: String, witness_gen_filepath: String) {
|
|||||||
start.elapsed()
|
start.elapsed()
|
||||||
);
|
);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
|
|
||||||
|
// continue recursive circuit by adding 2 further steps
|
||||||
|
println!("Adding steps to our RecursiveSNARK...");
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
let iteration_count_continue = 2;
|
||||||
|
|
||||||
|
let mut private_inputs_continue = Vec::new();
|
||||||
|
for i in 0..iteration_count_continue {
|
||||||
|
let mut private_input = HashMap::new();
|
||||||
|
private_input.insert("adder".to_string(), json!(5 + i));
|
||||||
|
private_inputs_continue.push(private_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = continue_recursive_circuit(
|
||||||
|
&mut recursive_snark,
|
||||||
|
z_last,
|
||||||
|
FileLocation::PathBuf(witness_generator_file),
|
||||||
|
r1cs,
|
||||||
|
private_inputs_continue,
|
||||||
|
start_public_input.to_vec(),
|
||||||
|
&pp,
|
||||||
|
);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
println!(
|
||||||
|
"Adding 2 steps to our RecursiveSNARK took {:?}",
|
||||||
|
start.elapsed()
|
||||||
|
);
|
||||||
|
|
||||||
|
// verify the recursive SNARK with the added steps
|
||||||
|
println!("Verifying a RecursiveSNARK...");
|
||||||
|
let start = Instant::now();
|
||||||
|
let res = recursive_snark.verify(&pp, iteration_count + iteration_count_continue, &start_public_input, &z0_secondary);
|
||||||
|
println!(
|
||||||
|
"RecursiveSNARK::verify: {:?}, took {:?}",
|
||||||
|
res,
|
||||||
|
start.elapsed()
|
||||||
|
);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
|
||||||
|
assert_eq!(res.clone().unwrap().0[0], F::<G1>::from(31));
|
||||||
|
assert_eq!(res.unwrap().0[1], F::<G1>::from(115));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
125
src/lib.rs
125
src/lib.rs
@@ -309,3 +309,128 @@ where
|
|||||||
|
|
||||||
Ok(recursive_snark)
|
Ok(recursive_snark)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
pub fn continue_recursive_circuit<G1, G2>(
|
||||||
|
recursive_snark: &mut RecursiveSNARK<G1, G2, C1<G1>, C2<G2>>,
|
||||||
|
last_zi: Vec<F<G1>>,
|
||||||
|
witness_generator_file: FileLocation,
|
||||||
|
r1cs: R1CS<F<G1>>,
|
||||||
|
private_inputs: Vec<HashMap<String, Value>>,
|
||||||
|
start_public_input: Vec<F<G1>>,
|
||||||
|
pp: &PublicParams<G1, G2, C1<G1>, C2<G2>>,
|
||||||
|
) -> Result<(), std::io::Error>
|
||||||
|
where
|
||||||
|
G1: Group<Base = <G2 as Group>::Scalar>,
|
||||||
|
G2: Group<Base = <G1 as Group>::Scalar>,
|
||||||
|
{
|
||||||
|
let root = current_dir().unwrap();
|
||||||
|
let witness_generator_output = root.join("circom_witness.wtns");
|
||||||
|
|
||||||
|
let iteration_count = private_inputs.len();
|
||||||
|
|
||||||
|
let mut current_public_input = last_zi
|
||||||
|
.iter()
|
||||||
|
.map(|&x| format!("{:?}", x).strip_prefix("0x").unwrap().to_string())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
let circuit_secondary = TrivialTestCircuit::default();
|
||||||
|
let z0_secondary = vec![G2::Scalar::ZERO];
|
||||||
|
|
||||||
|
for i in 0..iteration_count {
|
||||||
|
let witness = compute_witness::<G1, G2>(
|
||||||
|
current_public_input.clone(),
|
||||||
|
private_inputs[i].clone(),
|
||||||
|
witness_generator_file.clone(),
|
||||||
|
&witness_generator_output,
|
||||||
|
);
|
||||||
|
|
||||||
|
let circuit = CircomCircuit {
|
||||||
|
r1cs: r1cs.clone(),
|
||||||
|
witness: Some(witness),
|
||||||
|
};
|
||||||
|
|
||||||
|
let current_public_output = circuit.get_public_outputs();
|
||||||
|
current_public_input = current_public_output
|
||||||
|
.iter()
|
||||||
|
.map(|&x| format!("{:?}", x).strip_prefix("0x").unwrap().to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let res = recursive_snark.prove_step(
|
||||||
|
pp,
|
||||||
|
&circuit,
|
||||||
|
&circuit_secondary,
|
||||||
|
start_public_input.clone(),
|
||||||
|
z0_secondary.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(res.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::remove_file(witness_generator_output)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
pub async fn continue_recursive_circuit<G1, G2>(
|
||||||
|
recursive_snark: &mut RecursiveSNARK<G1, G2, C1<G1>, C2<G2>>,
|
||||||
|
last_zi: Vec<F<G1>>,
|
||||||
|
witness_generator_file: FileLocation,
|
||||||
|
r1cs: R1CS<F<G1>>,
|
||||||
|
private_inputs: Vec<HashMap<String, Value>>,
|
||||||
|
start_public_input: Vec<F<G1>>,
|
||||||
|
pp: &PublicParams<G1, G2, C1<G1>, C2<G2>>,
|
||||||
|
) -> Result<(), std::io::Error>
|
||||||
|
where
|
||||||
|
G1: Group<Base = <G2 as Group>::Scalar>,
|
||||||
|
G2: Group<Base = <G1 as Group>::Scalar>,
|
||||||
|
{
|
||||||
|
let root = current_dir().unwrap();
|
||||||
|
let witness_generator_output = root.join("circom_witness.wtns");
|
||||||
|
|
||||||
|
let iteration_count = private_inputs.len();
|
||||||
|
|
||||||
|
let mut current_public_input = last_zi
|
||||||
|
.iter()
|
||||||
|
.map(|&x| format!("{:?}", x).strip_prefix("0x").unwrap().to_string())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
let circuit_secondary = TrivialTestCircuit::default();
|
||||||
|
let z0_secondary = vec![G2::Scalar::ZERO];
|
||||||
|
|
||||||
|
for i in 0..iteration_count {
|
||||||
|
let witness = compute_witness::<G1, G2>(
|
||||||
|
current_public_input.clone(),
|
||||||
|
private_inputs[i].clone(),
|
||||||
|
witness_generator_file.clone(),
|
||||||
|
&witness_generator_output,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let circuit = CircomCircuit {
|
||||||
|
r1cs: r1cs.clone(),
|
||||||
|
witness: Some(witness),
|
||||||
|
};
|
||||||
|
|
||||||
|
let current_public_output = circuit.get_public_outputs();
|
||||||
|
current_public_input = current_public_output
|
||||||
|
.iter()
|
||||||
|
.map(|&x| format!("{:?}", x).strip_prefix("0x").unwrap().to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let res = recursive_snark.prove_step(
|
||||||
|
pp,
|
||||||
|
&circuit,
|
||||||
|
&circuit_secondary,
|
||||||
|
start_public_input.clone(),
|
||||||
|
z0_secondary.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(res.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::remove_file(witness_generator_output)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user