mirror of
https://github.com/arnaucube/testudo.git
synced 2026-01-12 16:51:28 +01:00
Merge pull request #18 from vmx/no-custom-fmt
chore: format Rust code the usual way
This commit is contained in:
221
benches/nizk.rs
221
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! {
|
||||
|
||||
106
benches/r1cs.rs
106
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");
|
||||
}
|
||||
}
|
||||
|
||||
188
benches/snark.rs
188
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);
|
||||
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);
|
||||
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 name = format!("SNARK_encode_{}", num_cons);
|
||||
group.bench_function(&name, move |b| {
|
||||
b.iter(|| {
|
||||
SNARK::encode(black_box(&inst), black_box(&gens));
|
||||
});
|
||||
});
|
||||
group.finish();
|
||||
}
|
||||
// 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 num_vars = (2_usize).pow(s as u32);
|
||||
let num_cons = num_vars;
|
||||
let num_inputs = 10;
|
||||
|
||||
let params = poseidon_params();
|
||||
let params = poseidon_params();
|
||||
|
||||
let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs);
|
||||
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(|| {
|
||||
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();
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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 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);
|
||||
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 of satisfiability
|
||||
let mut prover_transcript = PoseidonTranscript::new(¶ms);
|
||||
let proof = SNARK::prove(
|
||||
&inst,
|
||||
&comm,
|
||||
&decomm,
|
||||
vars,
|
||||
&inputs,
|
||||
&gens,
|
||||
&mut prover_transcript,
|
||||
);
|
||||
// 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! {
|
||||
|
||||
@@ -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;
|
||||
// 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<u8>)> = Vec::new();
|
||||
let mut B: Vec<(usize, usize, Vec<u8>)> = Vec::new();
|
||||
let mut C: Vec<(usize, usize, Vec<u8>)> = Vec::new();
|
||||
// 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<u8>)> = Vec::new();
|
||||
let mut B: Vec<(usize, usize, Vec<u8>)> = Vec::new();
|
||||
let mut C: Vec<(usize, usize, Vec<u8>)> = Vec::new();
|
||||
|
||||
let one = Scalar::one().into_repr().to_bytes_le();
|
||||
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)
|
||||
// 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 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 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 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));
|
||||
// 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();
|
||||
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
|
||||
// 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 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();
|
||||
// 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");
|
||||
// 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,
|
||||
)
|
||||
(
|
||||
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();
|
||||
// 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();
|
||||
let params = poseidon_params();
|
||||
|
||||
// produce public parameters
|
||||
let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_non_zero_entries);
|
||||
// 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);
|
||||
// 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,
|
||||
);
|
||||
// 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!");
|
||||
// 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!");
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
// 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;
|
||||
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 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);
|
||||
// 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 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);
|
||||
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());
|
||||
// verify the proof of satisfiability
|
||||
let mut verifier_transcript = PoseidonTranscript::new(¶ms);
|
||||
assert!(proof
|
||||
.verify(&inst, &inputs, &mut verifier_transcript, &gens)
|
||||
.is_ok());
|
||||
|
||||
println!();
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
// 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;
|
||||
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 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);
|
||||
// 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);
|
||||
// create a commitment to R1CSInstance
|
||||
let (comm, decomm) = SNARK::encode(&inst, &gens);
|
||||
|
||||
let params = poseidon_params();
|
||||
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,
|
||||
);
|
||||
// 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);
|
||||
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());
|
||||
// verify the proof of satisfiability
|
||||
let mut verifier_transcript = PoseidonTranscript::new(¶ms);
|
||||
assert!(proof
|
||||
.verify(&comm, &inputs, &mut verifier_transcript, &gens)
|
||||
.is_ok());
|
||||
|
||||
println!();
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
edition = "2018"
|
||||
tab_spaces = 2
|
||||
newline_style = "Unix"
|
||||
use_try_shorthand = true
|
||||
@@ -10,83 +10,83 @@ use ark_sponge::CryptographicSponge;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MultiCommitGens {
|
||||
pub n: usize,
|
||||
pub G: Vec<GroupElement>,
|
||||
pub h: GroupElement,
|
||||
pub n: usize,
|
||||
pub G: Vec<GroupElement>,
|
||||
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<GroupElement> = Vec::new();
|
||||
for _ in 0..n + 1 {
|
||||
let mut el_aff: Option<GroupElementAffine> = 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<GroupElement> = Vec::new();
|
||||
for _ in 0..n + 1 {
|
||||
let mut el_aff: Option<GroupElementAffine> = 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);
|
||||
|
||||
(
|
||||
MultiCommitGens {
|
||||
n: G1.len(),
|
||||
G: G1.to_vec(),
|
||||
h: self.h,
|
||||
},
|
||||
MultiCommitGens {
|
||||
n: G2.len(),
|
||||
G: G2.to_vec(),
|
||||
h: self.h,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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<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())
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Fr>,
|
||||
pub sponge: PoseidonSpongeVar<Fr>,
|
||||
pub params: PoseidonParameters<Fr>,
|
||||
pub cs: ConstraintSystemRef<Fr>,
|
||||
pub sponge: PoseidonSpongeVar<Fr>,
|
||||
pub params: PoseidonParameters<Fr>,
|
||||
}
|
||||
|
||||
impl PoseidonTranscripVar {
|
||||
fn new(
|
||||
cs: ConstraintSystemRef<Fr>,
|
||||
params: &PoseidonParameters<Fr>,
|
||||
challenge: Option<Fr>,
|
||||
) -> Self {
|
||||
let mut sponge = PoseidonSpongeVar::new(cs.clone(), params);
|
||||
fn new(
|
||||
cs: ConstraintSystemRef<Fr>,
|
||||
params: &PoseidonParameters<Fr>,
|
||||
challenge: Option<Fr>,
|
||||
) -> Self {
|
||||
let mut sponge = PoseidonSpongeVar::new(cs.clone(), params);
|
||||
|
||||
if let Some(c) = challenge {
|
||||
let c_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(c)).unwrap();
|
||||
sponge.absorb(&c_var).unwrap();
|
||||
if let Some(c) = challenge {
|
||||
let c_var = FpVar::<Fr>::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<Fr>) -> Result<(), SynthesisError> {
|
||||
self.sponge.absorb(&input)
|
||||
}
|
||||
}
|
||||
|
||||
fn append(&mut self, input: &FpVar<Fr>) -> Result<(), SynthesisError> {
|
||||
self.sponge.absorb(&input)
|
||||
}
|
||||
|
||||
fn append_vector(&mut self, input_vec: &[FpVar<Fr>]) -> Result<(), SynthesisError> {
|
||||
for input in input_vec.iter() {
|
||||
self.append(input)?;
|
||||
fn append_vector(&mut self, input_vec: &[FpVar<Fr>]) -> Result<(), SynthesisError> {
|
||||
for input in input_vec.iter() {
|
||||
self.append(input)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn challenge(&mut self) -> Result<FpVar<Fr>, SynthesisError> {
|
||||
let c_var = self.sponge.squeeze_field_elements(1).unwrap().remove(0);
|
||||
fn challenge(&mut self) -> Result<FpVar<Fr>, 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<Vec<FpVar<Fr>>, SynthesisError> {
|
||||
let c_vars = self.sponge.squeeze_field_elements(len).unwrap();
|
||||
fn challenge_vector(&mut self, len: usize) -> Result<Vec<FpVar<Fr>>, 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<FpVar<Fr>>,
|
||||
pub coeffs: Vec<FpVar<Fr>>,
|
||||
}
|
||||
|
||||
impl AllocVar<UniPoly, Fr> for UniPolyVar {
|
||||
fn new_variable<T: Borrow<UniPoly>>(
|
||||
cs: impl Into<Namespace<Fr>>,
|
||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||
mode: AllocationMode,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
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::<Fr>::new_variable(cs.clone(), || Ok(coeff), mode)?;
|
||||
coeffs_var.push(coeff_var);
|
||||
}
|
||||
Ok(Self { coeffs: coeffs_var })
|
||||
})
|
||||
}
|
||||
fn new_variable<T: Borrow<UniPoly>>(
|
||||
cs: impl Into<Namespace<Fr>>,
|
||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||
mode: AllocationMode,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
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::<Fr>::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<Fr> {
|
||||
self.coeffs[0].clone()
|
||||
}
|
||||
|
||||
pub fn eval_at_one(&self) -> FpVar<Fr> {
|
||||
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<Fr> {
|
||||
self.coeffs[0].clone()
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
// mul without reduce
|
||||
pub fn evaluate(&self, r: &FpVar<Fr>) -> FpVar<Fr> {
|
||||
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;
|
||||
pub fn eval_at_one(&self) -> FpVar<Fr> {
|
||||
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<Fr>) -> FpVar<Fr> {
|
||||
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;
|
||||
}
|
||||
eval
|
||||
}
|
||||
eval
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SumcheckVerificationCircuit {
|
||||
pub polys: Vec<UniPoly>,
|
||||
pub polys: Vec<UniPoly>,
|
||||
}
|
||||
|
||||
impl SumcheckVerificationCircuit {
|
||||
fn verifiy_sumcheck(
|
||||
&self,
|
||||
poly_vars: &[UniPolyVar],
|
||||
claim_var: &FpVar<Fr>,
|
||||
transcript_var: &mut PoseidonTranscripVar,
|
||||
) -> Result<(FpVar<Fr>, Vec<FpVar<Fr>>), SynthesisError> {
|
||||
let mut e_var = claim_var.clone();
|
||||
let mut r_vars: Vec<FpVar<Fr>> = Vec::new();
|
||||
fn verifiy_sumcheck(
|
||||
&self,
|
||||
poly_vars: &[UniPolyVar],
|
||||
claim_var: &FpVar<Fr>,
|
||||
transcript_var: &mut PoseidonTranscripVar,
|
||||
) -> Result<(FpVar<Fr>, Vec<FpVar<Fr>>), SynthesisError> {
|
||||
let mut e_var = claim_var.clone();
|
||||
let mut r_vars: Vec<FpVar<Fr>> = 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());
|
||||
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<Fr>,
|
||||
idx: usize,
|
||||
val_var: FpVar<Fr>,
|
||||
}
|
||||
|
||||
impl AllocVar<SparsePolyEntry, Fr> for SparsePolyEntryVar {
|
||||
fn new_variable<T: Borrow<SparsePolyEntry>>(
|
||||
cs: impl Into<Namespace<Fr>>,
|
||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||
_mode: AllocationMode,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
f().and_then(|s| {
|
||||
let cs = cs.into();
|
||||
let spe: &SparsePolyEntry = s.borrow();
|
||||
let val_var = FpVar::<Fr>::new_witness(cs, || Ok(spe.val))?;
|
||||
Ok(Self {
|
||||
idx: spe.idx,
|
||||
val_var,
|
||||
})
|
||||
})
|
||||
}
|
||||
fn new_variable<T: Borrow<SparsePolyEntry>>(
|
||||
cs: impl Into<Namespace<Fr>>,
|
||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||
_mode: AllocationMode,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
f().and_then(|s| {
|
||||
let cs = cs.into();
|
||||
let spe: &SparsePolyEntry = s.borrow();
|
||||
let val_var = FpVar::<Fr>::new_witness(cs, || Ok(spe.val))?;
|
||||
Ok(Self {
|
||||
idx: spe.idx,
|
||||
val_var,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SparsePolynomialVar {
|
||||
num_vars: usize,
|
||||
Z_var: Vec<SparsePolyEntryVar>,
|
||||
num_vars: usize,
|
||||
Z_var: Vec<SparsePolyEntryVar>,
|
||||
}
|
||||
|
||||
impl AllocVar<SparsePolynomial, Fr> for SparsePolynomialVar {
|
||||
fn new_variable<T: Borrow<SparsePolynomial>>(
|
||||
cs: impl Into<Namespace<Fr>>,
|
||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||
mode: AllocationMode,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
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<T: Borrow<SparsePolynomial>>(
|
||||
cs: impl Into<Namespace<Fr>>,
|
||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||
mode: AllocationMode,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
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<Fr>]) -> FpVar<Fr> {
|
||||
let mut chi_i_var = FpVar::<Fr>::one();
|
||||
let one = FpVar::<Fr>::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<Fr>]) -> FpVar<Fr> {
|
||||
let mut chi_i_var = FpVar::<Fr>::one();
|
||||
let one = FpVar::<Fr>::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<Fr>]) -> FpVar<Fr> {
|
||||
let mut sum = FpVar::<Fr>::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<Fr>]) -> FpVar<Fr> {
|
||||
let mut sum = FpVar::<Fr>::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<Fr>,
|
||||
pub input_as_sparse_poly: SparsePolynomial,
|
||||
pub evals: (Fr, Fr, Fr),
|
||||
pub params: PoseidonParameters<Fr>,
|
||||
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<Scalar>,
|
||||
pub claimed_transcript_sat_state: Scalar,
|
||||
pub num_vars: usize,
|
||||
pub num_cons: usize,
|
||||
pub input: Vec<Fr>,
|
||||
pub input_as_sparse_poly: SparsePolynomial,
|
||||
pub evals: (Fr, Fr, Fr),
|
||||
pub params: PoseidonParameters<Fr>,
|
||||
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<Scalar>,
|
||||
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<Fr> for R1CSVerificationCircuit {
|
||||
fn generate_constraints(self, cs: ConstraintSystemRef<Fr>) -> ark_relations::r1cs::Result<()> {
|
||||
let mut transcript_var =
|
||||
PoseidonTranscripVar::new(cs.clone(), &self.params, Some(self.prev_challenge));
|
||||
fn generate_constraints(self, cs: ConstraintSystemRef<Fr>) -> 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::<Vec<UniPolyVar>>();
|
||||
let poly_sc1_vars = self
|
||||
.sc_phase1
|
||||
.polys
|
||||
.iter()
|
||||
.map(|p| {
|
||||
UniPolyVar::new_variable(cs.clone(), || Ok(p), AllocationMode::Witness).unwrap()
|
||||
})
|
||||
.collect::<Vec<UniPolyVar>>();
|
||||
|
||||
let poly_sc2_vars = self
|
||||
.sc_phase2
|
||||
.polys
|
||||
.iter()
|
||||
.map(|p| UniPolyVar::new_variable(cs.clone(), || Ok(p), AllocationMode::Witness).unwrap())
|
||||
.collect::<Vec<UniPolyVar>>();
|
||||
let poly_sc2_vars = self
|
||||
.sc_phase2
|
||||
.polys
|
||||
.iter()
|
||||
.map(|p| {
|
||||
UniPolyVar::new_variable(cs.clone(), || Ok(p), AllocationMode::Witness).unwrap()
|
||||
})
|
||||
.collect::<Vec<UniPolyVar>>();
|
||||
|
||||
let input_vars = self
|
||||
.input
|
||||
.iter()
|
||||
.map(|i| FpVar::<Fr>::new_variable(cs.clone(), || Ok(i), AllocationMode::Witness).unwrap())
|
||||
.collect::<Vec<FpVar<Fr>>>();
|
||||
let input_vars = self
|
||||
.input
|
||||
.iter()
|
||||
.map(|i| {
|
||||
FpVar::<Fr>::new_variable(cs.clone(), || Ok(i), AllocationMode::Witness).unwrap()
|
||||
})
|
||||
.collect::<Vec<FpVar<Fr>>>();
|
||||
|
||||
let claimed_ry_vars = self
|
||||
.claimed_ry
|
||||
.iter()
|
||||
.map(|r| FpVar::<Fr>::new_variable(cs.clone(), || Ok(r), AllocationMode::Input).unwrap())
|
||||
.collect::<Vec<FpVar<Fr>>>();
|
||||
let claimed_ry_vars = self
|
||||
.claimed_ry
|
||||
.iter()
|
||||
.map(|r| {
|
||||
FpVar::<Fr>::new_variable(cs.clone(), || Ok(r), AllocationMode::Input).unwrap()
|
||||
})
|
||||
.collect::<Vec<FpVar<Fr>>>();
|
||||
|
||||
transcript_var.append_vector(&input_vars)?;
|
||||
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 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 tau_vars = transcript_var.challenge_vector(num_rounds_x)?;
|
||||
|
||||
let claim_phase1_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(Fr::zero()))?;
|
||||
let claim_phase1_var = FpVar::<Fr>::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 (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, Bz_claim, Cz_claim, prod_Az_Bz_claims) = &self.claims_phase2;
|
||||
|
||||
let Az_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(Az_claim))?;
|
||||
let Bz_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(Bz_claim))?;
|
||||
let Cz_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(Cz_claim))?;
|
||||
let prod_Az_Bz_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(prod_Az_Bz_claims))?;
|
||||
let one = FpVar::<Fr>::one();
|
||||
let prod_vars: Vec<FpVar<Fr>> = (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::<Fr>::one();
|
||||
let Az_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(Az_claim))?;
|
||||
let Bz_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(Bz_claim))?;
|
||||
let Cz_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(Cz_claim))?;
|
||||
let prod_Az_Bz_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(prod_Az_Bz_claims))?;
|
||||
let one = FpVar::<Fr>::one();
|
||||
let prod_vars: Vec<FpVar<Fr>> = (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::<Fr>::one();
|
||||
|
||||
for p_var in prod_vars.iter() {
|
||||
taus_bound_rx_var *= p_var;
|
||||
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::<Fr>::new_input(cs.clone(), || Ok(&self.eval_vars_at_ry))?;
|
||||
|
||||
let eval_Z_at_ry_var = (FpVar::<Fr>::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::<Fr>::new_witness(cs.clone(), || Ok(eval_A_r))?;
|
||||
let eval_B_r_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(eval_B_r))?;
|
||||
let eval_C_r_var = FpVar::<Fr>::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::<Fr>::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(())
|
||||
}
|
||||
|
||||
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::<Fr>::new_input(cs.clone(), || Ok(&self.eval_vars_at_ry))?;
|
||||
|
||||
let eval_Z_at_ry_var =
|
||||
(FpVar::<Fr>::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::<Fr>::new_witness(cs.clone(), || Ok(eval_A_r))?;
|
||||
let eval_B_r_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(eval_B_r))?;
|
||||
let eval_C_r_var = FpVar::<Fr>::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::<Fr>::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<Fr>,
|
||||
pub input_as_sparse_poly: SparsePolynomial,
|
||||
pub evals: (Fr, Fr, Fr),
|
||||
pub params: PoseidonParameters<Fr>,
|
||||
pub prev_challenge: Fr,
|
||||
pub claims_phase2: (Fr, Fr, Fr, Fr),
|
||||
pub eval_vars_at_ry: Fr,
|
||||
pub polys_sc1: Vec<UniPoly>,
|
||||
pub polys_sc2: Vec<UniPoly>,
|
||||
pub ry: Vec<Scalar>,
|
||||
pub transcript_sat_state: Scalar,
|
||||
pub num_vars: usize,
|
||||
pub num_cons: usize,
|
||||
pub input: Vec<Fr>,
|
||||
pub input_as_sparse_poly: SparsePolynomial,
|
||||
pub evals: (Fr, Fr, Fr),
|
||||
pub params: PoseidonParameters<Fr>,
|
||||
pub prev_challenge: Fr,
|
||||
pub claims_phase2: (Fr, Fr, Fr, Fr),
|
||||
pub eval_vars_at_ry: Fr,
|
||||
pub polys_sc1: Vec<UniPoly>,
|
||||
pub polys_sc2: Vec<UniPoly>,
|
||||
pub ry: Vec<Scalar>,
|
||||
pub transcript_sat_state: Scalar,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct VerifierCircuit {
|
||||
pub inner_circuit: R1CSVerificationCircuit,
|
||||
pub inner_proof: GrothProof<I>,
|
||||
pub inner_vk: PreparedVerifyingKey<I>,
|
||||
pub eval_vars_at_ry: Fr,
|
||||
pub claims_phase2: (Fr, Fr, Fr, Fr),
|
||||
pub ry: Vec<Fr>,
|
||||
pub transcript_sat_state: Scalar,
|
||||
pub inner_circuit: R1CSVerificationCircuit,
|
||||
pub inner_proof: GrothProof<I>,
|
||||
pub inner_vk: PreparedVerifyingKey<I>,
|
||||
pub eval_vars_at_ry: Fr,
|
||||
pub claims_phase2: (Fr, Fr, Fr, Fr),
|
||||
pub ry: Vec<Fr>,
|
||||
pub transcript_sat_state: Scalar,
|
||||
}
|
||||
|
||||
impl VerifierCircuit {
|
||||
pub fn new<R: Rng + CryptoRng>(
|
||||
config: &VerifierConfig,
|
||||
mut rng: &mut R,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let inner_circuit = R1CSVerificationCircuit::new(config);
|
||||
let (pk, vk) = Groth16::<I>::setup(inner_circuit.clone(), &mut rng).unwrap();
|
||||
let proof = Groth16::<I>::prove(&pk, inner_circuit.clone(), &mut rng)?;
|
||||
let pvk = Groth16::<I>::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<R: Rng + CryptoRng>(
|
||||
config: &VerifierConfig,
|
||||
mut rng: &mut R,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let inner_circuit = R1CSVerificationCircuit::new(config);
|
||||
let (pk, vk) = Groth16::<I>::setup(inner_circuit.clone(), &mut rng).unwrap();
|
||||
let proof = Groth16::<I>::prove(&pk, inner_circuit.clone(), &mut rng)?;
|
||||
let pvk = Groth16::<I>::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<Fq> for VerifierCircuit {
|
||||
fn generate_constraints(self, cs: ConstraintSystemRef<Fq>) -> ark_relations::r1cs::Result<()> {
|
||||
let proof_var = ProofVar::<I, IV>::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]);
|
||||
fn generate_constraints(self, cs: ConstraintSystemRef<Fq>) -> ark_relations::r1cs::Result<()> {
|
||||
let proof_var =
|
||||
ProofVar::<I, IV>::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<bool> = BitIteratorLE::new(c.into_repr().as_ref().to_vec()).collect();
|
||||
Vec::new_witness(cs.clone(), || Ok(bits))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let input_var = BooleanInputVar::<Fr, Fq>::new(bits);
|
||||
let bits = pubs
|
||||
.iter()
|
||||
.map(|c| {
|
||||
let bits: Vec<bool> = BitIteratorLE::new(c.into_repr().as_ref().to_vec()).collect();
|
||||
Vec::new_witness(cs.clone(), || Ok(bits))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let input_var = BooleanInputVar::<Fr, Fq>::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(())
|
||||
}
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
1176
src/dense_mlpoly.rs
1176
src/dense_mlpoly.rs
File diff suppressed because it is too large
Load Diff
@@ -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<u8>),
|
||||
#[error("Proof verification failed")]
|
||||
InternalError,
|
||||
#[error("Compressed group element failed to decompress: {0:?}")]
|
||||
DecompressionError(Vec<u8>),
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
66
src/group.rs
66
src/group.rs
@@ -19,62 +19,62 @@ pub type Fr = ark_bls12_377::Fr;
|
||||
pub struct CompressedGroup(pub Vec<u8>);
|
||||
|
||||
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<GroupElement>;
|
||||
fn decompress(encoded: &CompressedGroup) -> Option<GroupElement>;
|
||||
}
|
||||
|
||||
pub trait UnpackGroupElement {
|
||||
fn unpack(&self) -> Result<GroupElement, ProofVerifyError>;
|
||||
fn unpack(&self) -> Result<GroupElement, ProofVerifyError>;
|
||||
}
|
||||
|
||||
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<Self> {
|
||||
let res = GroupElement::deserialize(&*encoded.0);
|
||||
if let Ok(r) = res {
|
||||
Some(r)
|
||||
} else {
|
||||
println!("{:?}", res);
|
||||
None
|
||||
fn decompress(encoded: &CompressedGroup) -> Option<Self> {
|
||||
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<GroupElement, ProofVerifyError> {
|
||||
let encoded = self.0.clone();
|
||||
GroupElement::decompress(self).ok_or(ProofVerifyError::DecompressionError(encoded))
|
||||
}
|
||||
fn unpack(&self) -> Result<GroupElement, ProofVerifyError> {
|
||||
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::<Vec<<Scalar as PrimeField>::BigInt>>();
|
||||
let aff_points = points
|
||||
.iter()
|
||||
.map(|P| P.borrow().into_affine())
|
||||
.collect::<Vec<GroupElementAffine>>();
|
||||
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::<Vec<<Scalar as PrimeField>::BigInt>>();
|
||||
let aff_points = points
|
||||
.iter()
|
||||
.map(|P| P.borrow().into_affine())
|
||||
.collect::<Vec<GroupElementAffine>>();
|
||||
VariableBaseMSM::multi_scalar_mul(aff_points.as_slice(), repr_scalars.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
1261
src/lib.rs
1261
src/lib.rs
File diff suppressed because it is too large
Load Diff
60
src/math.rs
60
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<bool>;
|
||||
fn log_2(self) -> usize;
|
||||
fn square_root(self) -> usize;
|
||||
fn pow2(self) -> usize;
|
||||
fn get_bits(self, num_bits: usize) -> Vec<bool>;
|
||||
fn log_2(self) -> usize;
|
||||
}
|
||||
|
||||
impl Math for 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)
|
||||
}
|
||||
|
||||
/// Returns the num_bits from n in a canonical order
|
||||
fn get_bits(self, num_bits: usize) -> Vec<bool> {
|
||||
(0..num_bits)
|
||||
.map(|shift_amount| ((self & (1 << (num_bits - shift_amount - 1))) > 0))
|
||||
.collect::<Vec<bool>>()
|
||||
}
|
||||
|
||||
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
|
||||
#[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)
|
||||
}
|
||||
|
||||
/// Returns the num_bits from n in a canonical order
|
||||
fn get_bits(self, num_bits: usize) -> Vec<bool> {
|
||||
(0..num_bits)
|
||||
.map(|shift_amount| ((self & (1 << (num_bits - shift_amount - 1))) > 0))
|
||||
.collect::<Vec<bool>>()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<CompressedGroup>,
|
||||
R_vec: Vec<CompressedGroup>,
|
||||
L_vec: Vec<CompressedGroup>,
|
||||
R_vec: Vec<CompressedGroup>,
|
||||
}
|
||||
|
||||
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()[..];
|
||||
/// 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 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);
|
||||
// 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;
|
||||
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);
|
||||
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 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 (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::<Vec<Scalar>>()
|
||||
.as_slice(),
|
||||
G_R
|
||||
.iter()
|
||||
.chain(iter::once(Q))
|
||||
.chain(iter::once(H))
|
||||
.copied()
|
||||
.collect::<Vec<GroupElement>>()
|
||||
.as_slice(),
|
||||
);
|
||||
let L = GroupElement::vartime_multiscalar_mul(
|
||||
a_L.iter()
|
||||
.chain(iter::once(&c_L))
|
||||
.chain(iter::once(blind_L))
|
||||
.copied()
|
||||
.collect::<Vec<Scalar>>()
|
||||
.as_slice(),
|
||||
G_R.iter()
|
||||
.chain(iter::once(Q))
|
||||
.chain(iter::once(H))
|
||||
.copied()
|
||||
.collect::<Vec<GroupElement>>()
|
||||
.as_slice(),
|
||||
);
|
||||
|
||||
let R = GroupElement::vartime_multiscalar_mul(
|
||||
a_R
|
||||
.iter()
|
||||
.chain(iter::once(&c_R))
|
||||
.chain(iter::once(blind_R))
|
||||
.copied()
|
||||
.collect::<Vec<Scalar>>()
|
||||
.as_slice(),
|
||||
G_L
|
||||
.iter()
|
||||
.chain(iter::once(Q))
|
||||
.chain(iter::once(H))
|
||||
.copied()
|
||||
.collect::<Vec<GroupElement>>()
|
||||
.as_slice(),
|
||||
);
|
||||
let R = GroupElement::vartime_multiscalar_mul(
|
||||
a_R.iter()
|
||||
.chain(iter::once(&c_R))
|
||||
.chain(iter::once(blind_R))
|
||||
.copied()
|
||||
.collect::<Vec<Scalar>>()
|
||||
.as_slice(),
|
||||
G_L.iter()
|
||||
.chain(iter::once(Q))
|
||||
.chain(iter::once(H))
|
||||
.copied()
|
||||
.collect::<Vec<GroupElement>>()
|
||||
.as_slice(),
|
||||
);
|
||||
|
||||
transcript.append_point(&L.compress());
|
||||
transcript.append_point(&R.compress());
|
||||
transcript.append_point(&L.compress());
|
||||
transcript.append_point(&R.compress());
|
||||
|
||||
let u = transcript.challenge_scalar();
|
||||
let u_inv = u.inverse().unwrap();
|
||||
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]]);
|
||||
}
|
||||
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;
|
||||
blind_fin = blind_fin + u * u * blind_L + u_inv * u_inv * blind_R;
|
||||
|
||||
L_vec.push(L.compress());
|
||||
R_vec.push(R.compress());
|
||||
L_vec.push(L.compress());
|
||||
R_vec.push(R.compress());
|
||||
|
||||
a = a_L;
|
||||
b = b_L;
|
||||
G = G_L;
|
||||
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]);
|
||||
/// 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<Scalar>, Vec<Scalar>, Vec<Scalar>), 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<<lg_n below.
|
||||
return Err(ProofVerifyError::InternalError);
|
||||
}
|
||||
if n != (1 << lg_n) {
|
||||
return Err(ProofVerifyError::InternalError);
|
||||
}
|
||||
|
||||
(
|
||||
BulletReductionProof { L_vec, R_vec },
|
||||
Gamma_hat,
|
||||
a[0],
|
||||
b[0],
|
||||
G[0],
|
||||
blind_fin,
|
||||
)
|
||||
}
|
||||
// 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());
|
||||
}
|
||||
|
||||
/// 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<Scalar>, Vec<Scalar>, Vec<Scalar>), 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<<lg_n below.
|
||||
return Err(ProofVerifyError::InternalError);
|
||||
}
|
||||
if n != (1 << lg_n) {
|
||||
return Err(ProofVerifyError::InternalError);
|
||||
// 2. Compute 1/(u_k...u_1) and 1/u_k, ..., 1/u_1
|
||||
let mut challenges_inv: Vec<Scalar> = 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))
|
||||
}
|
||||
|
||||
// 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());
|
||||
/// 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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let Rs = self
|
||||
.R_vec
|
||||
.iter()
|
||||
.map(|p| GroupElement::decompress(p).ok_or(ProofVerifyError::InternalError))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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::<Vec<Scalar>>()
|
||||
.as_slice(),
|
||||
Ls.iter()
|
||||
.chain(Rs.iter())
|
||||
.chain(iter::once(Gamma))
|
||||
.copied()
|
||||
.collect::<Vec<GroupElement>>()
|
||||
.as_slice(),
|
||||
);
|
||||
|
||||
Ok((G_hat, Gamma_hat, a_hat))
|
||||
}
|
||||
|
||||
// 2. Compute 1/(u_k...u_1) and 1/u_k, ..., 1/u_1
|
||||
let mut challenges_inv: Vec<Scalar> = 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))
|
||||
}
|
||||
|
||||
/// 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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let Rs = self
|
||||
.R_vec
|
||||
.iter()
|
||||
.map(|p| GroupElement::decompress(p).ok_or(ProofVerifyError::InternalError))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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::<Vec<Scalar>>()
|
||||
.as_slice(),
|
||||
Ls.iter()
|
||||
.chain(Rs.iter())
|
||||
.chain(iter::once(Gamma))
|
||||
.copied()
|
||||
.collect::<Vec<GroupElement>>()
|
||||
.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
|
||||
}
|
||||
|
||||
1353
src/nizk/mod.rs
1353
src/nizk/mod.rs
File diff suppressed because it is too large
Load Diff
@@ -146,32 +146,31 @@ array!["228517621981785468369663538305998424621845824654552006112396193307208970
|
||||
|
||||
/// TODO
|
||||
pub fn poseidon_params() -> PoseidonParameters<Fr> {
|
||||
let arks = FR["ark"]
|
||||
.members()
|
||||
.map(|ark| {
|
||||
ark
|
||||
let arks = FR["ark"]
|
||||
.members()
|
||||
.map(|v| Fr::from_str(v.as_str().unwrap()).unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mds = FR["mds"]
|
||||
.members()
|
||||
.map(|m| {
|
||||
m.members()
|
||||
.map(|v| Fr::from_str(v.as_str().unwrap()).unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
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::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mds = FR["mds"]
|
||||
.members()
|
||||
.map(|m| {
|
||||
m.members()
|
||||
.map(|v| Fr::from_str(v.as_str().unwrap()).unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
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<Fr> = poseidon_params();
|
||||
pub static ref POSEIDON_PARAMETERS_FR_377: PoseidonParameters<Fr> = poseidon_params();
|
||||
}
|
||||
|
||||
@@ -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<Fr>,
|
||||
params: PoseidonParameters<Fr>,
|
||||
sponge: PoseidonSponge<Fr>,
|
||||
params: PoseidonParameters<Fr>,
|
||||
}
|
||||
|
||||
impl PoseidonTranscript {
|
||||
/// create a new transcript
|
||||
pub fn new(params: &PoseidonParameters<Fr>) -> Self {
|
||||
let sponge = PoseidonSponge::new(params);
|
||||
PoseidonTranscript {
|
||||
sponge,
|
||||
params: params.clone(),
|
||||
/// create a new transcript
|
||||
pub fn new(params: &PoseidonParameters<Fr>) -> 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 append_u64(&mut self, x: u64) {
|
||||
self.sponge.absorb(&x);
|
||||
}
|
||||
|
||||
pub fn append_bytes(&mut self, x: &Vec<u8>) {
|
||||
self.sponge.absorb(x);
|
||||
}
|
||||
|
||||
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_scalar_vector(&mut self, scalars: &[Scalar]) {
|
||||
for scalar in scalars.iter() {
|
||||
self.append_scalar(scalar);
|
||||
pub fn new_from_state(&mut self, challenge: &Scalar) {
|
||||
self.sponge = PoseidonSponge::new(&self.params);
|
||||
self.append_scalar(challenge);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn challenge_scalar(&mut self) -> Scalar {
|
||||
self.sponge.squeeze_field_elements(1).remove(0)
|
||||
}
|
||||
pub fn append_u64(&mut self, x: u64) {
|
||||
self.sponge.absorb(&x);
|
||||
}
|
||||
|
||||
pub fn challenge_vector(&mut self, len: usize) -> Vec<Scalar> {
|
||||
self.sponge.squeeze_field_elements(len)
|
||||
}
|
||||
pub fn append_bytes(&mut self, x: &Vec<u8>) {
|
||||
self.sponge.absorb(x);
|
||||
}
|
||||
|
||||
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_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_vector(&mut self, len: usize) -> Vec<Scalar> {
|
||||
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<I> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,475 +11,481 @@ use ark_std::One;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProductCircuit {
|
||||
left_vec: Vec<DensePolynomial>,
|
||||
right_vec: Vec<DensePolynomial>,
|
||||
left_vec: Vec<DensePolynomial>,
|
||||
right_vec: Vec<DensePolynomial>,
|
||||
}
|
||||
|
||||
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::<Vec<Scalar>>();
|
||||
let outp_right = (len / 4..len / 2)
|
||||
.map(|i| inp_left[i] * inp_right[i])
|
||||
.collect::<Vec<Scalar>>();
|
||||
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::<Vec<Scalar>>();
|
||||
let outp_right = (len / 4..len / 2)
|
||||
.map(|i| inp_left[i] * inp_right[i])
|
||||
.collect::<Vec<Scalar>>();
|
||||
|
||||
(
|
||||
DensePolynomial::new(outp_left),
|
||||
DensePolynomial::new(outp_right),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new(poly: &DensePolynomial) -> Self {
|
||||
let mut left_vec: Vec<DensePolynomial> = Vec::new();
|
||||
let mut right_vec: Vec<DensePolynomial> = 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);
|
||||
(
|
||||
DensePolynomial::new(outp_left),
|
||||
DensePolynomial::new(outp_right),
|
||||
)
|
||||
}
|
||||
|
||||
ProductCircuit {
|
||||
left_vec,
|
||||
right_vec,
|
||||
}
|
||||
}
|
||||
pub fn new(poly: &DensePolynomial) -> Self {
|
||||
let mut left_vec: Vec<DensePolynomial> = Vec::new();
|
||||
let mut right_vec: Vec<DensePolynomial> = Vec::new();
|
||||
|
||||
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]
|
||||
}
|
||||
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 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 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 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<Scalar>,
|
||||
pub proof: SumcheckInstanceProof,
|
||||
pub claims: Vec<Scalar>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl LayerProof {
|
||||
pub fn verify(
|
||||
&self,
|
||||
claim: Scalar,
|
||||
num_rounds: usize,
|
||||
degree_bound: usize,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (Scalar, Vec<Scalar>) {
|
||||
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<Scalar>) {
|
||||
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<Scalar>,
|
||||
pub claims_prod_right: Vec<Scalar>,
|
||||
pub proof: SumcheckInstanceProof,
|
||||
pub claims_prod_left: Vec<Scalar>,
|
||||
pub claims_prod_right: Vec<Scalar>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl LayerProofBatched {
|
||||
pub fn verify(
|
||||
&self,
|
||||
claim: Scalar,
|
||||
num_rounds: usize,
|
||||
degree_bound: usize,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (Scalar, Vec<Scalar>) {
|
||||
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<Scalar>) {
|
||||
self.proof
|
||||
.verify(claim, num_rounds, degree_bound, transcript)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
|
||||
pub struct ProductCircuitEvalProof {
|
||||
proof: Vec<LayerProof>,
|
||||
proof: Vec<LayerProof>,
|
||||
}
|
||||
|
||||
#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
|
||||
pub struct ProductCircuitEvalProofBatched {
|
||||
proof: Vec<LayerProofBatched>,
|
||||
claims_dotp: (Vec<Scalar>, Vec<Scalar>, Vec<Scalar>),
|
||||
proof: Vec<LayerProofBatched>,
|
||||
claims_dotp: (Vec<Scalar>, Vec<Scalar>, Vec<Scalar>),
|
||||
}
|
||||
|
||||
impl ProductCircuitEvalProof {
|
||||
#![allow(dead_code)]
|
||||
pub fn prove(
|
||||
circuit: &mut ProductCircuit,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (Self, Scalar, Vec<Scalar>) {
|
||||
let mut proof: Vec<LayerProof> = Vec::new();
|
||||
let num_layers = circuit.left_vec.len();
|
||||
#![allow(dead_code)]
|
||||
pub fn prove(
|
||||
circuit: &mut ProductCircuit,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (Self, Scalar, Vec<Scalar>) {
|
||||
let mut proof: Vec<LayerProof> = 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 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 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,
|
||||
);
|
||||
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]);
|
||||
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]);
|
||||
// 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;
|
||||
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(),
|
||||
});
|
||||
proof.push(LayerProof {
|
||||
proof: proof_prod,
|
||||
claims: claims_prod[0..claims_prod.len() - 1].to_vec(),
|
||||
});
|
||||
}
|
||||
|
||||
(ProductCircuitEvalProof { proof }, claim, rand)
|
||||
}
|
||||
|
||||
(ProductCircuitEvalProof { proof }, claim, rand)
|
||||
}
|
||||
pub fn verify(
|
||||
&self,
|
||||
eval: Scalar,
|
||||
len: usize,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (Scalar, Vec<Scalar>) {
|
||||
let num_layers = len.log_2();
|
||||
let mut claim = eval;
|
||||
let mut rand: Vec<Scalar> = 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);
|
||||
|
||||
pub fn verify(
|
||||
&self,
|
||||
eval: Scalar,
|
||||
len: usize,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (Scalar, Vec<Scalar>) {
|
||||
let num_layers = len.log_2();
|
||||
let mut claim = eval;
|
||||
let mut rand: Vec<Scalar> = 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]);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
(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<Scalar>) {
|
||||
assert!(!prod_circuit_vec.is_empty());
|
||||
pub fn prove(
|
||||
prod_circuit_vec: &mut Vec<&mut ProductCircuit>,
|
||||
dotp_circuit_vec: &mut Vec<&mut DotProductCircuit>,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (Self, Vec<Scalar>) {
|
||||
assert!(!prod_circuit_vec.is_empty());
|
||||
|
||||
let mut claims_dotp_final = (Vec::new(), Vec::new(), Vec::new());
|
||||
let mut claims_dotp_final = (Vec::new(), Vec::new(), Vec::new());
|
||||
|
||||
let mut proof_layers: Vec<LayerProofBatched> = 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::<Vec<Scalar>>();
|
||||
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 proof_layers: Vec<LayerProofBatched> = 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::<Vec<Scalar>>();
|
||||
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 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 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,
|
||||
);
|
||||
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());
|
||||
// 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::<Vec<Scalar>>();
|
||||
|
||||
let mut ext = vec![r_layer];
|
||||
ext.extend(rand_prod);
|
||||
rand = ext;
|
||||
|
||||
proof_layers.push(LayerProofBatched {
|
||||
proof,
|
||||
claims_prod_left,
|
||||
claims_prod_right,
|
||||
});
|
||||
}
|
||||
|
||||
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::<Vec<Scalar>>();
|
||||
|
||||
let mut ext = vec![r_layer];
|
||||
ext.extend(rand_prod);
|
||||
rand = ext;
|
||||
|
||||
proof_layers.push(LayerProofBatched {
|
||||
proof,
|
||||
claims_prod_left,
|
||||
claims_prod_right,
|
||||
});
|
||||
(
|
||||
ProductCircuitEvalProofBatched {
|
||||
proof: proof_layers,
|
||||
claims_dotp: claims_dotp_final,
|
||||
},
|
||||
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<Scalar>, Vec<Scalar>, Vec<Scalar>) {
|
||||
let num_layers = len.log_2();
|
||||
let mut rand: Vec<Scalar> = Vec::new();
|
||||
//let mut num_rounds = 0;
|
||||
assert_eq!(self.proof.len(), num_layers);
|
||||
|
||||
pub fn verify(
|
||||
&self,
|
||||
claims_prod_vec: &[Scalar],
|
||||
claims_dotp_vec: &[Scalar],
|
||||
len: usize,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (Vec<Scalar>, Vec<Scalar>, Vec<Scalar>) {
|
||||
let num_layers = len.log_2();
|
||||
let mut rand: Vec<Scalar> = 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<Scalar> = Vec::new();
|
||||
for (num_rounds, i) in (0..num_layers).enumerate() {
|
||||
if i == num_layers - 1 {
|
||||
claims_to_verify.extend(claims_dotp_vec);
|
||||
}
|
||||
|
||||
let mut claims_to_verify = claims_prod_vec.to_owned();
|
||||
let mut claims_to_verify_dotp: Vec<Scalar> = 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 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();
|
||||
|
||||
// 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 (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());
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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]);
|
||||
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
(claims_to_verify, claims_to_verify_dotp, rand)
|
||||
}
|
||||
(claims_to_verify, claims_to_verify_dotp, rand)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
pub struct R1CSCommitmentGens {
|
||||
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 }
|
||||
}
|
||||
A: SparseMatPolynomial,
|
||||
B: SparseMatPolynomial,
|
||||
C: SparseMatPolynomial,
|
||||
}
|
||||
|
||||
pub struct R1CSCommitmentGens {
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
#[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()));
|
||||
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_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 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);
|
||||
// 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();
|
||||
// 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::<Vec<SparseMatEntry>>();
|
||||
let mat_B = (0..B.len())
|
||||
.map(|i| SparseMatEntry::new(B[i].0, B[i].1, B[i].2))
|
||||
.collect::<Vec<SparseMatEntry>>();
|
||||
let mat_C = (0..C.len())
|
||||
.map(|i| SparseMatEntry::new(C[i].0, C[i].1, C[i].2))
|
||||
.collect::<Vec<SparseMatEntry>>();
|
||||
let mat_A = (0..A.len())
|
||||
.map(|i| SparseMatEntry::new(A[i].0, A[i].1, A[i].2))
|
||||
.collect::<Vec<SparseMatEntry>>();
|
||||
let mat_B = (0..B.len())
|
||||
.map(|i| SparseMatEntry::new(B[i].0, B[i].1, B[i].2))
|
||||
.collect::<Vec<SparseMatEntry>>();
|
||||
let mat_C = (0..C.len())
|
||||
.map(|i| SparseMatEntry::new(C[i].0, C[i].1, C[i].2))
|
||||
.collect::<Vec<SparseMatEntry>>();
|
||||
|
||||
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);
|
||||
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<u8> {
|
||||
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<Scalar>, Vec<Scalar>) {
|
||||
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<Scalar> = (0..size_z)
|
||||
.map(|_i| Scalar::rand(&mut rng))
|
||||
.collect::<Vec<Scalar>>();
|
||||
Z[num_vars] = Scalar::one(); // set the constant term to 1
|
||||
Z
|
||||
};
|
||||
|
||||
// three sparse matrices
|
||||
let mut A: Vec<SparseMatEntry> = Vec::new();
|
||||
let mut B: Vec<SparseMatEntry> = Vec::new();
|
||||
let mut C: Vec<SparseMatEntry> = 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(),
|
||||
));
|
||||
}
|
||||
R1CSInstance {
|
||||
num_cons,
|
||||
num_vars,
|
||||
num_inputs,
|
||||
A: poly_A,
|
||||
B: poly_B,
|
||||
C: poly_C,
|
||||
}
|
||||
}
|
||||
|
||||
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()));
|
||||
pub fn get_num_vars(&self) -> usize {
|
||||
self.num_vars
|
||||
}
|
||||
|
||||
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);
|
||||
pub fn get_num_cons(&self) -> usize {
|
||||
self.num_cons
|
||||
}
|
||||
|
||||
let inst = R1CSInstance {
|
||||
num_cons,
|
||||
num_vars,
|
||||
num_inputs,
|
||||
A: poly_A,
|
||||
B: poly_B,
|
||||
C: poly_C,
|
||||
};
|
||||
pub fn get_num_inputs(&self) -> usize {
|
||||
self.num_inputs
|
||||
}
|
||||
|
||||
assert!(inst.is_sat(&Z[..num_vars], &Z[num_vars + 1..]));
|
||||
pub fn get_digest(&self) -> Vec<u8> {
|
||||
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()
|
||||
}
|
||||
|
||||
(inst, Z[..num_vars].to_vec(), Z[num_vars + 1..].to_vec())
|
||||
}
|
||||
pub fn produce_synthetic_r1cs(
|
||||
num_cons: usize,
|
||||
num_vars: usize,
|
||||
num_inputs: usize,
|
||||
) -> (R1CSInstance, Vec<Scalar>, Vec<Scalar>) {
|
||||
Timer::print(&format!("number_of_constraints {}", num_cons));
|
||||
Timer::print(&format!("number_of_variables {}", num_vars));
|
||||
Timer::print(&format!("number_of_inputs {}", num_inputs));
|
||||
|
||||
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 mut rng = ark_std::rand::thread_rng();
|
||||
|
||||
let z = {
|
||||
let mut z = vars.to_vec();
|
||||
z.extend(&vec![Scalar::one()]);
|
||||
z.extend(input);
|
||||
z
|
||||
};
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
// num_inputs + 1 <= num_vars
|
||||
assert!(num_inputs < num_vars);
|
||||
|
||||
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();
|
||||
// z is organized as [vars,1,io]
|
||||
let size_z = num_vars + num_inputs + 1;
|
||||
|
||||
res == 0
|
||||
}
|
||||
// produce a random satisfying assignment
|
||||
let Z = {
|
||||
let mut Z: Vec<Scalar> = (0..size_z)
|
||||
.map(|_i| Scalar::rand(&mut rng))
|
||||
.collect::<Vec<Scalar>>();
|
||||
Z[num_vars] = Scalar::one(); // set the constant term to 1
|
||||
Z
|
||||
};
|
||||
|
||||
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)),
|
||||
)
|
||||
}
|
||||
// three sparse matrices
|
||||
let mut A: Vec<SparseMatEntry> = Vec::new();
|
||||
let mut B: Vec<SparseMatEntry> = Vec::new();
|
||||
let mut C: Vec<SparseMatEntry> = 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];
|
||||
|
||||
pub fn compute_eval_table_sparse(
|
||||
&self,
|
||||
num_rows: usize,
|
||||
num_cols: usize,
|
||||
evals: &[Scalar],
|
||||
) -> (Vec<Scalar>, Vec<Scalar>, Vec<Scalar>) {
|
||||
assert_eq!(num_rows, self.num_cons);
|
||||
assert!(num_cols > self.num_vars);
|
||||
let C_idx = (i + 3) % size_z;
|
||||
let C_val = Z[C_idx];
|
||||
|
||||
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);
|
||||
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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
(evals_A, evals_B, evals_C)
|
||||
}
|
||||
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()));
|
||||
|
||||
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])
|
||||
}
|
||||
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);
|
||||
|
||||
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 inst = R1CSInstance {
|
||||
num_cons,
|
||||
num_vars,
|
||||
num_inputs,
|
||||
A: poly_A,
|
||||
B: poly_B,
|
||||
C: poly_C,
|
||||
};
|
||||
|
||||
let r1cs_decomm = R1CSDecommitment { dense };
|
||||
assert!(inst.is_sat(&Z[..num_vars], &Z[num_vars + 1..]));
|
||||
|
||||
(r1cs_comm, r1cs_decomm)
|
||||
}
|
||||
(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<Scalar>, Vec<Scalar>, Vec<Scalar>) {
|
||||
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();
|
||||
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 }
|
||||
}
|
||||
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 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
820
src/r1csproof.rs
820
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<I>,
|
||||
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<I>,
|
||||
rx: Vec<Scalar>,
|
||||
ry: Vec<Scalar>,
|
||||
// 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<I>,
|
||||
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<I>,
|
||||
rx: Vec<Scalar>,
|
||||
ry: Vec<Scalar>,
|
||||
// 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);
|
||||
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,
|
||||
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<Scalar>, Vec<Scalar>) {
|
||||
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) };
|
||||
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<Scalar>, Vec<Scalar>) {
|
||||
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,
|
||||
);
|
||||
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)
|
||||
}
|
||||
(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<Scalar>, Vec<Scalar>) {
|
||||
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,
|
||||
);
|
||||
fn prove_phase_two(
|
||||
num_rounds: usize,
|
||||
claim: &Scalar,
|
||||
evals_z: &mut DensePolynomial,
|
||||
evals_ABC: &mut DensePolynomial,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (SumcheckInstanceProof, Vec<Scalar>, Vec<Scalar>) {
|
||||
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)
|
||||
}
|
||||
(sc_proof_phase_two, r, claims)
|
||||
}
|
||||
|
||||
fn protocol_name() -> &'static [u8] {
|
||||
b"R1CS proof"
|
||||
}
|
||||
fn protocol_name() -> &'static [u8] {
|
||||
b"R1CS proof"
|
||||
}
|
||||
|
||||
pub fn prove(
|
||||
inst: &R1CSInstance,
|
||||
vars: Vec<Scalar>,
|
||||
input: &[Scalar],
|
||||
gens: &R1CSGens,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (R1CSProof, Vec<Scalar>, Vec<Scalar>) {
|
||||
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());
|
||||
pub fn prove(
|
||||
inst: &R1CSInstance,
|
||||
vars: Vec<Scalar>,
|
||||
input: &[Scalar],
|
||||
gens: &R1CSGens,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (R1CSProof, Vec<Scalar>, Vec<Scalar>) {
|
||||
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());
|
||||
// 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::<I>::commit(&gens.gens_pc.ck, &poly_vars);
|
||||
comm.append_to_poseidon(transcript);
|
||||
timer_commit.stop();
|
||||
let timer_commit = Timer::new("polycommit");
|
||||
// commitment to the satisfying witness polynomial
|
||||
let comm = MultilinearPC::<I>::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);
|
||||
let c = transcript.challenge_scalar();
|
||||
transcript.new_from_state(&c);
|
||||
|
||||
transcript.append_scalar_vector(input);
|
||||
transcript.append_scalar_vector(input);
|
||||
|
||||
let timer_sc_proof_phase1 = Timer::new("prove_sc_phase_one");
|
||||
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
|
||||
};
|
||||
// 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);
|
||||
// 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 (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;
|
||||
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;
|
||||
// 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 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);
|
||||
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::<Vec<Scalar>>()
|
||||
};
|
||||
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::<Vec<Scalar>>()
|
||||
};
|
||||
|
||||
// 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();
|
||||
// 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::<I>::open(&gens.gens_pc.ck, &poly_vars, &dummy);
|
||||
println!(
|
||||
"proof size (no of quotients): {:?}",
|
||||
proof_eval_vars_at_ry.proofs.len()
|
||||
);
|
||||
timmer_opening.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::<I>::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();
|
||||
let timer_polyeval = Timer::new("polyeval");
|
||||
let eval_vars_at_ry = poly_vars.evaluate(&ry[1..]);
|
||||
timer_polyeval.stop();
|
||||
|
||||
timer_prove.stop();
|
||||
timer_prove.stop();
|
||||
|
||||
let c = transcript.challenge_scalar();
|
||||
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,
|
||||
)
|
||||
}
|
||||
(
|
||||
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);
|
||||
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 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::<Vec<SparsePolyEntry>>(),
|
||||
);
|
||||
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::<Vec<SparsePolyEntry>>(),
|
||||
);
|
||||
|
||||
let n = num_vars;
|
||||
let input_as_sparse_poly =
|
||||
SparsePolynomial::new(n.log_2() as usize, input_as_sparse_poly_entries);
|
||||
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 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 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 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::<P>::setup(circuit.clone(), &mut rng).unwrap();
|
||||
let ds = start.elapsed().as_millis();
|
||||
let start = Instant::now();
|
||||
let (pk, vk) = Groth16::<P>::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::<P>::prove(&pk, circuit, &mut rng).unwrap();
|
||||
let dp2 = start.elapsed().as_millis();
|
||||
prove_outer.stop();
|
||||
let prove_outer = Timer::new("proveoutercircuit");
|
||||
let start = Instant::now();
|
||||
let proof = Groth16::<P>::prove(&pk, circuit, &mut rng).unwrap();
|
||||
let dp2 = start.elapsed().as_millis();
|
||||
prove_outer.stop();
|
||||
|
||||
let start = Instant::now();
|
||||
let is_verified = Groth16::<P>::verify(&vk, &[], &proof).unwrap();
|
||||
assert!(is_verified);
|
||||
let start = Instant::now();
|
||||
let is_verified = Groth16::<P>::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();
|
||||
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::<I>::check(
|
||||
&gens.gens_pc.vk,
|
||||
&self.comm,
|
||||
&dummy,
|
||||
self.eval_vars_at_ry,
|
||||
&self.proof_eval_vars_at_ry,
|
||||
);
|
||||
// Verifies the proof of opening against the result of evaluating the
|
||||
// witness polynomial at point ry.
|
||||
let res = MultilinearPC::<I>::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();
|
||||
timer_verification.stop();
|
||||
assert!(res == true);
|
||||
let dv = start.elapsed().as_millis();
|
||||
|
||||
Ok((ds, dp1 + dp2, dv))
|
||||
}
|
||||
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<usize, ProofVerifyError> {
|
||||
self.comm.append_to_poseidon(transcript);
|
||||
// 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<usize, ProofVerifyError> {
|
||||
self.comm.append_to_poseidon(transcript);
|
||||
|
||||
let c = transcript.challenge_scalar();
|
||||
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::<Vec<SparsePolyEntry>>(),
|
||||
);
|
||||
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::<Vec<SparsePolyEntry>>(),
|
||||
);
|
||||
|
||||
let n = num_vars;
|
||||
let input_as_sparse_poly =
|
||||
SparsePolynomial::new(n.log_2() as usize, input_as_sparse_poly_entries);
|
||||
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 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 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_inner = verify_constraints_inner(circuit.clone(), &num_cons);
|
||||
|
||||
let nc_outer = verify_constraints_outer(circuit, &num_cons);
|
||||
Ok(nc_inner + nc_outer)
|
||||
}
|
||||
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::<Fq>::new_ref();
|
||||
circuit.generate_constraints(cs.clone()).unwrap();
|
||||
assert!(cs.is_satisfied().unwrap());
|
||||
cs.num_constraints()
|
||||
let cs = ConstraintSystem::<Fq>::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::<Fr>::new_ref();
|
||||
circuit
|
||||
.inner_circuit
|
||||
.generate_constraints(cs.clone())
|
||||
.unwrap();
|
||||
assert!(cs.is_satisfied().unwrap());
|
||||
cs.num_constraints()
|
||||
let cs = ConstraintSystem::<Fr>::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 crate::parameters::poseidon_params;
|
||||
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
use ark_std::UniformRand;
|
||||
use ark_std::UniformRand;
|
||||
|
||||
fn produce_tiny_r1cs() -> (R1CSInstance, Vec<Scalar>, Vec<Scalar>) {
|
||||
// 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;
|
||||
fn produce_tiny_r1cs() -> (R1CSInstance, Vec<Scalar>, Vec<Scalar>) {
|
||||
// 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();
|
||||
// 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));
|
||||
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));
|
||||
// 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);
|
||||
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
|
||||
// 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 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;
|
||||
let mut input = vec![Scalar::zero(); num_inputs];
|
||||
input[0] = i0;
|
||||
input[1] = i1;
|
||||
|
||||
(inst, vars, input)
|
||||
}
|
||||
(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_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]
|
||||
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);
|
||||
#[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 gens = R1CSGens::new(b"test-m", num_cons, num_vars);
|
||||
|
||||
let params = poseidon_params();
|
||||
// let mut random_tape = RandomTape::new(b"proof");
|
||||
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 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 inst_evals = inst.evaluate(&rx, &ry);
|
||||
|
||||
let mut verifier_transcript = PoseidonTranscript::new(¶ms);
|
||||
let mut verifier_transcript = PoseidonTranscript::new(¶ms);
|
||||
|
||||
// if you want to check the test fails
|
||||
// input[0] = Scalar::zero();
|
||||
// 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());
|
||||
}
|
||||
assert!(proof
|
||||
.verify_groth16(
|
||||
inst.get_num_vars(),
|
||||
inst.get_num_cons(),
|
||||
&input,
|
||||
&inst_evals,
|
||||
&mut verifier_transcript,
|
||||
&gens,
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Scalar> {
|
||||
self.tape.challenge_vector(label, len)
|
||||
}
|
||||
pub fn random_vector(&mut self, label: &'static [u8], len: usize) -> Vec<Scalar> {
|
||||
self.tape.challenge_vector(label, len)
|
||||
}
|
||||
}
|
||||
|
||||
2913
src/sparse_mlpoly.rs
2913
src/sparse_mlpoly.rs
File diff suppressed because it is too large
Load Diff
754
src/sumcheck.rs
754
src/sumcheck.rs
@@ -15,49 +15,49 @@ use itertools::izip;
|
||||
|
||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Debug)]
|
||||
pub struct SumcheckInstanceProof {
|
||||
pub polys: Vec<UniPoly>,
|
||||
pub polys: Vec<UniPoly>,
|
||||
}
|
||||
|
||||
impl SumcheckInstanceProof {
|
||||
pub fn new(polys: Vec<UniPoly>) -> SumcheckInstanceProof {
|
||||
SumcheckInstanceProof { polys }
|
||||
}
|
||||
|
||||
pub fn verify(
|
||||
&self,
|
||||
claim: Scalar,
|
||||
num_rounds: usize,
|
||||
degree_bound: usize,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> Result<(Scalar, Vec<Scalar>), ProofVerifyError> {
|
||||
let mut e = claim;
|
||||
let mut r: Vec<Scalar> = 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 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);
|
||||
|
||||
// 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();
|
||||
|
||||
r.push(r_i);
|
||||
|
||||
// evaluate the claimed degree-ell polynomial at r_i
|
||||
e = poly.evaluate(&r_i);
|
||||
pub fn new(polys: Vec<UniPoly>) -> SumcheckInstanceProof {
|
||||
SumcheckInstanceProof { polys }
|
||||
}
|
||||
|
||||
Ok((e, r))
|
||||
}
|
||||
pub fn verify(
|
||||
&self,
|
||||
claim: Scalar,
|
||||
num_rounds: usize,
|
||||
degree_bound: usize,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> Result<(Scalar, Vec<Scalar>), ProofVerifyError> {
|
||||
let mut e = claim;
|
||||
let mut r: Vec<Scalar> = 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 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);
|
||||
|
||||
// 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();
|
||||
|
||||
r.push(r_i);
|
||||
|
||||
// evaluate the claimed degree-ell polynomial at r_i
|
||||
e = poly.evaluate(&r_i);
|
||||
}
|
||||
|
||||
Ok((e, r))
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(CanonicalSerialize, CanonicalDeserialize, Debug)]
|
||||
@@ -180,379 +180,381 @@ impl SumcheckInstanceProof {
|
||||
// }
|
||||
|
||||
impl SumcheckInstanceProof {
|
||||
pub fn prove_cubic_with_additive_term<F>(
|
||||
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<Scalar>, Vec<Scalar>)
|
||||
where
|
||||
F: Fn(&Scalar, &Scalar, &Scalar, &Scalar) -> Scalar,
|
||||
{
|
||||
let mut e = *claim;
|
||||
let mut r: Vec<Scalar> = Vec::new();
|
||||
let mut cubic_polys: Vec<UniPoly> = 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();
|
||||
pub fn prove_cubic_with_additive_term<F>(
|
||||
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<Scalar>, Vec<Scalar>)
|
||||
where
|
||||
F: Fn(&Scalar, &Scalar, &Scalar, &Scalar) -> Scalar,
|
||||
{
|
||||
let mut e = *claim;
|
||||
let mut r: Vec<Scalar> = Vec::new();
|
||||
let mut cubic_polys: Vec<UniPoly> = 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]);
|
||||
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 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_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 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,
|
||||
);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
// 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);
|
||||
// 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);
|
||||
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<F>(
|
||||
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<Scalar>, Vec<Scalar>)
|
||||
where
|
||||
F: Fn(&Scalar, &Scalar, &Scalar) -> Scalar,
|
||||
{
|
||||
let mut e = *claim;
|
||||
let mut r: Vec<Scalar> = Vec::new();
|
||||
let mut cubic_polys: Vec<UniPoly> = 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();
|
||||
pub fn prove_cubic<F>(
|
||||
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<Scalar>, Vec<Scalar>)
|
||||
where
|
||||
F: Fn(&Scalar, &Scalar, &Scalar) -> Scalar,
|
||||
{
|
||||
let mut e = *claim;
|
||||
let mut r: Vec<Scalar> = Vec::new();
|
||||
let mut cubic_polys: Vec<UniPoly> = 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]);
|
||||
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 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 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,
|
||||
);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
// 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]],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn prove_cubic_batched<F>(
|
||||
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<Scalar>,
|
||||
(Vec<Scalar>, Vec<Scalar>, Scalar),
|
||||
(Vec<Scalar>, Vec<Scalar>, Vec<Scalar>),
|
||||
)
|
||||
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<Scalar> = Vec::new();
|
||||
let mut cubic_polys: Vec<UniPoly> = 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,
|
||||
);
|
||||
//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);
|
||||
}
|
||||
|
||||
evals.push((eval_point_0, eval_point_2, eval_point_3));
|
||||
}
|
||||
(
|
||||
SumcheckInstanceProof::new(cubic_polys),
|
||||
r,
|
||||
vec![poly_A[0], poly_B[0], poly_C[0]],
|
||||
)
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
pub fn prove_cubic_batched<F>(
|
||||
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<Scalar>,
|
||||
(Vec<Scalar>, Vec<Scalar>, Scalar),
|
||||
(Vec<Scalar>, Vec<Scalar>, Vec<Scalar>),
|
||||
)
|
||||
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<Scalar> = Vec::new();
|
||||
let mut cubic_polys: Vec<UniPoly> = 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));
|
||||
}
|
||||
|
||||
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 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 evals = vec![
|
||||
evals_combined_0,
|
||||
e - evals_combined_0,
|
||||
evals_combined_2,
|
||||
evals_combined_3,
|
||||
];
|
||||
let poly = UniPoly::from_evals(&evals);
|
||||
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);
|
||||
|
||||
// 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);
|
||||
(
|
||||
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]);
|
||||
pub fn prove_quad<F>(
|
||||
claim: &Scalar,
|
||||
num_rounds: usize,
|
||||
poly_A: &mut DensePolynomial,
|
||||
poly_B: &mut DensePolynomial,
|
||||
comb_func: F,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (Self, Vec<Scalar>, Vec<Scalar>)
|
||||
where
|
||||
F: Fn(&Scalar, &Scalar) -> Scalar,
|
||||
{
|
||||
let mut e = *claim;
|
||||
let mut r: Vec<Scalar> = Vec::new();
|
||||
let mut quad_polys: Vec<UniPoly> = Vec::new();
|
||||
|
||||
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);
|
||||
for _j in 0..num_rounds {
|
||||
let mut eval_point_0 = Scalar::zero();
|
||||
let mut eval_point_2 = Scalar::zero();
|
||||
|
||||
(
|
||||
SumcheckInstanceProof::new(cubic_polys),
|
||||
r,
|
||||
claims_prod,
|
||||
claims_dotp,
|
||||
)
|
||||
}
|
||||
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]);
|
||||
|
||||
pub fn prove_quad<F>(
|
||||
claim: &Scalar,
|
||||
num_rounds: usize,
|
||||
poly_A: &mut DensePolynomial,
|
||||
poly_B: &mut DensePolynomial,
|
||||
comb_func: F,
|
||||
transcript: &mut PoseidonTranscript,
|
||||
) -> (Self, Vec<Scalar>, Vec<Scalar>)
|
||||
where
|
||||
F: Fn(&Scalar, &Scalar) -> Scalar,
|
||||
{
|
||||
let mut e = *claim;
|
||||
let mut r: Vec<Scalar> = Vec::new();
|
||||
let mut quad_polys: Vec<UniPoly> = Vec::new();
|
||||
// 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);
|
||||
}
|
||||
|
||||
for _j in 0..num_rounds {
|
||||
let mut eval_point_0 = Scalar::zero();
|
||||
let mut eval_point_2 = Scalar::zero();
|
||||
let evals = vec![eval_point_0, e - eval_point_0, eval_point_2];
|
||||
let poly = UniPoly::from_evals(&evals);
|
||||
|
||||
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]);
|
||||
// append the prover's message to the transcript
|
||||
poly.append_to_poseidon(transcript);
|
||||
|
||||
// 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);
|
||||
}
|
||||
//derive the verifier's challenge for the next round
|
||||
let r_j = transcript.challenge_scalar();
|
||||
r.push(r_j);
|
||||
|
||||
let evals = vec![eval_point_0, e - eval_point_0, eval_point_2];
|
||||
let poly = UniPoly::from_evals(&evals);
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
110
src/timer.rs
110
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) {}
|
||||
}
|
||||
|
||||
@@ -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<Scalar>;
|
||||
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<Scalar>;
|
||||
}
|
||||
|
||||
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<Scalar> {
|
||||
(0..len)
|
||||
.map(|_i| self.challenge_scalar(label))
|
||||
.collect::<Vec<Scalar>>()
|
||||
}
|
||||
fn challenge_vector(&mut self, label: &'static [u8], len: usize) -> Vec<Scalar> {
|
||||
(0..len)
|
||||
.map(|_i| self.challenge_scalar(label))
|
||||
.collect::<Vec<Scalar>>()
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
281
src/unipoly.rs
281
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<Scalar>,
|
||||
// pub coeffs_fq: Vec<Fq>,
|
||||
pub coeffs: Vec<Scalar>,
|
||||
// pub coeffs_fq: Vec<Fq>,
|
||||
}
|
||||
|
||||
// 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<Scalar>,
|
||||
pub coeffs_except_linear_term: Vec<Scalar>,
|
||||
}
|
||||
|
||||
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();
|
||||
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 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]
|
||||
};
|
||||
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<Scalar> {
|
||||
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;
|
||||
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 commit(&self, gens: &MultiCommitGens, blind: &Scalar) -> GroupElement {
|
||||
self.coeffs.commit(blind, gens)
|
||||
}
|
||||
pub fn as_vec(&self) -> Vec<Scalar> {
|
||||
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;
|
||||
}
|
||||
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 ark_ff::One;
|
||||
|
||||
use super::*;
|
||||
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);
|
||||
#[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));
|
||||
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 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);
|
||||
|
||||
#[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));
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user