diff --git a/benches/nizk.rs b/benches/nizk.rs index 71a67a1..8fdd340 100644 --- a/benches/nizk.rs +++ b/benches/nizk.rs @@ -8,135 +8,138 @@ extern crate sha3; use std::time::{Duration, SystemTime}; use libspartan::{ - parameters::POSEIDON_PARAMETERS_FR_377, poseidon_transcript::PoseidonTranscript, Instance, - NIZKGens, NIZK, + parameters::POSEIDON_PARAMETERS_FR_377, poseidon_transcript::PoseidonTranscript, Instance, + NIZKGens, NIZK, }; use criterion::*; fn nizk_prove_benchmark(c: &mut Criterion) { - for &s in [24, 28, 30].iter() { - let mut group = c.benchmark_group("R1CS_prove_benchmark"); + for &s in [24, 28, 30].iter() { + let mut group = c.benchmark_group("R1CS_prove_benchmark"); - let num_vars = (2_usize).pow(s as u32); - let num_cons = num_vars; - let num_inputs = 10; - let start = SystemTime::now(); - let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let end = SystemTime::now(); - let duration = end.duration_since(start).unwrap(); - println!( - "Generating r1cs instance with {} constraints took {} ms", - num_cons, - duration.as_millis() - ); - let gens = NIZKGens::new(num_cons, num_vars, num_inputs); + let num_vars = (2_usize).pow(s as u32); + let num_cons = num_vars; + let num_inputs = 10; + let start = SystemTime::now(); + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + let end = SystemTime::now(); + let duration = end.duration_since(start).unwrap(); + println!( + "Generating r1cs instance with {} constraints took {} ms", + num_cons, + duration.as_millis() + ); + let gens = NIZKGens::new(num_cons, num_vars, num_inputs); - let name = format!("R1CS_prove_{}", num_vars); - group - .measurement_time(Duration::from_secs(60)) - .bench_function(&name, move |b| { - b.iter(|| { - let mut prover_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); - NIZK::prove( - black_box(&inst), - black_box(vars.clone()), - black_box(&inputs), - black_box(&gens), - black_box(&mut prover_transcript), - ); - }); - }); - group.finish(); - } + let name = format!("R1CS_prove_{}", num_vars); + group + .measurement_time(Duration::from_secs(60)) + .bench_function(&name, move |b| { + b.iter(|| { + let mut prover_transcript = + PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); + NIZK::prove( + black_box(&inst), + black_box(vars.clone()), + black_box(&inputs), + black_box(&gens), + black_box(&mut prover_transcript), + ); + }); + }); + group.finish(); + } } fn nizk_verify_benchmark(c: &mut Criterion) { - for &s in [4, 6, 8, 10, 12, 16, 20, 24, 28, 30].iter() { - let mut group = c.benchmark_group("R1CS_verify_benchmark"); + for &s in [4, 6, 8, 10, 12, 16, 20, 24, 28, 30].iter() { + let mut group = c.benchmark_group("R1CS_verify_benchmark"); - let num_vars = (2_usize).pow(s as u32); - let num_cons = num_vars; - // these are the public io - let num_inputs = 10; - let start = SystemTime::now(); - let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let end = SystemTime::now(); - let duration = end.duration_since(start).unwrap(); - println!( - "Generating r1cs instance with {} constraints took {} ms", - num_cons, - duration.as_millis() - ); - let gens = NIZKGens::new(num_cons, num_vars, num_inputs); - // produce a proof of satisfiability - let mut prover_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); - let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript); + let num_vars = (2_usize).pow(s as u32); + let num_cons = num_vars; + // these are the public io + let num_inputs = 10; + let start = SystemTime::now(); + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + let end = SystemTime::now(); + let duration = end.duration_since(start).unwrap(); + println!( + "Generating r1cs instance with {} constraints took {} ms", + num_cons, + duration.as_millis() + ); + let gens = NIZKGens::new(num_cons, num_vars, num_inputs); + // produce a proof of satisfiability + let mut prover_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); + let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript); - let name = format!("R1CS_verify_{}", num_cons); - group - .measurement_time(Duration::from_secs(60)) - .bench_function(&name, move |b| { - b.iter(|| { - let mut verifier_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); - assert!(proof - .verify( - black_box(&inst), - black_box(&inputs), - black_box(&mut verifier_transcript), - black_box(&gens), - ) - .is_ok()); - }); - }); - group.finish(); - } + let name = format!("R1CS_verify_{}", num_cons); + group + .measurement_time(Duration::from_secs(60)) + .bench_function(&name, move |b| { + b.iter(|| { + let mut verifier_transcript = + PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); + assert!(proof + .verify( + black_box(&inst), + black_box(&inputs), + black_box(&mut verifier_transcript), + black_box(&gens), + ) + .is_ok()); + }); + }); + group.finish(); + } } fn nizk_verify_groth16_benchmark(c: &mut Criterion) { - for &s in [4, 6, 8, 10, 12, 16, 20, 24, 28, 30].iter() { - let mut group = c.benchmark_group("R1CS_verify_groth16_benchmark"); + for &s in [4, 6, 8, 10, 12, 16, 20, 24, 28, 30].iter() { + let mut group = c.benchmark_group("R1CS_verify_groth16_benchmark"); - let num_vars = (2_usize).pow(s as u32); - let num_cons = num_vars; - // these are the public io - let num_inputs = 10; - let start = SystemTime::now(); - let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let end = SystemTime::now(); - let duration = end.duration_since(start).unwrap(); - println!( - "Generating r1cs instance with {} constraints took {} ms", - num_cons, - duration.as_millis() - ); - // produce a proof of satisfiability - let mut prover_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); - let gens = NIZKGens::new(num_cons, num_vars, num_inputs); - let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript); + let num_vars = (2_usize).pow(s as u32); + let num_cons = num_vars; + // these are the public io + let num_inputs = 10; + let start = SystemTime::now(); + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + let end = SystemTime::now(); + let duration = end.duration_since(start).unwrap(); + println!( + "Generating r1cs instance with {} constraints took {} ms", + num_cons, + duration.as_millis() + ); + // produce a proof of satisfiability + let mut prover_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); + let gens = NIZKGens::new(num_cons, num_vars, num_inputs); + let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript); - let name = format!("R1CS_verify_groth16_{}", num_cons); - group - .measurement_time(Duration::from_secs(60)) - .bench_function(&name, move |b| { - b.iter(|| { - let mut verifier_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); - assert!(proof - .verify_groth16( - black_box(&inst), - black_box(&inputs), - black_box(&mut verifier_transcript), - black_box(&gens) - ) - .is_ok()); - }); - }); - group.finish(); - } + let name = format!("R1CS_verify_groth16_{}", num_cons); + group + .measurement_time(Duration::from_secs(60)) + .bench_function(&name, move |b| { + b.iter(|| { + let mut verifier_transcript = + PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); + assert!(proof + .verify_groth16( + black_box(&inst), + black_box(&inputs), + black_box(&mut verifier_transcript), + black_box(&gens) + ) + .is_ok()); + }); + }); + group.finish(); + } } fn set_duration() -> Criterion { - Criterion::default().sample_size(2) + Criterion::default().sample_size(2) } criterion_group! { diff --git a/benches/r1cs.rs b/benches/r1cs.rs index 5852c3f..48b93ef 100644 --- a/benches/r1cs.rs +++ b/benches/r1cs.rs @@ -1,72 +1,72 @@ use std::time::Instant; use libspartan::{ - parameters::POSEIDON_PARAMETERS_FR_377, poseidon_transcript::PoseidonTranscript, Instance, - NIZKGens, NIZK, + parameters::POSEIDON_PARAMETERS_FR_377, poseidon_transcript::PoseidonTranscript, Instance, + NIZKGens, NIZK, }; use serde::Serialize; #[derive(Default, Clone, Serialize)] struct BenchmarkResults { - power: usize, - input_constraints: usize, - spartan_verifier_circuit_constraints: usize, - r1cs_instance_generation_time: u128, - spartan_proving_time: u128, - groth16_setup_time: u128, - groth16_proving_time: u128, - testudo_verification_time: u128, - testudo_proving_time: u128, + power: usize, + input_constraints: usize, + spartan_verifier_circuit_constraints: usize, + r1cs_instance_generation_time: u128, + spartan_proving_time: u128, + groth16_setup_time: u128, + groth16_proving_time: u128, + testudo_verification_time: u128, + testudo_proving_time: u128, } fn main() { - let mut writer = csv::Writer::from_path("testudo.csv").expect("unable to open csv writer"); - // for &s in [ - // 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - // ] - // .iter() - // For testing purposes we currently bench on very small instance to ensure - // correctness and then on biggest one for timings. - for &s in [4, 26].iter() { - println!("Running for {} inputs", s); - let mut br = BenchmarkResults::default(); - let num_vars = (2_usize).pow(s as u32); - let num_cons = num_vars; - br.power = s; - br.input_constraints = num_cons; - let num_inputs = 10; + let mut writer = csv::Writer::from_path("testudo.csv").expect("unable to open csv writer"); + // for &s in [ + // 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + // ] + // .iter() + // For testing purposes we currently bench on very small instance to ensure + // correctness and then on biggest one for timings. + for &s in [4, 26].iter() { + println!("Running for {} inputs", s); + let mut br = BenchmarkResults::default(); + let num_vars = (2_usize).pow(s as u32); + let num_cons = num_vars; + br.power = s; + br.input_constraints = num_cons; + let num_inputs = 10; - let start = Instant::now(); - let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let duration = start.elapsed().as_millis(); - br.r1cs_instance_generation_time = duration; - let mut prover_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); + let start = Instant::now(); + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + let duration = start.elapsed().as_millis(); + br.r1cs_instance_generation_time = duration; + let mut prover_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); - let gens = NIZKGens::new(num_cons, num_vars, num_inputs); + let gens = NIZKGens::new(num_cons, num_vars, num_inputs); - let start = Instant::now(); - let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript); - let duration = start.elapsed().as_millis(); - br.spartan_proving_time = duration; + let start = Instant::now(); + let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript); + let duration = start.elapsed().as_millis(); + br.spartan_proving_time = duration; - let mut verifier_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); - let res = proof.verify(&inst, &inputs, &mut verifier_transcript, &gens); - assert!(res.is_ok()); - br.spartan_verifier_circuit_constraints = res.unwrap(); + let mut verifier_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); + let res = proof.verify(&inst, &inputs, &mut verifier_transcript, &gens); + assert!(res.is_ok()); + br.spartan_verifier_circuit_constraints = res.unwrap(); - let mut verifier_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); - let res = proof.verify_groth16(&inst, &inputs, &mut verifier_transcript, &gens); - assert!(res.is_ok()); + let mut verifier_transcript = PoseidonTranscript::new(&POSEIDON_PARAMETERS_FR_377); + let res = proof.verify_groth16(&inst, &inputs, &mut verifier_transcript, &gens); + assert!(res.is_ok()); - let (ds, dp, dv) = res.unwrap(); - br.groth16_setup_time = ds; - br.groth16_proving_time = dp; + let (ds, dp, dv) = res.unwrap(); + br.groth16_setup_time = ds; + br.groth16_proving_time = dp; - br.testudo_proving_time = br.spartan_proving_time + br.groth16_proving_time; - br.testudo_verification_time = dv; - writer - .serialize(br) - .expect("unable to write results to csv"); - writer.flush().expect("wasn't able to flush"); - } + br.testudo_proving_time = br.spartan_proving_time + br.groth16_proving_time; + br.testudo_verification_time = dv; + writer + .serialize(br) + .expect("unable to write results to csv"); + writer.flush().expect("wasn't able to flush"); + } } diff --git a/benches/snark.rs b/benches/snark.rs index 3fc6f34..2bf7861 100644 --- a/benches/snark.rs +++ b/benches/snark.rs @@ -2,128 +2,130 @@ extern crate libspartan; extern crate merlin; use libspartan::{ - parameters::poseidon_params, poseidon_transcript::PoseidonTranscript, Instance, SNARKGens, SNARK, + parameters::poseidon_params, poseidon_transcript::PoseidonTranscript, Instance, SNARKGens, + SNARK, }; use criterion::*; fn snark_encode_benchmark(c: &mut Criterion) { - for &s in [10, 12, 16].iter() { - let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); - let mut group = c.benchmark_group("SNARK_encode_benchmark"); - group.plot_config(plot_config); - - let num_vars = (2_usize).pow(s as u32); - let num_cons = num_vars; - let num_inputs = 10; - let (inst, _vars, _inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - - // produce public parameters - let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); - - // produce a commitment to R1CS instance - let name = format!("SNARK_encode_{}", num_cons); - group.bench_function(&name, move |b| { - b.iter(|| { - SNARK::encode(black_box(&inst), black_box(&gens)); - }); - }); - group.finish(); - } + for &s in [10, 12, 16].iter() { + let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); + let mut group = c.benchmark_group("SNARK_encode_benchmark"); + group.plot_config(plot_config); + + let num_vars = (2_usize).pow(s as u32); + let num_cons = num_vars; + let num_inputs = 10; + let (inst, _vars, _inputs) = + Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + + // produce public parameters + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); + + // produce a commitment to R1CS instance + let name = format!("SNARK_encode_{}", num_cons); + group.bench_function(&name, move |b| { + b.iter(|| { + SNARK::encode(black_box(&inst), black_box(&gens)); + }); + }); + group.finish(); + } } fn snark_prove_benchmark(c: &mut Criterion) { - for &s in [10, 12, 16].iter() { - let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); - let mut group = c.benchmark_group("SNARK_prove_benchmark"); - group.plot_config(plot_config); + for &s in [10, 12, 16].iter() { + let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); + let mut group = c.benchmark_group("SNARK_prove_benchmark"); + group.plot_config(plot_config); + + let num_vars = (2_usize).pow(s as u32); + let num_cons = num_vars; + let num_inputs = 10; + + let params = poseidon_params(); + + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + + // produce public parameters + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); + + // produce a commitment to R1CS instance + let (comm, decomm) = SNARK::encode(&inst, &gens); + + // produce a proof + let name = format!("SNARK_prove_{}", num_cons); + group.bench_function(&name, move |b| { + b.iter(|| { + let mut prover_transcript = PoseidonTranscript::new(¶ms); + SNARK::prove( + black_box(&inst), + black_box(&comm), + black_box(&decomm), + black_box(vars.clone()), + black_box(&inputs), + black_box(&gens), + black_box(&mut prover_transcript), + ); + }); + }); + group.finish(); + } +} - let num_vars = (2_usize).pow(s as u32); - let num_cons = num_vars; - let num_inputs = 10; +fn snark_verify_benchmark(c: &mut Criterion) { + for &s in [10, 12, 16].iter() { + let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); + let mut group = c.benchmark_group("SNARK_verify_benchmark"); + group.plot_config(plot_config); - let params = poseidon_params(); + let params = poseidon_params(); - let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + let num_vars = (2_usize).pow(s as u32); + let num_cons = num_vars; + let num_inputs = 10; + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - // produce public parameters - let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); + // produce public parameters + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); - // produce a commitment to R1CS instance - let (comm, decomm) = SNARK::encode(&inst, &gens); + // produce a commitment to R1CS instance + let (comm, decomm) = SNARK::encode(&inst, &gens); - // produce a proof - let name = format!("SNARK_prove_{}", num_cons); - group.bench_function(&name, move |b| { - b.iter(|| { + // produce a proof of satisfiability let mut prover_transcript = PoseidonTranscript::new(¶ms); - SNARK::prove( - black_box(&inst), - black_box(&comm), - black_box(&decomm), - black_box(vars.clone()), - black_box(&inputs), - black_box(&gens), - black_box(&mut prover_transcript), + let proof = SNARK::prove( + &inst, + &comm, + &decomm, + vars, + &inputs, + &gens, + &mut prover_transcript, ); - }); - }); - group.finish(); - } -} -fn snark_verify_benchmark(c: &mut Criterion) { - for &s in [10, 12, 16].iter() { - let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); - let mut group = c.benchmark_group("SNARK_verify_benchmark"); - group.plot_config(plot_config); - - let params = poseidon_params(); - - let num_vars = (2_usize).pow(s as u32); - let num_cons = num_vars; - let num_inputs = 10; - let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - - // produce public parameters - let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); - - // produce a commitment to R1CS instance - let (comm, decomm) = SNARK::encode(&inst, &gens); - - // produce a proof of satisfiability - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let proof = SNARK::prove( - &inst, - &comm, - &decomm, - vars, - &inputs, - &gens, - &mut prover_transcript, - ); - - // verify the proof - let name = format!("SNARK_verify_{}", num_cons); - group.bench_function(&name, move |b| { - b.iter(|| { - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify( - black_box(&comm), - black_box(&inputs), - black_box(&mut verifier_transcript), - black_box(&gens) - ) - .is_ok()); - }); - }); - group.finish(); - } + // verify the proof + let name = format!("SNARK_verify_{}", num_cons); + group.bench_function(&name, move |b| { + b.iter(|| { + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify( + black_box(&comm), + black_box(&inputs), + black_box(&mut verifier_transcript), + black_box(&gens) + ) + .is_ok()); + }); + }); + group.finish(); + } } fn set_duration() -> Criterion { - Criterion::default().sample_size(10) + Criterion::default().sample_size(10) } criterion_group! { diff --git a/examples/cubic.rs b/examples/cubic.rs index 6ccdbfc..2f52cd6 100644 --- a/examples/cubic.rs +++ b/examples/cubic.rs @@ -12,139 +12,139 @@ use ark_bls12_377::Fr as Scalar; use ark_ff::{BigInteger, PrimeField}; use ark_std::{One, UniformRand, Zero}; use libspartan::{ - parameters::poseidon_params, poseidon_transcript::PoseidonTranscript, InputsAssignment, Instance, - SNARKGens, VarsAssignment, SNARK, + parameters::poseidon_params, poseidon_transcript::PoseidonTranscript, InputsAssignment, + Instance, SNARKGens, VarsAssignment, SNARK, }; #[allow(non_snake_case)] fn produce_r1cs() -> ( - usize, - usize, - usize, - usize, - Instance, - VarsAssignment, - InputsAssignment, + usize, + usize, + usize, + usize, + Instance, + VarsAssignment, + InputsAssignment, ) { - // parameters of the R1CS instance - let num_cons = 4; - let num_vars = 4; - let num_inputs = 1; - let num_non_zero_entries = 8; - - // We will encode the above constraints into three matrices, where - // the coefficients in the matrix are in the little-endian byte order - let mut A: Vec<(usize, usize, Vec)> = Vec::new(); - let mut B: Vec<(usize, usize, Vec)> = Vec::new(); - let mut C: Vec<(usize, usize, Vec)> = Vec::new(); - - let one = Scalar::one().into_repr().to_bytes_le(); - - // R1CS is a set of three sparse matrices A B C, where is a row for every - // constraint and a column for every entry in z = (vars, 1, inputs) - // An R1CS instance is satisfiable iff: - // Az \circ Bz = Cz, where z = (vars, 1, inputs) - - // constraint 0 entries in (A,B,C) - // constraint 0 is Z0 * Z0 - Z1 = 0. - A.push((0, 0, one.clone())); - B.push((0, 0, one.clone())); - C.push((0, 1, one.clone())); - - // constraint 1 entries in (A,B,C) - // constraint 1 is Z1 * Z0 - Z2 = 0. - A.push((1, 1, one.clone())); - B.push((1, 0, one.clone())); - C.push((1, 2, one.clone())); - - // constraint 2 entries in (A,B,C) - // constraint 2 is (Z2 + Z0) * 1 - Z3 = 0. - A.push((2, 2, one.clone())); - A.push((2, 0, one.clone())); - B.push((2, num_vars, one.clone())); - C.push((2, 3, one.clone())); - - // constraint 3 entries in (A,B,C) - // constraint 3 is (Z3 + 5) * 1 - I0 = 0. - A.push((3, 3, one.clone())); - A.push((3, num_vars, Scalar::from(5u32).into_repr().to_bytes_le())); - B.push((3, num_vars, one.clone())); - C.push((3, num_vars + 1, one)); - - let inst = Instance::new(num_cons, num_vars, num_inputs, &A, &B, &C).unwrap(); - - // compute a satisfying assignment - let mut rng = ark_std::rand::thread_rng(); - let z0 = Scalar::rand(&mut rng); - let z1 = z0 * z0; // constraint 0 - let z2 = z1 * z0; // constraint 1 - let z3 = z2 + z0; // constraint 2 - let i0 = z3 + Scalar::from(5u32); // constraint 3 - - // create a VarsAssignment - let mut vars = vec![Scalar::zero().into_repr().to_bytes_le(); num_vars]; - vars[0] = z0.into_repr().to_bytes_le(); - vars[1] = z1.into_repr().to_bytes_le(); - vars[2] = z2.into_repr().to_bytes_le(); - vars[3] = z3.into_repr().to_bytes_le(); - let assignment_vars = VarsAssignment::new(&vars).unwrap(); - - // create an InputsAssignment - let mut inputs = vec![Scalar::zero().into_repr().to_bytes_le(); num_inputs]; - inputs[0] = i0.into_repr().to_bytes_le(); - let assignment_inputs = InputsAssignment::new(&inputs).unwrap(); - - // check if the instance we created is satisfiable - let res = inst.is_sat(&assignment_vars, &assignment_inputs); - assert!(res.unwrap(), "should be satisfied"); - - ( - num_cons, - num_vars, - num_inputs, - num_non_zero_entries, - inst, - assignment_vars, - assignment_inputs, - ) + // parameters of the R1CS instance + let num_cons = 4; + let num_vars = 4; + let num_inputs = 1; + let num_non_zero_entries = 8; + + // We will encode the above constraints into three matrices, where + // the coefficients in the matrix are in the little-endian byte order + let mut A: Vec<(usize, usize, Vec)> = Vec::new(); + let mut B: Vec<(usize, usize, Vec)> = Vec::new(); + let mut C: Vec<(usize, usize, Vec)> = Vec::new(); + + let one = Scalar::one().into_repr().to_bytes_le(); + + // R1CS is a set of three sparse matrices A B C, where is a row for every + // constraint and a column for every entry in z = (vars, 1, inputs) + // An R1CS instance is satisfiable iff: + // Az \circ Bz = Cz, where z = (vars, 1, inputs) + + // constraint 0 entries in (A,B,C) + // constraint 0 is Z0 * Z0 - Z1 = 0. + A.push((0, 0, one.clone())); + B.push((0, 0, one.clone())); + C.push((0, 1, one.clone())); + + // constraint 1 entries in (A,B,C) + // constraint 1 is Z1 * Z0 - Z2 = 0. + A.push((1, 1, one.clone())); + B.push((1, 0, one.clone())); + C.push((1, 2, one.clone())); + + // constraint 2 entries in (A,B,C) + // constraint 2 is (Z2 + Z0) * 1 - Z3 = 0. + A.push((2, 2, one.clone())); + A.push((2, 0, one.clone())); + B.push((2, num_vars, one.clone())); + C.push((2, 3, one.clone())); + + // constraint 3 entries in (A,B,C) + // constraint 3 is (Z3 + 5) * 1 - I0 = 0. + A.push((3, 3, one.clone())); + A.push((3, num_vars, Scalar::from(5u32).into_repr().to_bytes_le())); + B.push((3, num_vars, one.clone())); + C.push((3, num_vars + 1, one)); + + let inst = Instance::new(num_cons, num_vars, num_inputs, &A, &B, &C).unwrap(); + + // compute a satisfying assignment + let mut rng = ark_std::rand::thread_rng(); + let z0 = Scalar::rand(&mut rng); + let z1 = z0 * z0; // constraint 0 + let z2 = z1 * z0; // constraint 1 + let z3 = z2 + z0; // constraint 2 + let i0 = z3 + Scalar::from(5u32); // constraint 3 + + // create a VarsAssignment + let mut vars = vec![Scalar::zero().into_repr().to_bytes_le(); num_vars]; + vars[0] = z0.into_repr().to_bytes_le(); + vars[1] = z1.into_repr().to_bytes_le(); + vars[2] = z2.into_repr().to_bytes_le(); + vars[3] = z3.into_repr().to_bytes_le(); + let assignment_vars = VarsAssignment::new(&vars).unwrap(); + + // create an InputsAssignment + let mut inputs = vec![Scalar::zero().into_repr().to_bytes_le(); num_inputs]; + inputs[0] = i0.into_repr().to_bytes_le(); + let assignment_inputs = InputsAssignment::new(&inputs).unwrap(); + + // check if the instance we created is satisfiable + let res = inst.is_sat(&assignment_vars, &assignment_inputs); + assert!(res.unwrap(), "should be satisfied"); + + ( + num_cons, + num_vars, + num_inputs, + num_non_zero_entries, + inst, + assignment_vars, + assignment_inputs, + ) } fn main() { - // produce an R1CS instance - let ( - num_cons, - num_vars, - num_inputs, - num_non_zero_entries, - inst, - assignment_vars, - assignment_inputs, - ) = produce_r1cs(); - - let params = poseidon_params(); - - // produce public parameters - let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_non_zero_entries); - - // create a commitment to the R1CS instance - let (comm, decomm) = SNARK::encode(&inst, &gens); - - // produce a proof of satisfiability - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let proof = SNARK::prove( - &inst, - &comm, - &decomm, - assignment_vars, - &assignment_inputs, - &gens, - &mut prover_transcript, - ); - - // verify the proof of satisfiability - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify(&comm, &assignment_inputs, &mut verifier_transcript, &gens) - .is_ok()); - println!("proof verification successful!"); + // produce an R1CS instance + let ( + num_cons, + num_vars, + num_inputs, + num_non_zero_entries, + inst, + assignment_vars, + assignment_inputs, + ) = produce_r1cs(); + + let params = poseidon_params(); + + // produce public parameters + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_non_zero_entries); + + // create a commitment to the R1CS instance + let (comm, decomm) = SNARK::encode(&inst, &gens); + + // produce a proof of satisfiability + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let proof = SNARK::prove( + &inst, + &comm, + &decomm, + assignment_vars, + &assignment_inputs, + &gens, + &mut prover_transcript, + ); + + // verify the proof of satisfiability + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify(&comm, &assignment_inputs, &mut verifier_transcript, &gens) + .is_ok()); + println!("proof verification successful!"); } diff --git a/profiler/nizk.rs b/profiler/nizk.rs index ed6c9ed..ce5afe7 100644 --- a/profiler/nizk.rs +++ b/profiler/nizk.rs @@ -11,42 +11,42 @@ use libspartan::poseidon_transcript::PoseidonTranscript; use libspartan::{Instance, NIZKGens, NIZK}; fn print(msg: &str) { - let star = "* "; - println!("{:indent$}{}{}", "", star, msg, indent = 2); + let star = "* "; + println!("{:indent$}{}{}", "", star, msg, indent = 2); } pub fn main() { - // the list of number of variables (and constraints) in an R1CS instance - let inst_sizes = vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; - - println!("Profiler:: NIZK"); - for &s in inst_sizes.iter() { - let num_vars = (2_usize).pow(s as u32); - let num_cons = num_vars; - let num_inputs = 10; - - // produce a synthetic R1CSInstance - let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - - // produce public generators - let gens = NIZKGens::new(num_cons, num_vars, num_inputs); - - let params = poseidon_params(); - // produce a proof of satisfiability - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript); - - let mut proof_encoded = Vec::new(); - proof.serialize(&mut proof_encoded).unwrap(); - let msg_proof_len = format!("NIZK::proof_compressed_len {:?}", proof_encoded.len()); - print(&msg_proof_len); - - // verify the proof of satisfiability - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify(&inst, &inputs, &mut verifier_transcript, &gens) - .is_ok()); - - println!(); - } + // the list of number of variables (and constraints) in an R1CS instance + let inst_sizes = vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; + + println!("Profiler:: NIZK"); + for &s in inst_sizes.iter() { + let num_vars = (2_usize).pow(s as u32); + let num_cons = num_vars; + let num_inputs = 10; + + // produce a synthetic R1CSInstance + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + + // produce public generators + let gens = NIZKGens::new(num_cons, num_vars, num_inputs); + + let params = poseidon_params(); + // produce a proof of satisfiability + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript); + + let mut proof_encoded = Vec::new(); + proof.serialize(&mut proof_encoded).unwrap(); + let msg_proof_len = format!("NIZK::proof_compressed_len {:?}", proof_encoded.len()); + print(&msg_proof_len); + + // verify the proof of satisfiability + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify(&inst, &inputs, &mut verifier_transcript, &gens) + .is_ok()); + + println!(); + } } diff --git a/profiler/snark.rs b/profiler/snark.rs index d111b1f..17ceab5 100644 --- a/profiler/snark.rs +++ b/profiler/snark.rs @@ -10,54 +10,54 @@ use libspartan::poseidon_transcript::PoseidonTranscript; use libspartan::{Instance, SNARKGens, SNARK}; fn print(msg: &str) { - let star = "* "; - println!("{:indent$}{}{}", "", star, msg, indent = 2); + let star = "* "; + println!("{:indent$}{}{}", "", star, msg, indent = 2); } pub fn main() { - // the list of number of variables (and constraints) in an R1CS instance - let inst_sizes = vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; - - println!("Profiler:: SNARK"); - for &s in inst_sizes.iter() { - let num_vars = (2_usize).pow(s as u32); - let num_cons = num_vars; - let num_inputs = 10; - - // produce a synthetic R1CSInstance - let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - - // produce public generators - let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); - - // create a commitment to R1CSInstance - let (comm, decomm) = SNARK::encode(&inst, &gens); - - let params = poseidon_params(); - - // produce a proof of satisfiability - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let proof = SNARK::prove( - &inst, - &comm, - &decomm, - vars, - &inputs, - &gens, - &mut prover_transcript, - ); - - let mut proof_encoded = Vec::new(); - proof.serialize(&mut proof_encoded).unwrap(); - let msg_proof_len = format!("SNARK::proof_compressed_len {:?}", proof_encoded.len()); - print(&msg_proof_len); - - // verify the proof of satisfiability - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify(&comm, &inputs, &mut verifier_transcript, &gens) - .is_ok()); - - println!(); - } + // the list of number of variables (and constraints) in an R1CS instance + let inst_sizes = vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; + + println!("Profiler:: SNARK"); + for &s in inst_sizes.iter() { + let num_vars = (2_usize).pow(s as u32); + let num_cons = num_vars; + let num_inputs = 10; + + // produce a synthetic R1CSInstance + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + + // produce public generators + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); + + // create a commitment to R1CSInstance + let (comm, decomm) = SNARK::encode(&inst, &gens); + + let params = poseidon_params(); + + // produce a proof of satisfiability + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let proof = SNARK::prove( + &inst, + &comm, + &decomm, + vars, + &inputs, + &gens, + &mut prover_transcript, + ); + + let mut proof_encoded = Vec::new(); + proof.serialize(&mut proof_encoded).unwrap(); + let msg_proof_len = format!("SNARK::proof_compressed_len {:?}", proof_encoded.len()); + print(&msg_proof_len); + + // verify the proof of satisfiability + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify(&comm, &inputs, &mut verifier_transcript, &gens) + .is_ok()); + + println!(); + } } diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 7b20d96..0000000 --- a/rustfmt.toml +++ /dev/null @@ -1,4 +0,0 @@ -edition = "2018" -tab_spaces = 2 -newline_style = "Unix" -use_try_shorthand = true diff --git a/src/commitments.rs b/src/commitments.rs index 2818ad3..5633504 100644 --- a/src/commitments.rs +++ b/src/commitments.rs @@ -10,83 +10,83 @@ use ark_sponge::CryptographicSponge; #[derive(Debug, Clone)] pub struct MultiCommitGens { - pub n: usize, - pub G: Vec, - pub h: GroupElement, + pub n: usize, + pub G: Vec, + pub h: GroupElement, } impl MultiCommitGens { - pub fn new(n: usize, label: &[u8]) -> Self { - let params = poseidon_params(); - let mut sponge = PoseidonSponge::new(¶ms); - sponge.absorb(&label); - sponge.absorb(&GROUP_BASEPOINT.compress().0); + pub fn new(n: usize, label: &[u8]) -> Self { + let params = poseidon_params(); + let mut sponge = PoseidonSponge::new(¶ms); + sponge.absorb(&label); + sponge.absorb(&GROUP_BASEPOINT.compress().0); - let mut gens: Vec = Vec::new(); - for _ in 0..n + 1 { - let mut el_aff: Option = None; - while el_aff.is_none() { - let uniform_bytes = sponge.squeeze_bytes(64); - el_aff = GroupElementAffine::from_random_bytes(&uniform_bytes); - } - let el = el_aff.unwrap().mul_by_cofactor_to_projective(); - gens.push(el); - } + let mut gens: Vec = Vec::new(); + for _ in 0..n + 1 { + let mut el_aff: Option = None; + while el_aff.is_none() { + let uniform_bytes = sponge.squeeze_bytes(64); + el_aff = GroupElementAffine::from_random_bytes(&uniform_bytes); + } + let el = el_aff.unwrap().mul_by_cofactor_to_projective(); + gens.push(el); + } - MultiCommitGens { - n, - G: gens[..n].to_vec(), - h: gens[n], + MultiCommitGens { + n, + G: gens[..n].to_vec(), + h: gens[n], + } } - } - pub fn clone(&self) -> MultiCommitGens { - MultiCommitGens { - n: self.n, - h: self.h, - G: self.G.clone(), + pub fn clone(&self) -> MultiCommitGens { + MultiCommitGens { + n: self.n, + h: self.h, + G: self.G.clone(), + } } - } - pub fn split_at(&self, mid: usize) -> (MultiCommitGens, MultiCommitGens) { - let (G1, G2) = self.G.split_at(mid); + pub fn split_at(&self, mid: usize) -> (MultiCommitGens, MultiCommitGens) { + let (G1, G2) = self.G.split_at(mid); - ( - MultiCommitGens { - n: G1.len(), - G: G1.to_vec(), - h: self.h, - }, - MultiCommitGens { - n: G2.len(), - G: G2.to_vec(), - h: self.h, - }, - ) - } + ( + MultiCommitGens { + n: G1.len(), + G: G1.to_vec(), + h: self.h, + }, + MultiCommitGens { + n: G2.len(), + G: G2.to_vec(), + h: self.h, + }, + ) + } } pub trait Commitments { - fn commit(&self, blind: &Scalar, gens_n: &MultiCommitGens) -> GroupElement; + fn commit(&self, blind: &Scalar, gens_n: &MultiCommitGens) -> GroupElement; } impl Commitments for Scalar { - fn commit(&self, blind: &Scalar, gens_n: &MultiCommitGens) -> GroupElement { - assert_eq!(gens_n.n, 1); - GroupElement::vartime_multiscalar_mul(&[*self, *blind], &[gens_n.G[0], gens_n.h]) - } + fn commit(&self, blind: &Scalar, gens_n: &MultiCommitGens) -> GroupElement { + assert_eq!(gens_n.n, 1); + GroupElement::vartime_multiscalar_mul(&[*self, *blind], &[gens_n.G[0], gens_n.h]) + } } impl Commitments for Vec { - fn commit(&self, blind: &Scalar, gens_n: &MultiCommitGens) -> GroupElement { - assert_eq!(gens_n.n, self.len()); - GroupElement::vartime_multiscalar_mul(self, &gens_n.G) + gens_n.h.mul(blind.into_repr()) - } + fn commit(&self, blind: &Scalar, gens_n: &MultiCommitGens) -> GroupElement { + assert_eq!(gens_n.n, self.len()); + GroupElement::vartime_multiscalar_mul(self, &gens_n.G) + gens_n.h.mul(blind.into_repr()) + } } impl Commitments for [Scalar] { - fn commit(&self, blind: &Scalar, gens_n: &MultiCommitGens) -> GroupElement { - assert_eq!(gens_n.n, self.len()); - GroupElement::vartime_multiscalar_mul(self, &gens_n.G) + gens_n.h.mul(blind.into_repr()) - } + fn commit(&self, blind: &Scalar, gens_n: &MultiCommitGens) -> GroupElement { + assert_eq!(gens_n.n, self.len()); + GroupElement::vartime_multiscalar_mul(self, &gens_n.G) + gens_n.h.mul(blind.into_repr()) + } } diff --git a/src/constraints.rs b/src/constraints.rs index a19e8e2..89f14f0 100644 --- a/src/constraints.rs +++ b/src/constraints.rs @@ -2,475 +2,487 @@ use std::{borrow::Borrow, vec}; use super::scalar::Scalar; use crate::{ - group::Fq, - math::Math, - sparse_mlpoly::{SparsePolyEntry, SparsePolynomial}, - unipoly::UniPoly, + group::Fq, + math::Math, + sparse_mlpoly::{SparsePolyEntry, SparsePolynomial}, + unipoly::UniPoly, }; use ark_bls12_377::{constraints::PairingVar as IV, Bls12_377 as I, Fr}; use ark_crypto_primitives::{ - snark::BooleanInputVar, CircuitSpecificSetupSNARK, SNARKGadget, SNARK, + snark::BooleanInputVar, CircuitSpecificSetupSNARK, SNARKGadget, SNARK, }; use ark_ff::{BitIteratorLE, PrimeField, Zero}; use ark_groth16::{ - constraints::{Groth16VerifierGadget, PreparedVerifyingKeyVar, ProofVar}, - Groth16, PreparedVerifyingKey, Proof as GrothProof, + constraints::{Groth16VerifierGadget, PreparedVerifyingKeyVar, ProofVar}, + Groth16, PreparedVerifyingKey, Proof as GrothProof, }; use ark_r1cs_std::{ - alloc::{AllocVar, AllocationMode}, - fields::fp::FpVar, - prelude::{Boolean, EqGadget, FieldVar}, + alloc::{AllocVar, AllocationMode}, + fields::fp::FpVar, + prelude::{Boolean, EqGadget, FieldVar}, }; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError}; use ark_sponge::{ - constraints::CryptographicSpongeVar, - poseidon::{constraints::PoseidonSpongeVar, PoseidonParameters}, + constraints::CryptographicSpongeVar, + poseidon::{constraints::PoseidonSpongeVar, PoseidonParameters}, }; use rand::{CryptoRng, Rng}; pub struct PoseidonTranscripVar { - pub cs: ConstraintSystemRef, - pub sponge: PoseidonSpongeVar, - pub params: PoseidonParameters, + pub cs: ConstraintSystemRef, + pub sponge: PoseidonSpongeVar, + pub params: PoseidonParameters, } impl PoseidonTranscripVar { - fn new( - cs: ConstraintSystemRef, - params: &PoseidonParameters, - challenge: Option, - ) -> Self { - let mut sponge = PoseidonSpongeVar::new(cs.clone(), params); - - if let Some(c) = challenge { - let c_var = FpVar::::new_witness(cs.clone(), || Ok(c)).unwrap(); - sponge.absorb(&c_var).unwrap(); + fn new( + cs: ConstraintSystemRef, + params: &PoseidonParameters, + challenge: Option, + ) -> Self { + let mut sponge = PoseidonSpongeVar::new(cs.clone(), params); + + if let Some(c) = challenge { + let c_var = FpVar::::new_witness(cs.clone(), || Ok(c)).unwrap(); + sponge.absorb(&c_var).unwrap(); + } + + Self { + cs, + sponge, + params: params.clone(), + } } - Self { - cs, - sponge, - params: params.clone(), + fn append(&mut self, input: &FpVar) -> Result<(), SynthesisError> { + self.sponge.absorb(&input) } - } - fn append(&mut self, input: &FpVar) -> Result<(), SynthesisError> { - self.sponge.absorb(&input) - } - - fn append_vector(&mut self, input_vec: &[FpVar]) -> Result<(), SynthesisError> { - for input in input_vec.iter() { - self.append(input)?; + fn append_vector(&mut self, input_vec: &[FpVar]) -> Result<(), SynthesisError> { + for input in input_vec.iter() { + self.append(input)?; + } + Ok(()) } - Ok(()) - } - fn challenge(&mut self) -> Result, SynthesisError> { - let c_var = self.sponge.squeeze_field_elements(1).unwrap().remove(0); + fn challenge(&mut self) -> Result, SynthesisError> { + let c_var = self.sponge.squeeze_field_elements(1).unwrap().remove(0); - Ok(c_var) - } + Ok(c_var) + } - fn challenge_vector(&mut self, len: usize) -> Result>, SynthesisError> { - let c_vars = self.sponge.squeeze_field_elements(len).unwrap(); + fn challenge_vector(&mut self, len: usize) -> Result>, SynthesisError> { + let c_vars = self.sponge.squeeze_field_elements(len).unwrap(); - Ok(c_vars) - } + Ok(c_vars) + } } #[derive(Clone)] pub struct UniPolyVar { - pub coeffs: Vec>, + pub coeffs: Vec>, } impl AllocVar for UniPolyVar { - fn new_variable>( - cs: impl Into>, - f: impl FnOnce() -> Result, - mode: AllocationMode, - ) -> Result { - f().and_then(|c| { - let cs = cs.into(); - let cp: &UniPoly = c.borrow(); - let mut coeffs_var = Vec::new(); - for coeff in cp.coeffs.iter() { - let coeff_var = FpVar::::new_variable(cs.clone(), || Ok(coeff), mode)?; - coeffs_var.push(coeff_var); - } - Ok(Self { coeffs: coeffs_var }) - }) - } + fn new_variable>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + f().and_then(|c| { + let cs = cs.into(); + let cp: &UniPoly = c.borrow(); + let mut coeffs_var = Vec::new(); + for coeff in cp.coeffs.iter() { + let coeff_var = FpVar::::new_variable(cs.clone(), || Ok(coeff), mode)?; + coeffs_var.push(coeff_var); + } + Ok(Self { coeffs: coeffs_var }) + }) + } } impl UniPolyVar { - pub fn eval_at_zero(&self) -> FpVar { - self.coeffs[0].clone() - } - - pub fn eval_at_one(&self) -> FpVar { - let mut res = self.coeffs[0].clone(); - for i in 1..self.coeffs.len() { - res = &res + &self.coeffs[i]; + pub fn eval_at_zero(&self) -> FpVar { + self.coeffs[0].clone() } - res - } - // mul without reduce - pub fn evaluate(&self, r: &FpVar) -> FpVar { - let mut eval = self.coeffs[0].clone(); - let mut power = r.clone(); + pub fn eval_at_one(&self) -> FpVar { + let mut res = self.coeffs[0].clone(); + for i in 1..self.coeffs.len() { + res = &res + &self.coeffs[i]; + } + res + } + + // mul without reduce + pub fn evaluate(&self, r: &FpVar) -> FpVar { + let mut eval = self.coeffs[0].clone(); + let mut power = r.clone(); - for i in 1..self.coeffs.len() { - eval += &power * &self.coeffs[i]; - power *= r; + for i in 1..self.coeffs.len() { + eval += &power * &self.coeffs[i]; + power *= r; + } + eval } - eval - } } #[derive(Clone)] pub struct SumcheckVerificationCircuit { - pub polys: Vec, + pub polys: Vec, } impl SumcheckVerificationCircuit { - fn verifiy_sumcheck( - &self, - poly_vars: &[UniPolyVar], - claim_var: &FpVar, - transcript_var: &mut PoseidonTranscripVar, - ) -> Result<(FpVar, Vec>), SynthesisError> { - let mut e_var = claim_var.clone(); - let mut r_vars: Vec> = Vec::new(); - - for (poly_var, _poly) in poly_vars.iter().zip(self.polys.iter()) { - let res = poly_var.eval_at_one() + poly_var.eval_at_zero(); - res.enforce_equal(&e_var)?; - transcript_var.append_vector(&poly_var.coeffs)?; - let r_i_var = transcript_var.challenge()?; - r_vars.push(r_i_var.clone()); - e_var = poly_var.evaluate(&r_i_var.clone()); + fn verifiy_sumcheck( + &self, + poly_vars: &[UniPolyVar], + claim_var: &FpVar, + transcript_var: &mut PoseidonTranscripVar, + ) -> Result<(FpVar, Vec>), SynthesisError> { + let mut e_var = claim_var.clone(); + let mut r_vars: Vec> = Vec::new(); + + for (poly_var, _poly) in poly_vars.iter().zip(self.polys.iter()) { + let res = poly_var.eval_at_one() + poly_var.eval_at_zero(); + res.enforce_equal(&e_var)?; + transcript_var.append_vector(&poly_var.coeffs)?; + let r_i_var = transcript_var.challenge()?; + r_vars.push(r_i_var.clone()); + e_var = poly_var.evaluate(&r_i_var.clone()); + } + + Ok((e_var, r_vars)) } - - Ok((e_var, r_vars)) - } } #[derive(Clone)] pub struct SparsePolyEntryVar { - idx: usize, - val_var: FpVar, + idx: usize, + val_var: FpVar, } impl AllocVar for SparsePolyEntryVar { - fn new_variable>( - cs: impl Into>, - f: impl FnOnce() -> Result, - _mode: AllocationMode, - ) -> Result { - f().and_then(|s| { - let cs = cs.into(); - let spe: &SparsePolyEntry = s.borrow(); - let val_var = FpVar::::new_witness(cs, || Ok(spe.val))?; - Ok(Self { - idx: spe.idx, - val_var, - }) - }) - } + fn new_variable>( + cs: impl Into>, + f: impl FnOnce() -> Result, + _mode: AllocationMode, + ) -> Result { + f().and_then(|s| { + let cs = cs.into(); + let spe: &SparsePolyEntry = s.borrow(); + let val_var = FpVar::::new_witness(cs, || Ok(spe.val))?; + Ok(Self { + idx: spe.idx, + val_var, + }) + }) + } } #[derive(Clone)] pub struct SparsePolynomialVar { - num_vars: usize, - Z_var: Vec, + num_vars: usize, + Z_var: Vec, } impl AllocVar for SparsePolynomialVar { - fn new_variable>( - cs: impl Into>, - f: impl FnOnce() -> Result, - mode: AllocationMode, - ) -> Result { - f().and_then(|s| { - let cs = cs.into(); - let sp: &SparsePolynomial = s.borrow(); - let mut Z_var = Vec::new(); - for spe in sp.Z.iter() { - let spe_var = SparsePolyEntryVar::new_variable(cs.clone(), || Ok(spe), mode)?; - Z_var.push(spe_var); - } - Ok(Self { - num_vars: sp.num_vars, - Z_var, - }) - }) - } + fn new_variable>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + f().and_then(|s| { + let cs = cs.into(); + let sp: &SparsePolynomial = s.borrow(); + let mut Z_var = Vec::new(); + for spe in sp.Z.iter() { + let spe_var = SparsePolyEntryVar::new_variable(cs.clone(), || Ok(spe), mode)?; + Z_var.push(spe_var); + } + Ok(Self { + num_vars: sp.num_vars, + Z_var, + }) + }) + } } impl SparsePolynomialVar { - fn compute_chi(a: &[bool], r_vars: &[FpVar]) -> FpVar { - let mut chi_i_var = FpVar::::one(); - let one = FpVar::::one(); - for (i, r_var) in r_vars.iter().enumerate() { - if a[i] { - chi_i_var *= r_var; - } else { - chi_i_var *= &one - r_var; - } + fn compute_chi(a: &[bool], r_vars: &[FpVar]) -> FpVar { + let mut chi_i_var = FpVar::::one(); + let one = FpVar::::one(); + for (i, r_var) in r_vars.iter().enumerate() { + if a[i] { + chi_i_var *= r_var; + } else { + chi_i_var *= &one - r_var; + } + } + chi_i_var } - chi_i_var - } - - pub fn evaluate(&self, r_var: &[FpVar]) -> FpVar { - let mut sum = FpVar::::zero(); - for spe_var in self.Z_var.iter() { - // potential problem - let bits = &spe_var.idx.get_bits(r_var.len()); - sum += SparsePolynomialVar::compute_chi(bits, r_var) * &spe_var.val_var; + + pub fn evaluate(&self, r_var: &[FpVar]) -> FpVar { + let mut sum = FpVar::::zero(); + for spe_var in self.Z_var.iter() { + // potential problem + let bits = &spe_var.idx.get_bits(r_var.len()); + sum += SparsePolynomialVar::compute_chi(bits, r_var) * &spe_var.val_var; + } + sum } - sum - } } #[derive(Clone)] pub struct R1CSVerificationCircuit { - pub num_vars: usize, - pub num_cons: usize, - pub input: Vec, - pub input_as_sparse_poly: SparsePolynomial, - pub evals: (Fr, Fr, Fr), - pub params: PoseidonParameters, - pub prev_challenge: Fr, - pub claims_phase2: (Scalar, Scalar, Scalar, Scalar), - pub eval_vars_at_ry: Fr, - pub sc_phase1: SumcheckVerificationCircuit, - pub sc_phase2: SumcheckVerificationCircuit, - // The point on which the polynomial was evaluated by the prover. - pub claimed_ry: Vec, - pub claimed_transcript_sat_state: Scalar, + pub num_vars: usize, + pub num_cons: usize, + pub input: Vec, + pub input_as_sparse_poly: SparsePolynomial, + pub evals: (Fr, Fr, Fr), + pub params: PoseidonParameters, + pub prev_challenge: Fr, + pub claims_phase2: (Scalar, Scalar, Scalar, Scalar), + pub eval_vars_at_ry: Fr, + pub sc_phase1: SumcheckVerificationCircuit, + pub sc_phase2: SumcheckVerificationCircuit, + // The point on which the polynomial was evaluated by the prover. + pub claimed_ry: Vec, + pub claimed_transcript_sat_state: Scalar, } impl R1CSVerificationCircuit { - fn new(config: &VerifierConfig) -> Self { - Self { - num_vars: config.num_vars, - num_cons: config.num_cons, - input: config.input.clone(), - input_as_sparse_poly: config.input_as_sparse_poly.clone(), - evals: config.evals, - params: config.params.clone(), - prev_challenge: config.prev_challenge, - claims_phase2: config.claims_phase2, - eval_vars_at_ry: config.eval_vars_at_ry, - sc_phase1: SumcheckVerificationCircuit { - polys: config.polys_sc1.clone(), - }, - sc_phase2: SumcheckVerificationCircuit { - polys: config.polys_sc2.clone(), - }, - claimed_ry: config.ry.clone(), - claimed_transcript_sat_state: config.transcript_sat_state, + fn new(config: &VerifierConfig) -> Self { + Self { + num_vars: config.num_vars, + num_cons: config.num_cons, + input: config.input.clone(), + input_as_sparse_poly: config.input_as_sparse_poly.clone(), + evals: config.evals, + params: config.params.clone(), + prev_challenge: config.prev_challenge, + claims_phase2: config.claims_phase2, + eval_vars_at_ry: config.eval_vars_at_ry, + sc_phase1: SumcheckVerificationCircuit { + polys: config.polys_sc1.clone(), + }, + sc_phase2: SumcheckVerificationCircuit { + polys: config.polys_sc2.clone(), + }, + claimed_ry: config.ry.clone(), + claimed_transcript_sat_state: config.transcript_sat_state, + } } - } } impl ConstraintSynthesizer for R1CSVerificationCircuit { - fn generate_constraints(self, cs: ConstraintSystemRef) -> ark_relations::r1cs::Result<()> { - let mut transcript_var = - PoseidonTranscripVar::new(cs.clone(), &self.params, Some(self.prev_challenge)); - - let poly_sc1_vars = self - .sc_phase1 - .polys - .iter() - .map(|p| UniPolyVar::new_variable(cs.clone(), || Ok(p), AllocationMode::Witness).unwrap()) - .collect::>(); - - let poly_sc2_vars = self - .sc_phase2 - .polys - .iter() - .map(|p| UniPolyVar::new_variable(cs.clone(), || Ok(p), AllocationMode::Witness).unwrap()) - .collect::>(); - - let input_vars = self - .input - .iter() - .map(|i| FpVar::::new_variable(cs.clone(), || Ok(i), AllocationMode::Witness).unwrap()) - .collect::>>(); - - let claimed_ry_vars = self - .claimed_ry - .iter() - .map(|r| FpVar::::new_variable(cs.clone(), || Ok(r), AllocationMode::Input).unwrap()) - .collect::>>(); - - transcript_var.append_vector(&input_vars)?; - - let num_rounds_x = self.num_cons.log_2(); - let _num_rounds_y = (2 * self.num_vars).log_2(); - - let tau_vars = transcript_var.challenge_vector(num_rounds_x)?; - - let claim_phase1_var = FpVar::::new_witness(cs.clone(), || Ok(Fr::zero()))?; - - let (claim_post_phase1_var, rx_var) = - self - .sc_phase1 - .verifiy_sumcheck(&poly_sc1_vars, &claim_phase1_var, &mut transcript_var)?; - - let (Az_claim, Bz_claim, Cz_claim, prod_Az_Bz_claims) = &self.claims_phase2; - - let Az_claim_var = FpVar::::new_input(cs.clone(), || Ok(Az_claim))?; - let Bz_claim_var = FpVar::::new_input(cs.clone(), || Ok(Bz_claim))?; - let Cz_claim_var = FpVar::::new_input(cs.clone(), || Ok(Cz_claim))?; - let prod_Az_Bz_claim_var = FpVar::::new_input(cs.clone(), || Ok(prod_Az_Bz_claims))?; - let one = FpVar::::one(); - let prod_vars: Vec> = (0..rx_var.len()) - .map(|i| (&rx_var[i] * &tau_vars[i]) + (&one - &rx_var[i]) * (&one - &tau_vars[i])) - .collect(); - let mut taus_bound_rx_var = FpVar::::one(); - - for p_var in prod_vars.iter() { - taus_bound_rx_var *= p_var; - } - - let expected_claim_post_phase1_var = - (&prod_Az_Bz_claim_var - &Cz_claim_var) * &taus_bound_rx_var; - - claim_post_phase1_var.enforce_equal(&expected_claim_post_phase1_var)?; - - let r_A_var = transcript_var.challenge()?; - let r_B_var = transcript_var.challenge()?; - let r_C_var = transcript_var.challenge()?; + fn generate_constraints(self, cs: ConstraintSystemRef) -> ark_relations::r1cs::Result<()> { + let mut transcript_var = + PoseidonTranscripVar::new(cs.clone(), &self.params, Some(self.prev_challenge)); + + let poly_sc1_vars = self + .sc_phase1 + .polys + .iter() + .map(|p| { + UniPolyVar::new_variable(cs.clone(), || Ok(p), AllocationMode::Witness).unwrap() + }) + .collect::>(); + + let poly_sc2_vars = self + .sc_phase2 + .polys + .iter() + .map(|p| { + UniPolyVar::new_variable(cs.clone(), || Ok(p), AllocationMode::Witness).unwrap() + }) + .collect::>(); + + let input_vars = self + .input + .iter() + .map(|i| { + FpVar::::new_variable(cs.clone(), || Ok(i), AllocationMode::Witness).unwrap() + }) + .collect::>>(); + + let claimed_ry_vars = self + .claimed_ry + .iter() + .map(|r| { + FpVar::::new_variable(cs.clone(), || Ok(r), AllocationMode::Input).unwrap() + }) + .collect::>>(); + + transcript_var.append_vector(&input_vars)?; + + let num_rounds_x = self.num_cons.log_2(); + let _num_rounds_y = (2 * self.num_vars).log_2(); + + let tau_vars = transcript_var.challenge_vector(num_rounds_x)?; + + let claim_phase1_var = FpVar::::new_witness(cs.clone(), || Ok(Fr::zero()))?; + + let (claim_post_phase1_var, rx_var) = self.sc_phase1.verifiy_sumcheck( + &poly_sc1_vars, + &claim_phase1_var, + &mut transcript_var, + )?; + + let (Az_claim, Bz_claim, Cz_claim, prod_Az_Bz_claims) = &self.claims_phase2; + + let Az_claim_var = FpVar::::new_input(cs.clone(), || Ok(Az_claim))?; + let Bz_claim_var = FpVar::::new_input(cs.clone(), || Ok(Bz_claim))?; + let Cz_claim_var = FpVar::::new_input(cs.clone(), || Ok(Cz_claim))?; + let prod_Az_Bz_claim_var = FpVar::::new_input(cs.clone(), || Ok(prod_Az_Bz_claims))?; + let one = FpVar::::one(); + let prod_vars: Vec> = (0..rx_var.len()) + .map(|i| (&rx_var[i] * &tau_vars[i]) + (&one - &rx_var[i]) * (&one - &tau_vars[i])) + .collect(); + let mut taus_bound_rx_var = FpVar::::one(); + + for p_var in prod_vars.iter() { + taus_bound_rx_var *= p_var; + } + + let expected_claim_post_phase1_var = + (&prod_Az_Bz_claim_var - &Cz_claim_var) * &taus_bound_rx_var; + + claim_post_phase1_var.enforce_equal(&expected_claim_post_phase1_var)?; + + let r_A_var = transcript_var.challenge()?; + let r_B_var = transcript_var.challenge()?; + let r_C_var = transcript_var.challenge()?; + + let claim_phase2_var = + &r_A_var * &Az_claim_var + &r_B_var * &Bz_claim_var + &r_C_var * &Cz_claim_var; + + let (claim_post_phase2_var, ry_var) = self.sc_phase2.verifiy_sumcheck( + &poly_sc2_vars, + &claim_phase2_var, + &mut transcript_var, + )?; + + // Because the verifier checks the commitment opening on point ry outside + // the circuit, the prover needs to send ry to the verifier (making the + // proof size O(log n)). As this point is normally obtained by the verifier + // from the second round of sumcheck, the circuit needs to ensure the + // claimed point, coming from the prover, is actually the point derived + // inside the circuit. These additional checks will be removed + // when the commitment verification is done inside the circuit. + for (i, r) in claimed_ry_vars.iter().enumerate() { + ry_var[i].enforce_equal(r)?; + } + + let input_as_sparse_poly_var = SparsePolynomialVar::new_variable( + cs.clone(), + || Ok(&self.input_as_sparse_poly), + AllocationMode::Witness, + )?; + + let poly_input_eval_var = input_as_sparse_poly_var.evaluate(&ry_var[1..]); + + let eval_vars_at_ry_var = FpVar::::new_input(cs.clone(), || Ok(&self.eval_vars_at_ry))?; + + let eval_Z_at_ry_var = (FpVar::::one() - &ry_var[0]) * &eval_vars_at_ry_var + + &ry_var[0] * &poly_input_eval_var; + + let (eval_A_r, eval_B_r, eval_C_r) = self.evals; + + let eval_A_r_var = FpVar::::new_witness(cs.clone(), || Ok(eval_A_r))?; + let eval_B_r_var = FpVar::::new_witness(cs.clone(), || Ok(eval_B_r))?; + let eval_C_r_var = FpVar::::new_witness(cs.clone(), || Ok(eval_C_r))?; + + let scalar_var = + &r_A_var * &eval_A_r_var + &r_B_var * &eval_B_r_var + &r_C_var * &eval_C_r_var; + + let expected_claim_post_phase2_var = eval_Z_at_ry_var * scalar_var; + claim_post_phase2_var.enforce_equal(&expected_claim_post_phase2_var)?; + + let expected_transcript_state_var = transcript_var.challenge()?; + let claimed_transcript_state_var = + FpVar::::new_input(cs, || Ok(self.claimed_transcript_sat_state))?; - let claim_phase2_var = - &r_A_var * &Az_claim_var + &r_B_var * &Bz_claim_var + &r_C_var * &Cz_claim_var; + // Ensure that the prover and verifier transcipt views are consistent at + // the end of the satisfiability proof. + expected_transcript_state_var.enforce_equal(&claimed_transcript_state_var)?; - let (claim_post_phase2_var, ry_var) = - self - .sc_phase2 - .verifiy_sumcheck(&poly_sc2_vars, &claim_phase2_var, &mut transcript_var)?; - - // Because the verifier checks the commitment opening on point ry outside - // the circuit, the prover needs to send ry to the verifier (making the - // proof size O(log n)). As this point is normally obtained by the verifier - // from the second round of sumcheck, the circuit needs to ensure the - // claimed point, coming from the prover, is actually the point derived - // inside the circuit. These additional checks will be removed - // when the commitment verification is done inside the circuit. - for (i, r) in claimed_ry_vars.iter().enumerate() { - ry_var[i].enforce_equal(r)?; + Ok(()) } - - let input_as_sparse_poly_var = SparsePolynomialVar::new_variable( - cs.clone(), - || Ok(&self.input_as_sparse_poly), - AllocationMode::Witness, - )?; - - let poly_input_eval_var = input_as_sparse_poly_var.evaluate(&ry_var[1..]); - - let eval_vars_at_ry_var = FpVar::::new_input(cs.clone(), || Ok(&self.eval_vars_at_ry))?; - - let eval_Z_at_ry_var = - (FpVar::::one() - &ry_var[0]) * &eval_vars_at_ry_var + &ry_var[0] * &poly_input_eval_var; - - let (eval_A_r, eval_B_r, eval_C_r) = self.evals; - - let eval_A_r_var = FpVar::::new_witness(cs.clone(), || Ok(eval_A_r))?; - let eval_B_r_var = FpVar::::new_witness(cs.clone(), || Ok(eval_B_r))?; - let eval_C_r_var = FpVar::::new_witness(cs.clone(), || Ok(eval_C_r))?; - - let scalar_var = &r_A_var * &eval_A_r_var + &r_B_var * &eval_B_r_var + &r_C_var * &eval_C_r_var; - - let expected_claim_post_phase2_var = eval_Z_at_ry_var * scalar_var; - claim_post_phase2_var.enforce_equal(&expected_claim_post_phase2_var)?; - - let expected_transcript_state_var = transcript_var.challenge()?; - let claimed_transcript_state_var = - FpVar::::new_input(cs, || Ok(self.claimed_transcript_sat_state))?; - - // Ensure that the prover and verifier transcipt views are consistent at - // the end of the satisfiability proof. - expected_transcript_state_var.enforce_equal(&claimed_transcript_state_var)?; - - Ok(()) - } } #[derive(Clone)] pub struct VerifierConfig { - pub num_vars: usize, - pub num_cons: usize, - pub input: Vec, - pub input_as_sparse_poly: SparsePolynomial, - pub evals: (Fr, Fr, Fr), - pub params: PoseidonParameters, - pub prev_challenge: Fr, - pub claims_phase2: (Fr, Fr, Fr, Fr), - pub eval_vars_at_ry: Fr, - pub polys_sc1: Vec, - pub polys_sc2: Vec, - pub ry: Vec, - pub transcript_sat_state: Scalar, + pub num_vars: usize, + pub num_cons: usize, + pub input: Vec, + pub input_as_sparse_poly: SparsePolynomial, + pub evals: (Fr, Fr, Fr), + pub params: PoseidonParameters, + pub prev_challenge: Fr, + pub claims_phase2: (Fr, Fr, Fr, Fr), + pub eval_vars_at_ry: Fr, + pub polys_sc1: Vec, + pub polys_sc2: Vec, + pub ry: Vec, + pub transcript_sat_state: Scalar, } #[derive(Clone)] pub struct VerifierCircuit { - pub inner_circuit: R1CSVerificationCircuit, - pub inner_proof: GrothProof, - pub inner_vk: PreparedVerifyingKey, - pub eval_vars_at_ry: Fr, - pub claims_phase2: (Fr, Fr, Fr, Fr), - pub ry: Vec, - pub transcript_sat_state: Scalar, + pub inner_circuit: R1CSVerificationCircuit, + pub inner_proof: GrothProof, + pub inner_vk: PreparedVerifyingKey, + pub eval_vars_at_ry: Fr, + pub claims_phase2: (Fr, Fr, Fr, Fr), + pub ry: Vec, + pub transcript_sat_state: Scalar, } impl VerifierCircuit { - pub fn new( - config: &VerifierConfig, - mut rng: &mut R, - ) -> Result { - let inner_circuit = R1CSVerificationCircuit::new(config); - let (pk, vk) = Groth16::::setup(inner_circuit.clone(), &mut rng).unwrap(); - let proof = Groth16::::prove(&pk, inner_circuit.clone(), &mut rng)?; - let pvk = Groth16::::process_vk(&vk).unwrap(); - Ok(Self { - inner_circuit, - inner_proof: proof, - inner_vk: pvk, - eval_vars_at_ry: config.eval_vars_at_ry, - claims_phase2: config.claims_phase2, - ry: config.ry.clone(), - transcript_sat_state: config.transcript_sat_state, - }) - } + pub fn new( + config: &VerifierConfig, + mut rng: &mut R, + ) -> Result { + let inner_circuit = R1CSVerificationCircuit::new(config); + let (pk, vk) = Groth16::::setup(inner_circuit.clone(), &mut rng).unwrap(); + let proof = Groth16::::prove(&pk, inner_circuit.clone(), &mut rng)?; + let pvk = Groth16::::process_vk(&vk).unwrap(); + Ok(Self { + inner_circuit, + inner_proof: proof, + inner_vk: pvk, + eval_vars_at_ry: config.eval_vars_at_ry, + claims_phase2: config.claims_phase2, + ry: config.ry.clone(), + transcript_sat_state: config.transcript_sat_state, + }) + } } impl ConstraintSynthesizer for VerifierCircuit { - fn generate_constraints(self, cs: ConstraintSystemRef) -> ark_relations::r1cs::Result<()> { - let proof_var = ProofVar::::new_witness(cs.clone(), || Ok(self.inner_proof.clone()))?; - let (v_A, v_B, v_C, v_AB) = self.claims_phase2; - let mut pubs = vec![]; - pubs.extend(self.ry); - pubs.extend(vec![v_A, v_B, v_C, v_AB]); - pubs.extend(vec![self.eval_vars_at_ry, self.transcript_sat_state]); - - let bits = pubs - .iter() - .map(|c| { - let bits: Vec = BitIteratorLE::new(c.into_repr().as_ref().to_vec()).collect(); - Vec::new_witness(cs.clone(), || Ok(bits)) - }) - .collect::, _>>()?; - let input_var = BooleanInputVar::::new(bits); - - let vk_var = PreparedVerifyingKeyVar::new_witness(cs, || Ok(self.inner_vk.clone()))?; - Groth16VerifierGadget::verify_with_processed_vk(&vk_var, &input_var, &proof_var)? - .enforce_equal(&Boolean::constant(true))?; - Ok(()) - } + fn generate_constraints(self, cs: ConstraintSystemRef) -> ark_relations::r1cs::Result<()> { + let proof_var = + ProofVar::::new_witness(cs.clone(), || Ok(self.inner_proof.clone()))?; + let (v_A, v_B, v_C, v_AB) = self.claims_phase2; + let mut pubs = vec![]; + pubs.extend(self.ry); + pubs.extend(vec![v_A, v_B, v_C, v_AB]); + pubs.extend(vec![self.eval_vars_at_ry, self.transcript_sat_state]); + + let bits = pubs + .iter() + .map(|c| { + let bits: Vec = BitIteratorLE::new(c.into_repr().as_ref().to_vec()).collect(); + Vec::new_witness(cs.clone(), || Ok(bits)) + }) + .collect::, _>>()?; + let input_var = BooleanInputVar::::new(bits); + + let vk_var = PreparedVerifyingKeyVar::new_witness(cs, || Ok(self.inner_vk.clone()))?; + Groth16VerifierGadget::verify_with_processed_vk(&vk_var, &input_var, &proof_var)? + .enforce_equal(&Boolean::constant(true))?; + Ok(()) + } } diff --git a/src/dense_mlpoly.rs b/src/dense_mlpoly.rs index 47bf913..e2a4081 100644 --- a/src/dense_mlpoly.rs +++ b/src/dense_mlpoly.rs @@ -5,8 +5,8 @@ use crate::poseidon_transcript::{AppendToPoseidon, PoseidonTranscript}; use super::commitments::{Commitments, MultiCommitGens}; use super::errors::ProofVerifyError; use super::group::{ - CompressGroupElement, CompressedGroup, DecompressGroupElement, GroupElement, - VartimeMultiscalarMul, + CompressGroupElement, CompressedGroup, DecompressGroupElement, GroupElement, + VartimeMultiscalarMul, }; use super::math::Math; use super::nizk::{DotProductProofGens, DotProductProofLog}; @@ -29,760 +29,760 @@ use rayon::prelude::*; // TODO: integrate the DenseMultilinearExtension(and Sparse) https://github.com/arkworks-rs/algebra/tree/master/poly/src/evaluations/multivariate/multilinear from arkworks into Spartan. This requires moving the specific Spartan functionalities in separate traits. #[derive(Debug, Clone, Eq, PartialEq, Hash, CanonicalDeserialize, CanonicalSerialize)] pub struct DensePolynomial { - num_vars: usize, // the number of variables in the multilinear polynomial - len: usize, - Z: Vec, // evaluations of the polynomial in all the 2^num_vars Boolean inputs + num_vars: usize, // the number of variables in the multilinear polynomial + len: usize, + Z: Vec, // evaluations of the polynomial in all the 2^num_vars Boolean inputs } impl MultilinearExtension for DensePolynomial { - fn num_vars(&self) -> usize { - self.get_num_vars() - } - - fn evaluate(&self, point: &[Scalar]) -> Option { - if point.len() == self.num_vars { - Some(self.evaluate(&point)) - } else { - None - } - } - - fn rand(num_vars: usize, rng: &mut R) -> Self { - let evals = (0..(1 << num_vars)).map(|_| Scalar::rand(rng)).collect(); - Self { - num_vars: num_vars, - len: 1 << num_vars, - Z: evals, - } - } - - fn relabel(&self, _a: usize, _b: usize, _k: usize) -> Self { - unimplemented!() - } - - fn fix_variables(&self, _partial_point: &[Scalar]) -> Self { - unimplemented!() - } - - fn to_evaluations(&self) -> Vec { - self.Z.to_vec() - } + fn num_vars(&self) -> usize { + self.get_num_vars() + } + + fn evaluate(&self, point: &[Scalar]) -> Option { + if point.len() == self.num_vars { + Some(self.evaluate(&point)) + } else { + None + } + } + + fn rand(num_vars: usize, rng: &mut R) -> Self { + let evals = (0..(1 << num_vars)).map(|_| Scalar::rand(rng)).collect(); + Self { + num_vars: num_vars, + len: 1 << num_vars, + Z: evals, + } + } + + fn relabel(&self, _a: usize, _b: usize, _k: usize) -> Self { + unimplemented!() + } + + fn fix_variables(&self, _partial_point: &[Scalar]) -> Self { + unimplemented!() + } + + fn to_evaluations(&self) -> Vec { + self.Z.to_vec() + } } impl Zero for DensePolynomial { - fn zero() -> Self { - Self { - num_vars: 0, - len: 1, - Z: vec![Scalar::zero()], + fn zero() -> Self { + Self { + num_vars: 0, + len: 1, + Z: vec![Scalar::zero()], + } } - } - fn is_zero(&self) -> bool { - self.num_vars == 0 && self.len == 1 && self.Z[0].is_zero() - } + fn is_zero(&self) -> bool { + self.num_vars == 0 && self.len == 1 && self.Z[0].is_zero() + } } impl Add for DensePolynomial { - type Output = DensePolynomial; - fn add(self, other: Self) -> Self { - &self + &other - } + type Output = DensePolynomial; + fn add(self, other: Self) -> Self { + &self + &other + } } // function needed because the result might have a different lifetime than the // operands impl<'a, 'b> Add<&'a DensePolynomial> for &'b DensePolynomial { - type Output = DensePolynomial; - - fn add(self, other: &'a DensePolynomial) -> Self::Output { - if other.is_zero() { - return self.clone(); - } - if self.is_zero() { - return other.clone(); - } - assert_eq!(self.num_vars, other.num_vars); + type Output = DensePolynomial; - let res: Vec = self - .Z - .iter() - .zip(other.Z.iter()) - .map(|(a, b)| *a + *b) - .collect(); - Self::Output { - num_vars: self.num_vars, - len: self.len, - Z: res, + fn add(self, other: &'a DensePolynomial) -> Self::Output { + if other.is_zero() { + return self.clone(); + } + if self.is_zero() { + return other.clone(); + } + assert_eq!(self.num_vars, other.num_vars); + + let res: Vec = self + .Z + .iter() + .zip(other.Z.iter()) + .map(|(a, b)| *a + *b) + .collect(); + Self::Output { + num_vars: self.num_vars, + len: self.len, + Z: res, + } } - } } impl AddAssign for DensePolynomial { - fn add_assign(&mut self, other: Self) { - *self = &*self + &other; - } + fn add_assign(&mut self, other: Self) { + *self = &*self + &other; + } } impl<'a, 'b> AddAssign<&'a DensePolynomial> for DensePolynomial { - fn add_assign(&mut self, other: &'a DensePolynomial) { - *self = &*self + other; - } + fn add_assign(&mut self, other: &'a DensePolynomial) { + *self = &*self + other; + } } impl<'a, 'b> AddAssign<(Scalar, &'a DensePolynomial)> for DensePolynomial { - fn add_assign(&mut self, (scalar, other): (Scalar, &'a DensePolynomial)) { - let other = Self { - num_vars: other.num_vars, - len: 1 << other.num_vars, - Z: other.Z.iter().map(|x| scalar * x).collect(), - }; - *self = &*self + &other; - } + fn add_assign(&mut self, (scalar, other): (Scalar, &'a DensePolynomial)) { + let other = Self { + num_vars: other.num_vars, + len: 1 << other.num_vars, + Z: other.Z.iter().map(|x| scalar * x).collect(), + }; + *self = &*self + &other; + } } impl Neg for DensePolynomial { - type Output = DensePolynomial; + type Output = DensePolynomial; - fn neg(self) -> Self::Output { - Self::Output { - num_vars: self.num_vars, - len: self.len, - Z: self.Z.iter().map(|x| -*x).collect(), + fn neg(self) -> Self::Output { + Self::Output { + num_vars: self.num_vars, + len: self.len, + Z: self.Z.iter().map(|x| -*x).collect(), + } } - } } impl Sub for DensePolynomial { - type Output = DensePolynomial; + type Output = DensePolynomial; - fn sub(self, other: Self) -> Self::Output { - &self - &other - } + fn sub(self, other: Self) -> Self::Output { + &self - &other + } } impl<'a, 'b> Sub<&'a DensePolynomial> for &'b DensePolynomial { - type Output = DensePolynomial; + type Output = DensePolynomial; - fn sub(self, other: &'a DensePolynomial) -> Self::Output { - self + &other.clone().neg() - } + fn sub(self, other: &'a DensePolynomial) -> Self::Output { + self + &other.clone().neg() + } } impl SubAssign for DensePolynomial { - fn sub_assign(&mut self, other: Self) { - *self = &*self - &other; - } + fn sub_assign(&mut self, other: Self) { + *self = &*self - &other; + } } impl<'a, 'b> SubAssign<&'a DensePolynomial> for DensePolynomial { - fn sub_assign(&mut self, other: &'a DensePolynomial) { - *self = &*self - other; - } + fn sub_assign(&mut self, other: &'a DensePolynomial) { + *self = &*self - other; + } } #[derive(Clone)] pub struct PolyCommitmentGens { - pub gens: DotProductProofGens, - pub ck: CommitterKey, - pub vk: VerifierKey, + pub gens: DotProductProofGens, + pub ck: CommitterKey, + pub vk: VerifierKey, } impl PolyCommitmentGens { - // num vars is the number of variables in the multilinear polynomial - // this gives the maximum degree bound - pub fn new(num_vars: usize, label: &'static [u8]) -> PolyCommitmentGens { - let (_left, right) = EqPolynomial::compute_factored_lens(num_vars); - let gens = DotProductProofGens::new(right.pow2(), label); - - // Generates the SRS and trims it based on the number of variables in the - // multilinear polynomial. - let mut rng = ark_std::test_rng(); - let pst_gens = MultilinearPC::::setup(num_vars, &mut rng); - let (ck, vk) = MultilinearPC::::trim(&pst_gens, num_vars); - - PolyCommitmentGens { gens, ck, vk } - } + // num vars is the number of variables in the multilinear polynomial + // this gives the maximum degree bound + pub fn new(num_vars: usize, label: &'static [u8]) -> PolyCommitmentGens { + let (_left, right) = EqPolynomial::compute_factored_lens(num_vars); + let gens = DotProductProofGens::new(right.pow2(), label); + + // Generates the SRS and trims it based on the number of variables in the + // multilinear polynomial. + let mut rng = ark_std::test_rng(); + let pst_gens = MultilinearPC::::setup(num_vars, &mut rng); + let (ck, vk) = MultilinearPC::::trim(&pst_gens, num_vars); + + PolyCommitmentGens { gens, ck, vk } + } } pub struct PolyCommitmentBlinds { - blinds: Vec, + blinds: Vec, } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct PolyCommitment { - C: Vec, + C: Vec, } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct ConstPolyCommitment { - C: CompressedGroup, + C: CompressedGroup, } pub struct EqPolynomial { - r: Vec, + r: Vec, } impl EqPolynomial { - pub fn new(r: Vec) -> Self { - EqPolynomial { r } - } - - pub fn evaluate(&self, rx: &[Scalar]) -> Scalar { - assert_eq!(self.r.len(), rx.len()); - (0..rx.len()) - .map(|i| self.r[i] * rx[i] + (Scalar::one() - self.r[i]) * (Scalar::one() - rx[i])) - .product() - } - - pub fn evals(&self) -> Vec { - let ell = self.r.len(); - - let mut evals: Vec = vec![Scalar::one(); ell.pow2()]; - let mut size = 1; - for j in 0..ell { - // in each iteration, we double the size of chis - size *= 2; - // TODO: this reverse causes inconsistent evaluation in comparison to the - //evaluation function in ark-poly-commit, we should look into this to - // avoid the extra constraints in the circuit - for i in (0..size).rev().step_by(2) { - // copy each element from the prior iteration twice - let scalar = evals[i / 2]; - evals[i] = scalar * self.r[j]; - evals[i - 1] = scalar - evals[i]; - } - } - evals - } - - pub fn compute_factored_lens(ell: usize) -> (usize, usize) { - (ell / 2, ell - ell / 2) - } - - pub fn compute_factored_evals(&self) -> (Vec, Vec) { - let ell = self.r.len(); - let (left_num_vars, _right_num_vars) = EqPolynomial::compute_factored_lens(ell); - - let L = EqPolynomial::new(self.r[..left_num_vars].to_vec()).evals(); - let R = EqPolynomial::new(self.r[left_num_vars..ell].to_vec()).evals(); - - (L, R) - } + pub fn new(r: Vec) -> Self { + EqPolynomial { r } + } + + pub fn evaluate(&self, rx: &[Scalar]) -> Scalar { + assert_eq!(self.r.len(), rx.len()); + (0..rx.len()) + .map(|i| self.r[i] * rx[i] + (Scalar::one() - self.r[i]) * (Scalar::one() - rx[i])) + .product() + } + + pub fn evals(&self) -> Vec { + let ell = self.r.len(); + + let mut evals: Vec = vec![Scalar::one(); ell.pow2()]; + let mut size = 1; + for j in 0..ell { + // in each iteration, we double the size of chis + size *= 2; + // TODO: this reverse causes inconsistent evaluation in comparison to the + //evaluation function in ark-poly-commit, we should look into this to + // avoid the extra constraints in the circuit + for i in (0..size).rev().step_by(2) { + // copy each element from the prior iteration twice + let scalar = evals[i / 2]; + evals[i] = scalar * self.r[j]; + evals[i - 1] = scalar - evals[i]; + } + } + evals + } + + pub fn compute_factored_lens(ell: usize) -> (usize, usize) { + (ell / 2, ell - ell / 2) + } + + pub fn compute_factored_evals(&self) -> (Vec, Vec) { + let ell = self.r.len(); + let (left_num_vars, _right_num_vars) = EqPolynomial::compute_factored_lens(ell); + + let L = EqPolynomial::new(self.r[..left_num_vars].to_vec()).evals(); + let R = EqPolynomial::new(self.r[left_num_vars..ell].to_vec()).evals(); + + (L, R) + } } pub struct IdentityPolynomial { - size_point: usize, + size_point: usize, } impl IdentityPolynomial { - pub fn new(size_point: usize) -> Self { - IdentityPolynomial { size_point } - } - - pub fn evaluate(&self, r: &[Scalar]) -> Scalar { - let len = r.len(); - assert_eq!(len, self.size_point); - (0..len) - .map(|i| Scalar::from((len - i - 1).pow2() as u64) * r[i]) - .sum() - } + pub fn new(size_point: usize) -> Self { + IdentityPolynomial { size_point } + } + + pub fn evaluate(&self, r: &[Scalar]) -> Scalar { + let len = r.len(); + assert_eq!(len, self.size_point); + (0..len) + .map(|i| Scalar::from((len - i - 1).pow2() as u64) * r[i]) + .sum() + } } impl DensePolynomial { - pub fn new(Z: Vec) -> Self { - DensePolynomial { - num_vars: Z.len().log_2(), - len: Z.len(), - Z, - } - } - - pub fn get_num_vars(&self) -> usize { - self.num_vars - } - - pub fn len(&self) -> usize { - self.len - } - - pub fn clone(&self) -> DensePolynomial { - DensePolynomial::new(self.Z[0..self.len].to_vec()) - } - - pub fn split(&self, idx: usize) -> (DensePolynomial, DensePolynomial) { - assert!(idx < self.len()); - ( - DensePolynomial::new(self.Z[..idx].to_vec()), - DensePolynomial::new(self.Z[idx..2 * idx].to_vec()), - ) - } - - #[cfg(feature = "multicore")] - fn commit_inner(&self, blinds: &[Scalar], gens: &MultiCommitGens) -> PolyCommitment { - let L_size = blinds.len(); - let R_size = self.Z.len() / L_size; - assert_eq!(L_size * R_size, self.Z.len()); - let C = (0..L_size) - .into_par_iter() - .map(|i| { - self.Z[R_size * i..R_size * (i + 1)] - .commit(&blinds[i], gens) - .compress() - }) - .collect(); - PolyCommitment { C } - } - - #[cfg(not(feature = "multicore"))] - fn commit_inner(&self, blinds: &[Scalar], gens: &MultiCommitGens) -> PolyCommitment { - let L_size = blinds.len(); - let R_size = self.Z.len() / L_size; - assert_eq!(L_size * R_size, self.Z.len()); - let C = (0..L_size) - .map(|i| { - self.Z[R_size * i..R_size * (i + 1)] - .commit(&blinds[i], gens) - .compress() - }) - .collect(); - PolyCommitment { C } - } - - pub fn commit( - &self, - gens: &PolyCommitmentGens, - random_tape: Option<&mut RandomTape>, - ) -> (PolyCommitment, PolyCommitmentBlinds) { - let n = self.Z.len(); - let ell = self.get_num_vars(); - assert_eq!(n, ell.pow2()); - - let (left_num_vars, right_num_vars) = EqPolynomial::compute_factored_lens(ell); - let L_size = left_num_vars.pow2(); - let R_size = right_num_vars.pow2(); - assert_eq!(L_size * R_size, n); - - let blinds = if let Some(t) = random_tape { - PolyCommitmentBlinds { - blinds: t.random_vector(b"poly_blinds", L_size), - } - } else { - PolyCommitmentBlinds { - blinds: vec![Scalar::zero(); L_size], - } - }; - - (self.commit_inner(&blinds.blinds, &gens.gens.gens_n), blinds) - } - - pub fn bound(&self, L: &[Scalar]) -> Vec { - let (left_num_vars, right_num_vars) = EqPolynomial::compute_factored_lens(self.get_num_vars()); - let L_size = left_num_vars.pow2(); - let R_size = right_num_vars.pow2(); - (0..R_size) - .map(|i| (0..L_size).map(|j| L[j] * self.Z[j * R_size + i]).sum()) - .collect() - } - - pub fn bound_poly_var_top(&mut self, r: &Scalar) { - let n = self.len() / 2; - for i in 0..n { - self.Z[i] = self.Z[i] + (self.Z[i + n] - self.Z[i]) * r; - } - self.num_vars -= 1; - self.len = n; - } - - pub fn bound_poly_var_bot(&mut self, r: &Scalar) { - let n = self.len() / 2; - for i in 0..n { - self.Z[i] = self.Z[2 * i] + (self.Z[2 * i + 1] - self.Z[2 * i]) * r; - } - self.num_vars -= 1; - self.len = n; - } - - // returns Z(r) in O(n) time - pub fn evaluate(&self, r: &[Scalar]) -> Scalar { - // r must have a value for each variable - assert_eq!(r.len(), self.get_num_vars()); - let chis = EqPolynomial::new(r.to_vec()).evals(); - assert_eq!(chis.len(), self.Z.len()); - DotProductProofLog::compute_dotproduct(&self.Z, &chis) - } - - fn vec(&self) -> &Vec { - &self.Z - } - - pub fn extend(&mut self, other: &DensePolynomial) { - // TODO: allow extension even when some vars are bound - assert_eq!(self.Z.len(), self.len); - let other_vec = other.vec(); - assert_eq!(other_vec.len(), self.len); - self.Z.extend(other_vec); - self.num_vars += 1; - self.len *= 2; - assert_eq!(self.Z.len(), self.len); - } - - pub fn merge<'a, I>(polys: I) -> DensePolynomial - where - I: IntoIterator, - { - let mut Z: Vec = Vec::new(); - for poly in polys.into_iter() { - Z.extend(poly.vec()); - } - - // pad the polynomial with zero polynomial at the end - Z.resize(Z.len().next_power_of_two(), Scalar::zero()); - - DensePolynomial::new(Z) - } - - pub fn from_usize(Z: &[usize]) -> Self { - DensePolynomial::new( - (0..Z.len()) - .map(|i| Scalar::from(Z[i] as u64)) - .collect::>(), - ) - } + pub fn new(Z: Vec) -> Self { + DensePolynomial { + num_vars: Z.len().log_2(), + len: Z.len(), + Z, + } + } + + pub fn get_num_vars(&self) -> usize { + self.num_vars + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn clone(&self) -> DensePolynomial { + DensePolynomial::new(self.Z[0..self.len].to_vec()) + } + + pub fn split(&self, idx: usize) -> (DensePolynomial, DensePolynomial) { + assert!(idx < self.len()); + ( + DensePolynomial::new(self.Z[..idx].to_vec()), + DensePolynomial::new(self.Z[idx..2 * idx].to_vec()), + ) + } + + #[cfg(feature = "multicore")] + fn commit_inner(&self, blinds: &[Scalar], gens: &MultiCommitGens) -> PolyCommitment { + let L_size = blinds.len(); + let R_size = self.Z.len() / L_size; + assert_eq!(L_size * R_size, self.Z.len()); + let C = (0..L_size) + .into_par_iter() + .map(|i| { + self.Z[R_size * i..R_size * (i + 1)] + .commit(&blinds[i], gens) + .compress() + }) + .collect(); + PolyCommitment { C } + } + + #[cfg(not(feature = "multicore"))] + fn commit_inner(&self, blinds: &[Scalar], gens: &MultiCommitGens) -> PolyCommitment { + let L_size = blinds.len(); + let R_size = self.Z.len() / L_size; + assert_eq!(L_size * R_size, self.Z.len()); + let C = (0..L_size) + .map(|i| { + self.Z[R_size * i..R_size * (i + 1)] + .commit(&blinds[i], gens) + .compress() + }) + .collect(); + PolyCommitment { C } + } + + pub fn commit( + &self, + gens: &PolyCommitmentGens, + random_tape: Option<&mut RandomTape>, + ) -> (PolyCommitment, PolyCommitmentBlinds) { + let n = self.Z.len(); + let ell = self.get_num_vars(); + assert_eq!(n, ell.pow2()); + + let (left_num_vars, right_num_vars) = EqPolynomial::compute_factored_lens(ell); + let L_size = left_num_vars.pow2(); + let R_size = right_num_vars.pow2(); + assert_eq!(L_size * R_size, n); + + let blinds = if let Some(t) = random_tape { + PolyCommitmentBlinds { + blinds: t.random_vector(b"poly_blinds", L_size), + } + } else { + PolyCommitmentBlinds { + blinds: vec![Scalar::zero(); L_size], + } + }; + + (self.commit_inner(&blinds.blinds, &gens.gens.gens_n), blinds) + } + + pub fn bound(&self, L: &[Scalar]) -> Vec { + let (left_num_vars, right_num_vars) = + EqPolynomial::compute_factored_lens(self.get_num_vars()); + let L_size = left_num_vars.pow2(); + let R_size = right_num_vars.pow2(); + (0..R_size) + .map(|i| (0..L_size).map(|j| L[j] * self.Z[j * R_size + i]).sum()) + .collect() + } + + pub fn bound_poly_var_top(&mut self, r: &Scalar) { + let n = self.len() / 2; + for i in 0..n { + self.Z[i] = self.Z[i] + (self.Z[i + n] - self.Z[i]) * r; + } + self.num_vars -= 1; + self.len = n; + } + + pub fn bound_poly_var_bot(&mut self, r: &Scalar) { + let n = self.len() / 2; + for i in 0..n { + self.Z[i] = self.Z[2 * i] + (self.Z[2 * i + 1] - self.Z[2 * i]) * r; + } + self.num_vars -= 1; + self.len = n; + } + + // returns Z(r) in O(n) time + pub fn evaluate(&self, r: &[Scalar]) -> Scalar { + // r must have a value for each variable + assert_eq!(r.len(), self.get_num_vars()); + let chis = EqPolynomial::new(r.to_vec()).evals(); + assert_eq!(chis.len(), self.Z.len()); + DotProductProofLog::compute_dotproduct(&self.Z, &chis) + } + + fn vec(&self) -> &Vec { + &self.Z + } + + pub fn extend(&mut self, other: &DensePolynomial) { + // TODO: allow extension even when some vars are bound + assert_eq!(self.Z.len(), self.len); + let other_vec = other.vec(); + assert_eq!(other_vec.len(), self.len); + self.Z.extend(other_vec); + self.num_vars += 1; + self.len *= 2; + assert_eq!(self.Z.len(), self.len); + } + + pub fn merge<'a, I>(polys: I) -> DensePolynomial + where + I: IntoIterator, + { + let mut Z: Vec = Vec::new(); + for poly in polys.into_iter() { + Z.extend(poly.vec()); + } + + // pad the polynomial with zero polynomial at the end + Z.resize(Z.len().next_power_of_two(), Scalar::zero()); + + DensePolynomial::new(Z) + } + + pub fn from_usize(Z: &[usize]) -> Self { + DensePolynomial::new( + (0..Z.len()) + .map(|i| Scalar::from(Z[i] as u64)) + .collect::>(), + ) + } } impl Index for DensePolynomial { - type Output = Scalar; + type Output = Scalar; - #[inline(always)] - fn index(&self, _index: usize) -> &Scalar { - &(self.Z[_index]) - } + #[inline(always)] + fn index(&self, _index: usize) -> &Scalar { + &(self.Z[_index]) + } } impl AppendToTranscript for PolyCommitment { - fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { - transcript.append_message(label, b"poly_commitment_begin"); - for i in 0..self.C.len() { - transcript.append_point(b"poly_commitment_share", &self.C[i]); + fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { + transcript.append_message(label, b"poly_commitment_begin"); + for i in 0..self.C.len() { + transcript.append_point(b"poly_commitment_share", &self.C[i]); + } + transcript.append_message(label, b"poly_commitment_end"); } - transcript.append_message(label, b"poly_commitment_end"); - } } impl AppendToPoseidon for PolyCommitment { - fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { - for i in 0..self.C.len() { - transcript.append_point(&self.C[i]); + fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { + for i in 0..self.C.len() { + transcript.append_point(&self.C[i]); + } } - } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct PolyEvalProof { - proof: DotProductProofLog, + proof: DotProductProofLog, } impl PolyEvalProof { - fn protocol_name() -> &'static [u8] { - b"polynomial evaluation proof" - } - - pub fn prove( - poly: &DensePolynomial, - blinds_opt: Option<&PolyCommitmentBlinds>, - r: &[Scalar], // point at which the polynomial is evaluated - Zr: &Scalar, // evaluation of \widetilde{Z}(r) - blind_Zr_opt: Option<&Scalar>, // specifies a blind for Zr - gens: &PolyCommitmentGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - ) -> (PolyEvalProof, CompressedGroup) { - // transcript.append_protocol_name(PolyEvalProof::protocol_name()); - - // assert vectors are of the right size - assert_eq!(poly.get_num_vars(), r.len()); - - let (left_num_vars, right_num_vars) = EqPolynomial::compute_factored_lens(r.len()); - let L_size = left_num_vars.pow2(); - let R_size = right_num_vars.pow2(); - - let default_blinds = PolyCommitmentBlinds { - blinds: vec![Scalar::zero(); L_size], - }; - let blinds = blinds_opt.map_or(&default_blinds, |p| p); - - assert_eq!(blinds.blinds.len(), L_size); - - let zero = Scalar::zero(); - let blind_Zr = blind_Zr_opt.map_or(&zero, |p| p); - - // compute the L and R vectors - let eq = EqPolynomial::new(r.to_vec()); - let (L, R) = eq.compute_factored_evals(); - assert_eq!(L.len(), L_size); - assert_eq!(R.len(), R_size); - - // compute the vector underneath L*Z and the L*blinds - // compute vector-matrix product between L and Z viewed as a matrix - let LZ = poly.bound(&L); - let LZ_blind: Scalar = (0..L.len()).map(|i| blinds.blinds[i] * L[i]).sum(); - - // a dot product proof of size R_size - let (proof, _C_LR, C_Zr_prime) = DotProductProofLog::prove( - &gens.gens, - transcript, - random_tape, - &LZ, - &LZ_blind, - &R, - Zr, - blind_Zr, - ); - - (PolyEvalProof { proof }, C_Zr_prime) - } - - pub fn verify( - &self, - gens: &PolyCommitmentGens, - transcript: &mut PoseidonTranscript, - r: &[Scalar], // point at which the polynomial is evaluated - C_Zr: &CompressedGroup, // commitment to \widetilde{Z}(r) - comm: &PolyCommitment, - ) -> Result<(), ProofVerifyError> { - // transcript.append_protocol_name(PolyEvalProof::protocol_name()); - - // compute L and R - let eq = EqPolynomial::new(r.to_vec()); - let (L, R) = eq.compute_factored_evals(); - - // compute a weighted sum of commitments and L - let C_decompressed = comm - .C - .iter() - .map(|pt| GroupElement::decompress(pt).unwrap()) - .collect::>(); - - let C_LZ = GroupElement::vartime_multiscalar_mul(&L, C_decompressed.as_slice()).compress(); - - self - .proof - .verify(R.len(), &gens.gens, transcript, &R, &C_LZ, C_Zr) - } - - pub fn verify_plain( - &self, - gens: &PolyCommitmentGens, - transcript: &mut PoseidonTranscript, - r: &[Scalar], // point at which the polynomial is evaluated - Zr: &Scalar, // evaluation \widetilde{Z}(r) - comm: &PolyCommitment, - ) -> Result<(), ProofVerifyError> { - // compute a commitment to Zr with a blind of zero - let C_Zr = Zr.commit(&Scalar::zero(), &gens.gens.gens_1).compress(); - - self.verify(gens, transcript, r, &C_Zr, comm) - } + fn protocol_name() -> &'static [u8] { + b"polynomial evaluation proof" + } + + pub fn prove( + poly: &DensePolynomial, + blinds_opt: Option<&PolyCommitmentBlinds>, + r: &[Scalar], // point at which the polynomial is evaluated + Zr: &Scalar, // evaluation of \widetilde{Z}(r) + blind_Zr_opt: Option<&Scalar>, // specifies a blind for Zr + gens: &PolyCommitmentGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + ) -> (PolyEvalProof, CompressedGroup) { + // transcript.append_protocol_name(PolyEvalProof::protocol_name()); + + // assert vectors are of the right size + assert_eq!(poly.get_num_vars(), r.len()); + + let (left_num_vars, right_num_vars) = EqPolynomial::compute_factored_lens(r.len()); + let L_size = left_num_vars.pow2(); + let R_size = right_num_vars.pow2(); + + let default_blinds = PolyCommitmentBlinds { + blinds: vec![Scalar::zero(); L_size], + }; + let blinds = blinds_opt.map_or(&default_blinds, |p| p); + + assert_eq!(blinds.blinds.len(), L_size); + + let zero = Scalar::zero(); + let blind_Zr = blind_Zr_opt.map_or(&zero, |p| p); + + // compute the L and R vectors + let eq = EqPolynomial::new(r.to_vec()); + let (L, R) = eq.compute_factored_evals(); + assert_eq!(L.len(), L_size); + assert_eq!(R.len(), R_size); + + // compute the vector underneath L*Z and the L*blinds + // compute vector-matrix product between L and Z viewed as a matrix + let LZ = poly.bound(&L); + let LZ_blind: Scalar = (0..L.len()).map(|i| blinds.blinds[i] * L[i]).sum(); + + // a dot product proof of size R_size + let (proof, _C_LR, C_Zr_prime) = DotProductProofLog::prove( + &gens.gens, + transcript, + random_tape, + &LZ, + &LZ_blind, + &R, + Zr, + blind_Zr, + ); + + (PolyEvalProof { proof }, C_Zr_prime) + } + + pub fn verify( + &self, + gens: &PolyCommitmentGens, + transcript: &mut PoseidonTranscript, + r: &[Scalar], // point at which the polynomial is evaluated + C_Zr: &CompressedGroup, // commitment to \widetilde{Z}(r) + comm: &PolyCommitment, + ) -> Result<(), ProofVerifyError> { + // transcript.append_protocol_name(PolyEvalProof::protocol_name()); + + // compute L and R + let eq = EqPolynomial::new(r.to_vec()); + let (L, R) = eq.compute_factored_evals(); + + // compute a weighted sum of commitments and L + let C_decompressed = comm + .C + .iter() + .map(|pt| GroupElement::decompress(pt).unwrap()) + .collect::>(); + + let C_LZ = GroupElement::vartime_multiscalar_mul(&L, C_decompressed.as_slice()).compress(); + + self.proof + .verify(R.len(), &gens.gens, transcript, &R, &C_LZ, C_Zr) + } + + pub fn verify_plain( + &self, + gens: &PolyCommitmentGens, + transcript: &mut PoseidonTranscript, + r: &[Scalar], // point at which the polynomial is evaluated + Zr: &Scalar, // evaluation \widetilde{Z}(r) + comm: &PolyCommitment, + ) -> Result<(), ProofVerifyError> { + // compute a commitment to Zr with a blind of zero + let C_Zr = Zr.commit(&Scalar::zero(), &gens.gens.gens_1).compress(); + + self.verify(gens, transcript, r, &C_Zr, comm) + } } #[cfg(test)] mod tests { - use crate::parameters::poseidon_params; - - use super::*; - use ark_std::UniformRand; - - fn evaluate_with_LR(Z: &[Scalar], r: &[Scalar]) -> Scalar { - let eq = EqPolynomial::new(r.to_vec()); - let (L, R) = eq.compute_factored_evals(); - - let ell = r.len(); - // ensure ell is even - assert!(ell % 2 == 0); - // compute n = 2^\ell - let n = ell.pow2(); - // compute m = sqrt(n) = 2^{\ell/2} - let m = n.square_root(); - - // compute vector-matrix product between L and Z viewed as a matrix - let LZ = (0..m) - .map(|i| (0..m).map(|j| L[j] * Z[j * m + i]).sum()) - .collect::>(); - - // compute dot product between LZ and R - DotProductProofLog::compute_dotproduct(&LZ, &R) - } - - #[test] - fn check_polynomial_evaluation() { - // Z = [1, 2, 1, 4] - let Z = vec![ - Scalar::one(), - Scalar::from(2), - Scalar::from(1), - Scalar::from(4), - ]; - - // r = [4,3] - let r = vec![Scalar::from(4), Scalar::from(3)]; - - let eval_with_LR = evaluate_with_LR(&Z, &r); - let poly = DensePolynomial::new(Z); - - let eval = poly.evaluate(&r); - assert_eq!(eval, Scalar::from(28)); - assert_eq!(eval_with_LR, eval); - } - - pub fn compute_factored_chis_at_r(r: &[Scalar]) -> (Vec, Vec) { - let mut L: Vec = Vec::new(); - let mut R: Vec = Vec::new(); - - let ell = r.len(); - assert!(ell % 2 == 0); // ensure ell is even - let n = ell.pow2(); - let m = n.square_root(); - - // compute row vector L - for i in 0..m { - let mut chi_i = Scalar::one(); - for j in 0..ell / 2 { - let bit_j = ((m * i) & (1 << (r.len() - j - 1))) > 0; - if bit_j { - chi_i *= r[j]; - } else { - chi_i *= Scalar::one() - r[j]; + use crate::parameters::poseidon_params; + + use super::*; + use ark_std::UniformRand; + + fn evaluate_with_LR(Z: &[Scalar], r: &[Scalar]) -> Scalar { + let eq = EqPolynomial::new(r.to_vec()); + let (L, R) = eq.compute_factored_evals(); + + let ell = r.len(); + // ensure ell is even + assert!(ell % 2 == 0); + // compute n = 2^\ell + let n = ell.pow2(); + // compute m = sqrt(n) = 2^{\ell/2} + let m = n.square_root(); + + // compute vector-matrix product between L and Z viewed as a matrix + let LZ = (0..m) + .map(|i| (0..m).map(|j| L[j] * Z[j * m + i]).sum()) + .collect::>(); + + // compute dot product between LZ and R + DotProductProofLog::compute_dotproduct(&LZ, &R) + } + + #[test] + fn check_polynomial_evaluation() { + // Z = [1, 2, 1, 4] + let Z = vec![ + Scalar::one(), + Scalar::from(2), + Scalar::from(1), + Scalar::from(4), + ]; + + // r = [4,3] + let r = vec![Scalar::from(4), Scalar::from(3)]; + + let eval_with_LR = evaluate_with_LR(&Z, &r); + let poly = DensePolynomial::new(Z); + + let eval = poly.evaluate(&r); + assert_eq!(eval, Scalar::from(28)); + assert_eq!(eval_with_LR, eval); + } + + pub fn compute_factored_chis_at_r(r: &[Scalar]) -> (Vec, Vec) { + let mut L: Vec = Vec::new(); + let mut R: Vec = Vec::new(); + + let ell = r.len(); + assert!(ell % 2 == 0); // ensure ell is even + let n = ell.pow2(); + let m = n.square_root(); + + // compute row vector L + for i in 0..m { + let mut chi_i = Scalar::one(); + for j in 0..ell / 2 { + let bit_j = ((m * i) & (1 << (r.len() - j - 1))) > 0; + if bit_j { + chi_i *= r[j]; + } else { + chi_i *= Scalar::one() - r[j]; + } + } + L.push(chi_i); + } + + // compute column vector R + for i in 0..m { + let mut chi_i = Scalar::one(); + for j in ell / 2..ell { + let bit_j = (i & (1 << (r.len() - j - 1))) > 0; + if bit_j { + chi_i *= r[j]; + } else { + chi_i *= Scalar::one() - r[j]; + } + } + R.push(chi_i); } - } - L.push(chi_i); + (L, R) } - // compute column vector R - for i in 0..m { - let mut chi_i = Scalar::one(); - for j in ell / 2..ell { - let bit_j = (i & (1 << (r.len() - j - 1))) > 0; - if bit_j { - chi_i *= r[j]; - } else { - chi_i *= Scalar::one() - r[j]; + pub fn compute_chis_at_r(r: &[Scalar]) -> Vec { + let ell = r.len(); + let n = ell.pow2(); + let mut chis: Vec = Vec::new(); + for i in 0..n { + let mut chi_i = Scalar::one(); + for j in 0..r.len() { + let bit_j = (i & (1 << (r.len() - j - 1))) > 0; + if bit_j { + chi_i *= r[j]; + } else { + chi_i *= Scalar::one() - r[j]; + } + } + chis.push(chi_i); } - } - R.push(chi_i); - } - (L, R) - } - - pub fn compute_chis_at_r(r: &[Scalar]) -> Vec { - let ell = r.len(); - let n = ell.pow2(); - let mut chis: Vec = Vec::new(); - for i in 0..n { - let mut chi_i = Scalar::one(); - for j in 0..r.len() { - let bit_j = (i & (1 << (r.len() - j - 1))) > 0; - if bit_j { - chi_i *= r[j]; - } else { - chi_i *= Scalar::one() - r[j]; + chis + } + + pub fn compute_outerproduct(L: Vec, R: Vec) -> Vec { + assert_eq!(L.len(), R.len()); + (0..L.len()) + .map(|i| (0..R.len()).map(|j| L[i] * R[j]).collect::>()) + .collect::>>() + .into_iter() + .flatten() + .collect::>() + } + + #[test] + fn check_memoized_chis() { + let mut rng = ark_std::rand::thread_rng(); + + let s = 10; + let mut r: Vec = Vec::new(); + for _i in 0..s { + r.push(Scalar::rand(&mut rng)); + } + let chis = tests::compute_chis_at_r(&r); + let chis_m = EqPolynomial::new(r).evals(); + assert_eq!(chis, chis_m); + } + + #[test] + fn check_factored_chis() { + let mut rng = ark_std::rand::thread_rng(); + + let s = 10; + let mut r: Vec = Vec::new(); + for _i in 0..s { + r.push(Scalar::rand(&mut rng)); } - } - chis.push(chi_i); - } - chis - } - - pub fn compute_outerproduct(L: Vec, R: Vec) -> Vec { - assert_eq!(L.len(), R.len()); - (0..L.len()) - .map(|i| (0..R.len()).map(|j| L[i] * R[j]).collect::>()) - .collect::>>() - .into_iter() - .flatten() - .collect::>() - } - - #[test] - fn check_memoized_chis() { - let mut rng = ark_std::rand::thread_rng(); - - let s = 10; - let mut r: Vec = Vec::new(); - for _i in 0..s { - r.push(Scalar::rand(&mut rng)); - } - let chis = tests::compute_chis_at_r(&r); - let chis_m = EqPolynomial::new(r).evals(); - assert_eq!(chis, chis_m); - } - - #[test] - fn check_factored_chis() { - let mut rng = ark_std::rand::thread_rng(); - - let s = 10; - let mut r: Vec = Vec::new(); - for _i in 0..s { - r.push(Scalar::rand(&mut rng)); - } - let chis = EqPolynomial::new(r.clone()).evals(); - let (L, R) = EqPolynomial::new(r).compute_factored_evals(); - let O = compute_outerproduct(L, R); - assert_eq!(chis, O); - } - - #[test] - fn check_memoized_factored_chis() { - let mut rng = ark_std::rand::thread_rng(); - - let s = 10; - let mut r: Vec = Vec::new(); - for _i in 0..s { - r.push(Scalar::rand(&mut rng)); - } - let (L, R) = tests::compute_factored_chis_at_r(&r); - let eq = EqPolynomial::new(r); - let (L2, R2) = eq.compute_factored_evals(); - assert_eq!(L, L2); - assert_eq!(R, R2); - } - - #[test] - fn check_polynomial_commit() { - let Z = vec![ - Scalar::from(1), - Scalar::from(2), - Scalar::from(1), - Scalar::from(4), - ]; - let poly = DensePolynomial::new(Z); - - // r = [4,3] - let r = vec![Scalar::from(4), Scalar::from(3)]; - let eval = poly.evaluate(&r); - assert_eq!(eval, Scalar::from(28)); - - let gens = PolyCommitmentGens::new(poly.get_num_vars(), b"test-two"); - let (poly_commitment, blinds) = poly.commit(&gens, None); - - let mut random_tape = RandomTape::new(b"proof"); - let params = poseidon_params(); - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let (proof, C_Zr) = PolyEvalProof::prove( - &poly, - Some(&blinds), - &r, - &eval, - None, - &gens, - &mut prover_transcript, - &mut random_tape, - ); - - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify(&gens, &mut verifier_transcript, &r, &C_Zr, &poly_commitment) - .is_ok()); - } + let chis = EqPolynomial::new(r.clone()).evals(); + let (L, R) = EqPolynomial::new(r).compute_factored_evals(); + let O = compute_outerproduct(L, R); + assert_eq!(chis, O); + } + + #[test] + fn check_memoized_factored_chis() { + let mut rng = ark_std::rand::thread_rng(); + + let s = 10; + let mut r: Vec = Vec::new(); + for _i in 0..s { + r.push(Scalar::rand(&mut rng)); + } + let (L, R) = tests::compute_factored_chis_at_r(&r); + let eq = EqPolynomial::new(r); + let (L2, R2) = eq.compute_factored_evals(); + assert_eq!(L, L2); + assert_eq!(R, R2); + } + + #[test] + fn check_polynomial_commit() { + let Z = vec![ + Scalar::from(1), + Scalar::from(2), + Scalar::from(1), + Scalar::from(4), + ]; + let poly = DensePolynomial::new(Z); + + // r = [4,3] + let r = vec![Scalar::from(4), Scalar::from(3)]; + let eval = poly.evaluate(&r); + assert_eq!(eval, Scalar::from(28)); + + let gens = PolyCommitmentGens::new(poly.get_num_vars(), b"test-two"); + let (poly_commitment, blinds) = poly.commit(&gens, None); + + let mut random_tape = RandomTape::new(b"proof"); + let params = poseidon_params(); + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let (proof, C_Zr) = PolyEvalProof::prove( + &poly, + Some(&blinds), + &r, + &eval, + None, + &gens, + &mut prover_transcript, + &mut random_tape, + ); + + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify(&gens, &mut verifier_transcript, &r, &C_Zr, &poly_commitment) + .is_ok()); + } } diff --git a/src/errors.rs b/src/errors.rs index 2a7dbe9..1dce8e2 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -3,30 +3,30 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum ProofVerifyError { - #[error("Proof verification failed")] - InternalError, - #[error("Compressed group element failed to decompress: {0:?}")] - DecompressionError(Vec), + #[error("Proof verification failed")] + InternalError, + #[error("Compressed group element failed to decompress: {0:?}")] + DecompressionError(Vec), } impl Default for ProofVerifyError { - fn default() -> Self { - ProofVerifyError::InternalError - } + fn default() -> Self { + ProofVerifyError::InternalError + } } #[derive(Clone, Debug, Eq, PartialEq)] pub enum R1CSError { - /// returned if the number of constraints is not a power of 2 - NonPowerOfTwoCons, - /// returned if the number of variables is not a power of 2 - NonPowerOfTwoVars, - /// returned if a wrong number of inputs in an assignment are supplied - InvalidNumberOfInputs, - /// returned if a wrong number of variables in an assignment are supplied - InvalidNumberOfVars, - /// returned if a [u8;32] does not parse into a valid Scalar in the field of ristretto255 - InvalidScalar, - /// returned if the supplied row or col in (row,col,val) tuple is out of range - InvalidIndex, + /// returned if the number of constraints is not a power of 2 + NonPowerOfTwoCons, + /// returned if the number of variables is not a power of 2 + NonPowerOfTwoVars, + /// returned if a wrong number of inputs in an assignment are supplied + InvalidNumberOfInputs, + /// returned if a wrong number of variables in an assignment are supplied + InvalidNumberOfVars, + /// returned if a [u8;32] does not parse into a valid Scalar in the field of ristretto255 + InvalidScalar, + /// returned if the supplied row or col in (row,col,val) tuple is out of range + InvalidIndex, } diff --git a/src/group.rs b/src/group.rs index 8e0baa9..0b5087c 100644 --- a/src/group.rs +++ b/src/group.rs @@ -19,62 +19,62 @@ pub type Fr = ark_bls12_377::Fr; pub struct CompressedGroup(pub Vec); lazy_static! { - pub static ref GROUP_BASEPOINT: GroupElement = GroupElement::prime_subgroup_generator(); + pub static ref GROUP_BASEPOINT: GroupElement = GroupElement::prime_subgroup_generator(); } pub trait CompressGroupElement { - fn compress(&self) -> CompressedGroup; + fn compress(&self) -> CompressedGroup; } pub trait DecompressGroupElement { - fn decompress(encoded: &CompressedGroup) -> Option; + fn decompress(encoded: &CompressedGroup) -> Option; } pub trait UnpackGroupElement { - fn unpack(&self) -> Result; + fn unpack(&self) -> Result; } impl CompressGroupElement for GroupElement { - fn compress(&self) -> CompressedGroup { - let mut point_encoding = Vec::new(); - self.serialize(&mut point_encoding).unwrap(); - CompressedGroup(point_encoding) - } + fn compress(&self) -> CompressedGroup { + let mut point_encoding = Vec::new(); + self.serialize(&mut point_encoding).unwrap(); + CompressedGroup(point_encoding) + } } impl DecompressGroupElement for GroupElement { - fn decompress(encoded: &CompressedGroup) -> Option { - let res = GroupElement::deserialize(&*encoded.0); - if let Ok(r) = res { - Some(r) - } else { - println!("{:?}", res); - None + fn decompress(encoded: &CompressedGroup) -> Option { + let res = GroupElement::deserialize(&*encoded.0); + if let Ok(r) = res { + Some(r) + } else { + println!("{:?}", res); + None + } } - } } impl UnpackGroupElement for CompressedGroup { - fn unpack(&self) -> Result { - let encoded = self.0.clone(); - GroupElement::decompress(self).ok_or(ProofVerifyError::DecompressionError(encoded)) - } + fn unpack(&self) -> Result { + let encoded = self.0.clone(); + GroupElement::decompress(self).ok_or(ProofVerifyError::DecompressionError(encoded)) + } } pub trait VartimeMultiscalarMul { - fn vartime_multiscalar_mul(scalars: &[Scalar], points: &[GroupElement]) -> GroupElement; + fn vartime_multiscalar_mul(scalars: &[Scalar], points: &[GroupElement]) -> GroupElement; } impl VartimeMultiscalarMul for GroupElement { - fn vartime_multiscalar_mul(scalars: &[Scalar], points: &[GroupElement]) -> GroupElement { - let repr_scalars = scalars - .iter() - .map(|S| S.borrow().into_repr()) - .collect::::BigInt>>(); - let aff_points = points - .iter() - .map(|P| P.borrow().into_affine()) - .collect::>(); - VariableBaseMSM::multi_scalar_mul(aff_points.as_slice(), repr_scalars.as_slice()) - } + fn vartime_multiscalar_mul(scalars: &[Scalar], points: &[GroupElement]) -> GroupElement { + let repr_scalars = scalars + .iter() + .map(|S| S.borrow().into_repr()) + .collect::::BigInt>>(); + let aff_points = points + .iter() + .map(|P| P.borrow().into_affine()) + .collect::>(); + VariableBaseMSM::multi_scalar_mul(aff_points.as_slice(), repr_scalars.as_slice()) + } } diff --git a/src/lib.rs b/src/lib.rs index fc53389..bb3a7ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,7 +49,7 @@ use errors::{ProofVerifyError, R1CSError}; use poseidon_transcript::{AppendToPoseidon, PoseidonTranscript}; use r1csinstance::{ - R1CSCommitment, R1CSCommitmentGens, R1CSDecommitment, R1CSEvalProof, R1CSInstance, + R1CSCommitment, R1CSCommitmentGens, R1CSDecommitment, R1CSEvalProof, R1CSInstance, }; use r1csproof::{R1CSGens, R1CSProof}; use random::RandomTape; @@ -59,63 +59,63 @@ use timer::Timer; /// `ComputationCommitment` holds a public preprocessed NP statement (e.g., R1CS) pub struct ComputationCommitment { - comm: R1CSCommitment, + comm: R1CSCommitment, } /// `ComputationDecommitment` holds information to decommit `ComputationCommitment` pub struct ComputationDecommitment { - decomm: R1CSDecommitment, + decomm: R1CSDecommitment, } /// `Assignment` holds an assignment of values to either the inputs or variables in an `Instance` #[derive(Clone)] pub struct Assignment { - assignment: Vec, + assignment: Vec, } impl Assignment { - /// Constructs a new `Assignment` from a vector - pub fn new(assignment: &Vec>) -> Result { - let bytes_to_scalar = |vec: &Vec>| -> Result, R1CSError> { - let mut vec_scalar: Vec = Vec::new(); - for v in vec { - let val = Scalar::from_random_bytes(v.as_slice()); - if let Some(v) = val { - vec_scalar.push(v); - } else { - return Err(R1CSError::InvalidScalar); - } - } - Ok(vec_scalar) - }; + /// Constructs a new `Assignment` from a vector + pub fn new(assignment: &Vec>) -> Result { + let bytes_to_scalar = |vec: &Vec>| -> Result, R1CSError> { + let mut vec_scalar: Vec = Vec::new(); + for v in vec { + let val = Scalar::from_random_bytes(v.as_slice()); + if let Some(v) = val { + vec_scalar.push(v); + } else { + return Err(R1CSError::InvalidScalar); + } + } + Ok(vec_scalar) + }; - let assignment_scalar = bytes_to_scalar(assignment); + let assignment_scalar = bytes_to_scalar(assignment); - // check for any parsing errors - if assignment_scalar.is_err() { - return Err(R1CSError::InvalidScalar); - } + // check for any parsing errors + if assignment_scalar.is_err() { + return Err(R1CSError::InvalidScalar); + } - Ok(Assignment { - assignment: assignment_scalar.unwrap(), - }) - } + Ok(Assignment { + assignment: assignment_scalar.unwrap(), + }) + } - /// pads Assignment to the specified length - fn pad(&self, len: usize) -> VarsAssignment { - // check that the new length is higher than current length - assert!(len > self.assignment.len()); + /// pads Assignment to the specified length + fn pad(&self, len: usize) -> VarsAssignment { + // check that the new length is higher than current length + assert!(len > self.assignment.len()); - let padded_assignment = { - let mut padded_assignment = self.assignment.clone(); - padded_assignment.extend(vec![Scalar::zero(); len - self.assignment.len()]); - padded_assignment - }; + let padded_assignment = { + let mut padded_assignment = self.assignment.clone(); + padded_assignment.extend(vec![Scalar::zero(); len - self.assignment.len()]); + padded_assignment + }; - VarsAssignment { - assignment: padded_assignment, + VarsAssignment { + assignment: padded_assignment, + } } - } } /// `VarsAssignment` holds an assignment of values to variables in an `Instance` @@ -127,718 +127,717 @@ pub type InputsAssignment = Assignment; /// `Instance` holds the description of R1CS matrices and a hash of the matrices #[derive(Debug)] pub struct Instance { - inst: R1CSInstance, - digest: Vec, + inst: R1CSInstance, + digest: Vec, } impl Instance { - /// Constructs a new `Instance` and an associated satisfying assignment - pub fn new( - num_cons: usize, - num_vars: usize, - num_inputs: usize, - A: &[(usize, usize, Vec)], - B: &[(usize, usize, Vec)], - C: &[(usize, usize, Vec)], - ) -> Result { - let (num_vars_padded, num_cons_padded) = { - let num_vars_padded = { - let mut num_vars_padded = num_vars; - - // ensure that num_inputs + 1 <= num_vars - num_vars_padded = max(num_vars_padded, num_inputs + 1); - - // ensure that num_vars_padded a power of two - if num_vars_padded.next_power_of_two() != num_vars_padded { - num_vars_padded = num_vars_padded.next_power_of_two(); - } - num_vars_padded - }; - - let num_cons_padded = { - let mut num_cons_padded = num_cons; + /// Constructs a new `Instance` and an associated satisfying assignment + pub fn new( + num_cons: usize, + num_vars: usize, + num_inputs: usize, + A: &[(usize, usize, Vec)], + B: &[(usize, usize, Vec)], + C: &[(usize, usize, Vec)], + ) -> Result { + let (num_vars_padded, num_cons_padded) = { + let num_vars_padded = { + let mut num_vars_padded = num_vars; + + // ensure that num_inputs + 1 <= num_vars + num_vars_padded = max(num_vars_padded, num_inputs + 1); + + // ensure that num_vars_padded a power of two + if num_vars_padded.next_power_of_two() != num_vars_padded { + num_vars_padded = num_vars_padded.next_power_of_two(); + } + num_vars_padded + }; + + let num_cons_padded = { + let mut num_cons_padded = num_cons; + + // ensure that num_cons_padded is at least 2 + if num_cons_padded == 0 || num_cons_padded == 1 { + num_cons_padded = 2; + } + + // ensure that num_cons_padded is power of 2 + if num_cons.next_power_of_two() != num_cons { + num_cons_padded = num_cons.next_power_of_two(); + } + num_cons_padded + }; + + (num_vars_padded, num_cons_padded) + }; - // ensure that num_cons_padded is at least 2 - if num_cons_padded == 0 || num_cons_padded == 1 { - num_cons_padded = 2; + let bytes_to_scalar = + |tups: &[(usize, usize, Vec)]| -> Result, R1CSError> { + let mut mat: Vec<(usize, usize, Scalar)> = Vec::new(); + for (row, col, val_bytes) in tups { + // row must be smaller than num_cons + if *row >= num_cons { + return Err(R1CSError::InvalidIndex); + } + + // col must be smaller than num_vars + 1 + num_inputs + if *col >= num_vars + 1 + num_inputs { + return Err(R1CSError::InvalidIndex); + } + + let val = Scalar::from_random_bytes(val_bytes.as_slice()); + if let Some(v) = val { + // if col >= num_vars, it means that it is referencing a 1 or input in the satisfying + // assignment + if *col >= num_vars { + mat.push((*row, *col + num_vars_padded - num_vars, v)); + } else { + mat.push((*row, *col, v)); + } + } else { + return Err(R1CSError::InvalidScalar); + } + } + + // pad with additional constraints up until num_cons_padded if the original constraints were 0 or 1 + // we do not need to pad otherwise because the dummy constraints are implicit in the sum-check protocol + if num_cons == 0 || num_cons == 1 { + for i in tups.len()..num_cons_padded { + mat.push((i, num_vars, Scalar::zero())); + } + } + + Ok(mat) + }; + + let A_scalar = bytes_to_scalar(A); + if A_scalar.is_err() { + return Err(A_scalar.err().unwrap()); } - // ensure that num_cons_padded is power of 2 - if num_cons.next_power_of_two() != num_cons { - num_cons_padded = num_cons.next_power_of_two(); - } - num_cons_padded - }; - - (num_vars_padded, num_cons_padded) - }; - - let bytes_to_scalar = - |tups: &[(usize, usize, Vec)]| -> Result, R1CSError> { - let mut mat: Vec<(usize, usize, Scalar)> = Vec::new(); - for (row, col, val_bytes) in tups { - // row must be smaller than num_cons - if *row >= num_cons { - return Err(R1CSError::InvalidIndex); - } - - // col must be smaller than num_vars + 1 + num_inputs - if *col >= num_vars + 1 + num_inputs { - return Err(R1CSError::InvalidIndex); - } - - let val = Scalar::from_random_bytes(val_bytes.as_slice()); - if let Some(v) = val { - // if col >= num_vars, it means that it is referencing a 1 or input in the satisfying - // assignment - if *col >= num_vars { - mat.push((*row, *col + num_vars_padded - num_vars, v)); - } else { - mat.push((*row, *col, v)); - } - } else { - return Err(R1CSError::InvalidScalar); - } + let B_scalar = bytes_to_scalar(B); + if B_scalar.is_err() { + return Err(B_scalar.err().unwrap()); } - // pad with additional constraints up until num_cons_padded if the original constraints were 0 or 1 - // we do not need to pad otherwise because the dummy constraints are implicit in the sum-check protocol - if num_cons == 0 || num_cons == 1 { - for i in tups.len()..num_cons_padded { - mat.push((i, num_vars, Scalar::zero())); - } + let C_scalar = bytes_to_scalar(C); + if C_scalar.is_err() { + return Err(C_scalar.err().unwrap()); } - Ok(mat) - }; + let inst = R1CSInstance::new( + num_cons_padded, + num_vars_padded, + num_inputs, + &A_scalar.unwrap(), + &B_scalar.unwrap(), + &C_scalar.unwrap(), + ); - let A_scalar = bytes_to_scalar(A); - if A_scalar.is_err() { - return Err(A_scalar.err().unwrap()); - } + let digest = inst.get_digest(); - let B_scalar = bytes_to_scalar(B); - if B_scalar.is_err() { - return Err(B_scalar.err().unwrap()); + Ok(Instance { inst, digest }) } - let C_scalar = bytes_to_scalar(C); - if C_scalar.is_err() { - return Err(C_scalar.err().unwrap()); - } + /// Checks if a given R1CSInstance is satisfiable with a given variables and inputs assignments + pub fn is_sat( + &self, + vars: &VarsAssignment, + inputs: &InputsAssignment, + ) -> Result { + if vars.assignment.len() > self.inst.get_num_vars() { + return Err(R1CSError::InvalidNumberOfInputs); + } - let inst = R1CSInstance::new( - num_cons_padded, - num_vars_padded, - num_inputs, - &A_scalar.unwrap(), - &B_scalar.unwrap(), - &C_scalar.unwrap(), - ); - - let digest = inst.get_digest(); - - Ok(Instance { inst, digest }) - } - - /// Checks if a given R1CSInstance is satisfiable with a given variables and inputs assignments - pub fn is_sat( - &self, - vars: &VarsAssignment, - inputs: &InputsAssignment, - ) -> Result { - if vars.assignment.len() > self.inst.get_num_vars() { - return Err(R1CSError::InvalidNumberOfInputs); - } + if inputs.assignment.len() != self.inst.get_num_inputs() { + return Err(R1CSError::InvalidNumberOfInputs); + } + + // we might need to pad variables + let padded_vars = { + let num_padded_vars = self.inst.get_num_vars(); + let num_vars = vars.assignment.len(); + if num_padded_vars > num_vars { + vars.pad(num_padded_vars) + } else { + vars.clone() + } + }; - if inputs.assignment.len() != self.inst.get_num_inputs() { - return Err(R1CSError::InvalidNumberOfInputs); + Ok(self + .inst + .is_sat(&padded_vars.assignment, &inputs.assignment)) } - // we might need to pad variables - let padded_vars = { - let num_padded_vars = self.inst.get_num_vars(); - let num_vars = vars.assignment.len(); - if num_padded_vars > num_vars { - vars.pad(num_padded_vars) - } else { - vars.clone() - } - }; - - Ok( - self - .inst - .is_sat(&padded_vars.assignment, &inputs.assignment), - ) - } - - /// Constructs a new synthetic R1CS `Instance` and an associated satisfying assignment - pub fn produce_synthetic_r1cs( - num_cons: usize, - num_vars: usize, - num_inputs: usize, - ) -> (Instance, VarsAssignment, InputsAssignment) { - let (inst, vars, inputs) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let digest = inst.get_digest(); - ( - Instance { inst, digest }, - VarsAssignment { assignment: vars }, - InputsAssignment { assignment: inputs }, - ) - } + /// Constructs a new synthetic R1CS `Instance` and an associated satisfying assignment + pub fn produce_synthetic_r1cs( + num_cons: usize, + num_vars: usize, + num_inputs: usize, + ) -> (Instance, VarsAssignment, InputsAssignment) { + let (inst, vars, inputs) = + R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + let digest = inst.get_digest(); + ( + Instance { inst, digest }, + VarsAssignment { assignment: vars }, + InputsAssignment { assignment: inputs }, + ) + } } /// `SNARKGens` holds public parameters for producing and verifying proofs with the Spartan SNARK pub struct SNARKGens { - gens_r1cs_sat: R1CSGens, - gens_r1cs_eval: R1CSCommitmentGens, + gens_r1cs_sat: R1CSGens, + gens_r1cs_eval: R1CSCommitmentGens, } impl SNARKGens { - /// Constructs a new `SNARKGens` given the size of the R1CS statement - /// `num_nz_entries` specifies the maximum number of non-zero entries in any of the three R1CS matrices - pub fn new(num_cons: usize, num_vars: usize, num_inputs: usize, num_nz_entries: usize) -> Self { - let num_vars_padded = { - let mut num_vars_padded = max(num_vars, num_inputs + 1); - if num_vars_padded != num_vars_padded.next_power_of_two() { - num_vars_padded = num_vars_padded.next_power_of_two(); - } - num_vars_padded - }; - - let gens_r1cs_sat = R1CSGens::new(b"gens_r1cs_sat", num_cons, num_vars_padded); - let gens_r1cs_eval = R1CSCommitmentGens::new( - b"gens_r1cs_eval", - num_cons, - num_vars_padded, - num_inputs, - num_nz_entries, - ); - SNARKGens { - gens_r1cs_sat, - gens_r1cs_eval, + /// Constructs a new `SNARKGens` given the size of the R1CS statement + /// `num_nz_entries` specifies the maximum number of non-zero entries in any of the three R1CS matrices + pub fn new(num_cons: usize, num_vars: usize, num_inputs: usize, num_nz_entries: usize) -> Self { + let num_vars_padded = { + let mut num_vars_padded = max(num_vars, num_inputs + 1); + if num_vars_padded != num_vars_padded.next_power_of_two() { + num_vars_padded = num_vars_padded.next_power_of_two(); + } + num_vars_padded + }; + + let gens_r1cs_sat = R1CSGens::new(b"gens_r1cs_sat", num_cons, num_vars_padded); + let gens_r1cs_eval = R1CSCommitmentGens::new( + b"gens_r1cs_eval", + num_cons, + num_vars_padded, + num_inputs, + num_nz_entries, + ); + SNARKGens { + gens_r1cs_sat, + gens_r1cs_eval, + } } - } } /// `SNARK` holds a proof produced by Spartan SNARK #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] pub struct SNARK { - r1cs_sat_proof: R1CSProof, - inst_evals: (Scalar, Scalar, Scalar), - r1cs_eval_proof: R1CSEvalProof, - rx: Vec, - ry: Vec, + r1cs_sat_proof: R1CSProof, + inst_evals: (Scalar, Scalar, Scalar), + r1cs_eval_proof: R1CSEvalProof, + rx: Vec, + ry: Vec, } impl SNARK { - fn protocol_name() -> &'static [u8] { - b"Spartan SNARK proof" - } - - /// A public computation to create a commitment to an R1CS instance - pub fn encode( - inst: &Instance, - gens: &SNARKGens, - ) -> (ComputationCommitment, ComputationDecommitment) { - let timer_encode = Timer::new("SNARK::encode"); - let (comm, decomm) = inst.inst.commit(&gens.gens_r1cs_eval); - timer_encode.stop(); - ( - ComputationCommitment { comm }, - ComputationDecommitment { decomm }, - ) - } - - /// A method to produce a SNARK proof of the satisfiability of an R1CS instance - pub fn prove( - inst: &Instance, - comm: &ComputationCommitment, - decomm: &ComputationDecommitment, - vars: VarsAssignment, - inputs: &InputsAssignment, - gens: &SNARKGens, - transcript: &mut PoseidonTranscript, - ) -> Self { - let timer_prove = Timer::new("SNARK::prove"); - - // we create a Transcript object seeded with a random Scalar - // to aid the prover produce its randomness - let mut random_tape = RandomTape::new(b"proof"); - - // transcript.append_protocol_name(SNARK::protocol_name()); - comm.comm.append_to_poseidon(transcript); - - let (r1cs_sat_proof, rx, ry) = { - let (proof, rx, ry) = { - // we might need to pad variables - let padded_vars = { - let num_padded_vars = inst.inst.get_num_vars(); - let num_vars = vars.assignment.len(); - if num_padded_vars > num_vars { - vars.pad(num_padded_vars) - } else { - vars - } - }; + fn protocol_name() -> &'static [u8] { + b"Spartan SNARK proof" + } - R1CSProof::prove( - &inst.inst, - padded_vars.assignment, - &inputs.assignment, - &gens.gens_r1cs_sat, - transcript, - // &mut random_tape, + /// A public computation to create a commitment to an R1CS instance + pub fn encode( + inst: &Instance, + gens: &SNARKGens, + ) -> (ComputationCommitment, ComputationDecommitment) { + let timer_encode = Timer::new("SNARK::encode"); + let (comm, decomm) = inst.inst.commit(&gens.gens_r1cs_eval); + timer_encode.stop(); + ( + ComputationCommitment { comm }, + ComputationDecommitment { decomm }, ) - }; - - let mut proof_encoded: Vec = Vec::new(); - proof.serialize(&mut proof_encoded).unwrap(); - Timer::print(&format!("len_r1cs_sat_proof {:?}", proof_encoded.len())); - - (proof, rx, ry) - }; - - // We need to reset the transcript state before starting the evaluation - // proof and share this state with the verifier because, on the verifier's - // side all the previous updates are done on the transcript - // circuit variable and the transcript outside the circuit will be - // inconsistent wrt to the prover's. - transcript.new_from_state(&r1cs_sat_proof.transcript_sat_state); - - // We send evaluations of A, B, C at r = (rx, ry) as claims - // to enable the verifier complete the first sum-check - let timer_eval = Timer::new("eval_sparse_polys"); - let inst_evals = { - let (Ar, Br, Cr) = inst.inst.evaluate(&rx, &ry); - transcript.append_scalar(&Ar); - transcript.append_scalar(&Br); - transcript.append_scalar(&Cr); - (Ar, Br, Cr) - }; - timer_eval.stop(); - - let r1cs_eval_proof = { - let proof = R1CSEvalProof::prove( - &decomm.decomm, - &rx, - &ry, - &inst_evals, - &gens.gens_r1cs_eval, - transcript, - &mut random_tape, - ); - - let mut proof_encoded: Vec = Vec::new(); - proof.serialize(&mut proof_encoded).unwrap(); - Timer::print(&format!("len_r1cs_eval_proof {:?}", proof_encoded.len())); - proof - }; - - timer_prove.stop(); - SNARK { - r1cs_sat_proof, - inst_evals, - r1cs_eval_proof, - rx, - ry, } - } - - /// A method to verify the SNARK proof of the satisfiability of an R1CS instance - pub fn verify( - &self, - comm: &ComputationCommitment, - input: &InputsAssignment, - transcript: &mut PoseidonTranscript, - gens: &SNARKGens, - ) -> Result<(u128, u128, u128), ProofVerifyError> { - let timer_verify = Timer::new("SNARK::verify"); - // transcript.append_protocol_name(SNARK::protocol_name()); - - // append a commitment to the computation to the transcript - comm.comm.append_to_poseidon(transcript); - - let timer_sat_proof = Timer::new("verify_sat_proof"); - assert_eq!(input.assignment.len(), comm.comm.get_num_inputs()); - // let (rx, ry) = - let res = self.r1cs_sat_proof.verify_groth16( - comm.comm.get_num_vars(), - comm.comm.get_num_cons(), - &input.assignment, - &self.inst_evals, - transcript, - &gens.gens_r1cs_sat, - )?; - timer_sat_proof.stop(); - - let timer_eval_proof = Timer::new("verify_eval_proof"); - // Reset the transcript using the state sent by the prover. - // TODO: find a way to retrieve this state from the circuit. Currently - // the API for generating constraints doesn't support returning values - // computed inside the circuit. - transcript.new_from_state(&self.r1cs_sat_proof.transcript_sat_state); - - let (Ar, Br, Cr) = &self.inst_evals; - transcript.append_scalar(&Ar); - transcript.append_scalar(&Br); - transcript.append_scalar(&Cr); - - self.r1cs_eval_proof.verify( - &comm.comm, - &self.rx, - &self.ry, - &self.inst_evals, - &gens.gens_r1cs_eval, - transcript, - )?; - timer_eval_proof.stop(); - timer_verify.stop(); - Ok(res) - } + + /// A method to produce a SNARK proof of the satisfiability of an R1CS instance + pub fn prove( + inst: &Instance, + comm: &ComputationCommitment, + decomm: &ComputationDecommitment, + vars: VarsAssignment, + inputs: &InputsAssignment, + gens: &SNARKGens, + transcript: &mut PoseidonTranscript, + ) -> Self { + let timer_prove = Timer::new("SNARK::prove"); + + // we create a Transcript object seeded with a random Scalar + // to aid the prover produce its randomness + let mut random_tape = RandomTape::new(b"proof"); + + // transcript.append_protocol_name(SNARK::protocol_name()); + comm.comm.append_to_poseidon(transcript); + + let (r1cs_sat_proof, rx, ry) = { + let (proof, rx, ry) = { + // we might need to pad variables + let padded_vars = { + let num_padded_vars = inst.inst.get_num_vars(); + let num_vars = vars.assignment.len(); + if num_padded_vars > num_vars { + vars.pad(num_padded_vars) + } else { + vars + } + }; + + R1CSProof::prove( + &inst.inst, + padded_vars.assignment, + &inputs.assignment, + &gens.gens_r1cs_sat, + transcript, + // &mut random_tape, + ) + }; + + let mut proof_encoded: Vec = Vec::new(); + proof.serialize(&mut proof_encoded).unwrap(); + Timer::print(&format!("len_r1cs_sat_proof {:?}", proof_encoded.len())); + + (proof, rx, ry) + }; + + // We need to reset the transcript state before starting the evaluation + // proof and share this state with the verifier because, on the verifier's + // side all the previous updates are done on the transcript + // circuit variable and the transcript outside the circuit will be + // inconsistent wrt to the prover's. + transcript.new_from_state(&r1cs_sat_proof.transcript_sat_state); + + // We send evaluations of A, B, C at r = (rx, ry) as claims + // to enable the verifier complete the first sum-check + let timer_eval = Timer::new("eval_sparse_polys"); + let inst_evals = { + let (Ar, Br, Cr) = inst.inst.evaluate(&rx, &ry); + transcript.append_scalar(&Ar); + transcript.append_scalar(&Br); + transcript.append_scalar(&Cr); + (Ar, Br, Cr) + }; + timer_eval.stop(); + + let r1cs_eval_proof = { + let proof = R1CSEvalProof::prove( + &decomm.decomm, + &rx, + &ry, + &inst_evals, + &gens.gens_r1cs_eval, + transcript, + &mut random_tape, + ); + + let mut proof_encoded: Vec = Vec::new(); + proof.serialize(&mut proof_encoded).unwrap(); + Timer::print(&format!("len_r1cs_eval_proof {:?}", proof_encoded.len())); + proof + }; + + timer_prove.stop(); + SNARK { + r1cs_sat_proof, + inst_evals, + r1cs_eval_proof, + rx, + ry, + } + } + + /// A method to verify the SNARK proof of the satisfiability of an R1CS instance + pub fn verify( + &self, + comm: &ComputationCommitment, + input: &InputsAssignment, + transcript: &mut PoseidonTranscript, + gens: &SNARKGens, + ) -> Result<(u128, u128, u128), ProofVerifyError> { + let timer_verify = Timer::new("SNARK::verify"); + // transcript.append_protocol_name(SNARK::protocol_name()); + + // append a commitment to the computation to the transcript + comm.comm.append_to_poseidon(transcript); + + let timer_sat_proof = Timer::new("verify_sat_proof"); + assert_eq!(input.assignment.len(), comm.comm.get_num_inputs()); + // let (rx, ry) = + let res = self.r1cs_sat_proof.verify_groth16( + comm.comm.get_num_vars(), + comm.comm.get_num_cons(), + &input.assignment, + &self.inst_evals, + transcript, + &gens.gens_r1cs_sat, + )?; + timer_sat_proof.stop(); + + let timer_eval_proof = Timer::new("verify_eval_proof"); + // Reset the transcript using the state sent by the prover. + // TODO: find a way to retrieve this state from the circuit. Currently + // the API for generating constraints doesn't support returning values + // computed inside the circuit. + transcript.new_from_state(&self.r1cs_sat_proof.transcript_sat_state); + + let (Ar, Br, Cr) = &self.inst_evals; + transcript.append_scalar(&Ar); + transcript.append_scalar(&Br); + transcript.append_scalar(&Cr); + + self.r1cs_eval_proof.verify( + &comm.comm, + &self.rx, + &self.ry, + &self.inst_evals, + &gens.gens_r1cs_eval, + transcript, + )?; + timer_eval_proof.stop(); + timer_verify.stop(); + Ok(res) + } } #[derive(Clone)] /// `NIZKGens` holds public parameters for producing and verifying proofs with the Spartan NIZK pub struct NIZKGens { - gens_r1cs_sat: R1CSGens, + gens_r1cs_sat: R1CSGens, } impl NIZKGens { - /// Constructs a new `NIZKGens` given the size of the R1CS statement - pub fn new(num_cons: usize, num_vars: usize, num_inputs: usize) -> Self { - let num_vars_padded = { - let mut num_vars_padded = max(num_vars, num_inputs + 1); - if num_vars_padded != num_vars_padded.next_power_of_two() { - num_vars_padded = num_vars_padded.next_power_of_two(); - } - num_vars_padded - }; - - let gens_r1cs_sat = R1CSGens::new(b"gens_r1cs_sat", num_cons, num_vars_padded); - NIZKGens { gens_r1cs_sat } - } + /// Constructs a new `NIZKGens` given the size of the R1CS statement + pub fn new(num_cons: usize, num_vars: usize, num_inputs: usize) -> Self { + let num_vars_padded = { + let mut num_vars_padded = max(num_vars, num_inputs + 1); + if num_vars_padded != num_vars_padded.next_power_of_two() { + num_vars_padded = num_vars_padded.next_power_of_two(); + } + num_vars_padded + }; + + let gens_r1cs_sat = R1CSGens::new(b"gens_r1cs_sat", num_cons, num_vars_padded); + NIZKGens { gens_r1cs_sat } + } } /// `NIZK` holds a proof produced by Spartan NIZK #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] pub struct NIZK { - r1cs_sat_proof: R1CSProof, - r: (Vec, Vec), + r1cs_sat_proof: R1CSProof, + r: (Vec, Vec), } impl NIZK { - fn protocol_name() -> &'static [u8] { - b"Spartan NIZK proof" - } - - /// A method to produce a NIZK proof of the satisfiability of an R1CS instance - pub fn prove( - inst: &Instance, - vars: VarsAssignment, - input: &InputsAssignment, - gens: &NIZKGens, - transcript: &mut PoseidonTranscript, - ) -> Self { - let timer_prove = Timer::new("NIZK::prove"); - // we create a Transcript object seeded with a random Scalar - // to aid the prover produce its randomness - let _random_tape = RandomTape::new(b"proof"); - - // transcript.append_protocol_name(NIZK::protocol_name()); - transcript.append_bytes(&inst.digest); - - let (r1cs_sat_proof, rx, ry) = { - // we might need to pad variables - let padded_vars = { - let num_padded_vars = inst.inst.get_num_vars(); - let num_vars = vars.assignment.len(); - if num_padded_vars > num_vars { - vars.pad(num_padded_vars) - } else { - vars + fn protocol_name() -> &'static [u8] { + b"Spartan NIZK proof" + } + + /// A method to produce a NIZK proof of the satisfiability of an R1CS instance + pub fn prove( + inst: &Instance, + vars: VarsAssignment, + input: &InputsAssignment, + gens: &NIZKGens, + transcript: &mut PoseidonTranscript, + ) -> Self { + let timer_prove = Timer::new("NIZK::prove"); + // we create a Transcript object seeded with a random Scalar + // to aid the prover produce its randomness + let _random_tape = RandomTape::new(b"proof"); + + // transcript.append_protocol_name(NIZK::protocol_name()); + transcript.append_bytes(&inst.digest); + + let (r1cs_sat_proof, rx, ry) = { + // we might need to pad variables + let padded_vars = { + let num_padded_vars = inst.inst.get_num_vars(); + let num_vars = vars.assignment.len(); + if num_padded_vars > num_vars { + vars.pad(num_padded_vars) + } else { + vars + } + }; + + let (proof, rx, ry) = R1CSProof::prove( + &inst.inst, + padded_vars.assignment, + &input.assignment, + &gens.gens_r1cs_sat, + transcript, + // &mut random_tape, + ); + let mut proof_encoded = Vec::new(); + proof.serialize(&mut proof_encoded).unwrap(); + Timer::print(&format!("len_r1cs_sat_proof {:?}", proof_encoded.len())); + (proof, rx, ry) + }; + + timer_prove.stop(); + NIZK { + r1cs_sat_proof, + r: (rx, ry), } - }; - - let (proof, rx, ry) = R1CSProof::prove( - &inst.inst, - padded_vars.assignment, - &input.assignment, - &gens.gens_r1cs_sat, - transcript, - // &mut random_tape, - ); - let mut proof_encoded = Vec::new(); - proof.serialize(&mut proof_encoded).unwrap(); - Timer::print(&format!("len_r1cs_sat_proof {:?}", proof_encoded.len())); - (proof, rx, ry) - }; - - timer_prove.stop(); - NIZK { - r1cs_sat_proof, - r: (rx, ry), } - } - - /// A method to verify a NIZK proof of the satisfiability of an R1CS instance - pub fn verify( - &self, - inst: &Instance, - input: &InputsAssignment, - transcript: &mut PoseidonTranscript, - gens: &NIZKGens, - ) -> Result { - let timer_verify = Timer::new("NIZK::verify"); - - transcript.append_bytes(&inst.digest); - - // We send evaluations of A, B, C at r = (rx, ry) as claims - // to enable the verifier complete the first sum-check - let timer_eval = Timer::new("eval_sparse_polys"); - let (claimed_rx, claimed_ry) = &self.r; - let inst_evals = inst.inst.evaluate(claimed_rx, claimed_ry); - timer_eval.stop(); - - let timer_sat_proof = Timer::new("verify_sat_proof"); - assert_eq!(input.assignment.len(), inst.inst.get_num_inputs()); - // let (rx, ry) = - let nc = self.r1cs_sat_proof.circuit_size( - inst.inst.get_num_vars(), - inst.inst.get_num_cons(), - &input.assignment, - &inst_evals, - transcript, - &gens.gens_r1cs_sat, - )?; - - // verify if claimed rx and ry are correct - // assert_eq!(rx, *claimed_rx); - // assert_eq!(ry, *claimed_ry); - timer_sat_proof.stop(); - timer_verify.stop(); - - Ok(nc) - } - - /// A method to verify a NIZK proof of the satisfiability of an R1CS instance with Groth16 - pub fn verify_groth16( - &self, - inst: &Instance, - input: &InputsAssignment, - transcript: &mut PoseidonTranscript, - gens: &NIZKGens, - ) -> Result<(u128, u128, u128), ProofVerifyError> { - let timer_verify = Timer::new("NIZK::verify"); - - // transcript.append_protocol_name(NIZK::protocol_name()); - transcript.append_bytes(&inst.digest); - - // We send evaluations of A, B, C at r = (rx, ry) as claims - // to enable the verifier complete the first sum-check - let timer_eval = Timer::new("eval_sparse_polys"); - let (claimed_rx, claimed_ry) = &self.r; - let inst_evals = inst.inst.evaluate(claimed_rx, claimed_ry); - timer_eval.stop(); - - let timer_sat_proof = Timer::new("verify_sat_proof"); - assert_eq!(input.assignment.len(), inst.inst.get_num_inputs()); - // let (rx, ry) = - let (ds, dp, dv) = self.r1cs_sat_proof.verify_groth16( - inst.inst.get_num_vars(), - inst.inst.get_num_cons(), - &input.assignment, - &inst_evals, - transcript, - &gens.gens_r1cs_sat, - )?; - - // verify if claimed rx and ry are correct - // assert_eq!(rx, *claimed_rx); - // assert_eq!(ry, *claimed_ry); - timer_sat_proof.stop(); - timer_verify.stop(); - - Ok((ds, dp, dv)) - } + + /// A method to verify a NIZK proof of the satisfiability of an R1CS instance + pub fn verify( + &self, + inst: &Instance, + input: &InputsAssignment, + transcript: &mut PoseidonTranscript, + gens: &NIZKGens, + ) -> Result { + let timer_verify = Timer::new("NIZK::verify"); + + transcript.append_bytes(&inst.digest); + + // We send evaluations of A, B, C at r = (rx, ry) as claims + // to enable the verifier complete the first sum-check + let timer_eval = Timer::new("eval_sparse_polys"); + let (claimed_rx, claimed_ry) = &self.r; + let inst_evals = inst.inst.evaluate(claimed_rx, claimed_ry); + timer_eval.stop(); + + let timer_sat_proof = Timer::new("verify_sat_proof"); + assert_eq!(input.assignment.len(), inst.inst.get_num_inputs()); + // let (rx, ry) = + let nc = self.r1cs_sat_proof.circuit_size( + inst.inst.get_num_vars(), + inst.inst.get_num_cons(), + &input.assignment, + &inst_evals, + transcript, + &gens.gens_r1cs_sat, + )?; + + // verify if claimed rx and ry are correct + // assert_eq!(rx, *claimed_rx); + // assert_eq!(ry, *claimed_ry); + timer_sat_proof.stop(); + timer_verify.stop(); + + Ok(nc) + } + + /// A method to verify a NIZK proof of the satisfiability of an R1CS instance with Groth16 + pub fn verify_groth16( + &self, + inst: &Instance, + input: &InputsAssignment, + transcript: &mut PoseidonTranscript, + gens: &NIZKGens, + ) -> Result<(u128, u128, u128), ProofVerifyError> { + let timer_verify = Timer::new("NIZK::verify"); + + // transcript.append_protocol_name(NIZK::protocol_name()); + transcript.append_bytes(&inst.digest); + + // We send evaluations of A, B, C at r = (rx, ry) as claims + // to enable the verifier complete the first sum-check + let timer_eval = Timer::new("eval_sparse_polys"); + let (claimed_rx, claimed_ry) = &self.r; + let inst_evals = inst.inst.evaluate(claimed_rx, claimed_ry); + timer_eval.stop(); + + let timer_sat_proof = Timer::new("verify_sat_proof"); + assert_eq!(input.assignment.len(), inst.inst.get_num_inputs()); + // let (rx, ry) = + let (ds, dp, dv) = self.r1cs_sat_proof.verify_groth16( + inst.inst.get_num_vars(), + inst.inst.get_num_cons(), + &input.assignment, + &inst_evals, + transcript, + &gens.gens_r1cs_sat, + )?; + + // verify if claimed rx and ry are correct + // assert_eq!(rx, *claimed_rx); + // assert_eq!(ry, *claimed_ry); + timer_sat_proof.stop(); + timer_verify.stop(); + + Ok((ds, dp, dv)) + } } #[cfg(test)] mod tests { - use crate::parameters::poseidon_params; - - use super::*; - use ark_ff::{BigInteger, One, PrimeField}; - - #[test] - pub fn check_snark() { - let num_vars = 256; - let num_cons = num_vars; - let num_inputs = 10; - - // produce public generators - let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); - - // produce a synthetic R1CSInstance - let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - - // create a commitment to R1CSInstance - let (comm, decomm) = SNARK::encode(&inst, &gens); - - let params = poseidon_params(); - - // produce a proof - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let proof = SNARK::prove( - &inst, - &comm, - &decomm, - vars, - &inputs, - &gens, - &mut prover_transcript, - ); - - // verify the proof - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify(&comm, &inputs, &mut verifier_transcript, &gens) - .is_ok()); - } - - #[test] - pub fn check_r1cs_invalid_index() { - let num_cons = 4; - let num_vars = 8; - let num_inputs = 1; - - let zero: [u8; 32] = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, - ]; - - let A = vec![(0, 0, zero.to_vec())]; - let B = vec![(100, 1, zero.to_vec())]; - let C = vec![(1, 1, zero.to_vec())]; - - let inst = Instance::new(num_cons, num_vars, num_inputs, &A, &B, &C); - assert!(inst.is_err()); - assert_eq!(inst.err(), Some(R1CSError::InvalidIndex)); - } - - #[test] - pub fn check_r1cs_invalid_scalar() { - let num_cons = 4; - let num_vars = 8; - let num_inputs = 1; - - let zero: [u8; 32] = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, - ]; - - let larger_than_mod = [ - 3, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, - 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, - ]; - - let A = vec![(0, 0, zero.to_vec())]; - let B = vec![(1, 1, larger_than_mod.to_vec())]; - let C = vec![(1, 1, zero.to_vec())]; - - let inst = Instance::new(num_cons, num_vars, num_inputs, &A, &B, &C); - assert!(inst.is_err()); - assert_eq!(inst.err(), Some(R1CSError::InvalidScalar)); - } - - #[test] - fn test_padded_constraints() { - // parameters of the R1CS instance - let num_cons = 1; - let num_vars = 0; - let num_inputs = 3; - let num_non_zero_entries = 3; - - // We will encode the above constraints into three matrices, where - // the coefficients in the matrix are in the little-endian byte order - let mut A: Vec<(usize, usize, Vec)> = Vec::new(); - let mut B: Vec<(usize, usize, Vec)> = Vec::new(); - let mut C: Vec<(usize, usize, Vec)> = Vec::new(); - - // Create a^2 + b + 13 - A.push((0, num_vars + 2, (Scalar::one().into_repr().to_bytes_le()))); // 1*a - B.push((0, num_vars + 2, Scalar::one().into_repr().to_bytes_le())); // 1*a - C.push((0, num_vars + 1, Scalar::one().into_repr().to_bytes_le())); // 1*z - C.push(( - 0, - num_vars, - (-Scalar::from(13u64)).into_repr().to_bytes_le(), - )); // -13*1 - C.push((0, num_vars + 3, (-Scalar::one()).into_repr().to_bytes_le())); // -1*b - - // Var Assignments (Z_0 = 16 is the only output) - let vars = vec![Scalar::zero().into_repr().to_bytes_le(); num_vars]; - - // create an InputsAssignment (a = 1, b = 2) - let mut inputs = vec![Scalar::zero().into_repr().to_bytes_le(); num_inputs]; - inputs[0] = Scalar::from(16u64).into_repr().to_bytes_le(); - inputs[1] = Scalar::from(1u64).into_repr().to_bytes_le(); - inputs[2] = Scalar::from(2u64).into_repr().to_bytes_le(); - - let assignment_inputs = InputsAssignment::new(&inputs).unwrap(); - let assignment_vars = VarsAssignment::new(&vars).unwrap(); - - // Check if instance is satisfiable - let inst = Instance::new(num_cons, num_vars, num_inputs, &A, &B, &C).unwrap(); - let res = inst.is_sat(&assignment_vars, &assignment_inputs); - assert!(res.unwrap(), "should be satisfied"); - - // SNARK public params - let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_non_zero_entries); - - // create a commitment to the R1CS instance - let (comm, decomm) = SNARK::encode(&inst, &gens); - - let params = poseidon_params(); - - // produce a SNARK - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let proof = SNARK::prove( - &inst, - &comm, - &decomm, - assignment_vars.clone(), - &assignment_inputs, - &gens, - &mut prover_transcript, - ); - - // verify the SNARK - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify(&comm, &assignment_inputs, &mut verifier_transcript, &gens) - .is_ok()); - - // NIZK public params - let gens = NIZKGens::new(num_cons, num_vars, num_inputs); - - let params = poseidon_params(); - - // produce a NIZK - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let proof = NIZK::prove( - &inst, - assignment_vars, - &assignment_inputs, - &gens, - &mut prover_transcript, - ); - - // verify the NIZK - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify_groth16(&inst, &assignment_inputs, &mut verifier_transcript, &gens) - .is_ok()); - } + use crate::parameters::poseidon_params; + + use super::*; + use ark_ff::{BigInteger, One, PrimeField}; + + #[test] + pub fn check_snark() { + let num_vars = 256; + let num_cons = num_vars; + let num_inputs = 10; + + // produce public generators + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); + + // produce a synthetic R1CSInstance + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + + // create a commitment to R1CSInstance + let (comm, decomm) = SNARK::encode(&inst, &gens); + + let params = poseidon_params(); + + // produce a proof + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let proof = SNARK::prove( + &inst, + &comm, + &decomm, + vars, + &inputs, + &gens, + &mut prover_transcript, + ); + + // verify the proof + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify(&comm, &inputs, &mut verifier_transcript, &gens) + .is_ok()); + } + + #[test] + pub fn check_r1cs_invalid_index() { + let num_cons = 4; + let num_vars = 8; + let num_inputs = 1; + + let zero: [u8; 32] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]; + + let A = vec![(0, 0, zero.to_vec())]; + let B = vec![(100, 1, zero.to_vec())]; + let C = vec![(1, 1, zero.to_vec())]; + + let inst = Instance::new(num_cons, num_vars, num_inputs, &A, &B, &C); + assert!(inst.is_err()); + assert_eq!(inst.err(), Some(R1CSError::InvalidIndex)); + } + + #[test] + pub fn check_r1cs_invalid_scalar() { + let num_cons = 4; + let num_vars = 8; + let num_inputs = 1; + + let zero: [u8; 32] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]; + + let larger_than_mod = [ + 3, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, + ]; + + let A = vec![(0, 0, zero.to_vec())]; + let B = vec![(1, 1, larger_than_mod.to_vec())]; + let C = vec![(1, 1, zero.to_vec())]; + + let inst = Instance::new(num_cons, num_vars, num_inputs, &A, &B, &C); + assert!(inst.is_err()); + assert_eq!(inst.err(), Some(R1CSError::InvalidScalar)); + } + + #[test] + fn test_padded_constraints() { + // parameters of the R1CS instance + let num_cons = 1; + let num_vars = 0; + let num_inputs = 3; + let num_non_zero_entries = 3; + + // We will encode the above constraints into three matrices, where + // the coefficients in the matrix are in the little-endian byte order + let mut A: Vec<(usize, usize, Vec)> = Vec::new(); + let mut B: Vec<(usize, usize, Vec)> = Vec::new(); + let mut C: Vec<(usize, usize, Vec)> = Vec::new(); + + // Create a^2 + b + 13 + A.push((0, num_vars + 2, (Scalar::one().into_repr().to_bytes_le()))); // 1*a + B.push((0, num_vars + 2, Scalar::one().into_repr().to_bytes_le())); // 1*a + C.push((0, num_vars + 1, Scalar::one().into_repr().to_bytes_le())); // 1*z + C.push(( + 0, + num_vars, + (-Scalar::from(13u64)).into_repr().to_bytes_le(), + )); // -13*1 + C.push((0, num_vars + 3, (-Scalar::one()).into_repr().to_bytes_le())); // -1*b + + // Var Assignments (Z_0 = 16 is the only output) + let vars = vec![Scalar::zero().into_repr().to_bytes_le(); num_vars]; + + // create an InputsAssignment (a = 1, b = 2) + let mut inputs = vec![Scalar::zero().into_repr().to_bytes_le(); num_inputs]; + inputs[0] = Scalar::from(16u64).into_repr().to_bytes_le(); + inputs[1] = Scalar::from(1u64).into_repr().to_bytes_le(); + inputs[2] = Scalar::from(2u64).into_repr().to_bytes_le(); + + let assignment_inputs = InputsAssignment::new(&inputs).unwrap(); + let assignment_vars = VarsAssignment::new(&vars).unwrap(); + + // Check if instance is satisfiable + let inst = Instance::new(num_cons, num_vars, num_inputs, &A, &B, &C).unwrap(); + let res = inst.is_sat(&assignment_vars, &assignment_inputs); + assert!(res.unwrap(), "should be satisfied"); + + // SNARK public params + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_non_zero_entries); + + // create a commitment to the R1CS instance + let (comm, decomm) = SNARK::encode(&inst, &gens); + + let params = poseidon_params(); + + // produce a SNARK + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let proof = SNARK::prove( + &inst, + &comm, + &decomm, + assignment_vars.clone(), + &assignment_inputs, + &gens, + &mut prover_transcript, + ); + + // verify the SNARK + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify(&comm, &assignment_inputs, &mut verifier_transcript, &gens) + .is_ok()); + + // NIZK public params + let gens = NIZKGens::new(num_cons, num_vars, num_inputs); + + let params = poseidon_params(); + + // produce a NIZK + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let proof = NIZK::prove( + &inst, + assignment_vars, + &assignment_inputs, + &gens, + &mut prover_transcript, + ); + + // verify the NIZK + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify_groth16(&inst, &assignment_inputs, &mut verifier_transcript, &gens) + .is_ok()); + } } diff --git a/src/math.rs b/src/math.rs index 33e9e14..c0d1634 100644 --- a/src/math.rs +++ b/src/math.rs @@ -1,36 +1,36 @@ pub trait Math { - fn square_root(self) -> usize; - fn pow2(self) -> usize; - fn get_bits(self, num_bits: usize) -> Vec; - fn log_2(self) -> usize; + fn square_root(self) -> usize; + fn pow2(self) -> usize; + fn get_bits(self, num_bits: usize) -> Vec; + fn log_2(self) -> usize; } impl Math for usize { - #[inline] - fn square_root(self) -> usize { - (self as f64).sqrt() as usize - } + #[inline] + fn square_root(self) -> usize { + (self as f64).sqrt() as usize + } - #[inline] - fn pow2(self) -> usize { - let base: usize = 2; - base.pow(self as u32) - } + #[inline] + fn pow2(self) -> usize { + let base: usize = 2; + base.pow(self as u32) + } - /// Returns the num_bits from n in a canonical order - fn get_bits(self, num_bits: usize) -> Vec { - (0..num_bits) - .map(|shift_amount| ((self & (1 << (num_bits - shift_amount - 1))) > 0)) - .collect::>() - } + /// Returns the num_bits from n in a canonical order + fn get_bits(self, num_bits: usize) -> Vec { + (0..num_bits) + .map(|shift_amount| ((self & (1 << (num_bits - shift_amount - 1))) > 0)) + .collect::>() + } - fn log_2(self) -> usize { - assert_ne!(self, 0); + fn log_2(self) -> usize { + assert_ne!(self, 0); - if self.is_power_of_two() { - (1usize.leading_zeros() - self.leading_zeros()) as usize - } else { - (0usize.leading_zeros() - self.leading_zeros()) as usize + if self.is_power_of_two() { + (1usize.leading_zeros() - self.leading_zeros()) as usize + } else { + (0usize.leading_zeros() - self.leading_zeros()) as usize + } } - } } diff --git a/src/nizk/bullet.rs b/src/nizk/bullet.rs index 8283a11..abb8b88 100644 --- a/src/nizk/bullet.rs +++ b/src/nizk/bullet.rs @@ -8,8 +8,8 @@ use crate::poseidon_transcript::PoseidonTranscript; use super::super::errors::ProofVerifyError; use super::super::group::{ - CompressGroupElement, CompressedGroup, DecompressGroupElement, GroupElement, - VartimeMultiscalarMul, + CompressGroupElement, CompressedGroup, DecompressGroupElement, GroupElement, + VartimeMultiscalarMul, }; use super::super::scalar::Scalar; use ark_ff::Field; @@ -20,247 +20,242 @@ use std::ops::MulAssign; #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct BulletReductionProof { - L_vec: Vec, - R_vec: Vec, + L_vec: Vec, + R_vec: Vec, } impl BulletReductionProof { - /// Create an inner-product proof. - /// - /// The proof is created with respect to the bases \\(G\\). - /// - /// The `transcript` is passed in as a parameter so that the - /// challenges depend on the *entire* transcript (including parent - /// protocols). - /// - /// The lengths of the vectors must all be the same, and must all be - /// either 0 or a power of 2. - pub fn prove( - transcript: &mut PoseidonTranscript, - Q: &GroupElement, - G_vec: &[GroupElement], - H: &GroupElement, - a_vec: &[Scalar], - b_vec: &[Scalar], - blind: &Scalar, - blinds_vec: &[(Scalar, Scalar)], - ) -> ( - BulletReductionProof, - GroupElement, - Scalar, - Scalar, - GroupElement, - Scalar, - ) { - // Create slices G, H, a, b backed by their respective - // vectors. This lets us reslice as we compress the lengths - // of the vectors in the main loop below. - let mut G = &mut G_vec.to_owned()[..]; - let mut a = &mut a_vec.to_owned()[..]; - let mut b = &mut b_vec.to_owned()[..]; - - // All of the input vectors must have a length that is a power of two. - let mut n = G.len(); - assert!(n.is_power_of_two()); - let lg_n = n.log_2(); - - // All of the input vectors must have the same length. - assert_eq!(G.len(), n); - assert_eq!(a.len(), n); - assert_eq!(b.len(), n); - assert_eq!(blinds_vec.len(), 2 * lg_n); - - let mut L_vec = Vec::with_capacity(lg_n); - let mut R_vec = Vec::with_capacity(lg_n); - let mut blinds_iter = blinds_vec.iter(); - let mut blind_fin = *blind; - - while n != 1 { - n /= 2; - let (a_L, a_R) = a.split_at_mut(n); - let (b_L, b_R) = b.split_at_mut(n); - let (G_L, G_R) = G.split_at_mut(n); - - let c_L = inner_product(a_L, b_R); - let c_R = inner_product(a_R, b_L); - - let (blind_L, blind_R) = blinds_iter.next().unwrap(); - - let L = GroupElement::vartime_multiscalar_mul( - a_L - .iter() - .chain(iter::once(&c_L)) - .chain(iter::once(blind_L)) - .copied() - .collect::>() - .as_slice(), - G_R - .iter() - .chain(iter::once(Q)) - .chain(iter::once(H)) - .copied() - .collect::>() - .as_slice(), - ); - - let R = GroupElement::vartime_multiscalar_mul( - a_R - .iter() - .chain(iter::once(&c_R)) - .chain(iter::once(blind_R)) - .copied() - .collect::>() - .as_slice(), - G_L - .iter() - .chain(iter::once(Q)) - .chain(iter::once(H)) - .copied() - .collect::>() - .as_slice(), - ); - - transcript.append_point(&L.compress()); - transcript.append_point(&R.compress()); - - let u = transcript.challenge_scalar(); - let u_inv = u.inverse().unwrap(); - - for i in 0..n { - a_L[i] = a_L[i] * u + u_inv * a_R[i]; - b_L[i] = b_L[i] * u_inv + u * b_R[i]; - G_L[i] = GroupElement::vartime_multiscalar_mul(&[u_inv, u], &[G_L[i], G_R[i]]); - } - - blind_fin = blind_fin + u * u * blind_L + u_inv * u_inv * blind_R; - - L_vec.push(L.compress()); - R_vec.push(R.compress()); - - a = a_L; - b = b_L; - G = G_L; + /// Create an inner-product proof. + /// + /// The proof is created with respect to the bases \\(G\\). + /// + /// The `transcript` is passed in as a parameter so that the + /// challenges depend on the *entire* transcript (including parent + /// protocols). + /// + /// The lengths of the vectors must all be the same, and must all be + /// either 0 or a power of 2. + pub fn prove( + transcript: &mut PoseidonTranscript, + Q: &GroupElement, + G_vec: &[GroupElement], + H: &GroupElement, + a_vec: &[Scalar], + b_vec: &[Scalar], + blind: &Scalar, + blinds_vec: &[(Scalar, Scalar)], + ) -> ( + BulletReductionProof, + GroupElement, + Scalar, + Scalar, + GroupElement, + Scalar, + ) { + // Create slices G, H, a, b backed by their respective + // vectors. This lets us reslice as we compress the lengths + // of the vectors in the main loop below. + let mut G = &mut G_vec.to_owned()[..]; + let mut a = &mut a_vec.to_owned()[..]; + let mut b = &mut b_vec.to_owned()[..]; + + // All of the input vectors must have a length that is a power of two. + let mut n = G.len(); + assert!(n.is_power_of_two()); + let lg_n = n.log_2(); + + // All of the input vectors must have the same length. + assert_eq!(G.len(), n); + assert_eq!(a.len(), n); + assert_eq!(b.len(), n); + assert_eq!(blinds_vec.len(), 2 * lg_n); + + let mut L_vec = Vec::with_capacity(lg_n); + let mut R_vec = Vec::with_capacity(lg_n); + let mut blinds_iter = blinds_vec.iter(); + let mut blind_fin = *blind; + + while n != 1 { + n /= 2; + let (a_L, a_R) = a.split_at_mut(n); + let (b_L, b_R) = b.split_at_mut(n); + let (G_L, G_R) = G.split_at_mut(n); + + let c_L = inner_product(a_L, b_R); + let c_R = inner_product(a_R, b_L); + + let (blind_L, blind_R) = blinds_iter.next().unwrap(); + + let L = GroupElement::vartime_multiscalar_mul( + a_L.iter() + .chain(iter::once(&c_L)) + .chain(iter::once(blind_L)) + .copied() + .collect::>() + .as_slice(), + G_R.iter() + .chain(iter::once(Q)) + .chain(iter::once(H)) + .copied() + .collect::>() + .as_slice(), + ); + + let R = GroupElement::vartime_multiscalar_mul( + a_R.iter() + .chain(iter::once(&c_R)) + .chain(iter::once(blind_R)) + .copied() + .collect::>() + .as_slice(), + G_L.iter() + .chain(iter::once(Q)) + .chain(iter::once(H)) + .copied() + .collect::>() + .as_slice(), + ); + + transcript.append_point(&L.compress()); + transcript.append_point(&R.compress()); + + let u = transcript.challenge_scalar(); + let u_inv = u.inverse().unwrap(); + + for i in 0..n { + a_L[i] = a_L[i] * u + u_inv * a_R[i]; + b_L[i] = b_L[i] * u_inv + u * b_R[i]; + G_L[i] = GroupElement::vartime_multiscalar_mul(&[u_inv, u], &[G_L[i], G_R[i]]); + } + + blind_fin = blind_fin + u * u * blind_L + u_inv * u_inv * blind_R; + + L_vec.push(L.compress()); + R_vec.push(R.compress()); + + a = a_L; + b = b_L; + G = G_L; + } + + let Gamma_hat = + GroupElement::vartime_multiscalar_mul(&[a[0], a[0] * b[0], blind_fin], &[G[0], *Q, *H]); + + ( + BulletReductionProof { L_vec, R_vec }, + Gamma_hat, + a[0], + b[0], + G[0], + blind_fin, + ) } - let Gamma_hat = - GroupElement::vartime_multiscalar_mul(&[a[0], a[0] * b[0], blind_fin], &[G[0], *Q, *H]); - - ( - BulletReductionProof { L_vec, R_vec }, - Gamma_hat, - a[0], - b[0], - G[0], - blind_fin, - ) - } - - /// Computes three vectors of verification scalars \\([u\_{i}^{2}]\\), \\([u\_{i}^{-2}]\\) and \\([s\_{i}]\\) for combined multiscalar multiplication - /// in a parent protocol. See [inner product protocol notes](index.html#verification-equation) for details. - /// The verifier must provide the input length \\(n\\) explicitly to avoid unbounded allocation within the inner product proof. - fn verification_scalars( - &self, - n: usize, - transcript: &mut PoseidonTranscript, - ) -> Result<(Vec, Vec, Vec), ProofVerifyError> { - let lg_n = self.L_vec.len(); - if lg_n >= 32 { - // 4 billion multiplications should be enough for anyone - // and this check prevents overflow in 1< Result<(Vec, Vec, Vec), ProofVerifyError> { + let lg_n = self.L_vec.len(); + if lg_n >= 32 { + // 4 billion multiplications should be enough for anyone + // and this check prevents overflow in 1< = challenges.clone(); + + ark_ff::fields::batch_inversion(&mut challenges_inv); + let mut allinv: Scalar = Scalar::one(); + for c in challenges.iter().filter(|s| !s.is_zero()) { + allinv.mul_assign(c); + } + allinv = allinv.inverse().unwrap(); + + // 3. Compute u_i^2 and (1/u_i)^2 + for i in 0..lg_n { + challenges[i] = challenges[i].square(); + challenges_inv[i] = challenges_inv[i].square(); + } + let challenges_sq = challenges; + let challenges_inv_sq = challenges_inv; + + // 4. Compute s values inductively. + let mut s = Vec::with_capacity(n); + s.push(allinv); + for i in 1..n { + let lg_i = (32 - 1 - (i as u32).leading_zeros()) as usize; + let k = 1 << lg_i; + // The challenges are stored in "creation order" as [u_k,...,u_1], + // so u_{lg(i)+1} = is indexed by (lg_n-1) - lg_i + let u_lg_i_sq = challenges_sq[(lg_n - 1) - lg_i]; + s.push(s[i - k] * u_lg_i_sq); + } + + Ok((challenges_sq, challenges_inv_sq, s)) } - if n != (1 << lg_n) { - return Err(ProofVerifyError::InternalError); - } - - // 1. Recompute x_k,...,x_1 based on the proof transcript - let mut challenges = Vec::with_capacity(lg_n); - for (L, R) in self.L_vec.iter().zip(self.R_vec.iter()) { - transcript.append_point(L); - transcript.append_point(R); - challenges.push(transcript.challenge_scalar()); - } - - // 2. Compute 1/(u_k...u_1) and 1/u_k, ..., 1/u_1 - let mut challenges_inv: Vec = challenges.clone(); - - ark_ff::fields::batch_inversion(&mut challenges_inv); - let mut allinv: Scalar = Scalar::one(); - for c in challenges.iter().filter(|s| !s.is_zero()) { - allinv.mul_assign(c); - } - allinv = allinv.inverse().unwrap(); - // 3. Compute u_i^2 and (1/u_i)^2 - for i in 0..lg_n { - challenges[i] = challenges[i].square(); - challenges_inv[i] = challenges_inv[i].square(); + /// This method is for testing that proof generation work, + /// but for efficiency the actual protocols would use `verification_scalars` + /// method to combine inner product verification with other checks + /// in a single multiscalar multiplication. + pub fn verify( + &self, + n: usize, + a: &[Scalar], + transcript: &mut PoseidonTranscript, + Gamma: &GroupElement, + G: &[GroupElement], + ) -> Result<(GroupElement, GroupElement, Scalar), ProofVerifyError> { + let (u_sq, u_inv_sq, s) = self.verification_scalars(n, transcript)?; + + let Ls = self + .L_vec + .iter() + .map(|p| GroupElement::decompress(p).ok_or(ProofVerifyError::InternalError)) + .collect::, _>>()?; + + let Rs = self + .R_vec + .iter() + .map(|p| GroupElement::decompress(p).ok_or(ProofVerifyError::InternalError)) + .collect::, _>>()?; + + let G_hat = GroupElement::vartime_multiscalar_mul(s.as_slice(), G); + let a_hat = inner_product(a, &s); + + let Gamma_hat = GroupElement::vartime_multiscalar_mul( + u_sq.iter() + .chain(u_inv_sq.iter()) + .chain(iter::once(&Scalar::one())) + .copied() + .collect::>() + .as_slice(), + Ls.iter() + .chain(Rs.iter()) + .chain(iter::once(Gamma)) + .copied() + .collect::>() + .as_slice(), + ); + + Ok((G_hat, Gamma_hat, a_hat)) } - let challenges_sq = challenges; - let challenges_inv_sq = challenges_inv; - - // 4. Compute s values inductively. - let mut s = Vec::with_capacity(n); - s.push(allinv); - for i in 1..n { - let lg_i = (32 - 1 - (i as u32).leading_zeros()) as usize; - let k = 1 << lg_i; - // The challenges are stored in "creation order" as [u_k,...,u_1], - // so u_{lg(i)+1} = is indexed by (lg_n-1) - lg_i - let u_lg_i_sq = challenges_sq[(lg_n - 1) - lg_i]; - s.push(s[i - k] * u_lg_i_sq); - } - - Ok((challenges_sq, challenges_inv_sq, s)) - } - - /// This method is for testing that proof generation work, - /// but for efficiency the actual protocols would use `verification_scalars` - /// method to combine inner product verification with other checks - /// in a single multiscalar multiplication. - pub fn verify( - &self, - n: usize, - a: &[Scalar], - transcript: &mut PoseidonTranscript, - Gamma: &GroupElement, - G: &[GroupElement], - ) -> Result<(GroupElement, GroupElement, Scalar), ProofVerifyError> { - let (u_sq, u_inv_sq, s) = self.verification_scalars(n, transcript)?; - - let Ls = self - .L_vec - .iter() - .map(|p| GroupElement::decompress(p).ok_or(ProofVerifyError::InternalError)) - .collect::, _>>()?; - - let Rs = self - .R_vec - .iter() - .map(|p| GroupElement::decompress(p).ok_or(ProofVerifyError::InternalError)) - .collect::, _>>()?; - - let G_hat = GroupElement::vartime_multiscalar_mul(s.as_slice(), G); - let a_hat = inner_product(a, &s); - - let Gamma_hat = GroupElement::vartime_multiscalar_mul( - u_sq - .iter() - .chain(u_inv_sq.iter()) - .chain(iter::once(&Scalar::one())) - .copied() - .collect::>() - .as_slice(), - Ls.iter() - .chain(Rs.iter()) - .chain(iter::once(Gamma)) - .copied() - .collect::>() - .as_slice(), - ); - - Ok((G_hat, Gamma_hat, a_hat)) - } } /// Computes an inner product of two vectors @@ -269,13 +264,13 @@ impl BulletReductionProof { /// \\] /// Panics if the lengths of \\(\mathbf{a}\\) and \\(\mathbf{b}\\) are not equal. pub fn inner_product(a: &[Scalar], b: &[Scalar]) -> Scalar { - assert!( - a.len() == b.len(), - "inner_product(a,b): lengths of vectors do not match" - ); - let mut out = Scalar::zero(); - for i in 0..a.len() { - out += a[i] * b[i]; - } - out + assert!( + a.len() == b.len(), + "inner_product(a,b): lengths of vectors do not match" + ); + let mut out = Scalar::zero(); + for i in 0..a.len() { + out += a[i] * b[i]; + } + out } diff --git a/src/nizk/mod.rs b/src/nizk/mod.rs index e904406..26bcc74 100644 --- a/src/nizk/mod.rs +++ b/src/nizk/mod.rs @@ -5,7 +5,7 @@ use crate::poseidon_transcript::{AppendToPoseidon, PoseidonTranscript}; use super::commitments::{Commitments, MultiCommitGens}; use super::errors::ProofVerifyError; use super::group::{ - CompressGroupElement, CompressedGroup, DecompressGroupElement, GroupElement, UnpackGroupElement, + CompressGroupElement, CompressedGroup, DecompressGroupElement, GroupElement, UnpackGroupElement, }; use super::random::RandomTape; use super::scalar::Scalar; @@ -18,744 +18,743 @@ use bullet::BulletReductionProof; #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] pub struct KnowledgeProof { - alpha: CompressedGroup, - z1: Scalar, - z2: Scalar, + alpha: CompressedGroup, + z1: Scalar, + z2: Scalar, } impl KnowledgeProof { - fn protocol_name() -> &'static [u8] { - b"knowledge proof" - } - - pub fn prove( - gens_n: &MultiCommitGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - x: &Scalar, - r: &Scalar, - ) -> (KnowledgeProof, CompressedGroup) { - // transcript.append_protocol_name(KnowledgeProof::protocol_name()); - - // produce two random Scalars - let t1 = random_tape.random_scalar(b"t1"); - let t2 = random_tape.random_scalar(b"t2"); - - let C = x.commit(r, gens_n).compress(); - C.append_to_poseidon(transcript); - - let alpha = t1.commit(&t2, gens_n).compress(); - alpha.append_to_poseidon(transcript); - - let c = transcript.challenge_scalar(); - - let z1 = c * x + t1; - let z2 = c * r + t2; - - (KnowledgeProof { alpha, z1, z2 }, C) - } - - pub fn verify( - &self, - gens_n: &MultiCommitGens, - transcript: &mut PoseidonTranscript, - C: &CompressedGroup, - ) -> Result<(), ProofVerifyError> { - // transcript.append_protocol_name(KnowledgeProof::protocol_name()); - C.append_to_poseidon(transcript); - self.alpha.append_to_poseidon(transcript); - - let c = transcript.challenge_scalar(); - - let lhs = self.z1.commit(&self.z2, gens_n).compress(); - let rhs = (C.unpack()?.mul(c.into_repr()) + self.alpha.unpack()?).compress(); - - if lhs == rhs { - Ok(()) - } else { - Err(ProofVerifyError::InternalError) + fn protocol_name() -> &'static [u8] { + b"knowledge proof" + } + + pub fn prove( + gens_n: &MultiCommitGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + x: &Scalar, + r: &Scalar, + ) -> (KnowledgeProof, CompressedGroup) { + // transcript.append_protocol_name(KnowledgeProof::protocol_name()); + + // produce two random Scalars + let t1 = random_tape.random_scalar(b"t1"); + let t2 = random_tape.random_scalar(b"t2"); + + let C = x.commit(r, gens_n).compress(); + C.append_to_poseidon(transcript); + + let alpha = t1.commit(&t2, gens_n).compress(); + alpha.append_to_poseidon(transcript); + + let c = transcript.challenge_scalar(); + + let z1 = c * x + t1; + let z2 = c * r + t2; + + (KnowledgeProof { alpha, z1, z2 }, C) + } + + pub fn verify( + &self, + gens_n: &MultiCommitGens, + transcript: &mut PoseidonTranscript, + C: &CompressedGroup, + ) -> Result<(), ProofVerifyError> { + // transcript.append_protocol_name(KnowledgeProof::protocol_name()); + C.append_to_poseidon(transcript); + self.alpha.append_to_poseidon(transcript); + + let c = transcript.challenge_scalar(); + + let lhs = self.z1.commit(&self.z2, gens_n).compress(); + let rhs = (C.unpack()?.mul(c.into_repr()) + self.alpha.unpack()?).compress(); + + if lhs == rhs { + Ok(()) + } else { + Err(ProofVerifyError::InternalError) + } } - } } #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] pub struct EqualityProof { - alpha: CompressedGroup, - z: Scalar, + alpha: CompressedGroup, + z: Scalar, } impl EqualityProof { - fn protocol_name() -> &'static [u8] { - b"equality proof" - } - - pub fn prove( - gens_n: &MultiCommitGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - v1: &Scalar, - s1: &Scalar, - v2: &Scalar, - s2: &Scalar, - ) -> (EqualityProof, CompressedGroup, CompressedGroup) { - // transcript.append_protocol_name(EqualityProof::protocol_name()); - - // produce a random Scalar - let r = random_tape.random_scalar(b"r"); - - let C1 = v1.commit(s1, gens_n).compress(); - transcript.append_point(&C1); - - let C2 = v2.commit(s2, gens_n).compress(); - transcript.append_point(&C2); - - let alpha = gens_n.h.mul(r.into_repr()).compress(); - transcript.append_point(&alpha); - - let c = transcript.challenge_scalar(); - - let z = c * ((*s1) - s2) + r; - - (EqualityProof { alpha, z }, C1, C2) - } - - pub fn verify( - &self, - gens_n: &MultiCommitGens, - transcript: &mut PoseidonTranscript, - C1: &CompressedGroup, - C2: &CompressedGroup, - ) -> Result<(), ProofVerifyError> { - // transcript.append_protocol_name(EqualityProof::protocol_name()); - - transcript.append_point(C1); - transcript.append_point(C2); - transcript.append_point(&self.alpha); - - let c = transcript.challenge_scalar(); - let rhs = { - let C = C1.unpack()? - C2.unpack()?; - (C.mul(c.into_repr()) + self.alpha.unpack()?).compress() - }; - println!("rhs {:?}", rhs); - - let lhs = gens_n.h.mul(self.z.into_repr()).compress(); - println!("lhs {:?}", lhs); - if lhs == rhs { - Ok(()) - } else { - Err(ProofVerifyError::InternalError) + fn protocol_name() -> &'static [u8] { + b"equality proof" + } + + pub fn prove( + gens_n: &MultiCommitGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + v1: &Scalar, + s1: &Scalar, + v2: &Scalar, + s2: &Scalar, + ) -> (EqualityProof, CompressedGroup, CompressedGroup) { + // transcript.append_protocol_name(EqualityProof::protocol_name()); + + // produce a random Scalar + let r = random_tape.random_scalar(b"r"); + + let C1 = v1.commit(s1, gens_n).compress(); + transcript.append_point(&C1); + + let C2 = v2.commit(s2, gens_n).compress(); + transcript.append_point(&C2); + + let alpha = gens_n.h.mul(r.into_repr()).compress(); + transcript.append_point(&alpha); + + let c = transcript.challenge_scalar(); + + let z = c * ((*s1) - s2) + r; + + (EqualityProof { alpha, z }, C1, C2) + } + + pub fn verify( + &self, + gens_n: &MultiCommitGens, + transcript: &mut PoseidonTranscript, + C1: &CompressedGroup, + C2: &CompressedGroup, + ) -> Result<(), ProofVerifyError> { + // transcript.append_protocol_name(EqualityProof::protocol_name()); + + transcript.append_point(C1); + transcript.append_point(C2); + transcript.append_point(&self.alpha); + + let c = transcript.challenge_scalar(); + let rhs = { + let C = C1.unpack()? - C2.unpack()?; + (C.mul(c.into_repr()) + self.alpha.unpack()?).compress() + }; + println!("rhs {:?}", rhs); + + let lhs = gens_n.h.mul(self.z.into_repr()).compress(); + println!("lhs {:?}", lhs); + if lhs == rhs { + Ok(()) + } else { + Err(ProofVerifyError::InternalError) + } } - } } #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] pub struct ProductProof { - alpha: CompressedGroup, - beta: CompressedGroup, - delta: CompressedGroup, - z: Vec, + alpha: CompressedGroup, + beta: CompressedGroup, + delta: CompressedGroup, + z: Vec, } impl ProductProof { - fn protocol_name() -> &'static [u8] { - b"product proof" - } - - pub fn prove( - gens_n: &MultiCommitGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - x: &Scalar, - rX: &Scalar, - y: &Scalar, - rY: &Scalar, - z: &Scalar, - rZ: &Scalar, - ) -> ( - ProductProof, - CompressedGroup, - CompressedGroup, - CompressedGroup, - ) { - // transcript.append_protocol_name(ProductProof::protocol_name()); - - // produce five random Scalar - let b1 = random_tape.random_scalar(b"b1"); - let b2 = random_tape.random_scalar(b"b2"); - let b3 = random_tape.random_scalar(b"b3"); - let b4 = random_tape.random_scalar(b"b4"); - let b5 = random_tape.random_scalar(b"b5"); - - let X_unc = x.commit(rX, gens_n); - - let X = X_unc.compress(); - transcript.append_point(&X); - let X_new = GroupElement::decompress(&X); - - assert_eq!(X_unc, X_new.unwrap()); - - let Y = y.commit(rY, gens_n).compress(); - transcript.append_point(&Y); - - let Z = z.commit(rZ, gens_n).compress(); - transcript.append_point(&Z); - - let alpha = b1.commit(&b2, gens_n).compress(); - transcript.append_point(&alpha); - - let beta = b3.commit(&b4, gens_n).compress(); - transcript.append_point(&beta); - - let delta = { - let gens_X = &MultiCommitGens { - n: 1, - G: vec![GroupElement::decompress(&X).unwrap()], - h: gens_n.h, - }; - b3.commit(&b5, gens_X).compress() - }; - transcript.append_point(&delta); - - let c = transcript.challenge_scalar(); - - let z1 = b1 + c * x; - let z2 = b2 + c * rX; - let z3 = b3 + c * y; - let z4 = b4 + c * rY; - let z5 = b5 + c * ((*rZ) - (*rX) * y); - let z = [z1, z2, z3, z4, z5].to_vec(); - - ( - ProductProof { - alpha, - beta, - delta, - z, - }, - X, - Y, - Z, - ) - } - - fn check_equality( - P: &CompressedGroup, - X: &CompressedGroup, - c: &Scalar, - gens_n: &MultiCommitGens, - z1: &Scalar, - z2: &Scalar, - ) -> bool { - println!("{:?}", X); - let lhs = (GroupElement::decompress(P).unwrap() - + GroupElement::decompress(X).unwrap().mul(c.into_repr())) - .compress(); - let rhs = z1.commit(z2, gens_n).compress(); - - lhs == rhs - } - - pub fn verify( - &self, - gens_n: &MultiCommitGens, - transcript: &mut PoseidonTranscript, - X: &CompressedGroup, - Y: &CompressedGroup, - Z: &CompressedGroup, - ) -> Result<(), ProofVerifyError> { - // transcript.append_protocol_name(ProductProof::protocol_name()); - - X.append_to_poseidon(transcript); - Y.append_to_poseidon(transcript); - Z.append_to_poseidon(transcript); - self.alpha.append_to_poseidon(transcript); - self.beta.append_to_poseidon(transcript); - self.delta.append_to_poseidon(transcript); - - let z1 = self.z[0]; - let z2 = self.z[1]; - let z3 = self.z[2]; - let z4 = self.z[3]; - let z5 = self.z[4]; - - let c = transcript.challenge_scalar(); - - if ProductProof::check_equality(&self.alpha, X, &c, gens_n, &z1, &z2) - && ProductProof::check_equality(&self.beta, Y, &c, gens_n, &z3, &z4) - && ProductProof::check_equality( - &self.delta, - Z, - &c, - &MultiCommitGens { - n: 1, - G: vec![X.unpack()?], - h: gens_n.h, - }, - &z3, - &z5, - ) - { - Ok(()) - } else { - Err(ProofVerifyError::InternalError) + fn protocol_name() -> &'static [u8] { + b"product proof" + } + + pub fn prove( + gens_n: &MultiCommitGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + x: &Scalar, + rX: &Scalar, + y: &Scalar, + rY: &Scalar, + z: &Scalar, + rZ: &Scalar, + ) -> ( + ProductProof, + CompressedGroup, + CompressedGroup, + CompressedGroup, + ) { + // transcript.append_protocol_name(ProductProof::protocol_name()); + + // produce five random Scalar + let b1 = random_tape.random_scalar(b"b1"); + let b2 = random_tape.random_scalar(b"b2"); + let b3 = random_tape.random_scalar(b"b3"); + let b4 = random_tape.random_scalar(b"b4"); + let b5 = random_tape.random_scalar(b"b5"); + + let X_unc = x.commit(rX, gens_n); + + let X = X_unc.compress(); + transcript.append_point(&X); + let X_new = GroupElement::decompress(&X); + + assert_eq!(X_unc, X_new.unwrap()); + + let Y = y.commit(rY, gens_n).compress(); + transcript.append_point(&Y); + + let Z = z.commit(rZ, gens_n).compress(); + transcript.append_point(&Z); + + let alpha = b1.commit(&b2, gens_n).compress(); + transcript.append_point(&alpha); + + let beta = b3.commit(&b4, gens_n).compress(); + transcript.append_point(&beta); + + let delta = { + let gens_X = &MultiCommitGens { + n: 1, + G: vec![GroupElement::decompress(&X).unwrap()], + h: gens_n.h, + }; + b3.commit(&b5, gens_X).compress() + }; + transcript.append_point(&delta); + + let c = transcript.challenge_scalar(); + + let z1 = b1 + c * x; + let z2 = b2 + c * rX; + let z3 = b3 + c * y; + let z4 = b4 + c * rY; + let z5 = b5 + c * ((*rZ) - (*rX) * y); + let z = [z1, z2, z3, z4, z5].to_vec(); + + ( + ProductProof { + alpha, + beta, + delta, + z, + }, + X, + Y, + Z, + ) + } + + fn check_equality( + P: &CompressedGroup, + X: &CompressedGroup, + c: &Scalar, + gens_n: &MultiCommitGens, + z1: &Scalar, + z2: &Scalar, + ) -> bool { + println!("{:?}", X); + let lhs = (GroupElement::decompress(P).unwrap() + + GroupElement::decompress(X).unwrap().mul(c.into_repr())) + .compress(); + let rhs = z1.commit(z2, gens_n).compress(); + + lhs == rhs + } + + pub fn verify( + &self, + gens_n: &MultiCommitGens, + transcript: &mut PoseidonTranscript, + X: &CompressedGroup, + Y: &CompressedGroup, + Z: &CompressedGroup, + ) -> Result<(), ProofVerifyError> { + // transcript.append_protocol_name(ProductProof::protocol_name()); + + X.append_to_poseidon(transcript); + Y.append_to_poseidon(transcript); + Z.append_to_poseidon(transcript); + self.alpha.append_to_poseidon(transcript); + self.beta.append_to_poseidon(transcript); + self.delta.append_to_poseidon(transcript); + + let z1 = self.z[0]; + let z2 = self.z[1]; + let z3 = self.z[2]; + let z4 = self.z[3]; + let z5 = self.z[4]; + + let c = transcript.challenge_scalar(); + + if ProductProof::check_equality(&self.alpha, X, &c, gens_n, &z1, &z2) + && ProductProof::check_equality(&self.beta, Y, &c, gens_n, &z3, &z4) + && ProductProof::check_equality( + &self.delta, + Z, + &c, + &MultiCommitGens { + n: 1, + G: vec![X.unpack()?], + h: gens_n.h, + }, + &z3, + &z5, + ) + { + Ok(()) + } else { + Err(ProofVerifyError::InternalError) + } } - } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct DotProductProof { - delta: CompressedGroup, - beta: CompressedGroup, - z: Vec, - z_delta: Scalar, - z_beta: Scalar, + delta: CompressedGroup, + beta: CompressedGroup, + z: Vec, + z_delta: Scalar, + z_beta: Scalar, } impl DotProductProof { - fn protocol_name() -> &'static [u8] { - b"dot product proof" - } - - pub fn compute_dotproduct(a: &[Scalar], b: &[Scalar]) -> Scalar { - assert_eq!(a.len(), b.len()); - (0..a.len()).map(|i| a[i] * b[i]).sum() - } - - pub fn prove( - gens_1: &MultiCommitGens, - gens_n: &MultiCommitGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - x_vec: &[Scalar], - blind_x: &Scalar, - a_vec: &[Scalar], - y: &Scalar, - blind_y: &Scalar, - ) -> (DotProductProof, CompressedGroup, CompressedGroup) { - // transcript.append_protocol_name(DotProductProof::protocol_name()); - - let n = x_vec.len(); - assert_eq!(x_vec.len(), a_vec.len()); - assert_eq!(gens_n.n, a_vec.len()); - assert_eq!(gens_1.n, 1); - - // produce randomness for the proofs - let d_vec = random_tape.random_vector(b"d_vec", n); - let r_delta = random_tape.random_scalar(b"r_delta"); - let r_beta = random_tape.random_scalar(b"r_beta"); - - let Cx = x_vec.commit(blind_x, gens_n).compress(); - Cx.append_to_poseidon(transcript); - - let Cy = y.commit(blind_y, gens_1).compress(); - Cy.append_to_poseidon(transcript); - - transcript.append_scalar_vector(a_vec); - - let delta = d_vec.commit(&r_delta, gens_n).compress(); - delta.append_to_poseidon(transcript); - - let dotproduct_a_d = DotProductProof::compute_dotproduct(a_vec, &d_vec); - - let beta = dotproduct_a_d.commit(&r_beta, gens_1).compress(); - beta.append_to_poseidon(transcript); - - let c = transcript.challenge_scalar(); - - let z = (0..d_vec.len()) - .map(|i| c * x_vec[i] + d_vec[i]) - .collect::>(); - - let z_delta = c * blind_x + r_delta; - let z_beta = c * blind_y + r_beta; - - ( - DotProductProof { - delta, - beta, - z, - z_delta, - z_beta, - }, - Cx, - Cy, - ) - } - - pub fn verify( - &self, - gens_1: &MultiCommitGens, - gens_n: &MultiCommitGens, - transcript: &mut PoseidonTranscript, - a: &[Scalar], - Cx: &CompressedGroup, - Cy: &CompressedGroup, - ) -> Result<(), ProofVerifyError> { - assert_eq!(gens_n.n, a.len()); - assert_eq!(gens_1.n, 1); - - // transcript.append_protocol_name(DotProductProof::protocol_name()); - Cx.append_to_poseidon(transcript); - Cy.append_to_poseidon(transcript); - transcript.append_scalar_vector(a); - self.delta.append_to_poseidon(transcript); - self.beta.append_to_poseidon(transcript); - - let c = transcript.challenge_scalar(); - - let mut result = Cx.unpack()?.mul(c.into_repr()) + self.delta.unpack()? - == self.z.commit(&self.z_delta, gens_n); - - let dotproduct_z_a = DotProductProof::compute_dotproduct(&self.z, a); - result &= Cy.unpack()?.mul(c.into_repr()) + self.beta.unpack()? - == dotproduct_z_a.commit(&self.z_beta, gens_1); - if result { - Ok(()) - } else { - Err(ProofVerifyError::InternalError) + fn protocol_name() -> &'static [u8] { + b"dot product proof" + } + + pub fn compute_dotproduct(a: &[Scalar], b: &[Scalar]) -> Scalar { + assert_eq!(a.len(), b.len()); + (0..a.len()).map(|i| a[i] * b[i]).sum() + } + + pub fn prove( + gens_1: &MultiCommitGens, + gens_n: &MultiCommitGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + x_vec: &[Scalar], + blind_x: &Scalar, + a_vec: &[Scalar], + y: &Scalar, + blind_y: &Scalar, + ) -> (DotProductProof, CompressedGroup, CompressedGroup) { + // transcript.append_protocol_name(DotProductProof::protocol_name()); + + let n = x_vec.len(); + assert_eq!(x_vec.len(), a_vec.len()); + assert_eq!(gens_n.n, a_vec.len()); + assert_eq!(gens_1.n, 1); + + // produce randomness for the proofs + let d_vec = random_tape.random_vector(b"d_vec", n); + let r_delta = random_tape.random_scalar(b"r_delta"); + let r_beta = random_tape.random_scalar(b"r_beta"); + + let Cx = x_vec.commit(blind_x, gens_n).compress(); + Cx.append_to_poseidon(transcript); + + let Cy = y.commit(blind_y, gens_1).compress(); + Cy.append_to_poseidon(transcript); + + transcript.append_scalar_vector(a_vec); + + let delta = d_vec.commit(&r_delta, gens_n).compress(); + delta.append_to_poseidon(transcript); + + let dotproduct_a_d = DotProductProof::compute_dotproduct(a_vec, &d_vec); + + let beta = dotproduct_a_d.commit(&r_beta, gens_1).compress(); + beta.append_to_poseidon(transcript); + + let c = transcript.challenge_scalar(); + + let z = (0..d_vec.len()) + .map(|i| c * x_vec[i] + d_vec[i]) + .collect::>(); + + let z_delta = c * blind_x + r_delta; + let z_beta = c * blind_y + r_beta; + + ( + DotProductProof { + delta, + beta, + z, + z_delta, + z_beta, + }, + Cx, + Cy, + ) + } + + pub fn verify( + &self, + gens_1: &MultiCommitGens, + gens_n: &MultiCommitGens, + transcript: &mut PoseidonTranscript, + a: &[Scalar], + Cx: &CompressedGroup, + Cy: &CompressedGroup, + ) -> Result<(), ProofVerifyError> { + assert_eq!(gens_n.n, a.len()); + assert_eq!(gens_1.n, 1); + + // transcript.append_protocol_name(DotProductProof::protocol_name()); + Cx.append_to_poseidon(transcript); + Cy.append_to_poseidon(transcript); + transcript.append_scalar_vector(a); + self.delta.append_to_poseidon(transcript); + self.beta.append_to_poseidon(transcript); + + let c = transcript.challenge_scalar(); + + let mut result = Cx.unpack()?.mul(c.into_repr()) + self.delta.unpack()? + == self.z.commit(&self.z_delta, gens_n); + + let dotproduct_z_a = DotProductProof::compute_dotproduct(&self.z, a); + result &= Cy.unpack()?.mul(c.into_repr()) + self.beta.unpack()? + == dotproduct_z_a.commit(&self.z_beta, gens_1); + if result { + Ok(()) + } else { + Err(ProofVerifyError::InternalError) + } } - } } #[derive(Clone)] pub struct DotProductProofGens { - n: usize, - pub gens_n: MultiCommitGens, - pub gens_1: MultiCommitGens, + n: usize, + pub gens_n: MultiCommitGens, + pub gens_1: MultiCommitGens, } impl DotProductProofGens { - pub fn new(n: usize, label: &[u8]) -> Self { - let (gens_n, gens_1) = MultiCommitGens::new(n + 1, label).split_at(n); - DotProductProofGens { n, gens_n, gens_1 } - } + pub fn new(n: usize, label: &[u8]) -> Self { + let (gens_n, gens_1) = MultiCommitGens::new(n + 1, label).split_at(n); + DotProductProofGens { n, gens_n, gens_1 } + } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct DotProductProofLog { - bullet_reduction_proof: BulletReductionProof, - delta: CompressedGroup, - beta: CompressedGroup, - z1: Scalar, - z2: Scalar, + bullet_reduction_proof: BulletReductionProof, + delta: CompressedGroup, + beta: CompressedGroup, + z1: Scalar, + z2: Scalar, } impl DotProductProofLog { - fn protocol_name() -> &'static [u8] { - b"dot product proof (log)" - } - - pub fn compute_dotproduct(a: &[Scalar], b: &[Scalar]) -> Scalar { - assert_eq!(a.len(), b.len()); - (0..a.len()).map(|i| a[i] * b[i]).sum() - } - - pub fn prove( - gens: &DotProductProofGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - x_vec: &[Scalar], - blind_x: &Scalar, - a_vec: &[Scalar], - y: &Scalar, - blind_y: &Scalar, - ) -> (DotProductProofLog, CompressedGroup, CompressedGroup) { - // transcript.append_protocol_name(DotProductProofLog::protocol_name()); - - let n = x_vec.len(); - assert_eq!(x_vec.len(), a_vec.len()); - assert_eq!(gens.n, n); - - // produce randomness for generating a proof - let d = random_tape.random_scalar(b"d"); - let r_delta = random_tape.random_scalar(b"r_delta"); - let r_beta = random_tape.random_scalar(b"r_delta"); - let blinds_vec = { - let v1 = random_tape.random_vector(b"blinds_vec_1", 2 * n.log_2()); - let v2 = random_tape.random_vector(b"blinds_vec_2", 2 * n.log_2()); - (0..v1.len()) - .map(|i| (v1[i], v2[i])) - .collect::>() - }; - - let Cx = x_vec.commit(blind_x, &gens.gens_n).compress(); - transcript.append_point(&Cx); - - let Cy = y.commit(blind_y, &gens.gens_1).compress(); - transcript.append_point(&Cy); - transcript.append_scalar_vector(a_vec); - - let blind_Gamma = (*blind_x) + blind_y; - let (bullet_reduction_proof, _Gamma_hat, x_hat, a_hat, g_hat, rhat_Gamma) = - BulletReductionProof::prove( - transcript, - &gens.gens_1.G[0], - &gens.gens_n.G, - &gens.gens_n.h, - x_vec, - a_vec, - &blind_Gamma, - &blinds_vec, - ); - let y_hat = x_hat * a_hat; - - let delta = { - let gens_hat = MultiCommitGens { - n: 1, - G: vec![g_hat], - h: gens.gens_1.h, - }; - d.commit(&r_delta, &gens_hat).compress() - }; - transcript.append_point(&delta); - - let beta = d.commit(&r_beta, &gens.gens_1).compress(); - transcript.append_point(&beta); - - let c = transcript.challenge_scalar(); - - let z1 = d + c * y_hat; - let z2 = a_hat * (c * rhat_Gamma + r_beta) + r_delta; - - ( - DotProductProofLog { - bullet_reduction_proof, - delta, - beta, - z1, - z2, - }, - Cx, - Cy, - ) - } - - pub fn verify( - &self, - n: usize, - gens: &DotProductProofGens, - transcript: &mut PoseidonTranscript, - a: &[Scalar], - Cx: &CompressedGroup, - Cy: &CompressedGroup, - ) -> Result<(), ProofVerifyError> { - assert_eq!(gens.n, n); - assert_eq!(a.len(), n); - - // transcript.append_protocol_name(DotProductProofLog::protocol_name()); - // Cx.append_to_poseidon( transcript); - // Cy.append_to_poseidon( transcript); - // a.append_to_poseidon( transcript); - - transcript.append_point(Cx); - transcript.append_point(Cy); - transcript.append_scalar_vector(a); - - let Gamma = Cx.unpack()? + Cy.unpack()?; - - let (g_hat, Gamma_hat, a_hat) = - self - .bullet_reduction_proof - .verify(n, a, transcript, &Gamma, &gens.gens_n.G)?; - // self.delta.append_to_poseidon( transcript); - // self.beta.append_to_poseidon( transcript); - - transcript.append_point(&self.delta); - transcript.append_point(&self.beta); - - let c = transcript.challenge_scalar(); - - let c_s = &c; - let beta_s = self.beta.unpack()?; - let a_hat_s = &a_hat; - let delta_s = self.delta.unpack()?; - let z1_s = &self.z1; - let z2_s = &self.z2; - - let lhs = - ((Gamma_hat.mul(c_s.into_repr()) + beta_s).mul(a_hat_s.into_repr()) + delta_s).compress(); - let rhs = ((g_hat + gens.gens_1.G[0].mul(a_hat_s.into_repr())).mul(z1_s.into_repr()) - + gens.gens_1.h.mul(z2_s.into_repr())) - .compress(); - - assert_eq!(lhs, rhs); - - if lhs == rhs { - Ok(()) - } else { - Err(ProofVerifyError::InternalError) + fn protocol_name() -> &'static [u8] { + b"dot product proof (log)" + } + + pub fn compute_dotproduct(a: &[Scalar], b: &[Scalar]) -> Scalar { + assert_eq!(a.len(), b.len()); + (0..a.len()).map(|i| a[i] * b[i]).sum() + } + + pub fn prove( + gens: &DotProductProofGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + x_vec: &[Scalar], + blind_x: &Scalar, + a_vec: &[Scalar], + y: &Scalar, + blind_y: &Scalar, + ) -> (DotProductProofLog, CompressedGroup, CompressedGroup) { + // transcript.append_protocol_name(DotProductProofLog::protocol_name()); + + let n = x_vec.len(); + assert_eq!(x_vec.len(), a_vec.len()); + assert_eq!(gens.n, n); + + // produce randomness for generating a proof + let d = random_tape.random_scalar(b"d"); + let r_delta = random_tape.random_scalar(b"r_delta"); + let r_beta = random_tape.random_scalar(b"r_delta"); + let blinds_vec = { + let v1 = random_tape.random_vector(b"blinds_vec_1", 2 * n.log_2()); + let v2 = random_tape.random_vector(b"blinds_vec_2", 2 * n.log_2()); + (0..v1.len()) + .map(|i| (v1[i], v2[i])) + .collect::>() + }; + + let Cx = x_vec.commit(blind_x, &gens.gens_n).compress(); + transcript.append_point(&Cx); + + let Cy = y.commit(blind_y, &gens.gens_1).compress(); + transcript.append_point(&Cy); + transcript.append_scalar_vector(a_vec); + + let blind_Gamma = (*blind_x) + blind_y; + let (bullet_reduction_proof, _Gamma_hat, x_hat, a_hat, g_hat, rhat_Gamma) = + BulletReductionProof::prove( + transcript, + &gens.gens_1.G[0], + &gens.gens_n.G, + &gens.gens_n.h, + x_vec, + a_vec, + &blind_Gamma, + &blinds_vec, + ); + let y_hat = x_hat * a_hat; + + let delta = { + let gens_hat = MultiCommitGens { + n: 1, + G: vec![g_hat], + h: gens.gens_1.h, + }; + d.commit(&r_delta, &gens_hat).compress() + }; + transcript.append_point(&delta); + + let beta = d.commit(&r_beta, &gens.gens_1).compress(); + transcript.append_point(&beta); + + let c = transcript.challenge_scalar(); + + let z1 = d + c * y_hat; + let z2 = a_hat * (c * rhat_Gamma + r_beta) + r_delta; + + ( + DotProductProofLog { + bullet_reduction_proof, + delta, + beta, + z1, + z2, + }, + Cx, + Cy, + ) + } + + pub fn verify( + &self, + n: usize, + gens: &DotProductProofGens, + transcript: &mut PoseidonTranscript, + a: &[Scalar], + Cx: &CompressedGroup, + Cy: &CompressedGroup, + ) -> Result<(), ProofVerifyError> { + assert_eq!(gens.n, n); + assert_eq!(a.len(), n); + + // transcript.append_protocol_name(DotProductProofLog::protocol_name()); + // Cx.append_to_poseidon( transcript); + // Cy.append_to_poseidon( transcript); + // a.append_to_poseidon( transcript); + + transcript.append_point(Cx); + transcript.append_point(Cy); + transcript.append_scalar_vector(a); + + let Gamma = Cx.unpack()? + Cy.unpack()?; + + let (g_hat, Gamma_hat, a_hat) = + self.bullet_reduction_proof + .verify(n, a, transcript, &Gamma, &gens.gens_n.G)?; + // self.delta.append_to_poseidon( transcript); + // self.beta.append_to_poseidon( transcript); + + transcript.append_point(&self.delta); + transcript.append_point(&self.beta); + + let c = transcript.challenge_scalar(); + + let c_s = &c; + let beta_s = self.beta.unpack()?; + let a_hat_s = &a_hat; + let delta_s = self.delta.unpack()?; + let z1_s = &self.z1; + let z2_s = &self.z2; + + let lhs = ((Gamma_hat.mul(c_s.into_repr()) + beta_s).mul(a_hat_s.into_repr()) + delta_s) + .compress(); + let rhs = ((g_hat + gens.gens_1.G[0].mul(a_hat_s.into_repr())).mul(z1_s.into_repr()) + + gens.gens_1.h.mul(z2_s.into_repr())) + .compress(); + + assert_eq!(lhs, rhs); + + if lhs == rhs { + Ok(()) + } else { + Err(ProofVerifyError::InternalError) + } } - } } #[cfg(test)] mod tests { - use crate::parameters::poseidon_params; - - use super::*; - use ark_std::UniformRand; - #[test] - fn check_knowledgeproof() { - let mut rng = ark_std::rand::thread_rng(); - - let gens_1 = MultiCommitGens::new(1, b"test-knowledgeproof"); - - let x = Scalar::rand(&mut rng); - let r = Scalar::rand(&mut rng); - - let params = poseidon_params(); - - let mut random_tape = RandomTape::new(b"proof"); - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let (proof, committed_value) = - KnowledgeProof::prove(&gens_1, &mut prover_transcript, &mut random_tape, &x, &r); - - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify(&gens_1, &mut verifier_transcript, &committed_value) - .is_ok()); - } - - #[test] - fn check_equalityproof() { - let mut rng = ark_std::rand::thread_rng(); - let params = poseidon_params(); - - let gens_1 = MultiCommitGens::new(1, b"test-equalityproof"); - let v1 = Scalar::rand(&mut rng); - let v2 = v1; - let s1 = Scalar::rand(&mut rng); - let s2 = Scalar::rand(&mut rng); - - let mut random_tape = RandomTape::new(b"proof"); - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let (proof, C1, C2) = EqualityProof::prove( - &gens_1, - &mut prover_transcript, - &mut random_tape, - &v1, - &s1, - &v2, - &s2, - ); - - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify(&gens_1, &mut verifier_transcript, &C1, &C2) - .is_ok()); - } - - #[test] - fn check_productproof() { - let mut rng = ark_std::rand::thread_rng(); - let pt = GroupElement::rand(&mut rng); - let pt_c = pt.compress(); - let pt2 = GroupElement::decompress(&pt_c).unwrap(); - assert_eq!(pt, pt2); - let params = poseidon_params(); - - let gens_1 = MultiCommitGens::new(1, b"test-productproof"); - let x = Scalar::rand(&mut rng); - let rX = Scalar::rand(&mut rng); - let y = Scalar::rand(&mut rng); - let rY = Scalar::rand(&mut rng); - let z = x * y; - let rZ = Scalar::rand(&mut rng); - - let mut random_tape = RandomTape::new(b"proof"); - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let (proof, X, Y, Z) = ProductProof::prove( - &gens_1, - &mut prover_transcript, - &mut random_tape, - &x, - &rX, - &y, - &rY, - &z, - &rZ, - ); - - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify(&gens_1, &mut verifier_transcript, &X, &Y, &Z) - .is_ok()); - } - - #[test] - fn check_dotproductproof() { - let mut rng = ark_std::rand::thread_rng(); - - let n = 1024; - - let gens_1 = MultiCommitGens::new(1, b"test-two"); - let gens_1024 = MultiCommitGens::new(n, b"test-1024"); - let params = poseidon_params(); - - let mut x: Vec = Vec::new(); - let mut a: Vec = Vec::new(); - for _ in 0..n { - x.push(Scalar::rand(&mut rng)); - a.push(Scalar::rand(&mut rng)); + use crate::parameters::poseidon_params; + + use super::*; + use ark_std::UniformRand; + #[test] + fn check_knowledgeproof() { + let mut rng = ark_std::rand::thread_rng(); + + let gens_1 = MultiCommitGens::new(1, b"test-knowledgeproof"); + + let x = Scalar::rand(&mut rng); + let r = Scalar::rand(&mut rng); + + let params = poseidon_params(); + + let mut random_tape = RandomTape::new(b"proof"); + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let (proof, committed_value) = + KnowledgeProof::prove(&gens_1, &mut prover_transcript, &mut random_tape, &x, &r); + + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify(&gens_1, &mut verifier_transcript, &committed_value) + .is_ok()); + } + + #[test] + fn check_equalityproof() { + let mut rng = ark_std::rand::thread_rng(); + let params = poseidon_params(); + + let gens_1 = MultiCommitGens::new(1, b"test-equalityproof"); + let v1 = Scalar::rand(&mut rng); + let v2 = v1; + let s1 = Scalar::rand(&mut rng); + let s2 = Scalar::rand(&mut rng); + + let mut random_tape = RandomTape::new(b"proof"); + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let (proof, C1, C2) = EqualityProof::prove( + &gens_1, + &mut prover_transcript, + &mut random_tape, + &v1, + &s1, + &v2, + &s2, + ); + + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify(&gens_1, &mut verifier_transcript, &C1, &C2) + .is_ok()); + } + + #[test] + fn check_productproof() { + let mut rng = ark_std::rand::thread_rng(); + let pt = GroupElement::rand(&mut rng); + let pt_c = pt.compress(); + let pt2 = GroupElement::decompress(&pt_c).unwrap(); + assert_eq!(pt, pt2); + let params = poseidon_params(); + + let gens_1 = MultiCommitGens::new(1, b"test-productproof"); + let x = Scalar::rand(&mut rng); + let rX = Scalar::rand(&mut rng); + let y = Scalar::rand(&mut rng); + let rY = Scalar::rand(&mut rng); + let z = x * y; + let rZ = Scalar::rand(&mut rng); + + let mut random_tape = RandomTape::new(b"proof"); + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let (proof, X, Y, Z) = ProductProof::prove( + &gens_1, + &mut prover_transcript, + &mut random_tape, + &x, + &rX, + &y, + &rY, + &z, + &rZ, + ); + + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify(&gens_1, &mut verifier_transcript, &X, &Y, &Z) + .is_ok()); + } + + #[test] + fn check_dotproductproof() { + let mut rng = ark_std::rand::thread_rng(); + + let n = 1024; + + let gens_1 = MultiCommitGens::new(1, b"test-two"); + let gens_1024 = MultiCommitGens::new(n, b"test-1024"); + let params = poseidon_params(); + + let mut x: Vec = Vec::new(); + let mut a: Vec = Vec::new(); + for _ in 0..n { + x.push(Scalar::rand(&mut rng)); + a.push(Scalar::rand(&mut rng)); + } + let y = DotProductProofLog::compute_dotproduct(&x, &a); + let r_x = Scalar::rand(&mut rng); + let r_y = Scalar::rand(&mut rng); + + let mut random_tape = RandomTape::new(b"proof"); + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let (proof, Cx, Cy) = DotProductProof::prove( + &gens_1, + &gens_1024, + &mut prover_transcript, + &mut random_tape, + &x, + &r_x, + &a, + &y, + &r_y, + ); + + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify(&gens_1, &gens_1024, &mut verifier_transcript, &a, &Cx, &Cy) + .is_ok()); + } + + #[test] + fn check_dotproductproof_log() { + let mut rng = ark_std::rand::thread_rng(); + + let n = 1024; + + let gens = DotProductProofGens::new(n, b"test-1024"); + + let x: Vec = (0..n).map(|_i| Scalar::rand(&mut rng)).collect(); + let a: Vec = (0..n).map(|_i| Scalar::rand(&mut rng)).collect(); + let y = DotProductProof::compute_dotproduct(&x, &a); + + let r_x = Scalar::rand(&mut rng); + let r_y = Scalar::rand(&mut rng); + + let params = poseidon_params(); + let mut random_tape = RandomTape::new(b"proof"); + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let (proof, Cx, Cy) = DotProductProofLog::prove( + &gens, + &mut prover_transcript, + &mut random_tape, + &x, + &r_x, + &a, + &y, + &r_y, + ); + + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify(n, &gens, &mut verifier_transcript, &a, &Cx, &Cy) + .is_ok()); } - let y = DotProductProofLog::compute_dotproduct(&x, &a); - let r_x = Scalar::rand(&mut rng); - let r_y = Scalar::rand(&mut rng); - - let mut random_tape = RandomTape::new(b"proof"); - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let (proof, Cx, Cy) = DotProductProof::prove( - &gens_1, - &gens_1024, - &mut prover_transcript, - &mut random_tape, - &x, - &r_x, - &a, - &y, - &r_y, - ); - - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify(&gens_1, &gens_1024, &mut verifier_transcript, &a, &Cx, &Cy) - .is_ok()); - } - - #[test] - fn check_dotproductproof_log() { - let mut rng = ark_std::rand::thread_rng(); - - let n = 1024; - - let gens = DotProductProofGens::new(n, b"test-1024"); - - let x: Vec = (0..n).map(|_i| Scalar::rand(&mut rng)).collect(); - let a: Vec = (0..n).map(|_i| Scalar::rand(&mut rng)).collect(); - let y = DotProductProof::compute_dotproduct(&x, &a); - - let r_x = Scalar::rand(&mut rng); - let r_y = Scalar::rand(&mut rng); - - let params = poseidon_params(); - let mut random_tape = RandomTape::new(b"proof"); - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let (proof, Cx, Cy) = DotProductProofLog::prove( - &gens, - &mut prover_transcript, - &mut random_tape, - &x, - &r_x, - &a, - &y, - &r_y, - ); - - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify(n, &gens, &mut verifier_transcript, &a, &Cx, &Cy) - .is_ok()); - } } diff --git a/src/parameters.rs b/src/parameters.rs index 142d327..5cf3761 100644 --- a/src/parameters.rs +++ b/src/parameters.rs @@ -146,32 +146,31 @@ array!["228517621981785468369663538305998424621845824654552006112396193307208970 /// TODO pub fn poseidon_params() -> PoseidonParameters { - let arks = FR["ark"] - .members() - .map(|ark| { - ark + let arks = FR["ark"] .members() - .map(|v| Fr::from_str(v.as_str().unwrap()).unwrap()) - .collect::>() - }) - .collect::>(); - let mds = FR["mds"] - .members() - .map(|m| { - m.members() - .map(|v| Fr::from_str(v.as_str().unwrap()).unwrap()) - .collect::>() - }) - .collect::>(); - PoseidonParameters::new( - FR["full_rounds"].as_u32().unwrap(), - FR["partial_rounds"].as_u32().unwrap(), - FR["alpha"].as_u64().unwrap(), - mds, - arks, - ) + .map(|ark| { + ark.members() + .map(|v| Fr::from_str(v.as_str().unwrap()).unwrap()) + .collect::>() + }) + .collect::>(); + let mds = FR["mds"] + .members() + .map(|m| { + m.members() + .map(|v| Fr::from_str(v.as_str().unwrap()).unwrap()) + .collect::>() + }) + .collect::>(); + PoseidonParameters::new( + FR["full_rounds"].as_u32().unwrap(), + FR["partial_rounds"].as_u32().unwrap(), + FR["alpha"].as_u64().unwrap(), + mds, + arks, + ) } lazy_static! { - pub static ref POSEIDON_PARAMETERS_FR_377: PoseidonParameters = poseidon_params(); + pub static ref POSEIDON_PARAMETERS_FR_377: PoseidonParameters = poseidon_params(); } diff --git a/src/poseidon_transcript.rs b/src/poseidon_transcript.rs index 577e2de..479e354 100644 --- a/src/poseidon_transcript.rs +++ b/src/poseidon_transcript.rs @@ -6,77 +6,77 @@ use ark_poly_commit::multilinear_pc::data_structures::Commitment; use ark_serialize::CanonicalSerialize; // use ark_r1cs_std::prelude::*; use ark_sponge::{ - poseidon::{PoseidonParameters, PoseidonSponge}, - CryptographicSponge, + poseidon::{PoseidonParameters, PoseidonSponge}, + CryptographicSponge, }; #[derive(Clone)] /// TODO pub struct PoseidonTranscript { - sponge: PoseidonSponge, - params: PoseidonParameters, + sponge: PoseidonSponge, + params: PoseidonParameters, } impl PoseidonTranscript { - /// create a new transcript - pub fn new(params: &PoseidonParameters) -> Self { - let sponge = PoseidonSponge::new(params); - PoseidonTranscript { - sponge, - params: params.clone(), + /// create a new transcript + pub fn new(params: &PoseidonParameters) -> Self { + let sponge = PoseidonSponge::new(params); + PoseidonTranscript { + sponge, + params: params.clone(), + } } - } - pub fn new_from_state(&mut self, challenge: &Scalar) { - self.sponge = PoseidonSponge::new(&self.params); - self.append_scalar(challenge); - } + pub fn new_from_state(&mut self, challenge: &Scalar) { + self.sponge = PoseidonSponge::new(&self.params); + self.append_scalar(challenge); + } - pub fn append_u64(&mut self, x: u64) { - self.sponge.absorb(&x); - } + pub fn append_u64(&mut self, x: u64) { + self.sponge.absorb(&x); + } - pub fn append_bytes(&mut self, x: &Vec) { - self.sponge.absorb(x); - } + pub fn append_bytes(&mut self, x: &Vec) { + self.sponge.absorb(x); + } - pub fn append_scalar(&mut self, scalar: &Scalar) { - self.sponge.absorb(&scalar); - } + pub fn append_scalar(&mut self, scalar: &Scalar) { + self.sponge.absorb(&scalar); + } - pub fn append_point(&mut self, point: &CompressedGroup) { - self.sponge.absorb(&point.0); - } + pub fn append_point(&mut self, point: &CompressedGroup) { + self.sponge.absorb(&point.0); + } - pub fn append_scalar_vector(&mut self, scalars: &[Scalar]) { - for scalar in scalars.iter() { - self.append_scalar(scalar); + pub fn append_scalar_vector(&mut self, scalars: &[Scalar]) { + for scalar in scalars.iter() { + self.append_scalar(scalar); + } } - } - pub fn challenge_scalar(&mut self) -> Scalar { - self.sponge.squeeze_field_elements(1).remove(0) - } + pub fn challenge_scalar(&mut self) -> Scalar { + self.sponge.squeeze_field_elements(1).remove(0) + } - pub fn challenge_vector(&mut self, len: usize) -> Vec { - self.sponge.squeeze_field_elements(len) - } + pub fn challenge_vector(&mut self, len: usize) -> Vec { + self.sponge.squeeze_field_elements(len) + } } pub trait AppendToPoseidon { - fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript); + fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript); } impl AppendToPoseidon for CompressedGroup { - fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { - transcript.append_point(self); - } + fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { + transcript.append_point(self); + } } impl AppendToPoseidon for Commitment { - fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { - let mut bytes = Vec::new(); - self.serialize(&mut bytes).unwrap(); - transcript.append_bytes(&bytes); - } + fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { + let mut bytes = Vec::new(); + self.serialize(&mut bytes).unwrap(); + transcript.append_bytes(&bytes); + } } diff --git a/src/product_tree.rs b/src/product_tree.rs index 5d4b071..7d76faf 100644 --- a/src/product_tree.rs +++ b/src/product_tree.rs @@ -11,475 +11,481 @@ use ark_std::One; #[derive(Debug)] pub struct ProductCircuit { - left_vec: Vec, - right_vec: Vec, + left_vec: Vec, + right_vec: Vec, } impl ProductCircuit { - fn compute_layer( - inp_left: &DensePolynomial, - inp_right: &DensePolynomial, - ) -> (DensePolynomial, DensePolynomial) { - let len = inp_left.len() + inp_right.len(); - let outp_left = (0..len / 4) - .map(|i| inp_left[i] * inp_right[i]) - .collect::>(); - let outp_right = (len / 4..len / 2) - .map(|i| inp_left[i] * inp_right[i]) - .collect::>(); - - ( - DensePolynomial::new(outp_left), - DensePolynomial::new(outp_right), - ) - } - - pub fn new(poly: &DensePolynomial) -> Self { - let mut left_vec: Vec = Vec::new(); - let mut right_vec: Vec = Vec::new(); - - let num_layers = poly.len().log_2(); - let (outp_left, outp_right) = poly.split(poly.len() / 2); - - left_vec.push(outp_left); - right_vec.push(outp_right); - - for i in 0..num_layers - 1 { - let (outp_left, outp_right) = ProductCircuit::compute_layer(&left_vec[i], &right_vec[i]); - left_vec.push(outp_left); - right_vec.push(outp_right); + fn compute_layer( + inp_left: &DensePolynomial, + inp_right: &DensePolynomial, + ) -> (DensePolynomial, DensePolynomial) { + let len = inp_left.len() + inp_right.len(); + let outp_left = (0..len / 4) + .map(|i| inp_left[i] * inp_right[i]) + .collect::>(); + let outp_right = (len / 4..len / 2) + .map(|i| inp_left[i] * inp_right[i]) + .collect::>(); + + ( + DensePolynomial::new(outp_left), + DensePolynomial::new(outp_right), + ) } - ProductCircuit { - left_vec, - right_vec, + pub fn new(poly: &DensePolynomial) -> Self { + let mut left_vec: Vec = Vec::new(); + let mut right_vec: Vec = Vec::new(); + + let num_layers = poly.len().log_2(); + let (outp_left, outp_right) = poly.split(poly.len() / 2); + + left_vec.push(outp_left); + right_vec.push(outp_right); + + for i in 0..num_layers - 1 { + let (outp_left, outp_right) = + ProductCircuit::compute_layer(&left_vec[i], &right_vec[i]); + left_vec.push(outp_left); + right_vec.push(outp_right); + } + + ProductCircuit { + left_vec, + right_vec, + } + } + + pub fn evaluate(&self) -> Scalar { + let len = self.left_vec.len(); + assert_eq!(self.left_vec[len - 1].get_num_vars(), 0); + assert_eq!(self.right_vec[len - 1].get_num_vars(), 0); + self.left_vec[len - 1][0] * self.right_vec[len - 1][0] } - } - - pub fn evaluate(&self) -> Scalar { - let len = self.left_vec.len(); - assert_eq!(self.left_vec[len - 1].get_num_vars(), 0); - assert_eq!(self.right_vec[len - 1].get_num_vars(), 0); - self.left_vec[len - 1][0] * self.right_vec[len - 1][0] - } } pub struct DotProductCircuit { - left: DensePolynomial, - right: DensePolynomial, - weight: DensePolynomial, + left: DensePolynomial, + right: DensePolynomial, + weight: DensePolynomial, } impl DotProductCircuit { - pub fn new(left: DensePolynomial, right: DensePolynomial, weight: DensePolynomial) -> Self { - assert_eq!(left.len(), right.len()); - assert_eq!(left.len(), weight.len()); - DotProductCircuit { - left, - right, - weight, + pub fn new(left: DensePolynomial, right: DensePolynomial, weight: DensePolynomial) -> Self { + assert_eq!(left.len(), right.len()); + assert_eq!(left.len(), weight.len()); + DotProductCircuit { + left, + right, + weight, + } + } + + pub fn evaluate(&self) -> Scalar { + (0..self.left.len()) + .map(|i| self.left[i] * self.right[i] * self.weight[i]) + .sum() + } + + pub fn split(&mut self) -> (DotProductCircuit, DotProductCircuit) { + let idx = self.left.len() / 2; + assert_eq!(idx * 2, self.left.len()); + let (l1, l2) = self.left.split(idx); + let (r1, r2) = self.right.split(idx); + let (w1, w2) = self.weight.split(idx); + ( + DotProductCircuit { + left: l1, + right: r1, + weight: w1, + }, + DotProductCircuit { + left: l2, + right: r2, + weight: w2, + }, + ) } - } - - pub fn evaluate(&self) -> Scalar { - (0..self.left.len()) - .map(|i| self.left[i] * self.right[i] * self.weight[i]) - .sum() - } - - pub fn split(&mut self) -> (DotProductCircuit, DotProductCircuit) { - let idx = self.left.len() / 2; - assert_eq!(idx * 2, self.left.len()); - let (l1, l2) = self.left.split(idx); - let (r1, r2) = self.right.split(idx); - let (w1, w2) = self.weight.split(idx); - ( - DotProductCircuit { - left: l1, - right: r1, - weight: w1, - }, - DotProductCircuit { - left: l2, - right: r2, - weight: w2, - }, - ) - } } #[allow(dead_code)] #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct LayerProof { - pub proof: SumcheckInstanceProof, - pub claims: Vec, + pub proof: SumcheckInstanceProof, + pub claims: Vec, } #[allow(dead_code)] impl LayerProof { - pub fn verify( - &self, - claim: Scalar, - num_rounds: usize, - degree_bound: usize, - transcript: &mut PoseidonTranscript, - ) -> (Scalar, Vec) { - self - .proof - .verify(claim, num_rounds, degree_bound, transcript) - .unwrap() - } + pub fn verify( + &self, + claim: Scalar, + num_rounds: usize, + degree_bound: usize, + transcript: &mut PoseidonTranscript, + ) -> (Scalar, Vec) { + self.proof + .verify(claim, num_rounds, degree_bound, transcript) + .unwrap() + } } #[allow(dead_code)] #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct LayerProofBatched { - pub proof: SumcheckInstanceProof, - pub claims_prod_left: Vec, - pub claims_prod_right: Vec, + pub proof: SumcheckInstanceProof, + pub claims_prod_left: Vec, + pub claims_prod_right: Vec, } #[allow(dead_code)] impl LayerProofBatched { - pub fn verify( - &self, - claim: Scalar, - num_rounds: usize, - degree_bound: usize, - transcript: &mut PoseidonTranscript, - ) -> (Scalar, Vec) { - self - .proof - .verify(claim, num_rounds, degree_bound, transcript) - .unwrap() - } + pub fn verify( + &self, + claim: Scalar, + num_rounds: usize, + degree_bound: usize, + transcript: &mut PoseidonTranscript, + ) -> (Scalar, Vec) { + self.proof + .verify(claim, num_rounds, degree_bound, transcript) + .unwrap() + } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct ProductCircuitEvalProof { - proof: Vec, + proof: Vec, } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct ProductCircuitEvalProofBatched { - proof: Vec, - claims_dotp: (Vec, Vec, Vec), + proof: Vec, + claims_dotp: (Vec, Vec, Vec), } impl ProductCircuitEvalProof { - #![allow(dead_code)] - pub fn prove( - circuit: &mut ProductCircuit, - transcript: &mut PoseidonTranscript, - ) -> (Self, Scalar, Vec) { - let mut proof: Vec = Vec::new(); - let num_layers = circuit.left_vec.len(); - - let mut claim = circuit.evaluate(); - let mut rand = Vec::new(); - for layer_id in (0..num_layers).rev() { - let len = circuit.left_vec[layer_id].len() + circuit.right_vec[layer_id].len(); - - let mut poly_C = DensePolynomial::new(EqPolynomial::new(rand.clone()).evals()); - assert_eq!(poly_C.len(), len / 2); - - let num_rounds_prod = poly_C.len().log_2(); - let comb_func_prod = |poly_A_comp: &Scalar, - poly_B_comp: &Scalar, - poly_C_comp: &Scalar| - -> Scalar { (*poly_A_comp) * poly_B_comp * poly_C_comp }; - let (proof_prod, rand_prod, claims_prod) = SumcheckInstanceProof::prove_cubic( - &claim, - num_rounds_prod, - &mut circuit.left_vec[layer_id], - &mut circuit.right_vec[layer_id], - &mut poly_C, - comb_func_prod, - transcript, - ); - - transcript.append_scalar(&claims_prod[0]); - transcript.append_scalar(&claims_prod[1]); - - // produce a random challenge - let r_layer = transcript.challenge_scalar(); - claim = claims_prod[0] + r_layer * (claims_prod[1] - claims_prod[0]); - - let mut ext = vec![r_layer]; - ext.extend(rand_prod); - rand = ext; - - proof.push(LayerProof { - proof: proof_prod, - claims: claims_prod[0..claims_prod.len() - 1].to_vec(), - }); - } + #![allow(dead_code)] + pub fn prove( + circuit: &mut ProductCircuit, + transcript: &mut PoseidonTranscript, + ) -> (Self, Scalar, Vec) { + let mut proof: Vec = Vec::new(); + let num_layers = circuit.left_vec.len(); + + let mut claim = circuit.evaluate(); + let mut rand = Vec::new(); + for layer_id in (0..num_layers).rev() { + let len = circuit.left_vec[layer_id].len() + circuit.right_vec[layer_id].len(); + + let mut poly_C = DensePolynomial::new(EqPolynomial::new(rand.clone()).evals()); + assert_eq!(poly_C.len(), len / 2); + + let num_rounds_prod = poly_C.len().log_2(); + let comb_func_prod = + |poly_A_comp: &Scalar, poly_B_comp: &Scalar, poly_C_comp: &Scalar| -> Scalar { + (*poly_A_comp) * poly_B_comp * poly_C_comp + }; + let (proof_prod, rand_prod, claims_prod) = SumcheckInstanceProof::prove_cubic( + &claim, + num_rounds_prod, + &mut circuit.left_vec[layer_id], + &mut circuit.right_vec[layer_id], + &mut poly_C, + comb_func_prod, + transcript, + ); + + transcript.append_scalar(&claims_prod[0]); + transcript.append_scalar(&claims_prod[1]); + + // produce a random challenge + let r_layer = transcript.challenge_scalar(); + claim = claims_prod[0] + r_layer * (claims_prod[1] - claims_prod[0]); + + let mut ext = vec![r_layer]; + ext.extend(rand_prod); + rand = ext; + + proof.push(LayerProof { + proof: proof_prod, + claims: claims_prod[0..claims_prod.len() - 1].to_vec(), + }); + } - (ProductCircuitEvalProof { proof }, claim, rand) - } - - pub fn verify( - &self, - eval: Scalar, - len: usize, - transcript: &mut PoseidonTranscript, - ) -> (Scalar, Vec) { - let num_layers = len.log_2(); - let mut claim = eval; - let mut rand: Vec = Vec::new(); - //let mut num_rounds = 0; - assert_eq!(self.proof.len(), num_layers); - for (num_rounds, i) in (0..num_layers).enumerate() { - let (claim_last, rand_prod) = self.proof[i].verify(claim, num_rounds, 3, transcript); - - let claims_prod = &self.proof[i].claims; - transcript.append_scalar(&claims_prod[0]); - transcript.append_scalar(&claims_prod[1]); - - assert_eq!(rand.len(), rand_prod.len()); - let eq: Scalar = (0..rand.len()) - .map(|i| { - rand[i] * rand_prod[i] + (Scalar::one() - rand[i]) * (Scalar::one() - rand_prod[i]) - }) - .product(); - assert_eq!(claims_prod[0] * claims_prod[1] * eq, claim_last); - - // produce a random challenge - let r_layer = transcript.challenge_scalar(); - claim = (Scalar::one() - r_layer) * claims_prod[0] + r_layer * claims_prod[1]; - let mut ext = vec![r_layer]; - ext.extend(rand_prod); - rand = ext; + (ProductCircuitEvalProof { proof }, claim, rand) } - (claim, rand) - } -} - -impl ProductCircuitEvalProofBatched { - pub fn prove( - prod_circuit_vec: &mut Vec<&mut ProductCircuit>, - dotp_circuit_vec: &mut Vec<&mut DotProductCircuit>, - transcript: &mut PoseidonTranscript, - ) -> (Self, Vec) { - assert!(!prod_circuit_vec.is_empty()); - - let mut claims_dotp_final = (Vec::new(), Vec::new(), Vec::new()); - - let mut proof_layers: Vec = Vec::new(); - let num_layers = prod_circuit_vec[0].left_vec.len(); - let mut claims_to_verify = (0..prod_circuit_vec.len()) - .map(|i| prod_circuit_vec[i].evaluate()) - .collect::>(); - let mut rand = Vec::new(); - for layer_id in (0..num_layers).rev() { - // prepare paralell instance that share poly_C first - let len = prod_circuit_vec[0].left_vec[layer_id].len() - + prod_circuit_vec[0].right_vec[layer_id].len(); - - let mut poly_C_par = DensePolynomial::new(EqPolynomial::new(rand.clone()).evals()); - assert_eq!(poly_C_par.len(), len / 2); - - let num_rounds_prod = poly_C_par.len().log_2(); - let comb_func_prod = |poly_A_comp: &Scalar, - poly_B_comp: &Scalar, - poly_C_comp: &Scalar| - -> Scalar { (*poly_A_comp) * poly_B_comp * poly_C_comp }; - - let mut poly_A_batched_par: Vec<&mut DensePolynomial> = Vec::new(); - let mut poly_B_batched_par: Vec<&mut DensePolynomial> = Vec::new(); - for prod_circuit in prod_circuit_vec.iter_mut() { - poly_A_batched_par.push(&mut prod_circuit.left_vec[layer_id]); - poly_B_batched_par.push(&mut prod_circuit.right_vec[layer_id]) - } - let poly_vec_par = ( - &mut poly_A_batched_par, - &mut poly_B_batched_par, - &mut poly_C_par, - ); - - // prepare sequential instances that don't share poly_C - let mut poly_A_batched_seq: Vec<&mut DensePolynomial> = Vec::new(); - let mut poly_B_batched_seq: Vec<&mut DensePolynomial> = Vec::new(); - let mut poly_C_batched_seq: Vec<&mut DensePolynomial> = Vec::new(); - if layer_id == 0 && !dotp_circuit_vec.is_empty() { - // add additional claims - for item in dotp_circuit_vec.iter() { - claims_to_verify.push(item.evaluate()); - assert_eq!(len / 2, item.left.len()); - assert_eq!(len / 2, item.right.len()); - assert_eq!(len / 2, item.weight.len()); - } - - for dotp_circuit in dotp_circuit_vec.iter_mut() { - poly_A_batched_seq.push(&mut dotp_circuit.left); - poly_B_batched_seq.push(&mut dotp_circuit.right); - poly_C_batched_seq.push(&mut dotp_circuit.weight); - } - } - let poly_vec_seq = ( - &mut poly_A_batched_seq, - &mut poly_B_batched_seq, - &mut poly_C_batched_seq, - ); - - // produce a fresh set of coeffs and a joint claim - let coeff_vec = transcript.challenge_vector(claims_to_verify.len()); - let claim = (0..claims_to_verify.len()) - .map(|i| claims_to_verify[i] * coeff_vec[i]) - .sum(); - - let (proof, rand_prod, claims_prod, claims_dotp) = SumcheckInstanceProof::prove_cubic_batched( - &claim, - num_rounds_prod, - poly_vec_par, - poly_vec_seq, - &coeff_vec, - comb_func_prod, - transcript, - ); - - let (claims_prod_left, claims_prod_right, _claims_eq) = claims_prod; - for i in 0..prod_circuit_vec.len() { - transcript.append_scalar(&claims_prod_left[i]); - transcript.append_scalar(&claims_prod_right[i]); - } - - if layer_id == 0 && !dotp_circuit_vec.is_empty() { - let (claims_dotp_left, claims_dotp_right, claims_dotp_weight) = claims_dotp; - for i in 0..dotp_circuit_vec.len() { - transcript.append_scalar(&claims_dotp_left[i]); - transcript.append_scalar(&claims_dotp_right[i]); - transcript.append_scalar(&claims_dotp_weight[i]); + pub fn verify( + &self, + eval: Scalar, + len: usize, + transcript: &mut PoseidonTranscript, + ) -> (Scalar, Vec) { + let num_layers = len.log_2(); + let mut claim = eval; + let mut rand: Vec = Vec::new(); + //let mut num_rounds = 0; + assert_eq!(self.proof.len(), num_layers); + for (num_rounds, i) in (0..num_layers).enumerate() { + let (claim_last, rand_prod) = self.proof[i].verify(claim, num_rounds, 3, transcript); + + let claims_prod = &self.proof[i].claims; + transcript.append_scalar(&claims_prod[0]); + transcript.append_scalar(&claims_prod[1]); + + assert_eq!(rand.len(), rand_prod.len()); + let eq: Scalar = (0..rand.len()) + .map(|i| { + rand[i] * rand_prod[i] + + (Scalar::one() - rand[i]) * (Scalar::one() - rand_prod[i]) + }) + .product(); + assert_eq!(claims_prod[0] * claims_prod[1] * eq, claim_last); + + // produce a random challenge + let r_layer = transcript.challenge_scalar(); + claim = (Scalar::one() - r_layer) * claims_prod[0] + r_layer * claims_prod[1]; + let mut ext = vec![r_layer]; + ext.extend(rand_prod); + rand = ext; } - claims_dotp_final = (claims_dotp_left, claims_dotp_right, claims_dotp_weight); - } - // produce a random challenge to condense two claims into a single claim - let r_layer = transcript.challenge_scalar(); - - claims_to_verify = (0..prod_circuit_vec.len()) - .map(|i| claims_prod_left[i] + r_layer * (claims_prod_right[i] - claims_prod_left[i])) - .collect::>(); - - let mut ext = vec![r_layer]; - ext.extend(rand_prod); - rand = ext; - - proof_layers.push(LayerProofBatched { - proof, - claims_prod_left, - claims_prod_right, - }); + (claim, rand) } +} - ( - ProductCircuitEvalProofBatched { - proof: proof_layers, - claims_dotp: claims_dotp_final, - }, - rand, - ) - } - - pub fn verify( - &self, - claims_prod_vec: &[Scalar], - claims_dotp_vec: &[Scalar], - len: usize, - transcript: &mut PoseidonTranscript, - ) -> (Vec, Vec, Vec) { - let num_layers = len.log_2(); - let mut rand: Vec = Vec::new(); - //let mut num_rounds = 0; - assert_eq!(self.proof.len(), num_layers); - - let mut claims_to_verify = claims_prod_vec.to_owned(); - let mut claims_to_verify_dotp: Vec = Vec::new(); - for (num_rounds, i) in (0..num_layers).enumerate() { - if i == num_layers - 1 { - claims_to_verify.extend(claims_dotp_vec); - } - - // produce random coefficients, one for each instance - let coeff_vec = transcript.challenge_vector(claims_to_verify.len()); - - // produce a joint claim - let claim = (0..claims_to_verify.len()) - .map(|i| claims_to_verify[i] * coeff_vec[i]) - .sum(); - - let (claim_last, rand_prod) = self.proof[i].verify(claim, num_rounds, 3, transcript); - - let claims_prod_left = &self.proof[i].claims_prod_left; - let claims_prod_right = &self.proof[i].claims_prod_right; - assert_eq!(claims_prod_left.len(), claims_prod_vec.len()); - assert_eq!(claims_prod_right.len(), claims_prod_vec.len()); - - for i in 0..claims_prod_vec.len() { - transcript.append_scalar(&claims_prod_left[i]); - transcript.append_scalar(&claims_prod_right[i]); - } - - assert_eq!(rand.len(), rand_prod.len()); - let eq: Scalar = (0..rand.len()) - .map(|i| { - rand[i] * rand_prod[i] + (Scalar::one() - rand[i]) * (Scalar::one() - rand_prod[i]) - }) - .product(); - let mut claim_expected: Scalar = (0..claims_prod_vec.len()) - .map(|i| coeff_vec[i] * (claims_prod_left[i] * claims_prod_right[i] * eq)) - .sum(); - - // add claims from the dotp instances - if i == num_layers - 1 { - let num_prod_instances = claims_prod_vec.len(); - let (claims_dotp_left, claims_dotp_right, claims_dotp_weight) = &self.claims_dotp; - for i in 0..claims_dotp_left.len() { - transcript.append_scalar(&claims_dotp_left[i]); - transcript.append_scalar(&claims_dotp_right[i]); - transcript.append_scalar(&claims_dotp_weight[i]); - - claim_expected += coeff_vec[i + num_prod_instances] - * claims_dotp_left[i] - * claims_dotp_right[i] - * claims_dotp_weight[i]; +impl ProductCircuitEvalProofBatched { + pub fn prove( + prod_circuit_vec: &mut Vec<&mut ProductCircuit>, + dotp_circuit_vec: &mut Vec<&mut DotProductCircuit>, + transcript: &mut PoseidonTranscript, + ) -> (Self, Vec) { + assert!(!prod_circuit_vec.is_empty()); + + let mut claims_dotp_final = (Vec::new(), Vec::new(), Vec::new()); + + let mut proof_layers: Vec = Vec::new(); + let num_layers = prod_circuit_vec[0].left_vec.len(); + let mut claims_to_verify = (0..prod_circuit_vec.len()) + .map(|i| prod_circuit_vec[i].evaluate()) + .collect::>(); + let mut rand = Vec::new(); + for layer_id in (0..num_layers).rev() { + // prepare paralell instance that share poly_C first + let len = prod_circuit_vec[0].left_vec[layer_id].len() + + prod_circuit_vec[0].right_vec[layer_id].len(); + + let mut poly_C_par = DensePolynomial::new(EqPolynomial::new(rand.clone()).evals()); + assert_eq!(poly_C_par.len(), len / 2); + + let num_rounds_prod = poly_C_par.len().log_2(); + let comb_func_prod = + |poly_A_comp: &Scalar, poly_B_comp: &Scalar, poly_C_comp: &Scalar| -> Scalar { + (*poly_A_comp) * poly_B_comp * poly_C_comp + }; + + let mut poly_A_batched_par: Vec<&mut DensePolynomial> = Vec::new(); + let mut poly_B_batched_par: Vec<&mut DensePolynomial> = Vec::new(); + for prod_circuit in prod_circuit_vec.iter_mut() { + poly_A_batched_par.push(&mut prod_circuit.left_vec[layer_id]); + poly_B_batched_par.push(&mut prod_circuit.right_vec[layer_id]) + } + let poly_vec_par = ( + &mut poly_A_batched_par, + &mut poly_B_batched_par, + &mut poly_C_par, + ); + + // prepare sequential instances that don't share poly_C + let mut poly_A_batched_seq: Vec<&mut DensePolynomial> = Vec::new(); + let mut poly_B_batched_seq: Vec<&mut DensePolynomial> = Vec::new(); + let mut poly_C_batched_seq: Vec<&mut DensePolynomial> = Vec::new(); + if layer_id == 0 && !dotp_circuit_vec.is_empty() { + // add additional claims + for item in dotp_circuit_vec.iter() { + claims_to_verify.push(item.evaluate()); + assert_eq!(len / 2, item.left.len()); + assert_eq!(len / 2, item.right.len()); + assert_eq!(len / 2, item.weight.len()); + } + + for dotp_circuit in dotp_circuit_vec.iter_mut() { + poly_A_batched_seq.push(&mut dotp_circuit.left); + poly_B_batched_seq.push(&mut dotp_circuit.right); + poly_C_batched_seq.push(&mut dotp_circuit.weight); + } + } + let poly_vec_seq = ( + &mut poly_A_batched_seq, + &mut poly_B_batched_seq, + &mut poly_C_batched_seq, + ); + + // produce a fresh set of coeffs and a joint claim + let coeff_vec = transcript.challenge_vector(claims_to_verify.len()); + let claim = (0..claims_to_verify.len()) + .map(|i| claims_to_verify[i] * coeff_vec[i]) + .sum(); + + let (proof, rand_prod, claims_prod, claims_dotp) = + SumcheckInstanceProof::prove_cubic_batched( + &claim, + num_rounds_prod, + poly_vec_par, + poly_vec_seq, + &coeff_vec, + comb_func_prod, + transcript, + ); + + let (claims_prod_left, claims_prod_right, _claims_eq) = claims_prod; + for i in 0..prod_circuit_vec.len() { + transcript.append_scalar(&claims_prod_left[i]); + transcript.append_scalar(&claims_prod_right[i]); + } + + if layer_id == 0 && !dotp_circuit_vec.is_empty() { + let (claims_dotp_left, claims_dotp_right, claims_dotp_weight) = claims_dotp; + for i in 0..dotp_circuit_vec.len() { + transcript.append_scalar(&claims_dotp_left[i]); + transcript.append_scalar(&claims_dotp_right[i]); + transcript.append_scalar(&claims_dotp_weight[i]); + } + claims_dotp_final = (claims_dotp_left, claims_dotp_right, claims_dotp_weight); + } + + // produce a random challenge to condense two claims into a single claim + let r_layer = transcript.challenge_scalar(); + + claims_to_verify = (0..prod_circuit_vec.len()) + .map(|i| { + claims_prod_left[i] + r_layer * (claims_prod_right[i] - claims_prod_left[i]) + }) + .collect::>(); + + let mut ext = vec![r_layer]; + ext.extend(rand_prod); + rand = ext; + + proof_layers.push(LayerProofBatched { + proof, + claims_prod_left, + claims_prod_right, + }); } - } - - assert_eq!(claim_expected, claim_last); - - // produce a random challenge - let r_layer = transcript.challenge_scalar(); - - claims_to_verify = (0..claims_prod_left.len()) - .map(|i| claims_prod_left[i] + r_layer * (claims_prod_right[i] - claims_prod_left[i])) - .collect(); - // add claims to verify for dotp circuit - if i == num_layers - 1 { - let (claims_dotp_left, claims_dotp_right, claims_dotp_weight) = &self.claims_dotp; - - for i in 0..claims_dotp_vec.len() / 2 { - // combine left claims - let claim_left = claims_dotp_left[2 * i] - + r_layer * (claims_dotp_left[2 * i + 1] - claims_dotp_left[2 * i]); - - let claim_right = claims_dotp_right[2 * i] - + r_layer * (claims_dotp_right[2 * i + 1] - claims_dotp_right[2 * i]); + ( + ProductCircuitEvalProofBatched { + proof: proof_layers, + claims_dotp: claims_dotp_final, + }, + rand, + ) + } - let claim_weight = claims_dotp_weight[2 * i] - + r_layer * (claims_dotp_weight[2 * i + 1] - claims_dotp_weight[2 * i]); - claims_to_verify_dotp.push(claim_left); - claims_to_verify_dotp.push(claim_right); - claims_to_verify_dotp.push(claim_weight); + pub fn verify( + &self, + claims_prod_vec: &[Scalar], + claims_dotp_vec: &[Scalar], + len: usize, + transcript: &mut PoseidonTranscript, + ) -> (Vec, Vec, Vec) { + let num_layers = len.log_2(); + let mut rand: Vec = Vec::new(); + //let mut num_rounds = 0; + assert_eq!(self.proof.len(), num_layers); + + let mut claims_to_verify = claims_prod_vec.to_owned(); + let mut claims_to_verify_dotp: Vec = Vec::new(); + for (num_rounds, i) in (0..num_layers).enumerate() { + if i == num_layers - 1 { + claims_to_verify.extend(claims_dotp_vec); + } + + // produce random coefficients, one for each instance + let coeff_vec = transcript.challenge_vector(claims_to_verify.len()); + + // produce a joint claim + let claim = (0..claims_to_verify.len()) + .map(|i| claims_to_verify[i] * coeff_vec[i]) + .sum(); + + let (claim_last, rand_prod) = self.proof[i].verify(claim, num_rounds, 3, transcript); + + let claims_prod_left = &self.proof[i].claims_prod_left; + let claims_prod_right = &self.proof[i].claims_prod_right; + assert_eq!(claims_prod_left.len(), claims_prod_vec.len()); + assert_eq!(claims_prod_right.len(), claims_prod_vec.len()); + + for i in 0..claims_prod_vec.len() { + transcript.append_scalar(&claims_prod_left[i]); + transcript.append_scalar(&claims_prod_right[i]); + } + + assert_eq!(rand.len(), rand_prod.len()); + let eq: Scalar = (0..rand.len()) + .map(|i| { + rand[i] * rand_prod[i] + + (Scalar::one() - rand[i]) * (Scalar::one() - rand_prod[i]) + }) + .product(); + let mut claim_expected: Scalar = (0..claims_prod_vec.len()) + .map(|i| coeff_vec[i] * (claims_prod_left[i] * claims_prod_right[i] * eq)) + .sum(); + + // add claims from the dotp instances + if i == num_layers - 1 { + let num_prod_instances = claims_prod_vec.len(); + let (claims_dotp_left, claims_dotp_right, claims_dotp_weight) = &self.claims_dotp; + for i in 0..claims_dotp_left.len() { + transcript.append_scalar(&claims_dotp_left[i]); + transcript.append_scalar(&claims_dotp_right[i]); + transcript.append_scalar(&claims_dotp_weight[i]); + + claim_expected += coeff_vec[i + num_prod_instances] + * claims_dotp_left[i] + * claims_dotp_right[i] + * claims_dotp_weight[i]; + } + } + + assert_eq!(claim_expected, claim_last); + + // produce a random challenge + let r_layer = transcript.challenge_scalar(); + + claims_to_verify = (0..claims_prod_left.len()) + .map(|i| { + claims_prod_left[i] + r_layer * (claims_prod_right[i] - claims_prod_left[i]) + }) + .collect(); + + // add claims to verify for dotp circuit + if i == num_layers - 1 { + let (claims_dotp_left, claims_dotp_right, claims_dotp_weight) = &self.claims_dotp; + + for i in 0..claims_dotp_vec.len() / 2 { + // combine left claims + let claim_left = claims_dotp_left[2 * i] + + r_layer * (claims_dotp_left[2 * i + 1] - claims_dotp_left[2 * i]); + + let claim_right = claims_dotp_right[2 * i] + + r_layer * (claims_dotp_right[2 * i + 1] - claims_dotp_right[2 * i]); + + let claim_weight = claims_dotp_weight[2 * i] + + r_layer * (claims_dotp_weight[2 * i + 1] - claims_dotp_weight[2 * i]); + claims_to_verify_dotp.push(claim_left); + claims_to_verify_dotp.push(claim_right); + claims_to_verify_dotp.push(claim_weight); + } + } + + let mut ext = vec![r_layer]; + ext.extend(rand_prod); + rand = ext; } - } - - let mut ext = vec![r_layer]; - ext.extend(rand_prod); - rand = ext; + (claims_to_verify, claims_to_verify_dotp, rand) } - (claims_to_verify, claims_to_verify_dotp, rand) - } } diff --git a/src/r1csinstance.rs b/src/r1csinstance.rs index 323d040..f9e89cb 100644 --- a/src/r1csinstance.rs +++ b/src/r1csinstance.rs @@ -7,8 +7,8 @@ use super::math::Math; use super::random::RandomTape; use super::scalar::Scalar; use super::sparse_mlpoly::{ - MultiSparseMatPolynomialAsDense, SparseMatEntry, SparseMatPolyCommitment, - SparseMatPolyCommitmentGens, SparseMatPolyEvalProof, SparseMatPolynomial, + MultiSparseMatPolynomialAsDense, SparseMatEntry, SparseMatPolyCommitment, + SparseMatPolyCommitmentGens, SparseMatPolyEvalProof, SparseMatPolynomial, }; use super::timer::Timer; use ark_ff::Field; @@ -21,365 +21,371 @@ use sha3::Shake256; #[derive(Debug, CanonicalSerialize, CanonicalDeserialize, Clone)] pub struct R1CSInstance { - num_cons: usize, - num_vars: usize, - num_inputs: usize, - A: SparseMatPolynomial, - B: SparseMatPolynomial, - C: SparseMatPolynomial, + num_cons: usize, + num_vars: usize, + num_inputs: usize, + A: SparseMatPolynomial, + B: SparseMatPolynomial, + C: SparseMatPolynomial, } pub struct R1CSCommitmentGens { - gens: SparseMatPolyCommitmentGens, + gens: SparseMatPolyCommitmentGens, } impl R1CSCommitmentGens { - pub fn new( - label: &'static [u8], - num_cons: usize, - num_vars: usize, - num_inputs: usize, - num_nz_entries: usize, - ) -> R1CSCommitmentGens { - assert!(num_inputs < num_vars); - let num_poly_vars_x = num_cons.log_2(); - let num_poly_vars_y = (2 * num_vars).log_2(); - let gens = - SparseMatPolyCommitmentGens::new(label, num_poly_vars_x, num_poly_vars_y, num_nz_entries, 3); - R1CSCommitmentGens { gens } - } + pub fn new( + label: &'static [u8], + num_cons: usize, + num_vars: usize, + num_inputs: usize, + num_nz_entries: usize, + ) -> R1CSCommitmentGens { + assert!(num_inputs < num_vars); + let num_poly_vars_x = num_cons.log_2(); + let num_poly_vars_y = (2 * num_vars).log_2(); + let gens = SparseMatPolyCommitmentGens::new( + label, + num_poly_vars_x, + num_poly_vars_y, + num_nz_entries, + 3, + ); + R1CSCommitmentGens { gens } + } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct R1CSCommitment { - num_cons: usize, - num_vars: usize, - num_inputs: usize, - comm: SparseMatPolyCommitment, + num_cons: usize, + num_vars: usize, + num_inputs: usize, + comm: SparseMatPolyCommitment, } impl AppendToTranscript for R1CSCommitment { - fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) { - transcript.append_u64(b"num_cons", self.num_cons as u64); - transcript.append_u64(b"num_vars", self.num_vars as u64); - transcript.append_u64(b"num_inputs", self.num_inputs as u64); - self.comm.append_to_transcript(b"comm", transcript); - } + fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) { + transcript.append_u64(b"num_cons", self.num_cons as u64); + transcript.append_u64(b"num_vars", self.num_vars as u64); + transcript.append_u64(b"num_inputs", self.num_inputs as u64); + self.comm.append_to_transcript(b"comm", transcript); + } } impl AppendToPoseidon for R1CSCommitment { - fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { - transcript.append_u64(self.num_cons as u64); - transcript.append_u64(self.num_vars as u64); - transcript.append_u64(self.num_inputs as u64); - self.comm.append_to_poseidon(transcript); - } + fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { + transcript.append_u64(self.num_cons as u64); + transcript.append_u64(self.num_vars as u64); + transcript.append_u64(self.num_inputs as u64); + self.comm.append_to_poseidon(transcript); + } } pub struct R1CSDecommitment { - dense: MultiSparseMatPolynomialAsDense, + dense: MultiSparseMatPolynomialAsDense, } impl R1CSCommitment { - pub fn get_num_cons(&self) -> usize { - self.num_cons - } + pub fn get_num_cons(&self) -> usize { + self.num_cons + } - pub fn get_num_vars(&self) -> usize { - self.num_vars - } + pub fn get_num_vars(&self) -> usize { + self.num_vars + } - pub fn get_num_inputs(&self) -> usize { - self.num_inputs - } + pub fn get_num_inputs(&self) -> usize { + self.num_inputs + } } impl R1CSInstance { - pub fn new( - num_cons: usize, - num_vars: usize, - num_inputs: usize, - A: &[(usize, usize, Scalar)], - B: &[(usize, usize, Scalar)], - C: &[(usize, usize, Scalar)], - ) -> R1CSInstance { - Timer::print(&format!("number_of_constraints {}", num_cons)); - Timer::print(&format!("number_of_variables {}", num_vars)); - Timer::print(&format!("number_of_inputs {}", num_inputs)); - Timer::print(&format!("number_non-zero_entries_A {}", A.len())); - Timer::print(&format!("number_non-zero_entries_B {}", B.len())); - Timer::print(&format!("number_non-zero_entries_C {}", C.len())); - - // check that num_cons is a power of 2 - assert_eq!(num_cons.next_power_of_two(), num_cons); - - // check that num_vars is a power of 2 - assert_eq!(num_vars.next_power_of_two(), num_vars); - - // check that number_inputs + 1 <= num_vars - assert!(num_inputs < num_vars); - - // no errors, so create polynomials - let num_poly_vars_x = num_cons.log_2(); - let num_poly_vars_y = (2 * num_vars).log_2(); - - let mat_A = (0..A.len()) - .map(|i| SparseMatEntry::new(A[i].0, A[i].1, A[i].2)) - .collect::>(); - let mat_B = (0..B.len()) - .map(|i| SparseMatEntry::new(B[i].0, B[i].1, B[i].2)) - .collect::>(); - let mat_C = (0..C.len()) - .map(|i| SparseMatEntry::new(C[i].0, C[i].1, C[i].2)) - .collect::>(); - - let poly_A = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, mat_A); - let poly_B = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, mat_B); - let poly_C = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, mat_C); - - R1CSInstance { - num_cons, - num_vars, - num_inputs, - A: poly_A, - B: poly_B, - C: poly_C, + pub fn new( + num_cons: usize, + num_vars: usize, + num_inputs: usize, + A: &[(usize, usize, Scalar)], + B: &[(usize, usize, Scalar)], + C: &[(usize, usize, Scalar)], + ) -> R1CSInstance { + Timer::print(&format!("number_of_constraints {}", num_cons)); + Timer::print(&format!("number_of_variables {}", num_vars)); + Timer::print(&format!("number_of_inputs {}", num_inputs)); + Timer::print(&format!("number_non-zero_entries_A {}", A.len())); + Timer::print(&format!("number_non-zero_entries_B {}", B.len())); + Timer::print(&format!("number_non-zero_entries_C {}", C.len())); + + // check that num_cons is a power of 2 + assert_eq!(num_cons.next_power_of_two(), num_cons); + + // check that num_vars is a power of 2 + assert_eq!(num_vars.next_power_of_two(), num_vars); + + // check that number_inputs + 1 <= num_vars + assert!(num_inputs < num_vars); + + // no errors, so create polynomials + let num_poly_vars_x = num_cons.log_2(); + let num_poly_vars_y = (2 * num_vars).log_2(); + + let mat_A = (0..A.len()) + .map(|i| SparseMatEntry::new(A[i].0, A[i].1, A[i].2)) + .collect::>(); + let mat_B = (0..B.len()) + .map(|i| SparseMatEntry::new(B[i].0, B[i].1, B[i].2)) + .collect::>(); + let mat_C = (0..C.len()) + .map(|i| SparseMatEntry::new(C[i].0, C[i].1, C[i].2)) + .collect::>(); + + let poly_A = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, mat_A); + let poly_B = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, mat_B); + let poly_C = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, mat_C); + + R1CSInstance { + num_cons, + num_vars, + num_inputs, + A: poly_A, + B: poly_B, + C: poly_C, + } } - } - - pub fn get_num_vars(&self) -> usize { - self.num_vars - } - - pub fn get_num_cons(&self) -> usize { - self.num_cons - } - - pub fn get_num_inputs(&self) -> usize { - self.num_inputs - } - - pub fn get_digest(&self) -> Vec { - let mut bytes = Vec::new(); - self.serialize(&mut bytes).unwrap(); - let mut shake = Shake256::default(); - shake.input(bytes); - let mut reader = shake.xof_result(); - let mut buf = [0u8; 256]; - reader.read_exact(&mut buf).unwrap(); - buf.to_vec() - } - - pub fn produce_synthetic_r1cs( - num_cons: usize, - num_vars: usize, - num_inputs: usize, - ) -> (R1CSInstance, Vec, Vec) { - Timer::print(&format!("number_of_constraints {}", num_cons)); - Timer::print(&format!("number_of_variables {}", num_vars)); - Timer::print(&format!("number_of_inputs {}", num_inputs)); - - let mut rng = ark_std::rand::thread_rng(); - - // assert num_cons and num_vars are power of 2 - assert_eq!((num_cons.log_2()).pow2(), num_cons); - assert_eq!((num_vars.log_2()).pow2(), num_vars); - - // num_inputs + 1 <= num_vars - assert!(num_inputs < num_vars); - - // z is organized as [vars,1,io] - let size_z = num_vars + num_inputs + 1; - - // produce a random satisfying assignment - let Z = { - let mut Z: Vec = (0..size_z) - .map(|_i| Scalar::rand(&mut rng)) - .collect::>(); - Z[num_vars] = Scalar::one(); // set the constant term to 1 - Z - }; - - // three sparse matrices - let mut A: Vec = Vec::new(); - let mut B: Vec = Vec::new(); - let mut C: Vec = Vec::new(); - let one = Scalar::one(); - for i in 0..num_cons { - let A_idx = i % size_z; - let B_idx = (i + 2) % size_z; - A.push(SparseMatEntry::new(i, A_idx, one)); - B.push(SparseMatEntry::new(i, B_idx, one)); - let AB_val = Z[A_idx] * Z[B_idx]; - - let C_idx = (i + 3) % size_z; - let C_val = Z[C_idx]; - - if C_val == Scalar::zero() { - C.push(SparseMatEntry::new(i, num_vars, AB_val)); - } else { - C.push(SparseMatEntry::new( - i, - C_idx, - AB_val * C_val.inverse().unwrap(), - )); - } + + pub fn get_num_vars(&self) -> usize { + self.num_vars + } + + pub fn get_num_cons(&self) -> usize { + self.num_cons + } + + pub fn get_num_inputs(&self) -> usize { + self.num_inputs + } + + pub fn get_digest(&self) -> Vec { + let mut bytes = Vec::new(); + self.serialize(&mut bytes).unwrap(); + let mut shake = Shake256::default(); + shake.input(bytes); + let mut reader = shake.xof_result(); + let mut buf = [0u8; 256]; + reader.read_exact(&mut buf).unwrap(); + buf.to_vec() + } + + pub fn produce_synthetic_r1cs( + num_cons: usize, + num_vars: usize, + num_inputs: usize, + ) -> (R1CSInstance, Vec, Vec) { + Timer::print(&format!("number_of_constraints {}", num_cons)); + Timer::print(&format!("number_of_variables {}", num_vars)); + Timer::print(&format!("number_of_inputs {}", num_inputs)); + + let mut rng = ark_std::rand::thread_rng(); + + // assert num_cons and num_vars are power of 2 + assert_eq!((num_cons.log_2()).pow2(), num_cons); + assert_eq!((num_vars.log_2()).pow2(), num_vars); + + // num_inputs + 1 <= num_vars + assert!(num_inputs < num_vars); + + // z is organized as [vars,1,io] + let size_z = num_vars + num_inputs + 1; + + // produce a random satisfying assignment + let Z = { + let mut Z: Vec = (0..size_z) + .map(|_i| Scalar::rand(&mut rng)) + .collect::>(); + Z[num_vars] = Scalar::one(); // set the constant term to 1 + Z + }; + + // three sparse matrices + let mut A: Vec = Vec::new(); + let mut B: Vec = Vec::new(); + let mut C: Vec = Vec::new(); + let one = Scalar::one(); + for i in 0..num_cons { + let A_idx = i % size_z; + let B_idx = (i + 2) % size_z; + A.push(SparseMatEntry::new(i, A_idx, one)); + B.push(SparseMatEntry::new(i, B_idx, one)); + let AB_val = Z[A_idx] * Z[B_idx]; + + let C_idx = (i + 3) % size_z; + let C_val = Z[C_idx]; + + if C_val == Scalar::zero() { + C.push(SparseMatEntry::new(i, num_vars, AB_val)); + } else { + C.push(SparseMatEntry::new( + i, + C_idx, + AB_val * C_val.inverse().unwrap(), + )); + } + } + + Timer::print(&format!("number_non-zero_entries_A {}", A.len())); + Timer::print(&format!("number_non-zero_entries_B {}", B.len())); + Timer::print(&format!("number_non-zero_entries_C {}", C.len())); + + let num_poly_vars_x = num_cons.log_2(); + let num_poly_vars_y = (2 * num_vars).log_2(); + let poly_A = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, A); + let poly_B = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, B); + let poly_C = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, C); + + let inst = R1CSInstance { + num_cons, + num_vars, + num_inputs, + A: poly_A, + B: poly_B, + C: poly_C, + }; + + assert!(inst.is_sat(&Z[..num_vars], &Z[num_vars + 1..])); + + (inst, Z[..num_vars].to_vec(), Z[num_vars + 1..].to_vec()) + } + + pub fn is_sat(&self, vars: &[Scalar], input: &[Scalar]) -> bool { + assert_eq!(vars.len(), self.num_vars); + assert_eq!(input.len(), self.num_inputs); + + let z = { + let mut z = vars.to_vec(); + z.extend(&vec![Scalar::one()]); + z.extend(input); + z + }; + + // verify if Az * Bz - Cz = [0...] + let Az = self + .A + .multiply_vec(self.num_cons, self.num_vars + self.num_inputs + 1, &z); + let Bz = self + .B + .multiply_vec(self.num_cons, self.num_vars + self.num_inputs + 1, &z); + let Cz = self + .C + .multiply_vec(self.num_cons, self.num_vars + self.num_inputs + 1, &z); + + assert_eq!(Az.len(), self.num_cons); + assert_eq!(Bz.len(), self.num_cons); + assert_eq!(Cz.len(), self.num_cons); + let res: usize = (0..self.num_cons) + .map(|i| usize::from(Az[i] * Bz[i] != Cz[i])) + .sum(); + + res == 0 } - Timer::print(&format!("number_non-zero_entries_A {}", A.len())); - Timer::print(&format!("number_non-zero_entries_B {}", B.len())); - Timer::print(&format!("number_non-zero_entries_C {}", C.len())); - - let num_poly_vars_x = num_cons.log_2(); - let num_poly_vars_y = (2 * num_vars).log_2(); - let poly_A = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, A); - let poly_B = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, B); - let poly_C = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, C); - - let inst = R1CSInstance { - num_cons, - num_vars, - num_inputs, - A: poly_A, - B: poly_B, - C: poly_C, - }; - - assert!(inst.is_sat(&Z[..num_vars], &Z[num_vars + 1..])); - - (inst, Z[..num_vars].to_vec(), Z[num_vars + 1..].to_vec()) - } - - pub fn is_sat(&self, vars: &[Scalar], input: &[Scalar]) -> bool { - assert_eq!(vars.len(), self.num_vars); - assert_eq!(input.len(), self.num_inputs); - - let z = { - let mut z = vars.to_vec(); - z.extend(&vec![Scalar::one()]); - z.extend(input); - z - }; - - // verify if Az * Bz - Cz = [0...] - let Az = self - .A - .multiply_vec(self.num_cons, self.num_vars + self.num_inputs + 1, &z); - let Bz = self - .B - .multiply_vec(self.num_cons, self.num_vars + self.num_inputs + 1, &z); - let Cz = self - .C - .multiply_vec(self.num_cons, self.num_vars + self.num_inputs + 1, &z); - - assert_eq!(Az.len(), self.num_cons); - assert_eq!(Bz.len(), self.num_cons); - assert_eq!(Cz.len(), self.num_cons); - let res: usize = (0..self.num_cons) - .map(|i| usize::from(Az[i] * Bz[i] != Cz[i])) - .sum(); - - res == 0 - } - - pub fn multiply_vec( - &self, - num_rows: usize, - num_cols: usize, - z: &[Scalar], - ) -> (DensePolynomial, DensePolynomial, DensePolynomial) { - assert_eq!(num_rows, self.num_cons); - assert_eq!(z.len(), num_cols); - assert!(num_cols > self.num_vars); - ( - DensePolynomial::new(self.A.multiply_vec(num_rows, num_cols, z)), - DensePolynomial::new(self.B.multiply_vec(num_rows, num_cols, z)), - DensePolynomial::new(self.C.multiply_vec(num_rows, num_cols, z)), - ) - } - - pub fn compute_eval_table_sparse( - &self, - num_rows: usize, - num_cols: usize, - evals: &[Scalar], - ) -> (Vec, Vec, Vec) { - assert_eq!(num_rows, self.num_cons); - assert!(num_cols > self.num_vars); - - let evals_A = self.A.compute_eval_table_sparse(evals, num_rows, num_cols); - let evals_B = self.B.compute_eval_table_sparse(evals, num_rows, num_cols); - let evals_C = self.C.compute_eval_table_sparse(evals, num_rows, num_cols); - - (evals_A, evals_B, evals_C) - } - - pub fn evaluate(&self, rx: &[Scalar], ry: &[Scalar]) -> (Scalar, Scalar, Scalar) { - let evals = SparseMatPolynomial::multi_evaluate(&[&self.A, &self.B, &self.C], rx, ry); - (evals[0], evals[1], evals[2]) - } - - pub fn commit(&self, gens: &R1CSCommitmentGens) -> (R1CSCommitment, R1CSDecommitment) { - let (comm, dense) = SparseMatPolynomial::multi_commit(&[&self.A, &self.B, &self.C], &gens.gens); - let r1cs_comm = R1CSCommitment { - num_cons: self.num_cons, - num_vars: self.num_vars, - num_inputs: self.num_inputs, - comm, - }; - - let r1cs_decomm = R1CSDecommitment { dense }; - - (r1cs_comm, r1cs_decomm) - } + pub fn multiply_vec( + &self, + num_rows: usize, + num_cols: usize, + z: &[Scalar], + ) -> (DensePolynomial, DensePolynomial, DensePolynomial) { + assert_eq!(num_rows, self.num_cons); + assert_eq!(z.len(), num_cols); + assert!(num_cols > self.num_vars); + ( + DensePolynomial::new(self.A.multiply_vec(num_rows, num_cols, z)), + DensePolynomial::new(self.B.multiply_vec(num_rows, num_cols, z)), + DensePolynomial::new(self.C.multiply_vec(num_rows, num_cols, z)), + ) + } + + pub fn compute_eval_table_sparse( + &self, + num_rows: usize, + num_cols: usize, + evals: &[Scalar], + ) -> (Vec, Vec, Vec) { + assert_eq!(num_rows, self.num_cons); + assert!(num_cols > self.num_vars); + + let evals_A = self.A.compute_eval_table_sparse(evals, num_rows, num_cols); + let evals_B = self.B.compute_eval_table_sparse(evals, num_rows, num_cols); + let evals_C = self.C.compute_eval_table_sparse(evals, num_rows, num_cols); + + (evals_A, evals_B, evals_C) + } + + pub fn evaluate(&self, rx: &[Scalar], ry: &[Scalar]) -> (Scalar, Scalar, Scalar) { + let evals = SparseMatPolynomial::multi_evaluate(&[&self.A, &self.B, &self.C], rx, ry); + (evals[0], evals[1], evals[2]) + } + + pub fn commit(&self, gens: &R1CSCommitmentGens) -> (R1CSCommitment, R1CSDecommitment) { + let (comm, dense) = + SparseMatPolynomial::multi_commit(&[&self.A, &self.B, &self.C], &gens.gens); + let r1cs_comm = R1CSCommitment { + num_cons: self.num_cons, + num_vars: self.num_vars, + num_inputs: self.num_inputs, + comm, + }; + + let r1cs_decomm = R1CSDecommitment { dense }; + + (r1cs_comm, r1cs_decomm) + } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct R1CSEvalProof { - proof: SparseMatPolyEvalProof, + proof: SparseMatPolyEvalProof, } impl R1CSEvalProof { - pub fn prove( - decomm: &R1CSDecommitment, - rx: &[Scalar], // point at which the polynomial is evaluated - ry: &[Scalar], - evals: &(Scalar, Scalar, Scalar), - gens: &R1CSCommitmentGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - ) -> R1CSEvalProof { - let timer = Timer::new("R1CSEvalProof::prove"); - let proof = SparseMatPolyEvalProof::prove( - &decomm.dense, - rx, - ry, - &[evals.0, evals.1, evals.2], - &gens.gens, - transcript, - random_tape, - ); - timer.stop(); - - R1CSEvalProof { proof } - } - - pub fn verify( - &self, - comm: &R1CSCommitment, - rx: &[Scalar], // point at which the R1CS matrix polynomials are evaluated - ry: &[Scalar], - evals: &(Scalar, Scalar, Scalar), - gens: &R1CSCommitmentGens, - transcript: &mut PoseidonTranscript, - ) -> Result<(), ProofVerifyError> { - self.proof.verify( - &comm.comm, - rx, - ry, - &[evals.0, evals.1, evals.2], - &gens.gens, - transcript, - ) - } + pub fn prove( + decomm: &R1CSDecommitment, + rx: &[Scalar], // point at which the polynomial is evaluated + ry: &[Scalar], + evals: &(Scalar, Scalar, Scalar), + gens: &R1CSCommitmentGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + ) -> R1CSEvalProof { + let timer = Timer::new("R1CSEvalProof::prove"); + let proof = SparseMatPolyEvalProof::prove( + &decomm.dense, + rx, + ry, + &[evals.0, evals.1, evals.2], + &gens.gens, + transcript, + random_tape, + ); + timer.stop(); + + R1CSEvalProof { proof } + } + + pub fn verify( + &self, + comm: &R1CSCommitment, + rx: &[Scalar], // point at which the R1CS matrix polynomials are evaluated + ry: &[Scalar], + evals: &(Scalar, Scalar, Scalar), + gens: &R1CSCommitmentGens, + transcript: &mut PoseidonTranscript, + ) -> Result<(), ProofVerifyError> { + self.proof.verify( + &comm.comm, + rx, + ry, + &[evals.0, evals.1, evals.2], + &gens.gens, + transcript, + ) + } } diff --git a/src/r1csproof.rs b/src/r1csproof.rs index bc6752a..2fc8b8e 100644 --- a/src/r1csproof.rs +++ b/src/r1csproof.rs @@ -31,510 +31,512 @@ use std::time::Instant; #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] pub struct R1CSProof { - // The PST commitment to the multilinear extension of the witness. - comm: Commitment, - sc_proof_phase1: SumcheckInstanceProof, - claims_phase2: (Scalar, Scalar, Scalar, Scalar), - sc_proof_phase2: SumcheckInstanceProof, - eval_vars_at_ry: Scalar, - proof_eval_vars_at_ry: Proof, - rx: Vec, - ry: Vec, - // The transcript state after the satisfiability proof was computed. - pub transcript_sat_state: Scalar, + // The PST commitment to the multilinear extension of the witness. + comm: Commitment, + sc_proof_phase1: SumcheckInstanceProof, + claims_phase2: (Scalar, Scalar, Scalar, Scalar), + sc_proof_phase2: SumcheckInstanceProof, + eval_vars_at_ry: Scalar, + proof_eval_vars_at_ry: Proof, + rx: Vec, + ry: Vec, + // The transcript state after the satisfiability proof was computed. + pub transcript_sat_state: Scalar, } #[derive(Clone)] pub struct R1CSSumcheckGens { - gens_1: MultiCommitGens, - gens_3: MultiCommitGens, - gens_4: MultiCommitGens, + gens_1: MultiCommitGens, + gens_3: MultiCommitGens, + gens_4: MultiCommitGens, } // TODO: fix passing gens_1_ref impl R1CSSumcheckGens { - pub fn new(label: &'static [u8], gens_1_ref: &MultiCommitGens) -> Self { - let gens_1 = gens_1_ref.clone(); - let gens_3 = MultiCommitGens::new(3, label); - let gens_4 = MultiCommitGens::new(4, label); - - R1CSSumcheckGens { - gens_1, - gens_3, - gens_4, + pub fn new(label: &'static [u8], gens_1_ref: &MultiCommitGens) -> Self { + let gens_1 = gens_1_ref.clone(); + let gens_3 = MultiCommitGens::new(3, label); + let gens_4 = MultiCommitGens::new(4, label); + + R1CSSumcheckGens { + gens_1, + gens_3, + gens_4, + } } - } } #[derive(Clone)] pub struct R1CSGens { - gens_sc: R1CSSumcheckGens, - gens_pc: PolyCommitmentGens, + gens_sc: R1CSSumcheckGens, + gens_pc: PolyCommitmentGens, } impl R1CSGens { - pub fn new(label: &'static [u8], _num_cons: usize, num_vars: usize) -> Self { - let num_poly_vars = num_vars.log_2(); - let gens_pc = PolyCommitmentGens::new(num_poly_vars, label); - let gens_sc = R1CSSumcheckGens::new(label, &gens_pc.gens.gens_1); - R1CSGens { gens_sc, gens_pc } - } + pub fn new(label: &'static [u8], _num_cons: usize, num_vars: usize) -> Self { + let num_poly_vars = num_vars.log_2(); + let gens_pc = PolyCommitmentGens::new(num_poly_vars, label); + let gens_sc = R1CSSumcheckGens::new(label, &gens_pc.gens.gens_1); + R1CSGens { gens_sc, gens_pc } + } } impl R1CSProof { - fn prove_phase_one( - num_rounds: usize, - evals_tau: &mut DensePolynomial, - evals_Az: &mut DensePolynomial, - evals_Bz: &mut DensePolynomial, - evals_Cz: &mut DensePolynomial, - transcript: &mut PoseidonTranscript, - ) -> (SumcheckInstanceProof, Vec, Vec) { - let comb_func = - |poly_tau_comp: &Scalar, - poly_A_comp: &Scalar, - poly_B_comp: &Scalar, - poly_C_comp: &Scalar| - -> Scalar { (*poly_tau_comp) * ((*poly_A_comp) * poly_B_comp - poly_C_comp) }; - - let (sc_proof_phase_one, r, claims) = SumcheckInstanceProof::prove_cubic_with_additive_term( - &Scalar::zero(), // claim is zero - num_rounds, - evals_tau, - evals_Az, - evals_Bz, - evals_Cz, - comb_func, - transcript, - ); - - (sc_proof_phase_one, r, claims) - } - - fn prove_phase_two( - num_rounds: usize, - claim: &Scalar, - evals_z: &mut DensePolynomial, - evals_ABC: &mut DensePolynomial, - transcript: &mut PoseidonTranscript, - ) -> (SumcheckInstanceProof, Vec, Vec) { - let comb_func = - |poly_A_comp: &Scalar, poly_B_comp: &Scalar| -> Scalar { (*poly_A_comp) * poly_B_comp }; - let (sc_proof_phase_two, r, claims) = SumcheckInstanceProof::prove_quad( - claim, num_rounds, evals_z, evals_ABC, comb_func, transcript, - ); - - (sc_proof_phase_two, r, claims) - } - - fn protocol_name() -> &'static [u8] { - b"R1CS proof" - } - - pub fn prove( - inst: &R1CSInstance, - vars: Vec, - input: &[Scalar], - gens: &R1CSGens, - transcript: &mut PoseidonTranscript, - ) -> (R1CSProof, Vec, Vec) { - let timer_prove = Timer::new("R1CSProof::prove"); - // we currently require the number of |inputs| + 1 to be at most number of vars - assert!(input.len() < vars.len()); - - // create the multilinear witness polynomial from the satisfying assiment - let poly_vars = DensePolynomial::new(vars.clone()); - - let timer_commit = Timer::new("polycommit"); - // commitment to the satisfying witness polynomial - let comm = MultilinearPC::::commit(&gens.gens_pc.ck, &poly_vars); - comm.append_to_poseidon(transcript); - timer_commit.stop(); - - let c = transcript.challenge_scalar(); - transcript.new_from_state(&c); - - transcript.append_scalar_vector(input); - - let timer_sc_proof_phase1 = Timer::new("prove_sc_phase_one"); - - // append input to variables to create a single vector z - let z = { - let num_inputs = input.len(); - let num_vars = vars.len(); - let mut z = vars; - z.extend(&vec![Scalar::one()]); // add constant term in z - z.extend(input); - z.extend(&vec![Scalar::zero(); num_vars - num_inputs - 1]); // we will pad with zeros - z - }; - - // derive the verifier's challenge tau - let (num_rounds_x, num_rounds_y) = (inst.get_num_cons().log_2(), z.len().log_2()); - let tau = transcript.challenge_vector(num_rounds_x); - // compute the initial evaluation table for R(\tau, x) - let mut poly_tau = DensePolynomial::new(EqPolynomial::new(tau).evals()); - let (mut poly_Az, mut poly_Bz, mut poly_Cz) = - inst.multiply_vec(inst.get_num_cons(), z.len(), &z); - - let (sc_proof_phase1, rx, _claims_phase1) = R1CSProof::prove_phase_one( - num_rounds_x, - &mut poly_tau, - &mut poly_Az, - &mut poly_Bz, - &mut poly_Cz, - transcript, - ); - assert_eq!(poly_tau.len(), 1); - assert_eq!(poly_Az.len(), 1); - assert_eq!(poly_Bz.len(), 1); - assert_eq!(poly_Cz.len(), 1); - timer_sc_proof_phase1.stop(); - - let (tau_claim, Az_claim, Bz_claim, Cz_claim) = - (&poly_tau[0], &poly_Az[0], &poly_Bz[0], &poly_Cz[0]); - let prod_Az_Bz_claims = (*Az_claim) * Bz_claim; - - // prove the final step of sum-check #1 - let taus_bound_rx = tau_claim; - let _claim_post_phase1 = ((*Az_claim) * Bz_claim - Cz_claim) * taus_bound_rx; - - let timer_sc_proof_phase2 = Timer::new("prove_sc_phase_two"); - // combine the three claims into a single claim - let r_A = transcript.challenge_scalar(); - let r_B = transcript.challenge_scalar(); - let r_C = transcript.challenge_scalar(); - let claim_phase2 = r_A * Az_claim + r_B * Bz_claim + r_C * Cz_claim; - - let evals_ABC = { - // compute the initial evaluation table for R(\tau, x) - let evals_rx = EqPolynomial::new(rx.clone()).evals(); - let (evals_A, evals_B, evals_C) = - inst.compute_eval_table_sparse(inst.get_num_cons(), z.len(), &evals_rx); - - assert_eq!(evals_A.len(), evals_B.len()); - assert_eq!(evals_A.len(), evals_C.len()); - (0..evals_A.len()) - .map(|i| r_A * evals_A[i] + r_B * evals_B[i] + r_C * evals_C[i]) - .collect::>() - }; - - // another instance of the sum-check protocol - let (sc_proof_phase2, ry, _claims_phase2) = R1CSProof::prove_phase_two( - num_rounds_y, - &claim_phase2, - &mut DensePolynomial::new(z), - &mut DensePolynomial::new(evals_ABC), - transcript, - ); - timer_sc_proof_phase2.stop(); - - // TODO: modify the polynomial evaluation in Spartan to be consistent - // with the evaluation in ark-poly-commit so that reversing is not needed - // anymore - let timmer_opening = Timer::new("polyopening"); - let mut dummy = ry[1..].to_vec().clone(); - dummy.reverse(); - let proof_eval_vars_at_ry = MultilinearPC::::open(&gens.gens_pc.ck, &poly_vars, &dummy); - println!( - "proof size (no of quotients): {:?}", - proof_eval_vars_at_ry.proofs.len() - ); - timmer_opening.stop(); - - let timer_polyeval = Timer::new("polyeval"); - let eval_vars_at_ry = poly_vars.evaluate(&ry[1..]); - timer_polyeval.stop(); - - timer_prove.stop(); - - let c = transcript.challenge_scalar(); - - ( - R1CSProof { - comm, - sc_proof_phase1, - claims_phase2: (*Az_claim, *Bz_claim, *Cz_claim, prod_Az_Bz_claims), - sc_proof_phase2, - eval_vars_at_ry, - proof_eval_vars_at_ry, - rx: rx.clone(), - ry: ry.clone(), - transcript_sat_state: c, - }, - rx, - ry, - ) - } - - pub fn verify_groth16( - &self, - num_vars: usize, - num_cons: usize, - input: &[Scalar], - evals: &(Scalar, Scalar, Scalar), - transcript: &mut PoseidonTranscript, - gens: &R1CSGens, - ) -> Result<(u128, u128, u128), ProofVerifyError> { - self.comm.append_to_poseidon(transcript); - - let c = transcript.challenge_scalar(); - - let mut input_as_sparse_poly_entries = vec![SparsePolyEntry::new(0, Scalar::one())]; - //remaining inputs - input_as_sparse_poly_entries.extend( - (0..input.len()) - .map(|i| SparsePolyEntry::new(i + 1, input[i])) - .collect::>(), - ); - - let n = num_vars; - let input_as_sparse_poly = - SparsePolynomial::new(n.log_2() as usize, input_as_sparse_poly_entries); - - let config = VerifierConfig { - num_vars, - num_cons, - input: input.to_vec(), - evals: *evals, - params: poseidon_params(), - prev_challenge: c, - claims_phase2: self.claims_phase2, - polys_sc1: self.sc_proof_phase1.polys.clone(), - polys_sc2: self.sc_proof_phase2.polys.clone(), - eval_vars_at_ry: self.eval_vars_at_ry, - input_as_sparse_poly, - // rx: self.rx.clone(), - ry: self.ry.clone(), - transcript_sat_state: self.transcript_sat_state, - }; - - let mut rng = ark_std::test_rng(); - - let prove_inner = Timer::new("proveinnercircuit"); - let start = Instant::now(); - let circuit = VerifierCircuit::new(&config, &mut rng).unwrap(); - let dp1 = start.elapsed().as_millis(); - prove_inner.stop(); - - let start = Instant::now(); - let (pk, vk) = Groth16::

::setup(circuit.clone(), &mut rng).unwrap(); - let ds = start.elapsed().as_millis(); - - let prove_outer = Timer::new("proveoutercircuit"); - let start = Instant::now(); - let proof = Groth16::

::prove(&pk, circuit, &mut rng).unwrap(); - let dp2 = start.elapsed().as_millis(); - prove_outer.stop(); - - let start = Instant::now(); - let is_verified = Groth16::

::verify(&vk, &[], &proof).unwrap(); - assert!(is_verified); - - let timer_verification = Timer::new("commitverification"); - let mut dummy = self.ry[1..].to_vec(); - // TODO: ensure ark-poly-commit and Spartan produce consistent results - // when evaluating a polynomial at a given point so this reverse is not - // needed. - dummy.reverse(); - - // Verifies the proof of opening against the result of evaluating the - // witness polynomial at point ry. - let res = MultilinearPC::::check( - &gens.gens_pc.vk, - &self.comm, - &dummy, - self.eval_vars_at_ry, - &self.proof_eval_vars_at_ry, - ); - - timer_verification.stop(); - assert!(res == true); - let dv = start.elapsed().as_millis(); - - Ok((ds, dp1 + dp2, dv)) - } - - // Helper function to find the number of constraint in the circuit which - // requires executing it. - pub fn circuit_size( - &self, - num_vars: usize, - num_cons: usize, - input: &[Scalar], - evals: &(Scalar, Scalar, Scalar), - transcript: &mut PoseidonTranscript, - _gens: &R1CSGens, - ) -> Result { - self.comm.append_to_poseidon(transcript); - - let c = transcript.challenge_scalar(); - - let mut input_as_sparse_poly_entries = vec![SparsePolyEntry::new(0, Scalar::one())]; - //remaining inputs - input_as_sparse_poly_entries.extend( - (0..input.len()) - .map(|i| SparsePolyEntry::new(i + 1, input[i])) - .collect::>(), - ); - - let n = num_vars; - let input_as_sparse_poly = - SparsePolynomial::new(n.log_2() as usize, input_as_sparse_poly_entries); - - let config = VerifierConfig { - num_vars, - num_cons, - input: input.to_vec(), - evals: *evals, - params: poseidon_params(), - prev_challenge: c, - claims_phase2: self.claims_phase2, - polys_sc1: self.sc_proof_phase1.polys.clone(), - polys_sc2: self.sc_proof_phase2.polys.clone(), - eval_vars_at_ry: self.eval_vars_at_ry, - input_as_sparse_poly, - // rx: self.rx.clone(), - ry: self.ry.clone(), - transcript_sat_state: self.transcript_sat_state, - }; - - let mut rng = ark_std::test_rng(); - let circuit = VerifierCircuit::new(&config, &mut rng).unwrap(); - - let nc_inner = verify_constraints_inner(circuit.clone(), &num_cons); - - let nc_outer = verify_constraints_outer(circuit, &num_cons); - Ok(nc_inner + nc_outer) - } + fn prove_phase_one( + num_rounds: usize, + evals_tau: &mut DensePolynomial, + evals_Az: &mut DensePolynomial, + evals_Bz: &mut DensePolynomial, + evals_Cz: &mut DensePolynomial, + transcript: &mut PoseidonTranscript, + ) -> (SumcheckInstanceProof, Vec, Vec) { + let comb_func = |poly_tau_comp: &Scalar, + poly_A_comp: &Scalar, + poly_B_comp: &Scalar, + poly_C_comp: &Scalar| + -> Scalar { + (*poly_tau_comp) * ((*poly_A_comp) * poly_B_comp - poly_C_comp) + }; + + let (sc_proof_phase_one, r, claims) = SumcheckInstanceProof::prove_cubic_with_additive_term( + &Scalar::zero(), // claim is zero + num_rounds, + evals_tau, + evals_Az, + evals_Bz, + evals_Cz, + comb_func, + transcript, + ); + + (sc_proof_phase_one, r, claims) + } + + fn prove_phase_two( + num_rounds: usize, + claim: &Scalar, + evals_z: &mut DensePolynomial, + evals_ABC: &mut DensePolynomial, + transcript: &mut PoseidonTranscript, + ) -> (SumcheckInstanceProof, Vec, Vec) { + let comb_func = + |poly_A_comp: &Scalar, poly_B_comp: &Scalar| -> Scalar { (*poly_A_comp) * poly_B_comp }; + let (sc_proof_phase_two, r, claims) = SumcheckInstanceProof::prove_quad( + claim, num_rounds, evals_z, evals_ABC, comb_func, transcript, + ); + + (sc_proof_phase_two, r, claims) + } + + fn protocol_name() -> &'static [u8] { + b"R1CS proof" + } + + pub fn prove( + inst: &R1CSInstance, + vars: Vec, + input: &[Scalar], + gens: &R1CSGens, + transcript: &mut PoseidonTranscript, + ) -> (R1CSProof, Vec, Vec) { + let timer_prove = Timer::new("R1CSProof::prove"); + // we currently require the number of |inputs| + 1 to be at most number of vars + assert!(input.len() < vars.len()); + + // create the multilinear witness polynomial from the satisfying assiment + let poly_vars = DensePolynomial::new(vars.clone()); + + let timer_commit = Timer::new("polycommit"); + // commitment to the satisfying witness polynomial + let comm = MultilinearPC::::commit(&gens.gens_pc.ck, &poly_vars); + comm.append_to_poseidon(transcript); + timer_commit.stop(); + + let c = transcript.challenge_scalar(); + transcript.new_from_state(&c); + + transcript.append_scalar_vector(input); + + let timer_sc_proof_phase1 = Timer::new("prove_sc_phase_one"); + + // append input to variables to create a single vector z + let z = { + let num_inputs = input.len(); + let num_vars = vars.len(); + let mut z = vars; + z.extend(&vec![Scalar::one()]); // add constant term in z + z.extend(input); + z.extend(&vec![Scalar::zero(); num_vars - num_inputs - 1]); // we will pad with zeros + z + }; + + // derive the verifier's challenge tau + let (num_rounds_x, num_rounds_y) = (inst.get_num_cons().log_2(), z.len().log_2()); + let tau = transcript.challenge_vector(num_rounds_x); + // compute the initial evaluation table for R(\tau, x) + let mut poly_tau = DensePolynomial::new(EqPolynomial::new(tau).evals()); + let (mut poly_Az, mut poly_Bz, mut poly_Cz) = + inst.multiply_vec(inst.get_num_cons(), z.len(), &z); + + let (sc_proof_phase1, rx, _claims_phase1) = R1CSProof::prove_phase_one( + num_rounds_x, + &mut poly_tau, + &mut poly_Az, + &mut poly_Bz, + &mut poly_Cz, + transcript, + ); + assert_eq!(poly_tau.len(), 1); + assert_eq!(poly_Az.len(), 1); + assert_eq!(poly_Bz.len(), 1); + assert_eq!(poly_Cz.len(), 1); + timer_sc_proof_phase1.stop(); + + let (tau_claim, Az_claim, Bz_claim, Cz_claim) = + (&poly_tau[0], &poly_Az[0], &poly_Bz[0], &poly_Cz[0]); + let prod_Az_Bz_claims = (*Az_claim) * Bz_claim; + + // prove the final step of sum-check #1 + let taus_bound_rx = tau_claim; + let _claim_post_phase1 = ((*Az_claim) * Bz_claim - Cz_claim) * taus_bound_rx; + + let timer_sc_proof_phase2 = Timer::new("prove_sc_phase_two"); + // combine the three claims into a single claim + let r_A = transcript.challenge_scalar(); + let r_B = transcript.challenge_scalar(); + let r_C = transcript.challenge_scalar(); + let claim_phase2 = r_A * Az_claim + r_B * Bz_claim + r_C * Cz_claim; + + let evals_ABC = { + // compute the initial evaluation table for R(\tau, x) + let evals_rx = EqPolynomial::new(rx.clone()).evals(); + let (evals_A, evals_B, evals_C) = + inst.compute_eval_table_sparse(inst.get_num_cons(), z.len(), &evals_rx); + + assert_eq!(evals_A.len(), evals_B.len()); + assert_eq!(evals_A.len(), evals_C.len()); + (0..evals_A.len()) + .map(|i| r_A * evals_A[i] + r_B * evals_B[i] + r_C * evals_C[i]) + .collect::>() + }; + + // another instance of the sum-check protocol + let (sc_proof_phase2, ry, _claims_phase2) = R1CSProof::prove_phase_two( + num_rounds_y, + &claim_phase2, + &mut DensePolynomial::new(z), + &mut DensePolynomial::new(evals_ABC), + transcript, + ); + timer_sc_proof_phase2.stop(); + + // TODO: modify the polynomial evaluation in Spartan to be consistent + // with the evaluation in ark-poly-commit so that reversing is not needed + // anymore + let timmer_opening = Timer::new("polyopening"); + let mut dummy = ry[1..].to_vec().clone(); + dummy.reverse(); + let proof_eval_vars_at_ry = MultilinearPC::::open(&gens.gens_pc.ck, &poly_vars, &dummy); + println!( + "proof size (no of quotients): {:?}", + proof_eval_vars_at_ry.proofs.len() + ); + timmer_opening.stop(); + + let timer_polyeval = Timer::new("polyeval"); + let eval_vars_at_ry = poly_vars.evaluate(&ry[1..]); + timer_polyeval.stop(); + + timer_prove.stop(); + + let c = transcript.challenge_scalar(); + + ( + R1CSProof { + comm, + sc_proof_phase1, + claims_phase2: (*Az_claim, *Bz_claim, *Cz_claim, prod_Az_Bz_claims), + sc_proof_phase2, + eval_vars_at_ry, + proof_eval_vars_at_ry, + rx: rx.clone(), + ry: ry.clone(), + transcript_sat_state: c, + }, + rx, + ry, + ) + } + + pub fn verify_groth16( + &self, + num_vars: usize, + num_cons: usize, + input: &[Scalar], + evals: &(Scalar, Scalar, Scalar), + transcript: &mut PoseidonTranscript, + gens: &R1CSGens, + ) -> Result<(u128, u128, u128), ProofVerifyError> { + self.comm.append_to_poseidon(transcript); + + let c = transcript.challenge_scalar(); + + let mut input_as_sparse_poly_entries = vec![SparsePolyEntry::new(0, Scalar::one())]; + //remaining inputs + input_as_sparse_poly_entries.extend( + (0..input.len()) + .map(|i| SparsePolyEntry::new(i + 1, input[i])) + .collect::>(), + ); + + let n = num_vars; + let input_as_sparse_poly = + SparsePolynomial::new(n.log_2() as usize, input_as_sparse_poly_entries); + + let config = VerifierConfig { + num_vars, + num_cons, + input: input.to_vec(), + evals: *evals, + params: poseidon_params(), + prev_challenge: c, + claims_phase2: self.claims_phase2, + polys_sc1: self.sc_proof_phase1.polys.clone(), + polys_sc2: self.sc_proof_phase2.polys.clone(), + eval_vars_at_ry: self.eval_vars_at_ry, + input_as_sparse_poly, + // rx: self.rx.clone(), + ry: self.ry.clone(), + transcript_sat_state: self.transcript_sat_state, + }; + + let mut rng = ark_std::test_rng(); + + let prove_inner = Timer::new("proveinnercircuit"); + let start = Instant::now(); + let circuit = VerifierCircuit::new(&config, &mut rng).unwrap(); + let dp1 = start.elapsed().as_millis(); + prove_inner.stop(); + + let start = Instant::now(); + let (pk, vk) = Groth16::

::setup(circuit.clone(), &mut rng).unwrap(); + let ds = start.elapsed().as_millis(); + + let prove_outer = Timer::new("proveoutercircuit"); + let start = Instant::now(); + let proof = Groth16::

::prove(&pk, circuit, &mut rng).unwrap(); + let dp2 = start.elapsed().as_millis(); + prove_outer.stop(); + + let start = Instant::now(); + let is_verified = Groth16::

::verify(&vk, &[], &proof).unwrap(); + assert!(is_verified); + + let timer_verification = Timer::new("commitverification"); + let mut dummy = self.ry[1..].to_vec(); + // TODO: ensure ark-poly-commit and Spartan produce consistent results + // when evaluating a polynomial at a given point so this reverse is not + // needed. + dummy.reverse(); + + // Verifies the proof of opening against the result of evaluating the + // witness polynomial at point ry. + let res = MultilinearPC::::check( + &gens.gens_pc.vk, + &self.comm, + &dummy, + self.eval_vars_at_ry, + &self.proof_eval_vars_at_ry, + ); + + timer_verification.stop(); + assert!(res == true); + let dv = start.elapsed().as_millis(); + + Ok((ds, dp1 + dp2, dv)) + } + + // Helper function to find the number of constraint in the circuit which + // requires executing it. + pub fn circuit_size( + &self, + num_vars: usize, + num_cons: usize, + input: &[Scalar], + evals: &(Scalar, Scalar, Scalar), + transcript: &mut PoseidonTranscript, + _gens: &R1CSGens, + ) -> Result { + self.comm.append_to_poseidon(transcript); + + let c = transcript.challenge_scalar(); + + let mut input_as_sparse_poly_entries = vec![SparsePolyEntry::new(0, Scalar::one())]; + //remaining inputs + input_as_sparse_poly_entries.extend( + (0..input.len()) + .map(|i| SparsePolyEntry::new(i + 1, input[i])) + .collect::>(), + ); + + let n = num_vars; + let input_as_sparse_poly = + SparsePolynomial::new(n.log_2() as usize, input_as_sparse_poly_entries); + + let config = VerifierConfig { + num_vars, + num_cons, + input: input.to_vec(), + evals: *evals, + params: poseidon_params(), + prev_challenge: c, + claims_phase2: self.claims_phase2, + polys_sc1: self.sc_proof_phase1.polys.clone(), + polys_sc2: self.sc_proof_phase2.polys.clone(), + eval_vars_at_ry: self.eval_vars_at_ry, + input_as_sparse_poly, + // rx: self.rx.clone(), + ry: self.ry.clone(), + transcript_sat_state: self.transcript_sat_state, + }; + + let mut rng = ark_std::test_rng(); + let circuit = VerifierCircuit::new(&config, &mut rng).unwrap(); + + let nc_inner = verify_constraints_inner(circuit.clone(), &num_cons); + + let nc_outer = verify_constraints_outer(circuit, &num_cons); + Ok(nc_inner + nc_outer) + } } fn verify_constraints_outer(circuit: VerifierCircuit, _num_cons: &usize) -> usize { - let cs = ConstraintSystem::::new_ref(); - circuit.generate_constraints(cs.clone()).unwrap(); - assert!(cs.is_satisfied().unwrap()); - cs.num_constraints() + let cs = ConstraintSystem::::new_ref(); + circuit.generate_constraints(cs.clone()).unwrap(); + assert!(cs.is_satisfied().unwrap()); + cs.num_constraints() } fn verify_constraints_inner(circuit: VerifierCircuit, _num_cons: &usize) -> usize { - let cs = ConstraintSystem::::new_ref(); - circuit - .inner_circuit - .generate_constraints(cs.clone()) - .unwrap(); - assert!(cs.is_satisfied().unwrap()); - cs.num_constraints() + let cs = ConstraintSystem::::new_ref(); + circuit + .inner_circuit + .generate_constraints(cs.clone()) + .unwrap(); + assert!(cs.is_satisfied().unwrap()); + cs.num_constraints() } #[cfg(test)] mod tests { - use crate::parameters::poseidon_params; - - use super::*; - - use ark_std::UniformRand; - - fn produce_tiny_r1cs() -> (R1CSInstance, Vec, Vec) { - // three constraints over five variables Z1, Z2, Z3, Z4, and Z5 - // rounded to the nearest power of two - let num_cons = 128; - let num_vars = 256; - let num_inputs = 2; - - // encode the above constraints into three matrices - let mut A: Vec<(usize, usize, Scalar)> = Vec::new(); - let mut B: Vec<(usize, usize, Scalar)> = Vec::new(); - let mut C: Vec<(usize, usize, Scalar)> = Vec::new(); - - let one = Scalar::one(); - // constraint 0 entries - // (Z1 + Z2) * I0 - Z3 = 0; - A.push((0, 0, one)); - A.push((0, 1, one)); - B.push((0, num_vars + 1, one)); - C.push((0, 2, one)); - - // constraint 1 entries - // (Z1 + I1) * (Z3) - Z4 = 0 - A.push((1, 0, one)); - A.push((1, num_vars + 2, one)); - B.push((1, 2, one)); - C.push((1, 3, one)); - // constraint 3 entries - // Z5 * 1 - 0 = 0 - A.push((2, 4, one)); - B.push((2, num_vars, one)); - - let inst = R1CSInstance::new(num_cons, num_vars, num_inputs, &A, &B, &C); - - // compute a satisfying assignment - let mut rng = ark_std::rand::thread_rng(); - let i0 = Scalar::rand(&mut rng); - let i1 = Scalar::rand(&mut rng); - let z1 = Scalar::rand(&mut rng); - let z2 = Scalar::rand(&mut rng); - let z3 = (z1 + z2) * i0; // constraint 1: (Z1 + Z2) * I0 - Z3 = 0; - let z4 = (z1 + i1) * z3; // constraint 2: (Z1 + I1) * (Z3) - Z4 = 0 - let z5 = Scalar::zero(); //constraint 3 - - let mut vars = vec![Scalar::zero(); num_vars]; - vars[0] = z1; - vars[1] = z2; - vars[2] = z3; - vars[3] = z4; - vars[4] = z5; - - let mut input = vec![Scalar::zero(); num_inputs]; - input[0] = i0; - input[1] = i1; - - (inst, vars, input) - } - - #[test] - fn test_tiny_r1cs() { - let (inst, vars, input) = tests::produce_tiny_r1cs(); - let is_sat = inst.is_sat(&vars, &input); - assert!(is_sat); - } - - #[test] - fn test_synthetic_r1cs() { - let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(1024, 1024, 10); - let is_sat = inst.is_sat(&vars, &input); - assert!(is_sat); - } - - #[test] - pub fn check_r1cs_proof() { - let num_vars = 1024; - let num_cons = num_vars; - let num_inputs = 10; - let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - - let gens = R1CSGens::new(b"test-m", num_cons, num_vars); - - let params = poseidon_params(); - // let mut random_tape = RandomTape::new(b"proof"); - - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let (proof, rx, ry) = R1CSProof::prove(&inst, vars, &input, &gens, &mut prover_transcript); - - let inst_evals = inst.evaluate(&rx, &ry); - - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - - // if you want to check the test fails - // input[0] = Scalar::zero(); - - assert!(proof - .verify_groth16( - inst.get_num_vars(), - inst.get_num_cons(), - &input, - &inst_evals, - &mut verifier_transcript, - &gens, - ) - .is_ok()); - } + use crate::parameters::poseidon_params; + + use super::*; + + use ark_std::UniformRand; + + fn produce_tiny_r1cs() -> (R1CSInstance, Vec, Vec) { + // three constraints over five variables Z1, Z2, Z3, Z4, and Z5 + // rounded to the nearest power of two + let num_cons = 128; + let num_vars = 256; + let num_inputs = 2; + + // encode the above constraints into three matrices + let mut A: Vec<(usize, usize, Scalar)> = Vec::new(); + let mut B: Vec<(usize, usize, Scalar)> = Vec::new(); + let mut C: Vec<(usize, usize, Scalar)> = Vec::new(); + + let one = Scalar::one(); + // constraint 0 entries + // (Z1 + Z2) * I0 - Z3 = 0; + A.push((0, 0, one)); + A.push((0, 1, one)); + B.push((0, num_vars + 1, one)); + C.push((0, 2, one)); + + // constraint 1 entries + // (Z1 + I1) * (Z3) - Z4 = 0 + A.push((1, 0, one)); + A.push((1, num_vars + 2, one)); + B.push((1, 2, one)); + C.push((1, 3, one)); + // constraint 3 entries + // Z5 * 1 - 0 = 0 + A.push((2, 4, one)); + B.push((2, num_vars, one)); + + let inst = R1CSInstance::new(num_cons, num_vars, num_inputs, &A, &B, &C); + + // compute a satisfying assignment + let mut rng = ark_std::rand::thread_rng(); + let i0 = Scalar::rand(&mut rng); + let i1 = Scalar::rand(&mut rng); + let z1 = Scalar::rand(&mut rng); + let z2 = Scalar::rand(&mut rng); + let z3 = (z1 + z2) * i0; // constraint 1: (Z1 + Z2) * I0 - Z3 = 0; + let z4 = (z1 + i1) * z3; // constraint 2: (Z1 + I1) * (Z3) - Z4 = 0 + let z5 = Scalar::zero(); //constraint 3 + + let mut vars = vec![Scalar::zero(); num_vars]; + vars[0] = z1; + vars[1] = z2; + vars[2] = z3; + vars[3] = z4; + vars[4] = z5; + + let mut input = vec![Scalar::zero(); num_inputs]; + input[0] = i0; + input[1] = i1; + + (inst, vars, input) + } + + #[test] + fn test_tiny_r1cs() { + let (inst, vars, input) = tests::produce_tiny_r1cs(); + let is_sat = inst.is_sat(&vars, &input); + assert!(is_sat); + } + + #[test] + fn test_synthetic_r1cs() { + let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(1024, 1024, 10); + let is_sat = inst.is_sat(&vars, &input); + assert!(is_sat); + } + + #[test] + pub fn check_r1cs_proof() { + let num_vars = 1024; + let num_cons = num_vars; + let num_inputs = 10; + let (inst, vars, input) = + R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + + let gens = R1CSGens::new(b"test-m", num_cons, num_vars); + + let params = poseidon_params(); + // let mut random_tape = RandomTape::new(b"proof"); + + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let (proof, rx, ry) = R1CSProof::prove(&inst, vars, &input, &gens, &mut prover_transcript); + + let inst_evals = inst.evaluate(&rx, &ry); + + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + + // if you want to check the test fails + // input[0] = Scalar::zero(); + + assert!(proof + .verify_groth16( + inst.get_num_vars(), + inst.get_num_cons(), + &input, + &inst_evals, + &mut verifier_transcript, + &gens, + ) + .is_ok()); + } } diff --git a/src/random.rs b/src/random.rs index 56793f7..6d4e8e5 100644 --- a/src/random.rs +++ b/src/random.rs @@ -4,25 +4,25 @@ use ark_std::UniformRand; use merlin::Transcript; pub struct RandomTape { - tape: Transcript, + tape: Transcript, } impl RandomTape { - pub fn new(name: &'static [u8]) -> Self { - let tape = { - let mut rng = ark_std::rand::thread_rng(); - let mut tape = Transcript::new(name); - tape.append_scalar(b"init_randomness", &Scalar::rand(&mut rng)); - tape - }; - Self { tape } - } + pub fn new(name: &'static [u8]) -> Self { + let tape = { + let mut rng = ark_std::rand::thread_rng(); + let mut tape = Transcript::new(name); + tape.append_scalar(b"init_randomness", &Scalar::rand(&mut rng)); + tape + }; + Self { tape } + } - pub fn random_scalar(&mut self, label: &'static [u8]) -> Scalar { - self.tape.challenge_scalar(label) - } + pub fn random_scalar(&mut self, label: &'static [u8]) -> Scalar { + self.tape.challenge_scalar(label) + } - pub fn random_vector(&mut self, label: &'static [u8], len: usize) -> Vec { - self.tape.challenge_vector(label, len) - } + pub fn random_vector(&mut self, label: &'static [u8], len: usize) -> Vec { + self.tape.challenge_vector(label, len) + } } diff --git a/src/sparse_mlpoly.rs b/src/sparse_mlpoly.rs index 3d8b0d5..5d24b7e 100644 --- a/src/sparse_mlpoly.rs +++ b/src/sparse_mlpoly.rs @@ -5,7 +5,7 @@ use crate::poseidon_transcript::{AppendToPoseidon, PoseidonTranscript}; use super::dense_mlpoly::DensePolynomial; use super::dense_mlpoly::{ - EqPolynomial, IdentityPolynomial, PolyCommitment, PolyCommitmentGens, PolyEvalProof, + EqPolynomial, IdentityPolynomial, PolyCommitment, PolyCommitmentGens, PolyEvalProof, }; use super::errors::ProofVerifyError; use super::math::Math; @@ -21,1705 +21,1708 @@ use merlin::Transcript; #[derive(Debug, CanonicalSerialize, CanonicalDeserialize, Clone)] pub struct SparseMatEntry { - row: usize, - col: usize, - val: Scalar, + row: usize, + col: usize, + val: Scalar, } impl SparseMatEntry { - pub fn new(row: usize, col: usize, val: Scalar) -> Self { - SparseMatEntry { row, col, val } - } + pub fn new(row: usize, col: usize, val: Scalar) -> Self { + SparseMatEntry { row, col, val } + } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize, Clone)] pub struct SparseMatPolynomial { - num_vars_x: usize, - num_vars_y: usize, - M: Vec, + num_vars_x: usize, + num_vars_y: usize, + M: Vec, } pub struct Derefs { - row_ops_val: Vec, - col_ops_val: Vec, - comb: DensePolynomial, + row_ops_val: Vec, + col_ops_val: Vec, + comb: DensePolynomial, } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct DerefsCommitment { - comm_ops_val: PolyCommitment, + comm_ops_val: PolyCommitment, } impl Derefs { - pub fn new(row_ops_val: Vec, col_ops_val: Vec) -> Self { - assert_eq!(row_ops_val.len(), col_ops_val.len()); - - let derefs = { - // combine all polynomials into a single polynomial (used below to produce a single commitment) - let comb = DensePolynomial::merge(row_ops_val.iter().chain(col_ops_val.iter())); - - Derefs { - row_ops_val, - col_ops_val, - comb, - } - }; - - derefs - } - - pub fn commit(&self, gens: &PolyCommitmentGens) -> DerefsCommitment { - let (comm_ops_val, _blinds) = self.comb.commit(gens, None); - DerefsCommitment { comm_ops_val } - } + pub fn new(row_ops_val: Vec, col_ops_val: Vec) -> Self { + assert_eq!(row_ops_val.len(), col_ops_val.len()); + + let derefs = { + // combine all polynomials into a single polynomial (used below to produce a single commitment) + let comb = DensePolynomial::merge(row_ops_val.iter().chain(col_ops_val.iter())); + + Derefs { + row_ops_val, + col_ops_val, + comb, + } + }; + + derefs + } + + pub fn commit(&self, gens: &PolyCommitmentGens) -> DerefsCommitment { + let (comm_ops_val, _blinds) = self.comb.commit(gens, None); + DerefsCommitment { comm_ops_val } + } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct DerefsEvalProof { - proof_derefs: PolyEvalProof, + proof_derefs: PolyEvalProof, } impl DerefsEvalProof { - fn protocol_name() -> &'static [u8] { - b"Derefs evaluation proof" - } - - fn prove_single( - joint_poly: &DensePolynomial, - r: &[Scalar], - evals: Vec, - gens: &PolyCommitmentGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - ) -> PolyEvalProof { - assert_eq!(joint_poly.get_num_vars(), r.len() + evals.len().log_2()); - - // append the claimed evaluations to transcript - // evals.append_to_transcript(b"evals_ops_val", transcript); - transcript.append_scalar_vector(&evals); - - // n-to-1 reduction - let (r_joint, eval_joint) = { - let challenges = transcript.challenge_vector(evals.len().log_2()); - let mut poly_evals = DensePolynomial::new(evals); - for i in (0..challenges.len()).rev() { - poly_evals.bound_poly_var_bot(&challenges[i]); - } - assert_eq!(poly_evals.len(), 1); - let joint_claim_eval = poly_evals[0]; - let mut r_joint = challenges; - r_joint.extend(r); - - debug_assert_eq!(joint_poly.evaluate(&r_joint), joint_claim_eval); - (r_joint, joint_claim_eval) - }; - // decommit the joint polynomial at r_joint - transcript.append_scalar(&eval_joint); - let (proof_derefs, _comm_derefs_eval) = PolyEvalProof::prove( - joint_poly, - None, - &r_joint, - &eval_joint, - None, - gens, - transcript, - random_tape, - ); - - proof_derefs - } - - // evalues both polynomials at r and produces a joint proof of opening - pub fn prove( - derefs: &Derefs, - eval_row_ops_val_vec: &[Scalar], - eval_col_ops_val_vec: &[Scalar], - r: &[Scalar], - gens: &PolyCommitmentGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - ) -> Self { - // transcript.append_protocol_name(DerefsEvalProof::protocol_name()); - - let evals = { - let mut evals = eval_row_ops_val_vec.to_owned(); - evals.extend(eval_col_ops_val_vec); - evals.resize(evals.len().next_power_of_two(), Scalar::zero()); - evals - }; - let proof_derefs = - DerefsEvalProof::prove_single(&derefs.comb, r, evals, gens, transcript, random_tape); - - DerefsEvalProof { proof_derefs } - } - - fn verify_single( - proof: &PolyEvalProof, - comm: &PolyCommitment, - r: &[Scalar], - evals: Vec, - gens: &PolyCommitmentGens, - transcript: &mut PoseidonTranscript, - ) -> Result<(), ProofVerifyError> { - // append the claimed evaluations to transcript - // evals.append_to_transcript(b"evals_ops_val", transcript); - transcript.append_scalar_vector(&evals); - - // n-to-1 reduction - let challenges = transcript.challenge_vector(evals.len().log_2()); - let mut poly_evals = DensePolynomial::new(evals); - for i in (0..challenges.len()).rev() { - poly_evals.bound_poly_var_bot(&challenges[i]); + fn protocol_name() -> &'static [u8] { + b"Derefs evaluation proof" + } + + fn prove_single( + joint_poly: &DensePolynomial, + r: &[Scalar], + evals: Vec, + gens: &PolyCommitmentGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + ) -> PolyEvalProof { + assert_eq!(joint_poly.get_num_vars(), r.len() + evals.len().log_2()); + + // append the claimed evaluations to transcript + // evals.append_to_transcript(b"evals_ops_val", transcript); + transcript.append_scalar_vector(&evals); + + // n-to-1 reduction + let (r_joint, eval_joint) = { + let challenges = transcript.challenge_vector(evals.len().log_2()); + let mut poly_evals = DensePolynomial::new(evals); + for i in (0..challenges.len()).rev() { + poly_evals.bound_poly_var_bot(&challenges[i]); + } + assert_eq!(poly_evals.len(), 1); + let joint_claim_eval = poly_evals[0]; + let mut r_joint = challenges; + r_joint.extend(r); + + debug_assert_eq!(joint_poly.evaluate(&r_joint), joint_claim_eval); + (r_joint, joint_claim_eval) + }; + // decommit the joint polynomial at r_joint + transcript.append_scalar(&eval_joint); + let (proof_derefs, _comm_derefs_eval) = PolyEvalProof::prove( + joint_poly, + None, + &r_joint, + &eval_joint, + None, + gens, + transcript, + random_tape, + ); + + proof_derefs + } + + // evalues both polynomials at r and produces a joint proof of opening + pub fn prove( + derefs: &Derefs, + eval_row_ops_val_vec: &[Scalar], + eval_col_ops_val_vec: &[Scalar], + r: &[Scalar], + gens: &PolyCommitmentGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + ) -> Self { + // transcript.append_protocol_name(DerefsEvalProof::protocol_name()); + + let evals = { + let mut evals = eval_row_ops_val_vec.to_owned(); + evals.extend(eval_col_ops_val_vec); + evals.resize(evals.len().next_power_of_two(), Scalar::zero()); + evals + }; + let proof_derefs = + DerefsEvalProof::prove_single(&derefs.comb, r, evals, gens, transcript, random_tape); + + DerefsEvalProof { proof_derefs } + } + + fn verify_single( + proof: &PolyEvalProof, + comm: &PolyCommitment, + r: &[Scalar], + evals: Vec, + gens: &PolyCommitmentGens, + transcript: &mut PoseidonTranscript, + ) -> Result<(), ProofVerifyError> { + // append the claimed evaluations to transcript + // evals.append_to_transcript(b"evals_ops_val", transcript); + transcript.append_scalar_vector(&evals); + + // n-to-1 reduction + let challenges = transcript.challenge_vector(evals.len().log_2()); + let mut poly_evals = DensePolynomial::new(evals); + for i in (0..challenges.len()).rev() { + poly_evals.bound_poly_var_bot(&challenges[i]); + } + assert_eq!(poly_evals.len(), 1); + let joint_claim_eval = poly_evals[0]; + let mut r_joint = challenges; + r_joint.extend(r); + + // decommit the joint polynomial at r_joint + // joint_claim_eval.append_to_transcript(b"joint_claim_eval", transcript); + transcript.append_scalar(&joint_claim_eval); + + proof.verify_plain(gens, transcript, &r_joint, &joint_claim_eval, comm) + } + + // verify evaluations of both polynomials at r + pub fn verify( + &self, + r: &[Scalar], + eval_row_ops_val_vec: &[Scalar], + eval_col_ops_val_vec: &[Scalar], + gens: &PolyCommitmentGens, + comm: &DerefsCommitment, + transcript: &mut PoseidonTranscript, + ) -> Result<(), ProofVerifyError> { + // transcript.append_protocol_name(DerefsEvalProof::protocol_name()); + let mut evals = eval_row_ops_val_vec.to_owned(); + evals.extend(eval_col_ops_val_vec); + evals.resize(evals.len().next_power_of_two(), Scalar::zero()); + + DerefsEvalProof::verify_single( + &self.proof_derefs, + &comm.comm_ops_val, + r, + evals, + gens, + transcript, + ) } - assert_eq!(poly_evals.len(), 1); - let joint_claim_eval = poly_evals[0]; - let mut r_joint = challenges; - r_joint.extend(r); - - // decommit the joint polynomial at r_joint - // joint_claim_eval.append_to_transcript(b"joint_claim_eval", transcript); - transcript.append_scalar(&joint_claim_eval); - - proof.verify_plain(gens, transcript, &r_joint, &joint_claim_eval, comm) - } - - // verify evaluations of both polynomials at r - pub fn verify( - &self, - r: &[Scalar], - eval_row_ops_val_vec: &[Scalar], - eval_col_ops_val_vec: &[Scalar], - gens: &PolyCommitmentGens, - comm: &DerefsCommitment, - transcript: &mut PoseidonTranscript, - ) -> Result<(), ProofVerifyError> { - // transcript.append_protocol_name(DerefsEvalProof::protocol_name()); - let mut evals = eval_row_ops_val_vec.to_owned(); - evals.extend(eval_col_ops_val_vec); - evals.resize(evals.len().next_power_of_two(), Scalar::zero()); - - DerefsEvalProof::verify_single( - &self.proof_derefs, - &comm.comm_ops_val, - r, - evals, - gens, - transcript, - ) - } } impl AppendToTranscript for DerefsCommitment { - fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { - transcript.append_message(b"derefs_commitment", b"begin_derefs_commitment"); - self.comm_ops_val.append_to_transcript(label, transcript); - transcript.append_message(b"derefs_commitment", b"end_derefs_commitment"); - } + fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { + transcript.append_message(b"derefs_commitment", b"begin_derefs_commitment"); + self.comm_ops_val.append_to_transcript(label, transcript); + transcript.append_message(b"derefs_commitment", b"end_derefs_commitment"); + } } impl AppendToPoseidon for DerefsCommitment { - fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { - self.comm_ops_val.append_to_poseidon(transcript); - } + fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { + self.comm_ops_val.append_to_poseidon(transcript); + } } struct AddrTimestamps { - ops_addr_usize: Vec>, - ops_addr: Vec, - read_ts: Vec, - audit_ts: DensePolynomial, + ops_addr_usize: Vec>, + ops_addr: Vec, + read_ts: Vec, + audit_ts: DensePolynomial, } impl AddrTimestamps { - pub fn new(num_cells: usize, num_ops: usize, ops_addr: Vec>) -> Self { - for item in ops_addr.iter() { - assert_eq!(item.len(), num_ops); + pub fn new(num_cells: usize, num_ops: usize, ops_addr: Vec>) -> Self { + for item in ops_addr.iter() { + assert_eq!(item.len(), num_ops); + } + + let mut audit_ts = vec![0usize; num_cells]; + let mut ops_addr_vec: Vec = Vec::new(); + let mut read_ts_vec: Vec = Vec::new(); + for ops_addr_inst in ops_addr.iter() { + let mut read_ts = vec![0usize; num_ops]; + + // since read timestamps are trustworthy, we can simply increment the r-ts to obtain a w-ts + // this is sufficient to ensure that the write-set, consisting of (addr, val, ts) tuples, is a set + for i in 0..num_ops { + let addr = ops_addr_inst[i]; + assert!(addr < num_cells); + let r_ts = audit_ts[addr]; + read_ts[i] = r_ts; + + let w_ts = r_ts + 1; + audit_ts[addr] = w_ts; + } + + ops_addr_vec.push(DensePolynomial::from_usize(ops_addr_inst)); + read_ts_vec.push(DensePolynomial::from_usize(&read_ts)); + } + + AddrTimestamps { + ops_addr: ops_addr_vec, + ops_addr_usize: ops_addr, + read_ts: read_ts_vec, + audit_ts: DensePolynomial::from_usize(&audit_ts), + } } - let mut audit_ts = vec![0usize; num_cells]; - let mut ops_addr_vec: Vec = Vec::new(); - let mut read_ts_vec: Vec = Vec::new(); - for ops_addr_inst in ops_addr.iter() { - let mut read_ts = vec![0usize; num_ops]; - - // since read timestamps are trustworthy, we can simply increment the r-ts to obtain a w-ts - // this is sufficient to ensure that the write-set, consisting of (addr, val, ts) tuples, is a set - for i in 0..num_ops { - let addr = ops_addr_inst[i]; - assert!(addr < num_cells); - let r_ts = audit_ts[addr]; - read_ts[i] = r_ts; - - let w_ts = r_ts + 1; - audit_ts[addr] = w_ts; - } - - ops_addr_vec.push(DensePolynomial::from_usize(ops_addr_inst)); - read_ts_vec.push(DensePolynomial::from_usize(&read_ts)); + fn deref_mem(addr: &[usize], mem_val: &[Scalar]) -> DensePolynomial { + DensePolynomial::new( + (0..addr.len()) + .map(|i| { + let a = addr[i]; + mem_val[a] + }) + .collect::>(), + ) } - AddrTimestamps { - ops_addr: ops_addr_vec, - ops_addr_usize: ops_addr, - read_ts: read_ts_vec, - audit_ts: DensePolynomial::from_usize(&audit_ts), + pub fn deref(&self, mem_val: &[Scalar]) -> Vec { + (0..self.ops_addr.len()) + .map(|i| AddrTimestamps::deref_mem(&self.ops_addr_usize[i], mem_val)) + .collect::>() } - } - - fn deref_mem(addr: &[usize], mem_val: &[Scalar]) -> DensePolynomial { - DensePolynomial::new( - (0..addr.len()) - .map(|i| { - let a = addr[i]; - mem_val[a] - }) - .collect::>(), - ) - } - - pub fn deref(&self, mem_val: &[Scalar]) -> Vec { - (0..self.ops_addr.len()) - .map(|i| AddrTimestamps::deref_mem(&self.ops_addr_usize[i], mem_val)) - .collect::>() - } } pub struct MultiSparseMatPolynomialAsDense { - batch_size: usize, - val: Vec, - row: AddrTimestamps, - col: AddrTimestamps, - comb_ops: DensePolynomial, - comb_mem: DensePolynomial, + batch_size: usize, + val: Vec, + row: AddrTimestamps, + col: AddrTimestamps, + comb_ops: DensePolynomial, + comb_mem: DensePolynomial, } pub struct SparseMatPolyCommitmentGens { - gens_ops: PolyCommitmentGens, - gens_mem: PolyCommitmentGens, - gens_derefs: PolyCommitmentGens, + gens_ops: PolyCommitmentGens, + gens_mem: PolyCommitmentGens, + gens_derefs: PolyCommitmentGens, } impl SparseMatPolyCommitmentGens { - pub fn new( - label: &'static [u8], - num_vars_x: usize, - num_vars_y: usize, - num_nz_entries: usize, - batch_size: usize, - ) -> SparseMatPolyCommitmentGens { - let num_vars_ops = - num_nz_entries.next_power_of_two().log_2() + (batch_size * 5).next_power_of_two().log_2(); - let num_vars_mem = if num_vars_x > num_vars_y { - num_vars_x - } else { - num_vars_y - } + 1; - let num_vars_derefs = - num_nz_entries.next_power_of_two().log_2() + (batch_size * 2).next_power_of_two().log_2(); - - let gens_ops = PolyCommitmentGens::new(num_vars_ops, label); - let gens_mem = PolyCommitmentGens::new(num_vars_mem, label); - let gens_derefs = PolyCommitmentGens::new(num_vars_derefs, label); - SparseMatPolyCommitmentGens { - gens_ops, - gens_mem, - gens_derefs, + pub fn new( + label: &'static [u8], + num_vars_x: usize, + num_vars_y: usize, + num_nz_entries: usize, + batch_size: usize, + ) -> SparseMatPolyCommitmentGens { + let num_vars_ops = num_nz_entries.next_power_of_two().log_2() + + (batch_size * 5).next_power_of_two().log_2(); + let num_vars_mem = if num_vars_x > num_vars_y { + num_vars_x + } else { + num_vars_y + } + 1; + let num_vars_derefs = num_nz_entries.next_power_of_two().log_2() + + (batch_size * 2).next_power_of_two().log_2(); + + let gens_ops = PolyCommitmentGens::new(num_vars_ops, label); + let gens_mem = PolyCommitmentGens::new(num_vars_mem, label); + let gens_derefs = PolyCommitmentGens::new(num_vars_derefs, label); + SparseMatPolyCommitmentGens { + gens_ops, + gens_mem, + gens_derefs, + } } - } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct SparseMatPolyCommitment { - batch_size: usize, - num_ops: usize, - num_mem_cells: usize, - comm_comb_ops: PolyCommitment, - comm_comb_mem: PolyCommitment, + batch_size: usize, + num_ops: usize, + num_mem_cells: usize, + comm_comb_ops: PolyCommitment, + comm_comb_mem: PolyCommitment, } impl AppendToTranscript for SparseMatPolyCommitment { - fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) { - transcript.append_u64(b"batch_size", self.batch_size as u64); - transcript.append_u64(b"num_ops", self.num_ops as u64); - transcript.append_u64(b"num_mem_cells", self.num_mem_cells as u64); - self - .comm_comb_ops - .append_to_transcript(b"comm_comb_ops", transcript); - self - .comm_comb_mem - .append_to_transcript(b"comm_comb_mem", transcript); - } + fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) { + transcript.append_u64(b"batch_size", self.batch_size as u64); + transcript.append_u64(b"num_ops", self.num_ops as u64); + transcript.append_u64(b"num_mem_cells", self.num_mem_cells as u64); + self.comm_comb_ops + .append_to_transcript(b"comm_comb_ops", transcript); + self.comm_comb_mem + .append_to_transcript(b"comm_comb_mem", transcript); + } } impl AppendToPoseidon for SparseMatPolyCommitment { - fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { - transcript.append_u64(self.batch_size as u64); - transcript.append_u64(self.num_ops as u64); - transcript.append_u64(self.num_mem_cells as u64); - self.comm_comb_ops.append_to_poseidon(transcript); - self.comm_comb_mem.append_to_poseidon(transcript); - } + fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { + transcript.append_u64(self.batch_size as u64); + transcript.append_u64(self.num_ops as u64); + transcript.append_u64(self.num_mem_cells as u64); + self.comm_comb_ops.append_to_poseidon(transcript); + self.comm_comb_mem.append_to_poseidon(transcript); + } } impl SparseMatPolynomial { - pub fn new(num_vars_x: usize, num_vars_y: usize, M: Vec) -> Self { - SparseMatPolynomial { - num_vars_x, - num_vars_y, - M, + pub fn new(num_vars_x: usize, num_vars_y: usize, M: Vec) -> Self { + SparseMatPolynomial { + num_vars_x, + num_vars_y, + M, + } + } + + pub fn get_num_nz_entries(&self) -> usize { + self.M.len().next_power_of_two() + } + + fn sparse_to_dense_vecs(&self, N: usize) -> (Vec, Vec, Vec) { + assert!(N >= self.get_num_nz_entries()); + let mut ops_row: Vec = vec![0; N]; + let mut ops_col: Vec = vec![0; N]; + let mut val: Vec = vec![Scalar::zero(); N]; + + for i in 0..self.M.len() { + ops_row[i] = self.M[i].row; + ops_col[i] = self.M[i].col; + val[i] = self.M[i].val; + } + (ops_row, ops_col, val) + } + + fn multi_sparse_to_dense_rep( + sparse_polys: &[&SparseMatPolynomial], + ) -> MultiSparseMatPolynomialAsDense { + assert!(!sparse_polys.is_empty()); + for i in 1..sparse_polys.len() { + assert_eq!(sparse_polys[i].num_vars_x, sparse_polys[0].num_vars_x); + assert_eq!(sparse_polys[i].num_vars_y, sparse_polys[0].num_vars_y); + } + + let N = (0..sparse_polys.len()) + .map(|i| sparse_polys[i].get_num_nz_entries()) + .max() + .unwrap() + .next_power_of_two(); + + let mut ops_row_vec: Vec> = Vec::new(); + let mut ops_col_vec: Vec> = Vec::new(); + let mut val_vec: Vec = Vec::new(); + for poly in sparse_polys { + let (ops_row, ops_col, val) = poly.sparse_to_dense_vecs(N); + ops_row_vec.push(ops_row); + ops_col_vec.push(ops_col); + val_vec.push(DensePolynomial::new(val)); + } + + let any_poly = &sparse_polys[0]; + + let num_mem_cells = if any_poly.num_vars_x > any_poly.num_vars_y { + any_poly.num_vars_x.pow2() + } else { + any_poly.num_vars_y.pow2() + }; + + let row = AddrTimestamps::new(num_mem_cells, N, ops_row_vec); + let col = AddrTimestamps::new(num_mem_cells, N, ops_col_vec); + + // combine polynomials into a single polynomial for commitment purposes + let comb_ops = DensePolynomial::merge( + row.ops_addr + .iter() + .chain(row.read_ts.iter()) + .chain(col.ops_addr.iter()) + .chain(col.read_ts.iter()) + .chain(val_vec.iter()), + ); + let mut comb_mem = row.audit_ts.clone(); + comb_mem.extend(&col.audit_ts); + + MultiSparseMatPolynomialAsDense { + batch_size: sparse_polys.len(), + row, + col, + val: val_vec, + comb_ops, + comb_mem, + } } - } - - pub fn get_num_nz_entries(&self) -> usize { - self.M.len().next_power_of_two() - } - - fn sparse_to_dense_vecs(&self, N: usize) -> (Vec, Vec, Vec) { - assert!(N >= self.get_num_nz_entries()); - let mut ops_row: Vec = vec![0; N]; - let mut ops_col: Vec = vec![0; N]; - let mut val: Vec = vec![Scalar::zero(); N]; - - for i in 0..self.M.len() { - ops_row[i] = self.M[i].row; - ops_col[i] = self.M[i].col; - val[i] = self.M[i].val; + + fn evaluate_with_tables(&self, eval_table_rx: &[Scalar], eval_table_ry: &[Scalar]) -> Scalar { + assert_eq!(self.num_vars_x.pow2(), eval_table_rx.len()); + assert_eq!(self.num_vars_y.pow2(), eval_table_ry.len()); + + (0..self.M.len()) + .map(|i| { + let row = self.M[i].row; + let col = self.M[i].col; + let val = &self.M[i].val; + eval_table_rx[row] * eval_table_ry[col] * val + }) + .sum() } - (ops_row, ops_col, val) - } - - fn multi_sparse_to_dense_rep( - sparse_polys: &[&SparseMatPolynomial], - ) -> MultiSparseMatPolynomialAsDense { - assert!(!sparse_polys.is_empty()); - for i in 1..sparse_polys.len() { - assert_eq!(sparse_polys[i].num_vars_x, sparse_polys[0].num_vars_x); - assert_eq!(sparse_polys[i].num_vars_y, sparse_polys[0].num_vars_y); + + pub fn multi_evaluate( + polys: &[&SparseMatPolynomial], + rx: &[Scalar], + ry: &[Scalar], + ) -> Vec { + let eval_table_rx = EqPolynomial::new(rx.to_vec()).evals(); + let eval_table_ry = EqPolynomial::new(ry.to_vec()).evals(); + + (0..polys.len()) + .map(|i| polys[i].evaluate_with_tables(&eval_table_rx, &eval_table_ry)) + .collect::>() } - let N = (0..sparse_polys.len()) - .map(|i| sparse_polys[i].get_num_nz_entries()) - .max() - .unwrap() - .next_power_of_two(); - - let mut ops_row_vec: Vec> = Vec::new(); - let mut ops_col_vec: Vec> = Vec::new(); - let mut val_vec: Vec = Vec::new(); - for poly in sparse_polys { - let (ops_row, ops_col, val) = poly.sparse_to_dense_vecs(N); - ops_row_vec.push(ops_row); - ops_col_vec.push(ops_col); - val_vec.push(DensePolynomial::new(val)); + pub fn multiply_vec(&self, num_rows: usize, num_cols: usize, z: &[Scalar]) -> Vec { + assert_eq!(z.len(), num_cols); + + (0..self.M.len()) + .map(|i| { + let row = self.M[i].row; + let col = self.M[i].col; + let val = &self.M[i].val; + (row, z[col] * val) + }) + .fold(vec![Scalar::zero(); num_rows], |mut Mz, (r, v)| { + Mz[r] += v; + Mz + }) } - let any_poly = &sparse_polys[0]; - - let num_mem_cells = if any_poly.num_vars_x > any_poly.num_vars_y { - any_poly.num_vars_x.pow2() - } else { - any_poly.num_vars_y.pow2() - }; - - let row = AddrTimestamps::new(num_mem_cells, N, ops_row_vec); - let col = AddrTimestamps::new(num_mem_cells, N, ops_col_vec); - - // combine polynomials into a single polynomial for commitment purposes - let comb_ops = DensePolynomial::merge( - row - .ops_addr - .iter() - .chain(row.read_ts.iter()) - .chain(col.ops_addr.iter()) - .chain(col.read_ts.iter()) - .chain(val_vec.iter()), - ); - let mut comb_mem = row.audit_ts.clone(); - comb_mem.extend(&col.audit_ts); - - MultiSparseMatPolynomialAsDense { - batch_size: sparse_polys.len(), - row, - col, - val: val_vec, - comb_ops, - comb_mem, + pub fn compute_eval_table_sparse( + &self, + rx: &[Scalar], + num_rows: usize, + num_cols: usize, + ) -> Vec { + assert_eq!(rx.len(), num_rows); + + let mut M_evals: Vec = vec![Scalar::zero(); num_cols]; + + for i in 0..self.M.len() { + let entry = &self.M[i]; + M_evals[entry.col] += rx[entry.row] * entry.val; + } + M_evals } - } - - fn evaluate_with_tables(&self, eval_table_rx: &[Scalar], eval_table_ry: &[Scalar]) -> Scalar { - assert_eq!(self.num_vars_x.pow2(), eval_table_rx.len()); - assert_eq!(self.num_vars_y.pow2(), eval_table_ry.len()); - - (0..self.M.len()) - .map(|i| { - let row = self.M[i].row; - let col = self.M[i].col; - let val = &self.M[i].val; - eval_table_rx[row] * eval_table_ry[col] * val - }) - .sum() - } - - pub fn multi_evaluate( - polys: &[&SparseMatPolynomial], - rx: &[Scalar], - ry: &[Scalar], - ) -> Vec { - let eval_table_rx = EqPolynomial::new(rx.to_vec()).evals(); - let eval_table_ry = EqPolynomial::new(ry.to_vec()).evals(); - - (0..polys.len()) - .map(|i| polys[i].evaluate_with_tables(&eval_table_rx, &eval_table_ry)) - .collect::>() - } - - pub fn multiply_vec(&self, num_rows: usize, num_cols: usize, z: &[Scalar]) -> Vec { - assert_eq!(z.len(), num_cols); - - (0..self.M.len()) - .map(|i| { - let row = self.M[i].row; - let col = self.M[i].col; - let val = &self.M[i].val; - (row, z[col] * val) - }) - .fold(vec![Scalar::zero(); num_rows], |mut Mz, (r, v)| { - Mz[r] += v; - Mz - }) - } - - pub fn compute_eval_table_sparse( - &self, - rx: &[Scalar], - num_rows: usize, - num_cols: usize, - ) -> Vec { - assert_eq!(rx.len(), num_rows); - - let mut M_evals: Vec = vec![Scalar::zero(); num_cols]; - - for i in 0..self.M.len() { - let entry = &self.M[i]; - M_evals[entry.col] += rx[entry.row] * entry.val; + + pub fn multi_commit( + sparse_polys: &[&SparseMatPolynomial], + gens: &SparseMatPolyCommitmentGens, + ) -> (SparseMatPolyCommitment, MultiSparseMatPolynomialAsDense) { + let batch_size = sparse_polys.len(); + let dense = SparseMatPolynomial::multi_sparse_to_dense_rep(sparse_polys); + + let (comm_comb_ops, _blinds_comb_ops) = dense.comb_ops.commit(&gens.gens_ops, None); + let (comm_comb_mem, _blinds_comb_mem) = dense.comb_mem.commit(&gens.gens_mem, None); + + ( + SparseMatPolyCommitment { + batch_size, + num_mem_cells: dense.row.audit_ts.len(), + num_ops: dense.row.read_ts[0].len(), + comm_comb_ops, + comm_comb_mem, + }, + dense, + ) } - M_evals - } - - pub fn multi_commit( - sparse_polys: &[&SparseMatPolynomial], - gens: &SparseMatPolyCommitmentGens, - ) -> (SparseMatPolyCommitment, MultiSparseMatPolynomialAsDense) { - let batch_size = sparse_polys.len(); - let dense = SparseMatPolynomial::multi_sparse_to_dense_rep(sparse_polys); - - let (comm_comb_ops, _blinds_comb_ops) = dense.comb_ops.commit(&gens.gens_ops, None); - let (comm_comb_mem, _blinds_comb_mem) = dense.comb_mem.commit(&gens.gens_mem, None); - - ( - SparseMatPolyCommitment { - batch_size, - num_mem_cells: dense.row.audit_ts.len(), - num_ops: dense.row.read_ts[0].len(), - comm_comb_ops, - comm_comb_mem, - }, - dense, - ) - } } impl MultiSparseMatPolynomialAsDense { - pub fn deref(&self, row_mem_val: &[Scalar], col_mem_val: &[Scalar]) -> Derefs { - let row_ops_val = self.row.deref(row_mem_val); - let col_ops_val = self.col.deref(col_mem_val); + pub fn deref(&self, row_mem_val: &[Scalar], col_mem_val: &[Scalar]) -> Derefs { + let row_ops_val = self.row.deref(row_mem_val); + let col_ops_val = self.col.deref(col_mem_val); - Derefs::new(row_ops_val, col_ops_val) - } + Derefs::new(row_ops_val, col_ops_val) + } } #[derive(Debug)] struct ProductLayer { - init: ProductCircuit, - read_vec: Vec, - write_vec: Vec, - audit: ProductCircuit, + init: ProductCircuit, + read_vec: Vec, + write_vec: Vec, + audit: ProductCircuit, } #[derive(Debug)] struct Layers { - prod_layer: ProductLayer, + prod_layer: ProductLayer, } impl Layers { - fn build_hash_layer( - eval_table: &[Scalar], - addrs_vec: &[DensePolynomial], - derefs_vec: &[DensePolynomial], - read_ts_vec: &[DensePolynomial], - audit_ts: &DensePolynomial, - r_mem_check: &(Scalar, Scalar), - ) -> ( - DensePolynomial, - Vec, - Vec, - DensePolynomial, - ) { - let (r_hash, r_multiset_check) = r_mem_check; - - //hash(addr, val, ts) = ts * r_hash_sqr + val * r_hash + addr - let r_hash_sqr = r_hash.square(); - let hash_func = |addr: &Scalar, val: &Scalar, ts: &Scalar| -> Scalar { - r_hash_sqr * ts + (*val) * r_hash + addr - }; - - // hash init and audit that does not depend on #instances - let num_mem_cells = eval_table.len(); - let poly_init_hashed = DensePolynomial::new( - (0..num_mem_cells) - .map(|i| { - // at init time, addr is given by i, init value is given by eval_table, and ts = 0 - hash_func(&Scalar::from(i as u64), &eval_table[i], &Scalar::zero()) - r_multiset_check - }) - .collect::>(), - ); - let poly_audit_hashed = DensePolynomial::new( - (0..num_mem_cells) - .map(|i| { - // at audit time, addr is given by i, value is given by eval_table, and ts is given by audit_ts - hash_func(&Scalar::from(i as u64), &eval_table[i], &audit_ts[i]) - r_multiset_check - }) - .collect::>(), - ); - - // hash read and write that depends on #instances - let mut poly_read_hashed_vec: Vec = Vec::new(); - let mut poly_write_hashed_vec: Vec = Vec::new(); - for i in 0..addrs_vec.len() { - let (addrs, derefs, read_ts) = (&addrs_vec[i], &derefs_vec[i], &read_ts_vec[i]); - assert_eq!(addrs.len(), derefs.len()); - assert_eq!(addrs.len(), read_ts.len()); - let num_ops = addrs.len(); - let poly_read_hashed = DensePolynomial::new( - (0..num_ops) - .map(|i| { - // at read time, addr is given by addrs, value is given by derefs, and ts is given by read_ts - hash_func(&addrs[i], &derefs[i], &read_ts[i]) - r_multiset_check - }) - .collect::>(), - ); - poly_read_hashed_vec.push(poly_read_hashed); - - let poly_write_hashed = DensePolynomial::new( - (0..num_ops) - .map(|i| { - // at write time, addr is given by addrs, value is given by derefs, and ts is given by write_ts = read_ts + 1 - hash_func(&addrs[i], &derefs[i], &(read_ts[i] + Scalar::one())) - r_multiset_check - }) - .collect::>(), - ); - poly_write_hashed_vec.push(poly_write_hashed); + fn build_hash_layer( + eval_table: &[Scalar], + addrs_vec: &[DensePolynomial], + derefs_vec: &[DensePolynomial], + read_ts_vec: &[DensePolynomial], + audit_ts: &DensePolynomial, + r_mem_check: &(Scalar, Scalar), + ) -> ( + DensePolynomial, + Vec, + Vec, + DensePolynomial, + ) { + let (r_hash, r_multiset_check) = r_mem_check; + + //hash(addr, val, ts) = ts * r_hash_sqr + val * r_hash + addr + let r_hash_sqr = r_hash.square(); + let hash_func = |addr: &Scalar, val: &Scalar, ts: &Scalar| -> Scalar { + r_hash_sqr * ts + (*val) * r_hash + addr + }; + + // hash init and audit that does not depend on #instances + let num_mem_cells = eval_table.len(); + let poly_init_hashed = DensePolynomial::new( + (0..num_mem_cells) + .map(|i| { + // at init time, addr is given by i, init value is given by eval_table, and ts = 0 + hash_func(&Scalar::from(i as u64), &eval_table[i], &Scalar::zero()) + - r_multiset_check + }) + .collect::>(), + ); + let poly_audit_hashed = DensePolynomial::new( + (0..num_mem_cells) + .map(|i| { + // at audit time, addr is given by i, value is given by eval_table, and ts is given by audit_ts + hash_func(&Scalar::from(i as u64), &eval_table[i], &audit_ts[i]) + - r_multiset_check + }) + .collect::>(), + ); + + // hash read and write that depends on #instances + let mut poly_read_hashed_vec: Vec = Vec::new(); + let mut poly_write_hashed_vec: Vec = Vec::new(); + for i in 0..addrs_vec.len() { + let (addrs, derefs, read_ts) = (&addrs_vec[i], &derefs_vec[i], &read_ts_vec[i]); + assert_eq!(addrs.len(), derefs.len()); + assert_eq!(addrs.len(), read_ts.len()); + let num_ops = addrs.len(); + let poly_read_hashed = DensePolynomial::new( + (0..num_ops) + .map(|i| { + // at read time, addr is given by addrs, value is given by derefs, and ts is given by read_ts + hash_func(&addrs[i], &derefs[i], &read_ts[i]) - r_multiset_check + }) + .collect::>(), + ); + poly_read_hashed_vec.push(poly_read_hashed); + + let poly_write_hashed = DensePolynomial::new( + (0..num_ops) + .map(|i| { + // at write time, addr is given by addrs, value is given by derefs, and ts is given by write_ts = read_ts + 1 + hash_func(&addrs[i], &derefs[i], &(read_ts[i] + Scalar::one())) + - r_multiset_check + }) + .collect::>(), + ); + poly_write_hashed_vec.push(poly_write_hashed); + } + + ( + poly_init_hashed, + poly_read_hashed_vec, + poly_write_hashed_vec, + poly_audit_hashed, + ) } - ( - poly_init_hashed, - poly_read_hashed_vec, - poly_write_hashed_vec, - poly_audit_hashed, - ) - } - - pub fn new( - eval_table: &[Scalar], - addr_timestamps: &AddrTimestamps, - poly_ops_val: &[DensePolynomial], - r_mem_check: &(Scalar, Scalar), - ) -> Self { - let (poly_init_hashed, poly_read_hashed_vec, poly_write_hashed_vec, poly_audit_hashed) = - Layers::build_hash_layer( - eval_table, - &addr_timestamps.ops_addr, - poly_ops_val, - &addr_timestamps.read_ts, - &addr_timestamps.audit_ts, - r_mem_check, - ); - - let prod_init = ProductCircuit::new(&poly_init_hashed); - let prod_read_vec = (0..poly_read_hashed_vec.len()) - .map(|i| ProductCircuit::new(&poly_read_hashed_vec[i])) - .collect::>(); - let prod_write_vec = (0..poly_write_hashed_vec.len()) - .map(|i| ProductCircuit::new(&poly_write_hashed_vec[i])) - .collect::>(); - let prod_audit = ProductCircuit::new(&poly_audit_hashed); - - // subset audit check - let hashed_writes: Scalar = (0..prod_write_vec.len()) - .map(|i| prod_write_vec[i].evaluate()) - .product(); - let hashed_write_set: Scalar = prod_init.evaluate() * hashed_writes; - - let hashed_reads: Scalar = (0..prod_read_vec.len()) - .map(|i| prod_read_vec[i].evaluate()) - .product(); - let hashed_read_set: Scalar = hashed_reads * prod_audit.evaluate(); - - //assert_eq!(hashed_read_set, hashed_write_set); - debug_assert_eq!(hashed_read_set, hashed_write_set); - - Layers { - prod_layer: ProductLayer { - init: prod_init, - read_vec: prod_read_vec, - write_vec: prod_write_vec, - audit: prod_audit, - }, + pub fn new( + eval_table: &[Scalar], + addr_timestamps: &AddrTimestamps, + poly_ops_val: &[DensePolynomial], + r_mem_check: &(Scalar, Scalar), + ) -> Self { + let (poly_init_hashed, poly_read_hashed_vec, poly_write_hashed_vec, poly_audit_hashed) = + Layers::build_hash_layer( + eval_table, + &addr_timestamps.ops_addr, + poly_ops_val, + &addr_timestamps.read_ts, + &addr_timestamps.audit_ts, + r_mem_check, + ); + + let prod_init = ProductCircuit::new(&poly_init_hashed); + let prod_read_vec = (0..poly_read_hashed_vec.len()) + .map(|i| ProductCircuit::new(&poly_read_hashed_vec[i])) + .collect::>(); + let prod_write_vec = (0..poly_write_hashed_vec.len()) + .map(|i| ProductCircuit::new(&poly_write_hashed_vec[i])) + .collect::>(); + let prod_audit = ProductCircuit::new(&poly_audit_hashed); + + // subset audit check + let hashed_writes: Scalar = (0..prod_write_vec.len()) + .map(|i| prod_write_vec[i].evaluate()) + .product(); + let hashed_write_set: Scalar = prod_init.evaluate() * hashed_writes; + + let hashed_reads: Scalar = (0..prod_read_vec.len()) + .map(|i| prod_read_vec[i].evaluate()) + .product(); + let hashed_read_set: Scalar = hashed_reads * prod_audit.evaluate(); + + //assert_eq!(hashed_read_set, hashed_write_set); + debug_assert_eq!(hashed_read_set, hashed_write_set); + + Layers { + prod_layer: ProductLayer { + init: prod_init, + read_vec: prod_read_vec, + write_vec: prod_write_vec, + audit: prod_audit, + }, + } } - } } #[derive(Debug)] struct PolyEvalNetwork { - row_layers: Layers, - col_layers: Layers, + row_layers: Layers, + col_layers: Layers, } impl PolyEvalNetwork { - pub fn new( - dense: &MultiSparseMatPolynomialAsDense, - derefs: &Derefs, - mem_rx: &[Scalar], - mem_ry: &[Scalar], - r_mem_check: &(Scalar, Scalar), - ) -> Self { - let row_layers = Layers::new(mem_rx, &dense.row, &derefs.row_ops_val, r_mem_check); - let col_layers = Layers::new(mem_ry, &dense.col, &derefs.col_ops_val, r_mem_check); - - PolyEvalNetwork { - row_layers, - col_layers, + pub fn new( + dense: &MultiSparseMatPolynomialAsDense, + derefs: &Derefs, + mem_rx: &[Scalar], + mem_ry: &[Scalar], + r_mem_check: &(Scalar, Scalar), + ) -> Self { + let row_layers = Layers::new(mem_rx, &dense.row, &derefs.row_ops_val, r_mem_check); + let col_layers = Layers::new(mem_ry, &dense.col, &derefs.col_ops_val, r_mem_check); + + PolyEvalNetwork { + row_layers, + col_layers, + } } - } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] struct HashLayerProof { - eval_row: (Vec, Vec, Scalar), - eval_col: (Vec, Vec, Scalar), - eval_val: Vec, - eval_derefs: (Vec, Vec), - proof_ops: PolyEvalProof, - proof_mem: PolyEvalProof, - proof_derefs: DerefsEvalProof, + eval_row: (Vec, Vec, Scalar), + eval_col: (Vec, Vec, Scalar), + eval_val: Vec, + eval_derefs: (Vec, Vec), + proof_ops: PolyEvalProof, + proof_mem: PolyEvalProof, + proof_derefs: DerefsEvalProof, } impl HashLayerProof { - fn protocol_name() -> &'static [u8] { - b"Sparse polynomial hash layer proof" - } - - fn prove_helper( - rand: (&Vec, &Vec), - addr_timestamps: &AddrTimestamps, - ) -> (Vec, Vec, Scalar) { - let (rand_mem, rand_ops) = rand; - - // decommit ops-addr at rand_ops - let mut eval_ops_addr_vec: Vec = Vec::new(); - for i in 0..addr_timestamps.ops_addr.len() { - let eval_ops_addr = addr_timestamps.ops_addr[i].evaluate(rand_ops); - eval_ops_addr_vec.push(eval_ops_addr); + fn protocol_name() -> &'static [u8] { + b"Sparse polynomial hash layer proof" } - // decommit read_ts at rand_ops - let mut eval_read_ts_vec: Vec = Vec::new(); - for i in 0..addr_timestamps.read_ts.len() { - let eval_read_ts = addr_timestamps.read_ts[i].evaluate(rand_ops); - eval_read_ts_vec.push(eval_read_ts); + fn prove_helper( + rand: (&Vec, &Vec), + addr_timestamps: &AddrTimestamps, + ) -> (Vec, Vec, Scalar) { + let (rand_mem, rand_ops) = rand; + + // decommit ops-addr at rand_ops + let mut eval_ops_addr_vec: Vec = Vec::new(); + for i in 0..addr_timestamps.ops_addr.len() { + let eval_ops_addr = addr_timestamps.ops_addr[i].evaluate(rand_ops); + eval_ops_addr_vec.push(eval_ops_addr); + } + + // decommit read_ts at rand_ops + let mut eval_read_ts_vec: Vec = Vec::new(); + for i in 0..addr_timestamps.read_ts.len() { + let eval_read_ts = addr_timestamps.read_ts[i].evaluate(rand_ops); + eval_read_ts_vec.push(eval_read_ts); + } + + // decommit audit-ts at rand_mem + let eval_audit_ts = addr_timestamps.audit_ts.evaluate(rand_mem); + + (eval_ops_addr_vec, eval_read_ts_vec, eval_audit_ts) } - // decommit audit-ts at rand_mem - let eval_audit_ts = addr_timestamps.audit_ts.evaluate(rand_mem); - - (eval_ops_addr_vec, eval_read_ts_vec, eval_audit_ts) - } - - fn prove( - rand: (&Vec, &Vec), - dense: &MultiSparseMatPolynomialAsDense, - derefs: &Derefs, - gens: &SparseMatPolyCommitmentGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - ) -> Self { - // transcript.append_protocol_name(HashLayerProof::protocol_name()); - - let (rand_mem, rand_ops) = rand; - - // decommit derefs at rand_ops - let eval_row_ops_val = (0..derefs.row_ops_val.len()) - .map(|i| derefs.row_ops_val[i].evaluate(rand_ops)) - .collect::>(); - let eval_col_ops_val = (0..derefs.col_ops_val.len()) - .map(|i| derefs.col_ops_val[i].evaluate(rand_ops)) - .collect::>(); - let proof_derefs = DerefsEvalProof::prove( - derefs, - &eval_row_ops_val, - &eval_col_ops_val, - rand_ops, - &gens.gens_derefs, - transcript, - random_tape, - ); - let eval_derefs = (eval_row_ops_val, eval_col_ops_val); - - // evaluate row_addr, row_read-ts, col_addr, col_read-ts, val at rand_ops - // evaluate row_audit_ts and col_audit_ts at rand_mem - let (eval_row_addr_vec, eval_row_read_ts_vec, eval_row_audit_ts) = - HashLayerProof::prove_helper((rand_mem, rand_ops), &dense.row); - let (eval_col_addr_vec, eval_col_read_ts_vec, eval_col_audit_ts) = - HashLayerProof::prove_helper((rand_mem, rand_ops), &dense.col); - let eval_val_vec = (0..dense.val.len()) - .map(|i| dense.val[i].evaluate(rand_ops)) - .collect::>(); - - // form a single decommitment using comm_comb_ops - let mut evals_ops: Vec = Vec::new(); - evals_ops.extend(&eval_row_addr_vec); - evals_ops.extend(&eval_row_read_ts_vec); - evals_ops.extend(&eval_col_addr_vec); - evals_ops.extend(&eval_col_read_ts_vec); - evals_ops.extend(&eval_val_vec); - evals_ops.resize(evals_ops.len().next_power_of_two(), Scalar::zero()); - transcript.append_scalar_vector(&evals_ops); - let challenges_ops = transcript.challenge_vector(evals_ops.len().log_2()); - - let mut poly_evals_ops = DensePolynomial::new(evals_ops); - for i in (0..challenges_ops.len()).rev() { - poly_evals_ops.bound_poly_var_bot(&challenges_ops[i]); - } - assert_eq!(poly_evals_ops.len(), 1); - let joint_claim_eval_ops = poly_evals_ops[0]; - let mut r_joint_ops = challenges_ops; - r_joint_ops.extend(rand_ops); - debug_assert_eq!(dense.comb_ops.evaluate(&r_joint_ops), joint_claim_eval_ops); - transcript.append_scalar(&joint_claim_eval_ops); - let (proof_ops, _comm_ops_eval) = PolyEvalProof::prove( - &dense.comb_ops, - None, - &r_joint_ops, - &joint_claim_eval_ops, - None, - &gens.gens_ops, - transcript, - random_tape, - ); - - // form a single decommitment using comb_comb_mem at rand_mem - let evals_mem: Vec = vec![eval_row_audit_ts, eval_col_audit_ts]; - // evals_mem.append_to_transcript(b"claim_evals_mem", transcript); - transcript.append_scalar_vector(&evals_mem); - let challenges_mem = transcript.challenge_vector(evals_mem.len().log_2()); - - let mut poly_evals_mem = DensePolynomial::new(evals_mem); - for i in (0..challenges_mem.len()).rev() { - poly_evals_mem.bound_poly_var_bot(&challenges_mem[i]); - } - assert_eq!(poly_evals_mem.len(), 1); - let joint_claim_eval_mem = poly_evals_mem[0]; - let mut r_joint_mem = challenges_mem; - r_joint_mem.extend(rand_mem); - debug_assert_eq!(dense.comb_mem.evaluate(&r_joint_mem), joint_claim_eval_mem); - transcript.append_scalar(&joint_claim_eval_mem); - let (proof_mem, _comm_mem_eval) = PolyEvalProof::prove( - &dense.comb_mem, - None, - &r_joint_mem, - &joint_claim_eval_mem, - None, - &gens.gens_mem, - transcript, - random_tape, - ); - - HashLayerProof { - eval_row: (eval_row_addr_vec, eval_row_read_ts_vec, eval_row_audit_ts), - eval_col: (eval_col_addr_vec, eval_col_read_ts_vec, eval_col_audit_ts), - eval_val: eval_val_vec, - eval_derefs, - proof_ops, - proof_mem, - proof_derefs, - } - } - - fn verify_helper( - rand: &(&Vec, &Vec), - claims: &(Scalar, Vec, Vec, Scalar), - eval_ops_val: &[Scalar], - eval_ops_addr: &[Scalar], - eval_read_ts: &[Scalar], - eval_audit_ts: &Scalar, - r: &[Scalar], - r_hash: &Scalar, - r_multiset_check: &Scalar, - ) -> Result<(), ProofVerifyError> { - let r_hash_sqr = r_hash.square(); - let hash_func = |addr: &Scalar, val: &Scalar, ts: &Scalar| -> Scalar { - r_hash_sqr * ts + (*val) * r_hash + addr - }; - - let (rand_mem, _rand_ops) = rand; - let (claim_init, claim_read, claim_write, claim_audit) = claims; - - // init - let eval_init_addr = IdentityPolynomial::new(rand_mem.len()).evaluate(rand_mem); - let eval_init_val = EqPolynomial::new(r.to_vec()).evaluate(rand_mem); - let hash_init_at_rand_mem = - hash_func(&eval_init_addr, &eval_init_val, &Scalar::zero()) - r_multiset_check; // verify the claim_last of init chunk - assert_eq!(&hash_init_at_rand_mem, claim_init); - - // read - for i in 0..eval_ops_addr.len() { - let hash_read_at_rand_ops = - hash_func(&eval_ops_addr[i], &eval_ops_val[i], &eval_read_ts[i]) - r_multiset_check; // verify the claim_last of init chunk - assert_eq!(&hash_read_at_rand_ops, &claim_read[i]); - } - - // write: shares addr, val component; only decommit write_ts - for i in 0..eval_ops_addr.len() { - let eval_write_ts = eval_read_ts[i] + Scalar::one(); - let hash_write_at_rand_ops = - hash_func(&eval_ops_addr[i], &eval_ops_val[i], &eval_write_ts) - r_multiset_check; // verify the claim_last of init chunk - assert_eq!(&hash_write_at_rand_ops, &claim_write[i]); + fn prove( + rand: (&Vec, &Vec), + dense: &MultiSparseMatPolynomialAsDense, + derefs: &Derefs, + gens: &SparseMatPolyCommitmentGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + ) -> Self { + // transcript.append_protocol_name(HashLayerProof::protocol_name()); + + let (rand_mem, rand_ops) = rand; + + // decommit derefs at rand_ops + let eval_row_ops_val = (0..derefs.row_ops_val.len()) + .map(|i| derefs.row_ops_val[i].evaluate(rand_ops)) + .collect::>(); + let eval_col_ops_val = (0..derefs.col_ops_val.len()) + .map(|i| derefs.col_ops_val[i].evaluate(rand_ops)) + .collect::>(); + let proof_derefs = DerefsEvalProof::prove( + derefs, + &eval_row_ops_val, + &eval_col_ops_val, + rand_ops, + &gens.gens_derefs, + transcript, + random_tape, + ); + let eval_derefs = (eval_row_ops_val, eval_col_ops_val); + + // evaluate row_addr, row_read-ts, col_addr, col_read-ts, val at rand_ops + // evaluate row_audit_ts and col_audit_ts at rand_mem + let (eval_row_addr_vec, eval_row_read_ts_vec, eval_row_audit_ts) = + HashLayerProof::prove_helper((rand_mem, rand_ops), &dense.row); + let (eval_col_addr_vec, eval_col_read_ts_vec, eval_col_audit_ts) = + HashLayerProof::prove_helper((rand_mem, rand_ops), &dense.col); + let eval_val_vec = (0..dense.val.len()) + .map(|i| dense.val[i].evaluate(rand_ops)) + .collect::>(); + + // form a single decommitment using comm_comb_ops + let mut evals_ops: Vec = Vec::new(); + evals_ops.extend(&eval_row_addr_vec); + evals_ops.extend(&eval_row_read_ts_vec); + evals_ops.extend(&eval_col_addr_vec); + evals_ops.extend(&eval_col_read_ts_vec); + evals_ops.extend(&eval_val_vec); + evals_ops.resize(evals_ops.len().next_power_of_two(), Scalar::zero()); + transcript.append_scalar_vector(&evals_ops); + let challenges_ops = transcript.challenge_vector(evals_ops.len().log_2()); + + let mut poly_evals_ops = DensePolynomial::new(evals_ops); + for i in (0..challenges_ops.len()).rev() { + poly_evals_ops.bound_poly_var_bot(&challenges_ops[i]); + } + assert_eq!(poly_evals_ops.len(), 1); + let joint_claim_eval_ops = poly_evals_ops[0]; + let mut r_joint_ops = challenges_ops; + r_joint_ops.extend(rand_ops); + debug_assert_eq!(dense.comb_ops.evaluate(&r_joint_ops), joint_claim_eval_ops); + transcript.append_scalar(&joint_claim_eval_ops); + let (proof_ops, _comm_ops_eval) = PolyEvalProof::prove( + &dense.comb_ops, + None, + &r_joint_ops, + &joint_claim_eval_ops, + None, + &gens.gens_ops, + transcript, + random_tape, + ); + + // form a single decommitment using comb_comb_mem at rand_mem + let evals_mem: Vec = vec![eval_row_audit_ts, eval_col_audit_ts]; + // evals_mem.append_to_transcript(b"claim_evals_mem", transcript); + transcript.append_scalar_vector(&evals_mem); + let challenges_mem = transcript.challenge_vector(evals_mem.len().log_2()); + + let mut poly_evals_mem = DensePolynomial::new(evals_mem); + for i in (0..challenges_mem.len()).rev() { + poly_evals_mem.bound_poly_var_bot(&challenges_mem[i]); + } + assert_eq!(poly_evals_mem.len(), 1); + let joint_claim_eval_mem = poly_evals_mem[0]; + let mut r_joint_mem = challenges_mem; + r_joint_mem.extend(rand_mem); + debug_assert_eq!(dense.comb_mem.evaluate(&r_joint_mem), joint_claim_eval_mem); + transcript.append_scalar(&joint_claim_eval_mem); + let (proof_mem, _comm_mem_eval) = PolyEvalProof::prove( + &dense.comb_mem, + None, + &r_joint_mem, + &joint_claim_eval_mem, + None, + &gens.gens_mem, + transcript, + random_tape, + ); + + HashLayerProof { + eval_row: (eval_row_addr_vec, eval_row_read_ts_vec, eval_row_audit_ts), + eval_col: (eval_col_addr_vec, eval_col_read_ts_vec, eval_col_audit_ts), + eval_val: eval_val_vec, + eval_derefs, + proof_ops, + proof_mem, + proof_derefs, + } } - // audit: shares addr and val with init - let eval_audit_addr = eval_init_addr; - let eval_audit_val = eval_init_val; - let hash_audit_at_rand_mem = - hash_func(&eval_audit_addr, &eval_audit_val, eval_audit_ts) - r_multiset_check; - assert_eq!(&hash_audit_at_rand_mem, claim_audit); // verify the last step of the sum-check for audit - - Ok(()) - } - - fn verify( - &self, - rand: (&Vec, &Vec), - claims_row: &(Scalar, Vec, Vec, Scalar), - claims_col: &(Scalar, Vec, Vec, Scalar), - claims_dotp: &[Scalar], - comm: &SparseMatPolyCommitment, - gens: &SparseMatPolyCommitmentGens, - comm_derefs: &DerefsCommitment, - rx: &[Scalar], - ry: &[Scalar], - r_hash: &Scalar, - r_multiset_check: &Scalar, - transcript: &mut PoseidonTranscript, - ) -> Result<(), ProofVerifyError> { - let timer = Timer::new("verify_hash_proof"); - // transcript.append_protocol_name(HashLayerProof::protocol_name()); - - let (rand_mem, rand_ops) = rand; - - // verify derefs at rand_ops - let (eval_row_ops_val, eval_col_ops_val) = &self.eval_derefs; - assert_eq!(eval_row_ops_val.len(), eval_col_ops_val.len()); - self.proof_derefs.verify( - rand_ops, - eval_row_ops_val, - eval_col_ops_val, - &gens.gens_derefs, - comm_derefs, - transcript, - )?; - - // verify the decommitments used in evaluation sum-check - let eval_val_vec = &self.eval_val; - assert_eq!(claims_dotp.len(), 3 * eval_row_ops_val.len()); - for i in 0..claims_dotp.len() / 3 { - let claim_row_ops_val = claims_dotp[3 * i]; - let claim_col_ops_val = claims_dotp[3 * i + 1]; - let claim_val = claims_dotp[3 * i + 2]; - - assert_eq!(claim_row_ops_val, eval_row_ops_val[i]); - assert_eq!(claim_col_ops_val, eval_col_ops_val[i]); - assert_eq!(claim_val, eval_val_vec[i]); + fn verify_helper( + rand: &(&Vec, &Vec), + claims: &(Scalar, Vec, Vec, Scalar), + eval_ops_val: &[Scalar], + eval_ops_addr: &[Scalar], + eval_read_ts: &[Scalar], + eval_audit_ts: &Scalar, + r: &[Scalar], + r_hash: &Scalar, + r_multiset_check: &Scalar, + ) -> Result<(), ProofVerifyError> { + let r_hash_sqr = r_hash.square(); + let hash_func = |addr: &Scalar, val: &Scalar, ts: &Scalar| -> Scalar { + r_hash_sqr * ts + (*val) * r_hash + addr + }; + + let (rand_mem, _rand_ops) = rand; + let (claim_init, claim_read, claim_write, claim_audit) = claims; + + // init + let eval_init_addr = IdentityPolynomial::new(rand_mem.len()).evaluate(rand_mem); + let eval_init_val = EqPolynomial::new(r.to_vec()).evaluate(rand_mem); + let hash_init_at_rand_mem = + hash_func(&eval_init_addr, &eval_init_val, &Scalar::zero()) - r_multiset_check; // verify the claim_last of init chunk + assert_eq!(&hash_init_at_rand_mem, claim_init); + + // read + for i in 0..eval_ops_addr.len() { + let hash_read_at_rand_ops = + hash_func(&eval_ops_addr[i], &eval_ops_val[i], &eval_read_ts[i]) - r_multiset_check; // verify the claim_last of init chunk + assert_eq!(&hash_read_at_rand_ops, &claim_read[i]); + } + + // write: shares addr, val component; only decommit write_ts + for i in 0..eval_ops_addr.len() { + let eval_write_ts = eval_read_ts[i] + Scalar::one(); + let hash_write_at_rand_ops = + hash_func(&eval_ops_addr[i], &eval_ops_val[i], &eval_write_ts) - r_multiset_check; // verify the claim_last of init chunk + assert_eq!(&hash_write_at_rand_ops, &claim_write[i]); + } + + // audit: shares addr and val with init + let eval_audit_addr = eval_init_addr; + let eval_audit_val = eval_init_val; + let hash_audit_at_rand_mem = + hash_func(&eval_audit_addr, &eval_audit_val, eval_audit_ts) - r_multiset_check; + assert_eq!(&hash_audit_at_rand_mem, claim_audit); // verify the last step of the sum-check for audit + + Ok(()) } - // verify addr-timestamps using comm_comb_ops at rand_ops - let (eval_row_addr_vec, eval_row_read_ts_vec, eval_row_audit_ts) = &self.eval_row; - let (eval_col_addr_vec, eval_col_read_ts_vec, eval_col_audit_ts) = &self.eval_col; - - let mut evals_ops: Vec = Vec::new(); - evals_ops.extend(eval_row_addr_vec); - evals_ops.extend(eval_row_read_ts_vec); - evals_ops.extend(eval_col_addr_vec); - evals_ops.extend(eval_col_read_ts_vec); - evals_ops.extend(eval_val_vec); - evals_ops.resize(evals_ops.len().next_power_of_two(), Scalar::zero()); - transcript.append_scalar_vector(&evals_ops); - // evals_ops.append_to_transcript(b"claim_evals_ops", transcript); - let challenges_ops = transcript.challenge_vector(evals_ops.len().log_2()); - - let mut poly_evals_ops = DensePolynomial::new(evals_ops); - for i in (0..challenges_ops.len()).rev() { - poly_evals_ops.bound_poly_var_bot(&challenges_ops[i]); + fn verify( + &self, + rand: (&Vec, &Vec), + claims_row: &(Scalar, Vec, Vec, Scalar), + claims_col: &(Scalar, Vec, Vec, Scalar), + claims_dotp: &[Scalar], + comm: &SparseMatPolyCommitment, + gens: &SparseMatPolyCommitmentGens, + comm_derefs: &DerefsCommitment, + rx: &[Scalar], + ry: &[Scalar], + r_hash: &Scalar, + r_multiset_check: &Scalar, + transcript: &mut PoseidonTranscript, + ) -> Result<(), ProofVerifyError> { + let timer = Timer::new("verify_hash_proof"); + // transcript.append_protocol_name(HashLayerProof::protocol_name()); + + let (rand_mem, rand_ops) = rand; + + // verify derefs at rand_ops + let (eval_row_ops_val, eval_col_ops_val) = &self.eval_derefs; + assert_eq!(eval_row_ops_val.len(), eval_col_ops_val.len()); + self.proof_derefs.verify( + rand_ops, + eval_row_ops_val, + eval_col_ops_val, + &gens.gens_derefs, + comm_derefs, + transcript, + )?; + + // verify the decommitments used in evaluation sum-check + let eval_val_vec = &self.eval_val; + assert_eq!(claims_dotp.len(), 3 * eval_row_ops_val.len()); + for i in 0..claims_dotp.len() / 3 { + let claim_row_ops_val = claims_dotp[3 * i]; + let claim_col_ops_val = claims_dotp[3 * i + 1]; + let claim_val = claims_dotp[3 * i + 2]; + + assert_eq!(claim_row_ops_val, eval_row_ops_val[i]); + assert_eq!(claim_col_ops_val, eval_col_ops_val[i]); + assert_eq!(claim_val, eval_val_vec[i]); + } + + // verify addr-timestamps using comm_comb_ops at rand_ops + let (eval_row_addr_vec, eval_row_read_ts_vec, eval_row_audit_ts) = &self.eval_row; + let (eval_col_addr_vec, eval_col_read_ts_vec, eval_col_audit_ts) = &self.eval_col; + + let mut evals_ops: Vec = Vec::new(); + evals_ops.extend(eval_row_addr_vec); + evals_ops.extend(eval_row_read_ts_vec); + evals_ops.extend(eval_col_addr_vec); + evals_ops.extend(eval_col_read_ts_vec); + evals_ops.extend(eval_val_vec); + evals_ops.resize(evals_ops.len().next_power_of_two(), Scalar::zero()); + transcript.append_scalar_vector(&evals_ops); + // evals_ops.append_to_transcript(b"claim_evals_ops", transcript); + let challenges_ops = transcript.challenge_vector(evals_ops.len().log_2()); + + let mut poly_evals_ops = DensePolynomial::new(evals_ops); + for i in (0..challenges_ops.len()).rev() { + poly_evals_ops.bound_poly_var_bot(&challenges_ops[i]); + } + assert_eq!(poly_evals_ops.len(), 1); + let joint_claim_eval_ops = poly_evals_ops[0]; + let mut r_joint_ops = challenges_ops; + r_joint_ops.extend(rand_ops); + transcript.append_scalar(&joint_claim_eval_ops); + assert!(self + .proof_ops + .verify_plain( + &gens.gens_ops, + transcript, + &r_joint_ops, + &joint_claim_eval_ops, + &comm.comm_comb_ops + ) + .is_ok()); + + // verify proof-mem using comm_comb_mem at rand_mem + // form a single decommitment using comb_comb_mem at rand_mem + let evals_mem: Vec = vec![*eval_row_audit_ts, *eval_col_audit_ts]; + // evals_mem.append_to_transcript(b"claim_evals_mem", transcript); + transcript.append_scalar_vector(&evals_mem); + let challenges_mem = transcript.challenge_vector(evals_mem.len().log_2()); + + let mut poly_evals_mem = DensePolynomial::new(evals_mem); + for i in (0..challenges_mem.len()).rev() { + poly_evals_mem.bound_poly_var_bot(&challenges_mem[i]); + } + assert_eq!(poly_evals_mem.len(), 1); + let joint_claim_eval_mem = poly_evals_mem[0]; + let mut r_joint_mem = challenges_mem; + r_joint_mem.extend(rand_mem); + // joint_claim_eval_mem.append_to_transcript(b"joint_claim_eval_mem", transcript); + transcript.append_scalar(&joint_claim_eval_mem); + self.proof_mem.verify_plain( + &gens.gens_mem, + transcript, + &r_joint_mem, + &joint_claim_eval_mem, + &comm.comm_comb_mem, + )?; + + // verify the claims from the product layer + let (eval_ops_addr, eval_read_ts, eval_audit_ts) = &self.eval_row; + HashLayerProof::verify_helper( + &(rand_mem, rand_ops), + claims_row, + eval_row_ops_val, + eval_ops_addr, + eval_read_ts, + eval_audit_ts, + rx, + r_hash, + r_multiset_check, + )?; + + let (eval_ops_addr, eval_read_ts, eval_audit_ts) = &self.eval_col; + HashLayerProof::verify_helper( + &(rand_mem, rand_ops), + claims_col, + eval_col_ops_val, + eval_ops_addr, + eval_read_ts, + eval_audit_ts, + ry, + r_hash, + r_multiset_check, + )?; + + timer.stop(); + Ok(()) } - assert_eq!(poly_evals_ops.len(), 1); - let joint_claim_eval_ops = poly_evals_ops[0]; - let mut r_joint_ops = challenges_ops; - r_joint_ops.extend(rand_ops); - transcript.append_scalar(&joint_claim_eval_ops); - assert!(self - .proof_ops - .verify_plain( - &gens.gens_ops, - transcript, - &r_joint_ops, - &joint_claim_eval_ops, - &comm.comm_comb_ops - ) - .is_ok()); - - // verify proof-mem using comm_comb_mem at rand_mem - // form a single decommitment using comb_comb_mem at rand_mem - let evals_mem: Vec = vec![*eval_row_audit_ts, *eval_col_audit_ts]; - // evals_mem.append_to_transcript(b"claim_evals_mem", transcript); - transcript.append_scalar_vector(&evals_mem); - let challenges_mem = transcript.challenge_vector(evals_mem.len().log_2()); - - let mut poly_evals_mem = DensePolynomial::new(evals_mem); - for i in (0..challenges_mem.len()).rev() { - poly_evals_mem.bound_poly_var_bot(&challenges_mem[i]); - } - assert_eq!(poly_evals_mem.len(), 1); - let joint_claim_eval_mem = poly_evals_mem[0]; - let mut r_joint_mem = challenges_mem; - r_joint_mem.extend(rand_mem); - // joint_claim_eval_mem.append_to_transcript(b"joint_claim_eval_mem", transcript); - transcript.append_scalar(&joint_claim_eval_mem); - self.proof_mem.verify_plain( - &gens.gens_mem, - transcript, - &r_joint_mem, - &joint_claim_eval_mem, - &comm.comm_comb_mem, - )?; - - // verify the claims from the product layer - let (eval_ops_addr, eval_read_ts, eval_audit_ts) = &self.eval_row; - HashLayerProof::verify_helper( - &(rand_mem, rand_ops), - claims_row, - eval_row_ops_val, - eval_ops_addr, - eval_read_ts, - eval_audit_ts, - rx, - r_hash, - r_multiset_check, - )?; - - let (eval_ops_addr, eval_read_ts, eval_audit_ts) = &self.eval_col; - HashLayerProof::verify_helper( - &(rand_mem, rand_ops), - claims_col, - eval_col_ops_val, - eval_ops_addr, - eval_read_ts, - eval_audit_ts, - ry, - r_hash, - r_multiset_check, - )?; - - timer.stop(); - Ok(()) - } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] struct ProductLayerProof { - eval_row: (Scalar, Vec, Vec, Scalar), - eval_col: (Scalar, Vec, Vec, Scalar), - eval_val: (Vec, Vec), - proof_mem: ProductCircuitEvalProofBatched, - proof_ops: ProductCircuitEvalProofBatched, + eval_row: (Scalar, Vec, Vec, Scalar), + eval_col: (Scalar, Vec, Vec, Scalar), + eval_val: (Vec, Vec), + proof_mem: ProductCircuitEvalProofBatched, + proof_ops: ProductCircuitEvalProofBatched, } impl ProductLayerProof { - fn protocol_name() -> &'static [u8] { - b"Sparse polynomial product layer proof" - } - - pub fn prove( - row_prod_layer: &mut ProductLayer, - col_prod_layer: &mut ProductLayer, - dense: &MultiSparseMatPolynomialAsDense, - derefs: &Derefs, - eval: &[Scalar], - transcript: &mut PoseidonTranscript, - ) -> (Self, Vec, Vec) { - // transcript.append_protocol_name(ProductLayerProof::protocol_name()); - - let row_eval_init = row_prod_layer.init.evaluate(); - let row_eval_audit = row_prod_layer.audit.evaluate(); - let row_eval_read = (0..row_prod_layer.read_vec.len()) - .map(|i| row_prod_layer.read_vec[i].evaluate()) - .collect::>(); - let row_eval_write = (0..row_prod_layer.write_vec.len()) - .map(|i| row_prod_layer.write_vec[i].evaluate()) - .collect::>(); - - // subset check - let ws: Scalar = (0..row_eval_write.len()) - .map(|i| row_eval_write[i]) - .product(); - let rs: Scalar = (0..row_eval_read.len()).map(|i| row_eval_read[i]).product(); - assert_eq!(row_eval_init * ws, rs * row_eval_audit); - - transcript.append_scalar(&row_eval_init); - transcript.append_scalar_vector(&row_eval_read); - transcript.append_scalar_vector(&row_eval_write); - transcript.append_scalar(&row_eval_audit); - - let col_eval_init = col_prod_layer.init.evaluate(); - let col_eval_audit = col_prod_layer.audit.evaluate(); - let col_eval_read: Vec = (0..col_prod_layer.read_vec.len()) - .map(|i| col_prod_layer.read_vec[i].evaluate()) - .collect(); - let col_eval_write: Vec = (0..col_prod_layer.write_vec.len()) - .map(|i| col_prod_layer.write_vec[i].evaluate()) - .collect(); - - // subset check - let ws: Scalar = (0..col_eval_write.len()) - .map(|i| col_eval_write[i]) - .product(); - let rs: Scalar = (0..col_eval_read.len()).map(|i| col_eval_read[i]).product(); - assert_eq!(col_eval_init * ws, rs * col_eval_audit); - - transcript.append_scalar(&col_eval_init); - transcript.append_scalar_vector(&col_eval_read); - transcript.append_scalar_vector(&col_eval_write); - transcript.append_scalar(&col_eval_audit); - - // prepare dotproduct circuit for batching then with ops-related product circuits - assert_eq!(eval.len(), derefs.row_ops_val.len()); - assert_eq!(eval.len(), derefs.col_ops_val.len()); - assert_eq!(eval.len(), dense.val.len()); - let mut dotp_circuit_left_vec: Vec = Vec::new(); - let mut dotp_circuit_right_vec: Vec = Vec::new(); - let mut eval_dotp_left_vec: Vec = Vec::new(); - let mut eval_dotp_right_vec: Vec = Vec::new(); - for i in 0..derefs.row_ops_val.len() { - // evaluate sparse polynomial evaluation using two dotp checks - let left = derefs.row_ops_val[i].clone(); - let right = derefs.col_ops_val[i].clone(); - let weights = dense.val[i].clone(); - - // build two dot product circuits to prove evaluation of sparse polynomial - let mut dotp_circuit = DotProductCircuit::new(left, right, weights); - let (dotp_circuit_left, dotp_circuit_right) = dotp_circuit.split(); - - let (eval_dotp_left, eval_dotp_right) = - (dotp_circuit_left.evaluate(), dotp_circuit_right.evaluate()); - - // eval_dotp_left.append_to_transcript(b"claim_eval_dotp_left", transcript); - // eval_dotp_right.append_to_transcript(b"claim_eval_dotp_right", transcript); - transcript.append_scalar(&eval_dotp_left); - transcript.append_scalar(&eval_dotp_right); - assert_eq!(eval_dotp_left + eval_dotp_right, eval[i]); - eval_dotp_left_vec.push(eval_dotp_left); - eval_dotp_right_vec.push(eval_dotp_right); - - dotp_circuit_left_vec.push(dotp_circuit_left); - dotp_circuit_right_vec.push(dotp_circuit_right); + fn protocol_name() -> &'static [u8] { + b"Sparse polynomial product layer proof" } - // The number of operations into the memory encoded by rx and ry are always the same (by design) - // So we can produce a batched product proof for all of them at the same time. - // prove the correctness of claim_row_eval_read, claim_row_eval_write, claim_col_eval_read, and claim_col_eval_write - // TODO: we currently only produce proofs for 3 batched sparse polynomial evaluations - assert_eq!(row_prod_layer.read_vec.len(), 3); - let (row_read_A, row_read_B, row_read_C) = { - let (vec_A, vec_BC) = row_prod_layer.read_vec.split_at_mut(1); - let (vec_B, vec_C) = vec_BC.split_at_mut(1); - (vec_A, vec_B, vec_C) - }; - - let (row_write_A, row_write_B, row_write_C) = { - let (vec_A, vec_BC) = row_prod_layer.write_vec.split_at_mut(1); - let (vec_B, vec_C) = vec_BC.split_at_mut(1); - (vec_A, vec_B, vec_C) - }; - - let (col_read_A, col_read_B, col_read_C) = { - let (vec_A, vec_BC) = col_prod_layer.read_vec.split_at_mut(1); - let (vec_B, vec_C) = vec_BC.split_at_mut(1); - (vec_A, vec_B, vec_C) - }; - - let (col_write_A, col_write_B, col_write_C) = { - let (vec_A, vec_BC) = col_prod_layer.write_vec.split_at_mut(1); - let (vec_B, vec_C) = vec_BC.split_at_mut(1); - (vec_A, vec_B, vec_C) - }; - - let (dotp_left_A, dotp_left_B, dotp_left_C) = { - let (vec_A, vec_BC) = dotp_circuit_left_vec.split_at_mut(1); - let (vec_B, vec_C) = vec_BC.split_at_mut(1); - (vec_A, vec_B, vec_C) - }; - - let (dotp_right_A, dotp_right_B, dotp_right_C) = { - let (vec_A, vec_BC) = dotp_circuit_right_vec.split_at_mut(1); - let (vec_B, vec_C) = vec_BC.split_at_mut(1); - (vec_A, vec_B, vec_C) - }; - - let (proof_ops, rand_ops) = ProductCircuitEvalProofBatched::prove( - &mut vec![ - &mut row_read_A[0], - &mut row_read_B[0], - &mut row_read_C[0], - &mut row_write_A[0], - &mut row_write_B[0], - &mut row_write_C[0], - &mut col_read_A[0], - &mut col_read_B[0], - &mut col_read_C[0], - &mut col_write_A[0], - &mut col_write_B[0], - &mut col_write_C[0], - ], - &mut vec![ - &mut dotp_left_A[0], - &mut dotp_right_A[0], - &mut dotp_left_B[0], - &mut dotp_right_B[0], - &mut dotp_left_C[0], - &mut dotp_right_C[0], - ], - transcript, - ); - - // produce a batched proof of memory-related product circuits - let (proof_mem, rand_mem) = ProductCircuitEvalProofBatched::prove( - &mut vec![ - &mut row_prod_layer.init, - &mut row_prod_layer.audit, - &mut col_prod_layer.init, - &mut col_prod_layer.audit, - ], - &mut Vec::new(), - transcript, - ); - - let product_layer_proof = ProductLayerProof { - eval_row: (row_eval_init, row_eval_read, row_eval_write, row_eval_audit), - eval_col: (col_eval_init, col_eval_read, col_eval_write, col_eval_audit), - eval_val: (eval_dotp_left_vec, eval_dotp_right_vec), - proof_mem, - proof_ops, - }; - - let mut product_layer_proof_encoded: Vec = Vec::new(); - product_layer_proof - .serialize(&mut product_layer_proof_encoded) - .unwrap(); - let msg = format!( - "len_product_layer_proof {:?}", - product_layer_proof_encoded.len() - ); - Timer::print(&msg); - - (product_layer_proof, rand_mem, rand_ops) - } - - pub fn verify( - &self, - num_ops: usize, - num_cells: usize, - eval: &[Scalar], - transcript: &mut PoseidonTranscript, - ) -> Result< - ( - Vec, - Vec, - Vec, - Vec, - Vec, - ), - ProofVerifyError, - > { - // transcript.append_protocol_name(ProductLayerProof::protocol_name()); - - let timer = Timer::new("verify_prod_proof"); - let num_instances = eval.len(); - - // subset check - let (row_eval_init, row_eval_read, row_eval_write, row_eval_audit) = &self.eval_row; - assert_eq!(row_eval_write.len(), num_instances); - assert_eq!(row_eval_read.len(), num_instances); - let ws: Scalar = (0..row_eval_write.len()) - .map(|i| row_eval_write[i]) - .product(); - let rs: Scalar = (0..row_eval_read.len()).map(|i| row_eval_read[i]).product(); - assert_eq!(ws * row_eval_init, rs * row_eval_audit); - - // row_eval_init.append_to_transcript(b"claim_row_eval_init", transcript); - // row_eval_read.append_to_transcript(b"claim_row_eval_read", transcript); - // row_eval_write.append_to_transcript(b"claim_row_eval_write", transcript); - // row_eval_audit.append_to_transcript(b"claim_row_eval_audit", transcript); - - transcript.append_scalar(row_eval_init); - transcript.append_scalar_vector(row_eval_read); - transcript.append_scalar_vector(row_eval_write); - transcript.append_scalar(row_eval_audit); - - // subset check - let (col_eval_init, col_eval_read, col_eval_write, col_eval_audit) = &self.eval_col; - assert_eq!(col_eval_write.len(), num_instances); - assert_eq!(col_eval_read.len(), num_instances); - let ws: Scalar = (0..col_eval_write.len()) - .map(|i| col_eval_write[i]) - .product(); - let rs: Scalar = (0..col_eval_read.len()).map(|i| col_eval_read[i]).product(); - assert_eq!(ws * col_eval_init, rs * col_eval_audit); - - // col_eval_init.append_to_transcript(b"claim_col_eval_init", transcript); - // col_eval_read.append_to_transcript(b"claim_col_eval_read", transcript); - // col_eval_write.append_to_transcript(b"claim_col_eval_write", transcript); - // col_eval_audit.append_to_transcript(b"claim_col_eval_audit", transcript); - - transcript.append_scalar(col_eval_init); - transcript.append_scalar_vector(col_eval_read); - transcript.append_scalar_vector(col_eval_write); - transcript.append_scalar(col_eval_audit); - - // verify the evaluation of the sparse polynomial - let (eval_dotp_left, eval_dotp_right) = &self.eval_val; - assert_eq!(eval_dotp_left.len(), eval_dotp_left.len()); - assert_eq!(eval_dotp_left.len(), num_instances); - let mut claims_dotp_circuit: Vec = Vec::new(); - for i in 0..num_instances { - assert_eq!(eval_dotp_left[i] + eval_dotp_right[i], eval[i]); - // eval_dotp_left[i].append_to_transcript(b"claim_eval_dotp_left", transcript); - // eval_dotp_right[i].append_to_transcript(b"claim_eval_dotp_right", transcript) - transcript.append_scalar(&eval_dotp_left[i]); - transcript.append_scalar(&eval_dotp_right[i]); - - claims_dotp_circuit.push(eval_dotp_left[i]); - claims_dotp_circuit.push(eval_dotp_right[i]); + pub fn prove( + row_prod_layer: &mut ProductLayer, + col_prod_layer: &mut ProductLayer, + dense: &MultiSparseMatPolynomialAsDense, + derefs: &Derefs, + eval: &[Scalar], + transcript: &mut PoseidonTranscript, + ) -> (Self, Vec, Vec) { + // transcript.append_protocol_name(ProductLayerProof::protocol_name()); + + let row_eval_init = row_prod_layer.init.evaluate(); + let row_eval_audit = row_prod_layer.audit.evaluate(); + let row_eval_read = (0..row_prod_layer.read_vec.len()) + .map(|i| row_prod_layer.read_vec[i].evaluate()) + .collect::>(); + let row_eval_write = (0..row_prod_layer.write_vec.len()) + .map(|i| row_prod_layer.write_vec[i].evaluate()) + .collect::>(); + + // subset check + let ws: Scalar = (0..row_eval_write.len()) + .map(|i| row_eval_write[i]) + .product(); + let rs: Scalar = (0..row_eval_read.len()).map(|i| row_eval_read[i]).product(); + assert_eq!(row_eval_init * ws, rs * row_eval_audit); + + transcript.append_scalar(&row_eval_init); + transcript.append_scalar_vector(&row_eval_read); + transcript.append_scalar_vector(&row_eval_write); + transcript.append_scalar(&row_eval_audit); + + let col_eval_init = col_prod_layer.init.evaluate(); + let col_eval_audit = col_prod_layer.audit.evaluate(); + let col_eval_read: Vec = (0..col_prod_layer.read_vec.len()) + .map(|i| col_prod_layer.read_vec[i].evaluate()) + .collect(); + let col_eval_write: Vec = (0..col_prod_layer.write_vec.len()) + .map(|i| col_prod_layer.write_vec[i].evaluate()) + .collect(); + + // subset check + let ws: Scalar = (0..col_eval_write.len()) + .map(|i| col_eval_write[i]) + .product(); + let rs: Scalar = (0..col_eval_read.len()).map(|i| col_eval_read[i]).product(); + assert_eq!(col_eval_init * ws, rs * col_eval_audit); + + transcript.append_scalar(&col_eval_init); + transcript.append_scalar_vector(&col_eval_read); + transcript.append_scalar_vector(&col_eval_write); + transcript.append_scalar(&col_eval_audit); + + // prepare dotproduct circuit for batching then with ops-related product circuits + assert_eq!(eval.len(), derefs.row_ops_val.len()); + assert_eq!(eval.len(), derefs.col_ops_val.len()); + assert_eq!(eval.len(), dense.val.len()); + let mut dotp_circuit_left_vec: Vec = Vec::new(); + let mut dotp_circuit_right_vec: Vec = Vec::new(); + let mut eval_dotp_left_vec: Vec = Vec::new(); + let mut eval_dotp_right_vec: Vec = Vec::new(); + for i in 0..derefs.row_ops_val.len() { + // evaluate sparse polynomial evaluation using two dotp checks + let left = derefs.row_ops_val[i].clone(); + let right = derefs.col_ops_val[i].clone(); + let weights = dense.val[i].clone(); + + // build two dot product circuits to prove evaluation of sparse polynomial + let mut dotp_circuit = DotProductCircuit::new(left, right, weights); + let (dotp_circuit_left, dotp_circuit_right) = dotp_circuit.split(); + + let (eval_dotp_left, eval_dotp_right) = + (dotp_circuit_left.evaluate(), dotp_circuit_right.evaluate()); + + // eval_dotp_left.append_to_transcript(b"claim_eval_dotp_left", transcript); + // eval_dotp_right.append_to_transcript(b"claim_eval_dotp_right", transcript); + transcript.append_scalar(&eval_dotp_left); + transcript.append_scalar(&eval_dotp_right); + assert_eq!(eval_dotp_left + eval_dotp_right, eval[i]); + eval_dotp_left_vec.push(eval_dotp_left); + eval_dotp_right_vec.push(eval_dotp_right); + + dotp_circuit_left_vec.push(dotp_circuit_left); + dotp_circuit_right_vec.push(dotp_circuit_right); + } + + // The number of operations into the memory encoded by rx and ry are always the same (by design) + // So we can produce a batched product proof for all of them at the same time. + // prove the correctness of claim_row_eval_read, claim_row_eval_write, claim_col_eval_read, and claim_col_eval_write + // TODO: we currently only produce proofs for 3 batched sparse polynomial evaluations + assert_eq!(row_prod_layer.read_vec.len(), 3); + let (row_read_A, row_read_B, row_read_C) = { + let (vec_A, vec_BC) = row_prod_layer.read_vec.split_at_mut(1); + let (vec_B, vec_C) = vec_BC.split_at_mut(1); + (vec_A, vec_B, vec_C) + }; + + let (row_write_A, row_write_B, row_write_C) = { + let (vec_A, vec_BC) = row_prod_layer.write_vec.split_at_mut(1); + let (vec_B, vec_C) = vec_BC.split_at_mut(1); + (vec_A, vec_B, vec_C) + }; + + let (col_read_A, col_read_B, col_read_C) = { + let (vec_A, vec_BC) = col_prod_layer.read_vec.split_at_mut(1); + let (vec_B, vec_C) = vec_BC.split_at_mut(1); + (vec_A, vec_B, vec_C) + }; + + let (col_write_A, col_write_B, col_write_C) = { + let (vec_A, vec_BC) = col_prod_layer.write_vec.split_at_mut(1); + let (vec_B, vec_C) = vec_BC.split_at_mut(1); + (vec_A, vec_B, vec_C) + }; + + let (dotp_left_A, dotp_left_B, dotp_left_C) = { + let (vec_A, vec_BC) = dotp_circuit_left_vec.split_at_mut(1); + let (vec_B, vec_C) = vec_BC.split_at_mut(1); + (vec_A, vec_B, vec_C) + }; + + let (dotp_right_A, dotp_right_B, dotp_right_C) = { + let (vec_A, vec_BC) = dotp_circuit_right_vec.split_at_mut(1); + let (vec_B, vec_C) = vec_BC.split_at_mut(1); + (vec_A, vec_B, vec_C) + }; + + let (proof_ops, rand_ops) = ProductCircuitEvalProofBatched::prove( + &mut vec![ + &mut row_read_A[0], + &mut row_read_B[0], + &mut row_read_C[0], + &mut row_write_A[0], + &mut row_write_B[0], + &mut row_write_C[0], + &mut col_read_A[0], + &mut col_read_B[0], + &mut col_read_C[0], + &mut col_write_A[0], + &mut col_write_B[0], + &mut col_write_C[0], + ], + &mut vec![ + &mut dotp_left_A[0], + &mut dotp_right_A[0], + &mut dotp_left_B[0], + &mut dotp_right_B[0], + &mut dotp_left_C[0], + &mut dotp_right_C[0], + ], + transcript, + ); + + // produce a batched proof of memory-related product circuits + let (proof_mem, rand_mem) = ProductCircuitEvalProofBatched::prove( + &mut vec![ + &mut row_prod_layer.init, + &mut row_prod_layer.audit, + &mut col_prod_layer.init, + &mut col_prod_layer.audit, + ], + &mut Vec::new(), + transcript, + ); + + let product_layer_proof = ProductLayerProof { + eval_row: (row_eval_init, row_eval_read, row_eval_write, row_eval_audit), + eval_col: (col_eval_init, col_eval_read, col_eval_write, col_eval_audit), + eval_val: (eval_dotp_left_vec, eval_dotp_right_vec), + proof_mem, + proof_ops, + }; + + let mut product_layer_proof_encoded: Vec = Vec::new(); + product_layer_proof + .serialize(&mut product_layer_proof_encoded) + .unwrap(); + let msg = format!( + "len_product_layer_proof {:?}", + product_layer_proof_encoded.len() + ); + Timer::print(&msg); + + (product_layer_proof, rand_mem, rand_ops) } - // verify the correctness of claim_row_eval_read, claim_row_eval_write, claim_col_eval_read, and claim_col_eval_write - let mut claims_prod_circuit: Vec = Vec::new(); - claims_prod_circuit.extend(row_eval_read); - claims_prod_circuit.extend(row_eval_write); - claims_prod_circuit.extend(col_eval_read); - claims_prod_circuit.extend(col_eval_write); - - let (claims_ops, claims_dotp, rand_ops) = self.proof_ops.verify( - &claims_prod_circuit, - &claims_dotp_circuit, - num_ops, - transcript, - ); - // verify the correctness of claim_row_eval_init and claim_row_eval_audit - let (claims_mem, _claims_mem_dotp, rand_mem) = self.proof_mem.verify( - &[ - *row_eval_init, - *row_eval_audit, - *col_eval_init, - *col_eval_audit, - ], - &Vec::new(), - num_cells, - transcript, - ); - timer.stop(); - - Ok((claims_mem, rand_mem, claims_ops, claims_dotp, rand_ops)) - } + pub fn verify( + &self, + num_ops: usize, + num_cells: usize, + eval: &[Scalar], + transcript: &mut PoseidonTranscript, + ) -> Result< + ( + Vec, + Vec, + Vec, + Vec, + Vec, + ), + ProofVerifyError, + > { + // transcript.append_protocol_name(ProductLayerProof::protocol_name()); + + let timer = Timer::new("verify_prod_proof"); + let num_instances = eval.len(); + + // subset check + let (row_eval_init, row_eval_read, row_eval_write, row_eval_audit) = &self.eval_row; + assert_eq!(row_eval_write.len(), num_instances); + assert_eq!(row_eval_read.len(), num_instances); + let ws: Scalar = (0..row_eval_write.len()) + .map(|i| row_eval_write[i]) + .product(); + let rs: Scalar = (0..row_eval_read.len()).map(|i| row_eval_read[i]).product(); + assert_eq!(ws * row_eval_init, rs * row_eval_audit); + + // row_eval_init.append_to_transcript(b"claim_row_eval_init", transcript); + // row_eval_read.append_to_transcript(b"claim_row_eval_read", transcript); + // row_eval_write.append_to_transcript(b"claim_row_eval_write", transcript); + // row_eval_audit.append_to_transcript(b"claim_row_eval_audit", transcript); + + transcript.append_scalar(row_eval_init); + transcript.append_scalar_vector(row_eval_read); + transcript.append_scalar_vector(row_eval_write); + transcript.append_scalar(row_eval_audit); + + // subset check + let (col_eval_init, col_eval_read, col_eval_write, col_eval_audit) = &self.eval_col; + assert_eq!(col_eval_write.len(), num_instances); + assert_eq!(col_eval_read.len(), num_instances); + let ws: Scalar = (0..col_eval_write.len()) + .map(|i| col_eval_write[i]) + .product(); + let rs: Scalar = (0..col_eval_read.len()).map(|i| col_eval_read[i]).product(); + assert_eq!(ws * col_eval_init, rs * col_eval_audit); + + // col_eval_init.append_to_transcript(b"claim_col_eval_init", transcript); + // col_eval_read.append_to_transcript(b"claim_col_eval_read", transcript); + // col_eval_write.append_to_transcript(b"claim_col_eval_write", transcript); + // col_eval_audit.append_to_transcript(b"claim_col_eval_audit", transcript); + + transcript.append_scalar(col_eval_init); + transcript.append_scalar_vector(col_eval_read); + transcript.append_scalar_vector(col_eval_write); + transcript.append_scalar(col_eval_audit); + + // verify the evaluation of the sparse polynomial + let (eval_dotp_left, eval_dotp_right) = &self.eval_val; + assert_eq!(eval_dotp_left.len(), eval_dotp_left.len()); + assert_eq!(eval_dotp_left.len(), num_instances); + let mut claims_dotp_circuit: Vec = Vec::new(); + for i in 0..num_instances { + assert_eq!(eval_dotp_left[i] + eval_dotp_right[i], eval[i]); + // eval_dotp_left[i].append_to_transcript(b"claim_eval_dotp_left", transcript); + // eval_dotp_right[i].append_to_transcript(b"claim_eval_dotp_right", transcript) + transcript.append_scalar(&eval_dotp_left[i]); + transcript.append_scalar(&eval_dotp_right[i]); + + claims_dotp_circuit.push(eval_dotp_left[i]); + claims_dotp_circuit.push(eval_dotp_right[i]); + } + + // verify the correctness of claim_row_eval_read, claim_row_eval_write, claim_col_eval_read, and claim_col_eval_write + let mut claims_prod_circuit: Vec = Vec::new(); + claims_prod_circuit.extend(row_eval_read); + claims_prod_circuit.extend(row_eval_write); + claims_prod_circuit.extend(col_eval_read); + claims_prod_circuit.extend(col_eval_write); + + let (claims_ops, claims_dotp, rand_ops) = self.proof_ops.verify( + &claims_prod_circuit, + &claims_dotp_circuit, + num_ops, + transcript, + ); + // verify the correctness of claim_row_eval_init and claim_row_eval_audit + let (claims_mem, _claims_mem_dotp, rand_mem) = self.proof_mem.verify( + &[ + *row_eval_init, + *row_eval_audit, + *col_eval_init, + *col_eval_audit, + ], + &Vec::new(), + num_cells, + transcript, + ); + timer.stop(); + + Ok((claims_mem, rand_mem, claims_ops, claims_dotp, rand_ops)) + } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] struct PolyEvalNetworkProof { - proof_prod_layer: ProductLayerProof, - proof_hash_layer: HashLayerProof, + proof_prod_layer: ProductLayerProof, + proof_hash_layer: HashLayerProof, } impl PolyEvalNetworkProof { - fn protocol_name() -> &'static [u8] { - b"Sparse polynomial evaluation proof" - } - - pub fn prove( - network: &mut PolyEvalNetwork, - dense: &MultiSparseMatPolynomialAsDense, - derefs: &Derefs, - evals: &[Scalar], - gens: &SparseMatPolyCommitmentGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - ) -> Self { - // transcript.append_protocol_name(PolyEvalNetworkProof::protocol_name()); - - let (proof_prod_layer, rand_mem, rand_ops) = ProductLayerProof::prove( - &mut network.row_layers.prod_layer, - &mut network.col_layers.prod_layer, - dense, - derefs, - evals, - transcript, - ); - - // proof of hash layer for row and col - let proof_hash_layer = HashLayerProof::prove( - (&rand_mem, &rand_ops), - dense, - derefs, - gens, - transcript, - random_tape, - ); - - PolyEvalNetworkProof { - proof_prod_layer, - proof_hash_layer, + fn protocol_name() -> &'static [u8] { + b"Sparse polynomial evaluation proof" + } + + pub fn prove( + network: &mut PolyEvalNetwork, + dense: &MultiSparseMatPolynomialAsDense, + derefs: &Derefs, + evals: &[Scalar], + gens: &SparseMatPolyCommitmentGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + ) -> Self { + // transcript.append_protocol_name(PolyEvalNetworkProof::protocol_name()); + + let (proof_prod_layer, rand_mem, rand_ops) = ProductLayerProof::prove( + &mut network.row_layers.prod_layer, + &mut network.col_layers.prod_layer, + dense, + derefs, + evals, + transcript, + ); + + // proof of hash layer for row and col + let proof_hash_layer = HashLayerProof::prove( + (&rand_mem, &rand_ops), + dense, + derefs, + gens, + transcript, + random_tape, + ); + + PolyEvalNetworkProof { + proof_prod_layer, + proof_hash_layer, + } + } + + pub fn verify( + &self, + comm: &SparseMatPolyCommitment, + comm_derefs: &DerefsCommitment, + evals: &[Scalar], + gens: &SparseMatPolyCommitmentGens, + rx: &[Scalar], + ry: &[Scalar], + r_mem_check: &(Scalar, Scalar), + nz: usize, + transcript: &mut PoseidonTranscript, + ) -> Result<(), ProofVerifyError> { + let timer = Timer::new("verify_polyeval_proof"); + // transcript.append_protocol_name(PolyEvalNetworkProof::protocol_name()); + + let num_instances = evals.len(); + let (r_hash, r_multiset_check) = r_mem_check; + + let num_ops = nz.next_power_of_two(); + let num_cells = rx.len().pow2(); + assert_eq!(rx.len(), ry.len()); + + let (claims_mem, rand_mem, mut claims_ops, claims_dotp, rand_ops) = + self.proof_prod_layer + .verify(num_ops, num_cells, evals, transcript)?; + assert_eq!(claims_mem.len(), 4); + assert_eq!(claims_ops.len(), 4 * num_instances); + assert_eq!(claims_dotp.len(), 3 * num_instances); + + let (claims_ops_row, claims_ops_col) = claims_ops.split_at_mut(2 * num_instances); + let (claims_ops_row_read, claims_ops_row_write) = + claims_ops_row.split_at_mut(num_instances); + let (claims_ops_col_read, claims_ops_col_write) = + claims_ops_col.split_at_mut(num_instances); + + // verify the proof of hash layer + self.proof_hash_layer.verify( + (&rand_mem, &rand_ops), + &( + claims_mem[0], + claims_ops_row_read.to_vec(), + claims_ops_row_write.to_vec(), + claims_mem[1], + ), + &( + claims_mem[2], + claims_ops_col_read.to_vec(), + claims_ops_col_write.to_vec(), + claims_mem[3], + ), + &claims_dotp, + comm, + gens, + comm_derefs, + rx, + ry, + r_hash, + r_multiset_check, + transcript, + )?; + timer.stop(); + + Ok(()) } - } - - pub fn verify( - &self, - comm: &SparseMatPolyCommitment, - comm_derefs: &DerefsCommitment, - evals: &[Scalar], - gens: &SparseMatPolyCommitmentGens, - rx: &[Scalar], - ry: &[Scalar], - r_mem_check: &(Scalar, Scalar), - nz: usize, - transcript: &mut PoseidonTranscript, - ) -> Result<(), ProofVerifyError> { - let timer = Timer::new("verify_polyeval_proof"); - // transcript.append_protocol_name(PolyEvalNetworkProof::protocol_name()); - - let num_instances = evals.len(); - let (r_hash, r_multiset_check) = r_mem_check; - - let num_ops = nz.next_power_of_two(); - let num_cells = rx.len().pow2(); - assert_eq!(rx.len(), ry.len()); - - let (claims_mem, rand_mem, mut claims_ops, claims_dotp, rand_ops) = self - .proof_prod_layer - .verify(num_ops, num_cells, evals, transcript)?; - assert_eq!(claims_mem.len(), 4); - assert_eq!(claims_ops.len(), 4 * num_instances); - assert_eq!(claims_dotp.len(), 3 * num_instances); - - let (claims_ops_row, claims_ops_col) = claims_ops.split_at_mut(2 * num_instances); - let (claims_ops_row_read, claims_ops_row_write) = claims_ops_row.split_at_mut(num_instances); - let (claims_ops_col_read, claims_ops_col_write) = claims_ops_col.split_at_mut(num_instances); - - // verify the proof of hash layer - self.proof_hash_layer.verify( - (&rand_mem, &rand_ops), - &( - claims_mem[0], - claims_ops_row_read.to_vec(), - claims_ops_row_write.to_vec(), - claims_mem[1], - ), - &( - claims_mem[2], - claims_ops_col_read.to_vec(), - claims_ops_col_write.to_vec(), - claims_mem[3], - ), - &claims_dotp, - comm, - gens, - comm_derefs, - rx, - ry, - r_hash, - r_multiset_check, - transcript, - )?; - timer.stop(); - - Ok(()) - } } #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct SparseMatPolyEvalProof { - comm_derefs: DerefsCommitment, - poly_eval_network_proof: PolyEvalNetworkProof, + comm_derefs: DerefsCommitment, + poly_eval_network_proof: PolyEvalNetworkProof, } impl SparseMatPolyEvalProof { - fn protocol_name() -> &'static [u8] { - b"Sparse polynomial evaluation proof" - } - - fn equalize(rx: &[Scalar], ry: &[Scalar]) -> (Vec, Vec) { - match rx.len().cmp(&ry.len()) { - Ordering::Less => { - let diff = ry.len() - rx.len(); - let mut rx_ext = vec![Scalar::zero(); diff]; - rx_ext.extend(rx); - (rx_ext, ry.to_vec()) - } - Ordering::Greater => { - let diff = rx.len() - ry.len(); - let mut ry_ext = vec![Scalar::zero(); diff]; - ry_ext.extend(ry); - (rx.to_vec(), ry_ext) - } - Ordering::Equal => (rx.to_vec(), ry.to_vec()), + fn protocol_name() -> &'static [u8] { + b"Sparse polynomial evaluation proof" } - } - - pub fn prove( - dense: &MultiSparseMatPolynomialAsDense, - rx: &[Scalar], // point at which the polynomial is evaluated - ry: &[Scalar], - evals: &[Scalar], // a vector evaluation of \widetilde{M}(r = (rx,ry)) for each M - gens: &SparseMatPolyCommitmentGens, - transcript: &mut PoseidonTranscript, - random_tape: &mut RandomTape, - ) -> SparseMatPolyEvalProof { - // transcript.append_protocol_name(SparseMatPolyEvalProof::protocol_name()); - - // ensure there is one eval for each polynomial in dense - assert_eq!(evals.len(), dense.batch_size); - - let (mem_rx, mem_ry) = { - // equalize the lengths of rx and ry - let (rx_ext, ry_ext) = SparseMatPolyEvalProof::equalize(rx, ry); - let poly_rx = EqPolynomial::new(rx_ext).evals(); - let poly_ry = EqPolynomial::new(ry_ext).evals(); - (poly_rx, poly_ry) - }; - - let derefs = dense.deref(&mem_rx, &mem_ry); - - // commit to non-deterministic choices of the prover - let timer_commit = Timer::new("commit_nondet_witness"); - let comm_derefs = { - let comm = derefs.commit(&gens.gens_derefs); - comm.append_to_poseidon(transcript); - comm - }; - timer_commit.stop(); - - let poly_eval_network_proof = { - // produce a random element from the transcript for hash function - let r_mem_check = transcript.challenge_vector(2); - - // build a network to evaluate the sparse polynomial - let timer_build_network = Timer::new("build_layered_network"); - let mut net = PolyEvalNetwork::new( - dense, - &derefs, - &mem_rx, - &mem_ry, - &(r_mem_check[0], r_mem_check[1]), - ); - timer_build_network.stop(); - - let timer_eval_network = Timer::new("evalproof_layered_network"); - let poly_eval_network_proof = PolyEvalNetworkProof::prove( - &mut net, - dense, - &derefs, - evals, - gens, - transcript, - random_tape, - ); - timer_eval_network.stop(); - - poly_eval_network_proof - }; - - SparseMatPolyEvalProof { - comm_derefs, - poly_eval_network_proof, + + fn equalize(rx: &[Scalar], ry: &[Scalar]) -> (Vec, Vec) { + match rx.len().cmp(&ry.len()) { + Ordering::Less => { + let diff = ry.len() - rx.len(); + let mut rx_ext = vec![Scalar::zero(); diff]; + rx_ext.extend(rx); + (rx_ext, ry.to_vec()) + } + Ordering::Greater => { + let diff = rx.len() - ry.len(); + let mut ry_ext = vec![Scalar::zero(); diff]; + ry_ext.extend(ry); + (rx.to_vec(), ry_ext) + } + Ordering::Equal => (rx.to_vec(), ry.to_vec()), + } + } + + pub fn prove( + dense: &MultiSparseMatPolynomialAsDense, + rx: &[Scalar], // point at which the polynomial is evaluated + ry: &[Scalar], + evals: &[Scalar], // a vector evaluation of \widetilde{M}(r = (rx,ry)) for each M + gens: &SparseMatPolyCommitmentGens, + transcript: &mut PoseidonTranscript, + random_tape: &mut RandomTape, + ) -> SparseMatPolyEvalProof { + // transcript.append_protocol_name(SparseMatPolyEvalProof::protocol_name()); + + // ensure there is one eval for each polynomial in dense + assert_eq!(evals.len(), dense.batch_size); + + let (mem_rx, mem_ry) = { + // equalize the lengths of rx and ry + let (rx_ext, ry_ext) = SparseMatPolyEvalProof::equalize(rx, ry); + let poly_rx = EqPolynomial::new(rx_ext).evals(); + let poly_ry = EqPolynomial::new(ry_ext).evals(); + (poly_rx, poly_ry) + }; + + let derefs = dense.deref(&mem_rx, &mem_ry); + + // commit to non-deterministic choices of the prover + let timer_commit = Timer::new("commit_nondet_witness"); + let comm_derefs = { + let comm = derefs.commit(&gens.gens_derefs); + comm.append_to_poseidon(transcript); + comm + }; + timer_commit.stop(); + + let poly_eval_network_proof = { + // produce a random element from the transcript for hash function + let r_mem_check = transcript.challenge_vector(2); + + // build a network to evaluate the sparse polynomial + let timer_build_network = Timer::new("build_layered_network"); + let mut net = PolyEvalNetwork::new( + dense, + &derefs, + &mem_rx, + &mem_ry, + &(r_mem_check[0], r_mem_check[1]), + ); + timer_build_network.stop(); + + let timer_eval_network = Timer::new("evalproof_layered_network"); + let poly_eval_network_proof = PolyEvalNetworkProof::prove( + &mut net, + dense, + &derefs, + evals, + gens, + transcript, + random_tape, + ); + timer_eval_network.stop(); + + poly_eval_network_proof + }; + + SparseMatPolyEvalProof { + comm_derefs, + poly_eval_network_proof, + } + } + + pub fn verify( + &self, + comm: &SparseMatPolyCommitment, + rx: &[Scalar], // point at which the polynomial is evaluated + ry: &[Scalar], + evals: &[Scalar], // evaluation of \widetilde{M}(r = (rx,ry)) + gens: &SparseMatPolyCommitmentGens, + transcript: &mut PoseidonTranscript, + ) -> Result<(), ProofVerifyError> { + // transcript.append_protocol_name(SparseMatPolyEvalProof::protocol_name()); + + // equalize the lengths of rx and ry + let (rx_ext, ry_ext) = SparseMatPolyEvalProof::equalize(rx, ry); + + let (nz, num_mem_cells) = (comm.num_ops, comm.num_mem_cells); + assert_eq!(rx_ext.len().pow2(), num_mem_cells); + + // add claims to transcript and obtain challenges for randomized mem-check circuit + self.comm_derefs.append_to_poseidon(transcript); + + // produce a random element from the transcript for hash function + let r_mem_check = transcript.challenge_vector(2); + + self.poly_eval_network_proof.verify( + comm, + &self.comm_derefs, + evals, + gens, + &rx_ext, + &ry_ext, + &(r_mem_check[0], r_mem_check[1]), + nz, + transcript, + ) } - } - - pub fn verify( - &self, - comm: &SparseMatPolyCommitment, - rx: &[Scalar], // point at which the polynomial is evaluated - ry: &[Scalar], - evals: &[Scalar], // evaluation of \widetilde{M}(r = (rx,ry)) - gens: &SparseMatPolyCommitmentGens, - transcript: &mut PoseidonTranscript, - ) -> Result<(), ProofVerifyError> { - // transcript.append_protocol_name(SparseMatPolyEvalProof::protocol_name()); - - // equalize the lengths of rx and ry - let (rx_ext, ry_ext) = SparseMatPolyEvalProof::equalize(rx, ry); - - let (nz, num_mem_cells) = (comm.num_ops, comm.num_mem_cells); - assert_eq!(rx_ext.len().pow2(), num_mem_cells); - - // add claims to transcript and obtain challenges for randomized mem-check circuit - self.comm_derefs.append_to_poseidon(transcript); - - // produce a random element from the transcript for hash function - let r_mem_check = transcript.challenge_vector(2); - - self.poly_eval_network_proof.verify( - comm, - &self.comm_derefs, - evals, - gens, - &rx_ext, - &ry_ext, - &(r_mem_check[0], r_mem_check[1]), - nz, - transcript, - ) - } } #[derive(Clone)] pub struct SparsePolyEntry { - pub idx: usize, - pub val: Scalar, + pub idx: usize, + pub val: Scalar, } impl SparsePolyEntry { - pub fn new(idx: usize, val: Scalar) -> Self { - SparsePolyEntry { idx, val } - } + pub fn new(idx: usize, val: Scalar) -> Self { + SparsePolyEntry { idx, val } + } } #[derive(Clone)] pub struct SparsePolynomial { - pub num_vars: usize, - pub Z: Vec, + pub num_vars: usize, + pub Z: Vec, } impl SparsePolynomial { - pub fn new(num_vars: usize, Z: Vec) -> Self { - SparsePolynomial { num_vars, Z } - } - - // TF IS THIS?? - - fn compute_chi(a: &[bool], r: &[Scalar]) -> Scalar { - assert_eq!(a.len(), r.len()); - let mut chi_i = Scalar::one(); - for j in 0..r.len() { - if a[j] { - chi_i *= r[j]; - } else { - chi_i *= Scalar::one() - r[j]; - } + pub fn new(num_vars: usize, Z: Vec) -> Self { + SparsePolynomial { num_vars, Z } + } + + // TF IS THIS?? + + fn compute_chi(a: &[bool], r: &[Scalar]) -> Scalar { + assert_eq!(a.len(), r.len()); + let mut chi_i = Scalar::one(); + for j in 0..r.len() { + if a[j] { + chi_i *= r[j]; + } else { + chi_i *= Scalar::one() - r[j]; + } + } + chi_i + } + + // Takes O(n log n). TODO: do this in O(n) where n is the number of entries in Z + pub fn evaluate(&self, r: &[Scalar]) -> Scalar { + assert_eq!(self.num_vars, r.len()); + + (0..self.Z.len()) + .map(|i| { + let bits = self.Z[i].idx.get_bits(r.len()); + println!("{:?}", bits); + SparsePolynomial::compute_chi(&bits, r) * self.Z[i].val + }) + .sum() } - chi_i - } - - // Takes O(n log n). TODO: do this in O(n) where n is the number of entries in Z - pub fn evaluate(&self, r: &[Scalar]) -> Scalar { - assert_eq!(self.num_vars, r.len()); - - (0..self.Z.len()) - .map(|i| { - let bits = self.Z[i].idx.get_bits(r.len()); - println!("{:?}", bits); - SparsePolynomial::compute_chi(&bits, r) * self.Z[i].val - }) - .sum() - } } #[cfg(test)] mod tests { - use crate::parameters::poseidon_params; - - use super::*; - use ark_std::UniformRand; - use rand::RngCore; - - #[test] - fn check_sparse_polyeval_proof() { - let mut rng = ark_std::rand::thread_rng(); - - let num_nz_entries: usize = 256; - let num_rows: usize = 256; - let num_cols: usize = 256; - let num_vars_x: usize = num_rows.log_2(); - let num_vars_y: usize = num_cols.log_2(); - - let mut M: Vec = Vec::new(); - - for _i in 0..num_nz_entries { - M.push(SparseMatEntry::new( - (rng.next_u64() % (num_rows as u64)) as usize, - (rng.next_u64() % (num_cols as u64)) as usize, - Scalar::rand(&mut rng), - )); + use crate::parameters::poseidon_params; + + use super::*; + use ark_std::UniformRand; + use rand::RngCore; + + #[test] + fn check_sparse_polyeval_proof() { + let mut rng = ark_std::rand::thread_rng(); + + let num_nz_entries: usize = 256; + let num_rows: usize = 256; + let num_cols: usize = 256; + let num_vars_x: usize = num_rows.log_2(); + let num_vars_y: usize = num_cols.log_2(); + + let mut M: Vec = Vec::new(); + + for _i in 0..num_nz_entries { + M.push(SparseMatEntry::new( + (rng.next_u64() % (num_rows as u64)) as usize, + (rng.next_u64() % (num_cols as u64)) as usize, + Scalar::rand(&mut rng), + )); + } + + let poly_M = SparseMatPolynomial::new(num_vars_x, num_vars_y, M); + let gens = SparseMatPolyCommitmentGens::new( + b"gens_sparse_poly", + num_vars_x, + num_vars_y, + num_nz_entries, + 3, + ); + + // commitment + let (poly_comm, dense) = + SparseMatPolynomial::multi_commit(&[&poly_M, &poly_M, &poly_M], &gens); + + // evaluation + let rx: Vec = (0..num_vars_x) + .map(|_i| Scalar::rand(&mut rng)) + .collect::>(); + let ry: Vec = (0..num_vars_y) + .map(|_i| Scalar::rand(&mut rng)) + .collect::>(); + let eval = SparseMatPolynomial::multi_evaluate(&[&poly_M], &rx, &ry); + let evals = vec![eval[0], eval[0], eval[0]]; + + let params = poseidon_params(); + let mut random_tape = RandomTape::new(b"proof"); + let mut prover_transcript = PoseidonTranscript::new(¶ms); + let proof = SparseMatPolyEvalProof::prove( + &dense, + &rx, + &ry, + &evals, + &gens, + &mut prover_transcript, + &mut random_tape, + ); + + let mut verifier_transcript = PoseidonTranscript::new(¶ms); + assert!(proof + .verify( + &poly_comm, + &rx, + &ry, + &evals, + &gens, + &mut verifier_transcript, + ) + .is_ok()); } - - let poly_M = SparseMatPolynomial::new(num_vars_x, num_vars_y, M); - let gens = SparseMatPolyCommitmentGens::new( - b"gens_sparse_poly", - num_vars_x, - num_vars_y, - num_nz_entries, - 3, - ); - - // commitment - let (poly_comm, dense) = SparseMatPolynomial::multi_commit(&[&poly_M, &poly_M, &poly_M], &gens); - - // evaluation - let rx: Vec = (0..num_vars_x) - .map(|_i| Scalar::rand(&mut rng)) - .collect::>(); - let ry: Vec = (0..num_vars_y) - .map(|_i| Scalar::rand(&mut rng)) - .collect::>(); - let eval = SparseMatPolynomial::multi_evaluate(&[&poly_M], &rx, &ry); - let evals = vec![eval[0], eval[0], eval[0]]; - - let params = poseidon_params(); - let mut random_tape = RandomTape::new(b"proof"); - let mut prover_transcript = PoseidonTranscript::new(¶ms); - let proof = SparseMatPolyEvalProof::prove( - &dense, - &rx, - &ry, - &evals, - &gens, - &mut prover_transcript, - &mut random_tape, - ); - - let mut verifier_transcript = PoseidonTranscript::new(¶ms); - assert!(proof - .verify( - &poly_comm, - &rx, - &ry, - &evals, - &gens, - &mut verifier_transcript, - ) - .is_ok()); - } } diff --git a/src/sumcheck.rs b/src/sumcheck.rs index b34f037..e657e2b 100644 --- a/src/sumcheck.rs +++ b/src/sumcheck.rs @@ -15,49 +15,49 @@ use itertools::izip; #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] pub struct SumcheckInstanceProof { - pub polys: Vec, + pub polys: Vec, } impl SumcheckInstanceProof { - pub fn new(polys: Vec) -> SumcheckInstanceProof { - SumcheckInstanceProof { polys } - } + pub fn new(polys: Vec) -> SumcheckInstanceProof { + SumcheckInstanceProof { polys } + } - pub fn verify( - &self, - claim: Scalar, - num_rounds: usize, - degree_bound: usize, - transcript: &mut PoseidonTranscript, - ) -> Result<(Scalar, Vec), ProofVerifyError> { - let mut e = claim; - let mut r: Vec = Vec::new(); + pub fn verify( + &self, + claim: Scalar, + num_rounds: usize, + degree_bound: usize, + transcript: &mut PoseidonTranscript, + ) -> Result<(Scalar, Vec), ProofVerifyError> { + let mut e = claim; + let mut r: Vec = Vec::new(); - // verify that there is a univariate polynomial for each round - assert_eq!(self.polys.len(), num_rounds); - for i in 0..self.polys.len() { - let poly = self.polys[i].clone(); + // verify that there is a univariate polynomial for each round + assert_eq!(self.polys.len(), num_rounds); + for i in 0..self.polys.len() { + let poly = self.polys[i].clone(); - // verify degree bound - assert_eq!(poly.degree(), degree_bound); - // check if G_k(0) + G_k(1) = e + // verify degree bound + assert_eq!(poly.degree(), degree_bound); + // check if G_k(0) + G_k(1) = e - assert_eq!(poly.eval_at_zero() + poly.eval_at_one(), e); + assert_eq!(poly.eval_at_zero() + poly.eval_at_one(), e); - // append the prover's message to the transcript - poly.append_to_poseidon(transcript); + // append the prover's message to the transcript + poly.append_to_poseidon(transcript); - //derive the verifier's challenge for the next round - let r_i = transcript.challenge_scalar(); + //derive the verifier's challenge for the next round + let r_i = transcript.challenge_scalar(); - r.push(r_i); + r.push(r_i); - // evaluate the claimed degree-ell polynomial at r_i - e = poly.evaluate(&r_i); - } + // evaluate the claimed degree-ell polynomial at r_i + e = poly.evaluate(&r_i); + } - Ok((e, r)) - } + Ok((e, r)) + } } // #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] @@ -180,379 +180,381 @@ impl SumcheckInstanceProof { // } impl SumcheckInstanceProof { - pub fn prove_cubic_with_additive_term( - claim: &Scalar, - num_rounds: usize, - poly_tau: &mut DensePolynomial, - poly_A: &mut DensePolynomial, - poly_B: &mut DensePolynomial, - poly_C: &mut DensePolynomial, - comb_func: F, - transcript: &mut PoseidonTranscript, - ) -> (Self, Vec, Vec) - where - F: Fn(&Scalar, &Scalar, &Scalar, &Scalar) -> Scalar, - { - let mut e = *claim; - let mut r: Vec = Vec::new(); - let mut cubic_polys: Vec = Vec::new(); - for _j in 0..num_rounds { - let mut eval_point_0 = Scalar::zero(); - let mut eval_point_2 = Scalar::zero(); - let mut eval_point_3 = Scalar::zero(); - - let len = poly_tau.len() / 2; - for i in 0..len { - // eval 0: bound_func is A(low) - eval_point_0 += comb_func(&poly_tau[i], &poly_A[i], &poly_B[i], &poly_C[i]); - - // eval 2: bound_func is -A(low) + 2*A(high) - let poly_tau_bound_point = poly_tau[len + i] + poly_tau[len + i] - poly_tau[i]; - let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i]; - - eval_point_2 += comb_func( - &poly_tau_bound_point, - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - ); - - // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) - let poly_tau_bound_point = poly_tau_bound_point + poly_tau[len + i] - poly_tau[i]; - let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i]; - - eval_point_3 += comb_func( - &poly_tau_bound_point, - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - ); - } - - let evals = vec![eval_point_0, e - eval_point_0, eval_point_2, eval_point_3]; - let poly = UniPoly::from_evals(&evals); - - // append the prover's message to the transcript - poly.append_to_poseidon(transcript); - //derive the verifier's challenge for the next round - let r_j = transcript.challenge_scalar(); - r.push(r_j); - - // bound all tables to the verifier's challenege - poly_tau.bound_poly_var_top(&r_j); - poly_A.bound_poly_var_top(&r_j); - poly_B.bound_poly_var_top(&r_j); - poly_C.bound_poly_var_top(&r_j); - - e = poly.evaluate(&r_j); - cubic_polys.push(poly); + pub fn prove_cubic_with_additive_term( + claim: &Scalar, + num_rounds: usize, + poly_tau: &mut DensePolynomial, + poly_A: &mut DensePolynomial, + poly_B: &mut DensePolynomial, + poly_C: &mut DensePolynomial, + comb_func: F, + transcript: &mut PoseidonTranscript, + ) -> (Self, Vec, Vec) + where + F: Fn(&Scalar, &Scalar, &Scalar, &Scalar) -> Scalar, + { + let mut e = *claim; + let mut r: Vec = Vec::new(); + let mut cubic_polys: Vec = Vec::new(); + for _j in 0..num_rounds { + let mut eval_point_0 = Scalar::zero(); + let mut eval_point_2 = Scalar::zero(); + let mut eval_point_3 = Scalar::zero(); + + let len = poly_tau.len() / 2; + for i in 0..len { + // eval 0: bound_func is A(low) + eval_point_0 += comb_func(&poly_tau[i], &poly_A[i], &poly_B[i], &poly_C[i]); + + // eval 2: bound_func is -A(low) + 2*A(high) + let poly_tau_bound_point = poly_tau[len + i] + poly_tau[len + i] - poly_tau[i]; + let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i]; + + eval_point_2 += comb_func( + &poly_tau_bound_point, + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + ); + + // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) + let poly_tau_bound_point = poly_tau_bound_point + poly_tau[len + i] - poly_tau[i]; + let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i]; + + eval_point_3 += comb_func( + &poly_tau_bound_point, + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + ); + } + + let evals = vec![eval_point_0, e - eval_point_0, eval_point_2, eval_point_3]; + let poly = UniPoly::from_evals(&evals); + + // append the prover's message to the transcript + poly.append_to_poseidon(transcript); + //derive the verifier's challenge for the next round + let r_j = transcript.challenge_scalar(); + r.push(r_j); + + // bound all tables to the verifier's challenege + poly_tau.bound_poly_var_top(&r_j); + poly_A.bound_poly_var_top(&r_j); + poly_B.bound_poly_var_top(&r_j); + poly_C.bound_poly_var_top(&r_j); + + e = poly.evaluate(&r_j); + cubic_polys.push(poly); + } + ( + SumcheckInstanceProof::new(cubic_polys), + r, + vec![poly_tau[0], poly_A[0], poly_B[0], poly_C[0]], + ) } - ( - SumcheckInstanceProof::new(cubic_polys), - r, - vec![poly_tau[0], poly_A[0], poly_B[0], poly_C[0]], - ) - } - pub fn prove_cubic( - claim: &Scalar, - num_rounds: usize, - poly_A: &mut DensePolynomial, - poly_B: &mut DensePolynomial, - poly_C: &mut DensePolynomial, - comb_func: F, - transcript: &mut PoseidonTranscript, - ) -> (Self, Vec, Vec) - where - F: Fn(&Scalar, &Scalar, &Scalar) -> Scalar, - { - let mut e = *claim; - let mut r: Vec = Vec::new(); - let mut cubic_polys: Vec = Vec::new(); - for _j in 0..num_rounds { - let mut eval_point_0 = Scalar::zero(); - let mut eval_point_2 = Scalar::zero(); - let mut eval_point_3 = Scalar::zero(); - - let len = poly_A.len() / 2; - for i in 0..len { - // eval 0: bound_func is A(low) - eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C[i]); - - // eval 2: bound_func is -A(low) + 2*A(high) - let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i]; - eval_point_2 += comb_func( - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - ); - - // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) - let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i]; - - eval_point_3 += comb_func( - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - ); - } - - let evals = vec![eval_point_0, e - eval_point_0, eval_point_2, eval_point_3]; - let poly = UniPoly::from_evals(&evals); - - // append the prover's message to the transcript - poly.append_to_poseidon(transcript); - - //derive the verifier's challenge for the next round - let r_j = transcript.challenge_scalar(); - r.push(r_j); - // bound all tables to the verifier's challenege - poly_A.bound_poly_var_top(&r_j); - poly_B.bound_poly_var_top(&r_j); - poly_C.bound_poly_var_top(&r_j); - e = poly.evaluate(&r_j); - cubic_polys.push(poly); + pub fn prove_cubic( + claim: &Scalar, + num_rounds: usize, + poly_A: &mut DensePolynomial, + poly_B: &mut DensePolynomial, + poly_C: &mut DensePolynomial, + comb_func: F, + transcript: &mut PoseidonTranscript, + ) -> (Self, Vec, Vec) + where + F: Fn(&Scalar, &Scalar, &Scalar) -> Scalar, + { + let mut e = *claim; + let mut r: Vec = Vec::new(); + let mut cubic_polys: Vec = Vec::new(); + for _j in 0..num_rounds { + let mut eval_point_0 = Scalar::zero(); + let mut eval_point_2 = Scalar::zero(); + let mut eval_point_3 = Scalar::zero(); + + let len = poly_A.len() / 2; + for i in 0..len { + // eval 0: bound_func is A(low) + eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C[i]); + + // eval 2: bound_func is -A(low) + 2*A(high) + let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i]; + eval_point_2 += comb_func( + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + ); + + // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) + let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i]; + + eval_point_3 += comb_func( + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + ); + } + + let evals = vec![eval_point_0, e - eval_point_0, eval_point_2, eval_point_3]; + let poly = UniPoly::from_evals(&evals); + + // append the prover's message to the transcript + poly.append_to_poseidon(transcript); + + //derive the verifier's challenge for the next round + let r_j = transcript.challenge_scalar(); + r.push(r_j); + // bound all tables to the verifier's challenege + poly_A.bound_poly_var_top(&r_j); + poly_B.bound_poly_var_top(&r_j); + poly_C.bound_poly_var_top(&r_j); + e = poly.evaluate(&r_j); + cubic_polys.push(poly); + } + + ( + SumcheckInstanceProof::new(cubic_polys), + r, + vec![poly_A[0], poly_B[0], poly_C[0]], + ) } - ( - SumcheckInstanceProof::new(cubic_polys), - r, - vec![poly_A[0], poly_B[0], poly_C[0]], + pub fn prove_cubic_batched( + claim: &Scalar, + num_rounds: usize, + poly_vec_par: ( + &mut Vec<&mut DensePolynomial>, + &mut Vec<&mut DensePolynomial>, + &mut DensePolynomial, + ), + poly_vec_seq: ( + &mut Vec<&mut DensePolynomial>, + &mut Vec<&mut DensePolynomial>, + &mut Vec<&mut DensePolynomial>, + ), + coeffs: &[Scalar], + comb_func: F, + transcript: &mut PoseidonTranscript, + ) -> ( + Self, + Vec, + (Vec, Vec, Scalar), + (Vec, Vec, Vec), ) - } - - pub fn prove_cubic_batched( - claim: &Scalar, - num_rounds: usize, - poly_vec_par: ( - &mut Vec<&mut DensePolynomial>, - &mut Vec<&mut DensePolynomial>, - &mut DensePolynomial, - ), - poly_vec_seq: ( - &mut Vec<&mut DensePolynomial>, - &mut Vec<&mut DensePolynomial>, - &mut Vec<&mut DensePolynomial>, - ), - coeffs: &[Scalar], - comb_func: F, - transcript: &mut PoseidonTranscript, - ) -> ( - Self, - Vec, - (Vec, Vec, Scalar), - (Vec, Vec, Vec), - ) - where - F: Fn(&Scalar, &Scalar, &Scalar) -> Scalar, - { - let (poly_A_vec_par, poly_B_vec_par, poly_C_par) = poly_vec_par; - let (poly_A_vec_seq, poly_B_vec_seq, poly_C_vec_seq) = poly_vec_seq; - - //let (poly_A_vec_seq, poly_B_vec_seq, poly_C_vec_seq) = poly_vec_seq; - let mut e = *claim; - let mut r: Vec = Vec::new(); - let mut cubic_polys: Vec = Vec::new(); - - for _j in 0..num_rounds { - let mut evals: Vec<(Scalar, Scalar, Scalar)> = Vec::new(); - - for (poly_A, poly_B) in poly_A_vec_par.iter().zip(poly_B_vec_par.iter()) { - let mut eval_point_0 = Scalar::zero(); - let mut eval_point_2 = Scalar::zero(); - let mut eval_point_3 = Scalar::zero(); - - let len = poly_A.len() / 2; - for i in 0..len { - // eval 0: bound_func is A(low) - eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C_par[i]); - - // eval 2: bound_func is -A(low) + 2*A(high) - let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C_par[len + i] + poly_C_par[len + i] - poly_C_par[i]; - eval_point_2 += comb_func( - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - ); - - // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) - let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C_bound_point + poly_C_par[len + i] - poly_C_par[i]; - - eval_point_3 += comb_func( - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - ); + where + F: Fn(&Scalar, &Scalar, &Scalar) -> Scalar, + { + let (poly_A_vec_par, poly_B_vec_par, poly_C_par) = poly_vec_par; + let (poly_A_vec_seq, poly_B_vec_seq, poly_C_vec_seq) = poly_vec_seq; + + //let (poly_A_vec_seq, poly_B_vec_seq, poly_C_vec_seq) = poly_vec_seq; + let mut e = *claim; + let mut r: Vec = Vec::new(); + let mut cubic_polys: Vec = Vec::new(); + + for _j in 0..num_rounds { + let mut evals: Vec<(Scalar, Scalar, Scalar)> = Vec::new(); + + for (poly_A, poly_B) in poly_A_vec_par.iter().zip(poly_B_vec_par.iter()) { + let mut eval_point_0 = Scalar::zero(); + let mut eval_point_2 = Scalar::zero(); + let mut eval_point_3 = Scalar::zero(); + + let len = poly_A.len() / 2; + for i in 0..len { + // eval 0: bound_func is A(low) + eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C_par[i]); + + // eval 2: bound_func is -A(low) + 2*A(high) + let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = + poly_C_par[len + i] + poly_C_par[len + i] - poly_C_par[i]; + eval_point_2 += comb_func( + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + ); + + // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) + let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = + poly_C_bound_point + poly_C_par[len + i] - poly_C_par[i]; + + eval_point_3 += comb_func( + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + ); + } + + evals.push((eval_point_0, eval_point_2, eval_point_3)); + } + + for (poly_A, poly_B, poly_C) in izip!( + poly_A_vec_seq.iter(), + poly_B_vec_seq.iter(), + poly_C_vec_seq.iter() + ) { + let mut eval_point_0 = Scalar::zero(); + let mut eval_point_2 = Scalar::zero(); + let mut eval_point_3 = Scalar::zero(); + let len = poly_A.len() / 2; + for i in 0..len { + // eval 0: bound_func is A(low) + eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C[i]); + // eval 2: bound_func is -A(low) + 2*A(high) + let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i]; + eval_point_2 += comb_func( + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + ); + // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) + let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i]; + eval_point_3 += comb_func( + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + ); + } + evals.push((eval_point_0, eval_point_2, eval_point_3)); + } + + let evals_combined_0 = (0..evals.len()).map(|i| evals[i].0 * coeffs[i]).sum(); + let evals_combined_2 = (0..evals.len()).map(|i| evals[i].1 * coeffs[i]).sum(); + let evals_combined_3 = (0..evals.len()).map(|i| evals[i].2 * coeffs[i]).sum(); + + let evals = vec![ + evals_combined_0, + e - evals_combined_0, + evals_combined_2, + evals_combined_3, + ]; + let poly = UniPoly::from_evals(&evals); + + // append the prover's message to the transcript + poly.append_to_poseidon(transcript); + + //derive the verifier's challenge for the next round + let r_j = transcript.challenge_scalar(); + r.push(r_j); + + // bound all tables to the verifier's challenege + for (poly_A, poly_B) in poly_A_vec_par.iter_mut().zip(poly_B_vec_par.iter_mut()) { + poly_A.bound_poly_var_top(&r_j); + poly_B.bound_poly_var_top(&r_j); + } + poly_C_par.bound_poly_var_top(&r_j); + + for (poly_A, poly_B, poly_C) in izip!( + poly_A_vec_seq.iter_mut(), + poly_B_vec_seq.iter_mut(), + poly_C_vec_seq.iter_mut() + ) { + poly_A.bound_poly_var_top(&r_j); + poly_B.bound_poly_var_top(&r_j); + poly_C.bound_poly_var_top(&r_j); + } + + e = poly.evaluate(&r_j); + cubic_polys.push(poly); } - evals.push((eval_point_0, eval_point_2, eval_point_3)); - } - - for (poly_A, poly_B, poly_C) in izip!( - poly_A_vec_seq.iter(), - poly_B_vec_seq.iter(), - poly_C_vec_seq.iter() - ) { - let mut eval_point_0 = Scalar::zero(); - let mut eval_point_2 = Scalar::zero(); - let mut eval_point_3 = Scalar::zero(); - let len = poly_A.len() / 2; - for i in 0..len { - // eval 0: bound_func is A(low) - eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C[i]); - // eval 2: bound_func is -A(low) + 2*A(high) - let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i]; - eval_point_2 += comb_func( - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - ); - // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) - let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i]; - eval_point_3 += comb_func( - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - ); - } - evals.push((eval_point_0, eval_point_2, eval_point_3)); - } - - let evals_combined_0 = (0..evals.len()).map(|i| evals[i].0 * coeffs[i]).sum(); - let evals_combined_2 = (0..evals.len()).map(|i| evals[i].1 * coeffs[i]).sum(); - let evals_combined_3 = (0..evals.len()).map(|i| evals[i].2 * coeffs[i]).sum(); - - let evals = vec![ - evals_combined_0, - e - evals_combined_0, - evals_combined_2, - evals_combined_3, - ]; - let poly = UniPoly::from_evals(&evals); - - // append the prover's message to the transcript - poly.append_to_poseidon(transcript); - - //derive the verifier's challenge for the next round - let r_j = transcript.challenge_scalar(); - r.push(r_j); - - // bound all tables to the verifier's challenege - for (poly_A, poly_B) in poly_A_vec_par.iter_mut().zip(poly_B_vec_par.iter_mut()) { - poly_A.bound_poly_var_top(&r_j); - poly_B.bound_poly_var_top(&r_j); - } - poly_C_par.bound_poly_var_top(&r_j); - - for (poly_A, poly_B, poly_C) in izip!( - poly_A_vec_seq.iter_mut(), - poly_B_vec_seq.iter_mut(), - poly_C_vec_seq.iter_mut() - ) { - poly_A.bound_poly_var_top(&r_j); - poly_B.bound_poly_var_top(&r_j); - poly_C.bound_poly_var_top(&r_j); - } - - e = poly.evaluate(&r_j); - cubic_polys.push(poly); + let poly_A_par_final = (0..poly_A_vec_par.len()) + .map(|i| poly_A_vec_par[i][0]) + .collect(); + let poly_B_par_final = (0..poly_B_vec_par.len()) + .map(|i| poly_B_vec_par[i][0]) + .collect(); + let claims_prod = (poly_A_par_final, poly_B_par_final, poly_C_par[0]); + + let poly_A_seq_final = (0..poly_A_vec_seq.len()) + .map(|i| poly_A_vec_seq[i][0]) + .collect(); + let poly_B_seq_final = (0..poly_B_vec_seq.len()) + .map(|i| poly_B_vec_seq[i][0]) + .collect(); + let poly_C_seq_final = (0..poly_C_vec_seq.len()) + .map(|i| poly_C_vec_seq[i][0]) + .collect(); + let claims_dotp = (poly_A_seq_final, poly_B_seq_final, poly_C_seq_final); + + ( + SumcheckInstanceProof::new(cubic_polys), + r, + claims_prod, + claims_dotp, + ) } - let poly_A_par_final = (0..poly_A_vec_par.len()) - .map(|i| poly_A_vec_par[i][0]) - .collect(); - let poly_B_par_final = (0..poly_B_vec_par.len()) - .map(|i| poly_B_vec_par[i][0]) - .collect(); - let claims_prod = (poly_A_par_final, poly_B_par_final, poly_C_par[0]); - - let poly_A_seq_final = (0..poly_A_vec_seq.len()) - .map(|i| poly_A_vec_seq[i][0]) - .collect(); - let poly_B_seq_final = (0..poly_B_vec_seq.len()) - .map(|i| poly_B_vec_seq[i][0]) - .collect(); - let poly_C_seq_final = (0..poly_C_vec_seq.len()) - .map(|i| poly_C_vec_seq[i][0]) - .collect(); - let claims_dotp = (poly_A_seq_final, poly_B_seq_final, poly_C_seq_final); - - ( - SumcheckInstanceProof::new(cubic_polys), - r, - claims_prod, - claims_dotp, - ) - } - - pub fn prove_quad( - claim: &Scalar, - num_rounds: usize, - poly_A: &mut DensePolynomial, - poly_B: &mut DensePolynomial, - comb_func: F, - transcript: &mut PoseidonTranscript, - ) -> (Self, Vec, Vec) - where - F: Fn(&Scalar, &Scalar) -> Scalar, - { - let mut e = *claim; - let mut r: Vec = Vec::new(); - let mut quad_polys: Vec = Vec::new(); - - for _j in 0..num_rounds { - let mut eval_point_0 = Scalar::zero(); - let mut eval_point_2 = Scalar::zero(); - - let len = poly_A.len() / 2; - for i in 0..len { - // eval 0: bound_func is A(low) - eval_point_0 += comb_func(&poly_A[i], &poly_B[i]); - - // eval 2: bound_func is -A(low) + 2*A(high) - let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; - eval_point_2 += comb_func(&poly_A_bound_point, &poly_B_bound_point); - } - - let evals = vec![eval_point_0, e - eval_point_0, eval_point_2]; - let poly = UniPoly::from_evals(&evals); - - // append the prover's message to the transcript - poly.append_to_poseidon(transcript); - - //derive the verifier's challenge for the next round - let r_j = transcript.challenge_scalar(); - r.push(r_j); - - // bound all tables to the verifier's challenege - poly_A.bound_poly_var_top(&r_j); - poly_B.bound_poly_var_top(&r_j); - e = poly.evaluate(&r_j); - quad_polys.push(poly); - } + pub fn prove_quad( + claim: &Scalar, + num_rounds: usize, + poly_A: &mut DensePolynomial, + poly_B: &mut DensePolynomial, + comb_func: F, + transcript: &mut PoseidonTranscript, + ) -> (Self, Vec, Vec) + where + F: Fn(&Scalar, &Scalar) -> Scalar, + { + let mut e = *claim; + let mut r: Vec = Vec::new(); + let mut quad_polys: Vec = Vec::new(); + + for _j in 0..num_rounds { + let mut eval_point_0 = Scalar::zero(); + let mut eval_point_2 = Scalar::zero(); + + let len = poly_A.len() / 2; + for i in 0..len { + // eval 0: bound_func is A(low) + eval_point_0 += comb_func(&poly_A[i], &poly_B[i]); + + // eval 2: bound_func is -A(low) + 2*A(high) + let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; + eval_point_2 += comb_func(&poly_A_bound_point, &poly_B_bound_point); + } + + let evals = vec![eval_point_0, e - eval_point_0, eval_point_2]; + let poly = UniPoly::from_evals(&evals); + + // append the prover's message to the transcript + poly.append_to_poseidon(transcript); + + //derive the verifier's challenge for the next round + let r_j = transcript.challenge_scalar(); + r.push(r_j); + + // bound all tables to the verifier's challenege + poly_A.bound_poly_var_top(&r_j); + poly_B.bound_poly_var_top(&r_j); + e = poly.evaluate(&r_j); + quad_polys.push(poly); + } - ( - SumcheckInstanceProof::new(quad_polys), - r, - vec![poly_A[0], poly_B[0]], - ) - } + ( + SumcheckInstanceProof::new(quad_polys), + r, + vec![poly_A[0], poly_B[0]], + ) + } } // impl ZKSumcheckInstanceProof { diff --git a/src/timer.rs b/src/timer.rs index 8356a35..30d6684 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -12,77 +12,77 @@ pub static CALL_DEPTH: AtomicUsize = AtomicUsize::new(0); #[cfg(feature = "profile")] pub struct Timer { - label: String, - timer: Instant, + label: String, + timer: Instant, } #[cfg(feature = "profile")] impl Timer { - #[inline(always)] - pub fn new(label: &str) -> Self { - let timer = Instant::now(); - CALL_DEPTH.fetch_add(1, Ordering::Relaxed); - let star = "* "; - println!( - "{:indent$}{}{}", - "", - star, - label.yellow().bold(), - indent = 2 * CALL_DEPTH.fetch_add(0, Ordering::Relaxed) - ); - Self { - label: label.to_string(), - timer, + #[inline(always)] + pub fn new(label: &str) -> Self { + let timer = Instant::now(); + CALL_DEPTH.fetch_add(1, Ordering::Relaxed); + let star = "* "; + println!( + "{:indent$}{}{}", + "", + star, + label.yellow().bold(), + indent = 2 * CALL_DEPTH.fetch_add(0, Ordering::Relaxed) + ); + Self { + label: label.to_string(), + timer, + } } - } - #[inline(always)] - pub fn stop(&self) { - let duration = self.timer.elapsed(); - let star = "* "; - println!( - "{:indent$}{}{} {:?}", - "", - star, - self.label.blue().bold(), - duration, - indent = 2 * CALL_DEPTH.fetch_add(0, Ordering::Relaxed) - ); - CALL_DEPTH.fetch_sub(1, Ordering::Relaxed); - } + #[inline(always)] + pub fn stop(&self) { + let duration = self.timer.elapsed(); + let star = "* "; + println!( + "{:indent$}{}{} {:?}", + "", + star, + self.label.blue().bold(), + duration, + indent = 2 * CALL_DEPTH.fetch_add(0, Ordering::Relaxed) + ); + CALL_DEPTH.fetch_sub(1, Ordering::Relaxed); + } - #[inline(always)] - pub fn print(msg: &str) { - CALL_DEPTH.fetch_add(1, Ordering::Relaxed); - let star = "* "; - println!( - "{:indent$}{}{}", - "", - star, - msg.to_string().green().bold(), - indent = 2 * CALL_DEPTH.fetch_add(0, Ordering::Relaxed) - ); - CALL_DEPTH.fetch_sub(1, Ordering::Relaxed); - } + #[inline(always)] + pub fn print(msg: &str) { + CALL_DEPTH.fetch_add(1, Ordering::Relaxed); + let star = "* "; + println!( + "{:indent$}{}{}", + "", + star, + msg.to_string().green().bold(), + indent = 2 * CALL_DEPTH.fetch_add(0, Ordering::Relaxed) + ); + CALL_DEPTH.fetch_sub(1, Ordering::Relaxed); + } } #[cfg(not(feature = "profile"))] pub struct Timer { - _label: String, + _label: String, } #[cfg(not(feature = "profile"))] impl Timer { - #[inline(always)] - pub fn new(label: &str) -> Self { - Self { - _label: label.to_string(), + #[inline(always)] + pub fn new(label: &str) -> Self { + Self { + _label: label.to_string(), + } } - } - #[inline(always)] - pub fn stop(&self) {} + #[inline(always)] + pub fn stop(&self) {} - #[inline(always)] - pub fn print(_msg: &str) {} + #[inline(always)] + pub fn print(_msg: &str) {} } diff --git a/src/transcript.rs b/src/transcript.rs index a5c22ea..810f730 100644 --- a/src/transcript.rs +++ b/src/transcript.rs @@ -5,63 +5,63 @@ use ark_serialize::CanonicalSerialize; use merlin::Transcript; pub trait ProofTranscript { - fn append_protocol_name(&mut self, protocol_name: &'static [u8]); - fn append_scalar(&mut self, label: &'static [u8], scalar: &Scalar); - fn append_point(&mut self, label: &'static [u8], point: &CompressedGroup); - fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar; - fn challenge_vector(&mut self, label: &'static [u8], len: usize) -> Vec; + fn append_protocol_name(&mut self, protocol_name: &'static [u8]); + fn append_scalar(&mut self, label: &'static [u8], scalar: &Scalar); + fn append_point(&mut self, label: &'static [u8], point: &CompressedGroup); + fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar; + fn challenge_vector(&mut self, label: &'static [u8], len: usize) -> Vec; } impl ProofTranscript for Transcript { - fn append_protocol_name(&mut self, protocol_name: &'static [u8]) { - self.append_message(b"protocol-name", protocol_name); - } + fn append_protocol_name(&mut self, protocol_name: &'static [u8]) { + self.append_message(b"protocol-name", protocol_name); + } - fn append_scalar(&mut self, label: &'static [u8], scalar: &Scalar) { - self.append_message(label, scalar.into_repr().to_bytes_le().as_slice()); - } + fn append_scalar(&mut self, label: &'static [u8], scalar: &Scalar) { + self.append_message(label, scalar.into_repr().to_bytes_le().as_slice()); + } - fn append_point(&mut self, label: &'static [u8], point: &CompressedGroup) { - let mut point_encoded = Vec::new(); - point.serialize(&mut point_encoded).unwrap(); - self.append_message(label, point_encoded.as_slice()); - } + fn append_point(&mut self, label: &'static [u8], point: &CompressedGroup) { + let mut point_encoded = Vec::new(); + point.serialize(&mut point_encoded).unwrap(); + self.append_message(label, point_encoded.as_slice()); + } - fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar { - let mut buf = [0u8; 64]; - self.challenge_bytes(label, &mut buf); - Scalar::from_le_bytes_mod_order(&buf) - } + fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar { + let mut buf = [0u8; 64]; + self.challenge_bytes(label, &mut buf); + Scalar::from_le_bytes_mod_order(&buf) + } - fn challenge_vector(&mut self, label: &'static [u8], len: usize) -> Vec { - (0..len) - .map(|_i| self.challenge_scalar(label)) - .collect::>() - } + fn challenge_vector(&mut self, label: &'static [u8], len: usize) -> Vec { + (0..len) + .map(|_i| self.challenge_scalar(label)) + .collect::>() + } } pub trait AppendToTranscript { - fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript); + fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript); } impl AppendToTranscript for Scalar { - fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { - transcript.append_scalar(label, self); - } + fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { + transcript.append_scalar(label, self); + } } impl AppendToTranscript for [Scalar] { - fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { - transcript.append_message(label, b"begin_append_vector"); - for item in self { - transcript.append_scalar(label, item); + fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { + transcript.append_message(label, b"begin_append_vector"); + for item in self { + transcript.append_scalar(label, item); + } + transcript.append_message(label, b"end_append_vector"); } - transcript.append_message(label, b"end_append_vector"); - } } impl AppendToTranscript for CompressedGroup { - fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { - transcript.append_point(label, self); - } + fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { + transcript.append_point(label, self); + } } diff --git a/src/unipoly.rs b/src/unipoly.rs index d97b189..6b28187 100644 --- a/src/unipoly.rs +++ b/src/unipoly.rs @@ -11,187 +11,188 @@ use merlin::Transcript; // ax^3 + bx^2 + cx + d stored as vec![d,c,b,a] #[derive(Debug, CanonicalDeserialize, CanonicalSerialize, Clone)] pub struct UniPoly { - pub coeffs: Vec, - // pub coeffs_fq: Vec, + pub coeffs: Vec, + // pub coeffs_fq: Vec, } // ax^2 + bx + c stored as vec![c,a] // ax^3 + bx^2 + cx + d stored as vec![d,b,a] #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)] pub struct CompressedUniPoly { - pub coeffs_except_linear_term: Vec, + pub coeffs_except_linear_term: Vec, } impl UniPoly { - pub fn from_evals(evals: &[Scalar]) -> Self { - // we only support degree-2 or degree-3 univariate polynomials - assert!(evals.len() == 3 || evals.len() == 4); - let coeffs = if evals.len() == 3 { - // ax^2 + bx + c - let two_inv = Scalar::from(2).inverse().unwrap(); - - let c = evals[0]; - let a = two_inv * (evals[2] - evals[1] - evals[1] + c); - let b = evals[1] - c - a; - vec![c, b, a] - } else { - // ax^3 + bx^2 + cx + d - let two_inv = Scalar::from(2).inverse().unwrap(); - let six_inv = Scalar::from(6).inverse().unwrap(); - - let d = evals[0]; - let a = six_inv - * (evals[3] - evals[2] - evals[2] - evals[2] + evals[1] + evals[1] + evals[1] - evals[0]); - let b = two_inv - * (evals[0] + evals[0] - evals[1] - evals[1] - evals[1] - evals[1] - evals[1] - + evals[2] - + evals[2] - + evals[2] - + evals[2] - - evals[3]); - let c = evals[1] - d - a - b; - vec![d, c, b, a] - }; - - UniPoly { coeffs } - } - - pub fn degree(&self) -> usize { - self.coeffs.len() - 1 - } - - pub fn as_vec(&self) -> Vec { - self.coeffs.clone() - } - - pub fn eval_at_zero(&self) -> Scalar { - self.coeffs[0] - } - - pub fn eval_at_one(&self) -> Scalar { - (0..self.coeffs.len()).map(|i| self.coeffs[i]).sum() - } - - pub fn evaluate(&self, r: &Scalar) -> Scalar { - let mut eval = self.coeffs[0]; - let mut power = *r; - for i in 1..self.coeffs.len() { - eval += power * self.coeffs[i]; - power *= r; + pub fn from_evals(evals: &[Scalar]) -> Self { + // we only support degree-2 or degree-3 univariate polynomials + assert!(evals.len() == 3 || evals.len() == 4); + let coeffs = if evals.len() == 3 { + // ax^2 + bx + c + let two_inv = Scalar::from(2).inverse().unwrap(); + + let c = evals[0]; + let a = two_inv * (evals[2] - evals[1] - evals[1] + c); + let b = evals[1] - c - a; + vec![c, b, a] + } else { + // ax^3 + bx^2 + cx + d + let two_inv = Scalar::from(2).inverse().unwrap(); + let six_inv = Scalar::from(6).inverse().unwrap(); + + let d = evals[0]; + let a = six_inv + * (evals[3] - evals[2] - evals[2] - evals[2] + evals[1] + evals[1] + evals[1] + - evals[0]); + let b = two_inv + * (evals[0] + evals[0] - evals[1] - evals[1] - evals[1] - evals[1] - evals[1] + + evals[2] + + evals[2] + + evals[2] + + evals[2] + - evals[3]); + let c = evals[1] - d - a - b; + vec![d, c, b, a] + }; + + UniPoly { coeffs } } - eval - } - - pub fn compress(&self) -> CompressedUniPoly { - let coeffs_except_linear_term = [&self.coeffs[..1], &self.coeffs[2..]].concat(); - assert_eq!(coeffs_except_linear_term.len() + 1, self.coeffs.len()); - CompressedUniPoly { - coeffs_except_linear_term, + + pub fn degree(&self) -> usize { + self.coeffs.len() - 1 + } + + pub fn as_vec(&self) -> Vec { + self.coeffs.clone() + } + + pub fn eval_at_zero(&self) -> Scalar { + self.coeffs[0] + } + + pub fn eval_at_one(&self) -> Scalar { + (0..self.coeffs.len()).map(|i| self.coeffs[i]).sum() } - } - pub fn commit(&self, gens: &MultiCommitGens, blind: &Scalar) -> GroupElement { - self.coeffs.commit(blind, gens) - } + pub fn evaluate(&self, r: &Scalar) -> Scalar { + let mut eval = self.coeffs[0]; + let mut power = *r; + for i in 1..self.coeffs.len() { + eval += power * self.coeffs[i]; + power *= r; + } + eval + } + + pub fn compress(&self) -> CompressedUniPoly { + let coeffs_except_linear_term = [&self.coeffs[..1], &self.coeffs[2..]].concat(); + assert_eq!(coeffs_except_linear_term.len() + 1, self.coeffs.len()); + CompressedUniPoly { + coeffs_except_linear_term, + } + } + + pub fn commit(&self, gens: &MultiCommitGens, blind: &Scalar) -> GroupElement { + self.coeffs.commit(blind, gens) + } } impl CompressedUniPoly { - // we require eval(0) + eval(1) = hint, so we can solve for the linear term as: - // linear_term = hint - 2 * constant_term - deg2 term - deg3 term - pub fn decompress(&self, hint: &Scalar) -> UniPoly { - let mut linear_term = - (*hint) - self.coeffs_except_linear_term[0] - self.coeffs_except_linear_term[0]; - for i in 1..self.coeffs_except_linear_term.len() { - linear_term -= self.coeffs_except_linear_term[i]; + // we require eval(0) + eval(1) = hint, so we can solve for the linear term as: + // linear_term = hint - 2 * constant_term - deg2 term - deg3 term + pub fn decompress(&self, hint: &Scalar) -> UniPoly { + let mut linear_term = + (*hint) - self.coeffs_except_linear_term[0] - self.coeffs_except_linear_term[0]; + for i in 1..self.coeffs_except_linear_term.len() { + linear_term -= self.coeffs_except_linear_term[i]; + } + + let mut coeffs = vec![self.coeffs_except_linear_term[0], linear_term]; + coeffs.extend(&self.coeffs_except_linear_term[1..]); + assert_eq!(self.coeffs_except_linear_term.len() + 1, coeffs.len()); + UniPoly { coeffs } } - - let mut coeffs = vec![self.coeffs_except_linear_term[0], linear_term]; - coeffs.extend(&self.coeffs_except_linear_term[1..]); - assert_eq!(self.coeffs_except_linear_term.len() + 1, coeffs.len()); - UniPoly { coeffs } - } } impl AppendToPoseidon for UniPoly { - fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { - // transcript.append_message(label, b"UniPoly_begin"); - for i in 0..self.coeffs.len() { - transcript.append_scalar(&self.coeffs[i]); + fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) { + // transcript.append_message(label, b"UniPoly_begin"); + for i in 0..self.coeffs.len() { + transcript.append_scalar(&self.coeffs[i]); + } + // transcript.append_message(label, b"UniPoly_end"); } - // transcript.append_message(label, b"UniPoly_end"); - } } impl AppendToTranscript for UniPoly { - fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { - transcript.append_message(label, b"UniPoly_begin"); - for i in 0..self.coeffs.len() { - transcript.append_scalar(b"coeff", &self.coeffs[i]); + fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { + transcript.append_message(label, b"UniPoly_begin"); + for i in 0..self.coeffs.len() { + transcript.append_scalar(b"coeff", &self.coeffs[i]); + } + transcript.append_message(label, b"UniPoly_end"); } - transcript.append_message(label, b"UniPoly_end"); - } } #[cfg(test)] mod tests { - use ark_ff::One; - - use super::*; - - #[test] - fn test_from_evals_quad() { - // polynomial is 2x^2 + 3x + 1 - let e0 = Scalar::one(); - let e1 = Scalar::from(6); - let e2 = Scalar::from(15); - let evals = vec![e0, e1, e2]; - let poly = UniPoly::from_evals(&evals); - - assert_eq!(poly.eval_at_zero(), e0); - assert_eq!(poly.eval_at_one(), e1); - assert_eq!(poly.coeffs.len(), 3); - assert_eq!(poly.coeffs[0], Scalar::one()); - assert_eq!(poly.coeffs[1], Scalar::from(3)); - assert_eq!(poly.coeffs[2], Scalar::from(2)); - - let hint = e0 + e1; - let compressed_poly = poly.compress(); - let decompressed_poly = compressed_poly.decompress(&hint); - for i in 0..decompressed_poly.coeffs.len() { - assert_eq!(decompressed_poly.coeffs[i], poly.coeffs[i]); + use ark_ff::One; + + use super::*; + + #[test] + fn test_from_evals_quad() { + // polynomial is 2x^2 + 3x + 1 + let e0 = Scalar::one(); + let e1 = Scalar::from(6); + let e2 = Scalar::from(15); + let evals = vec![e0, e1, e2]; + let poly = UniPoly::from_evals(&evals); + + assert_eq!(poly.eval_at_zero(), e0); + assert_eq!(poly.eval_at_one(), e1); + assert_eq!(poly.coeffs.len(), 3); + assert_eq!(poly.coeffs[0], Scalar::one()); + assert_eq!(poly.coeffs[1], Scalar::from(3)); + assert_eq!(poly.coeffs[2], Scalar::from(2)); + + let hint = e0 + e1; + let compressed_poly = poly.compress(); + let decompressed_poly = compressed_poly.decompress(&hint); + for i in 0..decompressed_poly.coeffs.len() { + assert_eq!(decompressed_poly.coeffs[i], poly.coeffs[i]); + } + + let e3 = Scalar::from(28); + assert_eq!(poly.evaluate(&Scalar::from(3)), e3); } - let e3 = Scalar::from(28); - assert_eq!(poly.evaluate(&Scalar::from(3)), e3); - } - - #[test] - fn test_from_evals_cubic() { - // polynomial is x^3 + 2x^2 + 3x + 1 - let e0 = Scalar::one(); - let e1 = Scalar::from(7); - let e2 = Scalar::from(23); - let e3 = Scalar::from(55); - let evals = vec![e0, e1, e2, e3]; - let poly = UniPoly::from_evals(&evals); - - assert_eq!(poly.eval_at_zero(), e0); - assert_eq!(poly.eval_at_one(), e1); - assert_eq!(poly.coeffs.len(), 4); - assert_eq!(poly.coeffs[0], Scalar::one()); - assert_eq!(poly.coeffs[1], Scalar::from(3)); - assert_eq!(poly.coeffs[2], Scalar::from(2)); - assert_eq!(poly.coeffs[3], Scalar::from(1)); - - let hint = e0 + e1; - let compressed_poly = poly.compress(); - let decompressed_poly = compressed_poly.decompress(&hint); - for i in 0..decompressed_poly.coeffs.len() { - assert_eq!(decompressed_poly.coeffs[i], poly.coeffs[i]); + #[test] + fn test_from_evals_cubic() { + // polynomial is x^3 + 2x^2 + 3x + 1 + let e0 = Scalar::one(); + let e1 = Scalar::from(7); + let e2 = Scalar::from(23); + let e3 = Scalar::from(55); + let evals = vec![e0, e1, e2, e3]; + let poly = UniPoly::from_evals(&evals); + + assert_eq!(poly.eval_at_zero(), e0); + assert_eq!(poly.eval_at_one(), e1); + assert_eq!(poly.coeffs.len(), 4); + assert_eq!(poly.coeffs[0], Scalar::one()); + assert_eq!(poly.coeffs[1], Scalar::from(3)); + assert_eq!(poly.coeffs[2], Scalar::from(2)); + assert_eq!(poly.coeffs[3], Scalar::from(1)); + + let hint = e0 + e1; + let compressed_poly = poly.compress(); + let decompressed_poly = compressed_poly.decompress(&hint); + for i in 0..decompressed_poly.coeffs.len() { + assert_eq!(decompressed_poly.coeffs[i], poly.coeffs[i]); + } + + let e4 = Scalar::from(109); + assert_eq!(poly.evaluate(&Scalar::from(4)), e4); } - - let e4 = Scalar::from(109); - assert_eq!(poly.evaluate(&Scalar::from(4)), e4); - } }