1
0
Fork 0
mirror of synced 2025-09-22 11:47:57 +00:00

add compressed proof verification, upgrade nova to latest

This commit is contained in:
schaeff 2023-04-21 22:38:23 +02:00
parent 8015912eab
commit 46685b1076
12 changed files with 225 additions and 34 deletions

1
.gitignore vendored
View file

@ -17,6 +17,7 @@ universal_setup.dat
witness
witness.json
nova.params
running_instance.json
# ZoKrates source files at the root of the repository
/*.zok

25
Cargo.lock generated
View file

@ -1946,18 +1946,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "merlin"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d"
dependencies = [
"byteorder",
"keccak",
"rand_core 0.6.4",
"zeroize",
]
[[package]]
name = "miniz_oxide"
version = "0.6.2"
@ -1995,9 +1983,9 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nova-snark"
version = "0.13.0"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77651083c7aecec78b063779fe6271f5f6cf98252f4b584f7f58010dbca7c0ca"
checksum = "b09c8cf02c93500dee9244cd73547f20d133ca6d3fbbe9eae0205e5b2db05f36"
dependencies = [
"bellperson",
"bincode 1.3.3",
@ -2010,13 +1998,12 @@ dependencies = [
"generic-array 0.14.7",
"itertools 0.9.0",
"lurk-pasta-msm",
"merlin",
"neptune",
"num-bigint 0.4.3",
"num-integer",
"num-traits 0.2.15",
"rand_chacha",
"rand_core 0.5.1",
"rand_core 0.6.4",
"rayon",
"serde",
"sha3 0.8.2",
@ -2481,12 +2468,6 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
[[package]]
name = "rand_core"
version = "0.6.4"

View file

@ -15,7 +15,7 @@ getrandom = { version = "0.2", features = ["js", "wasm-bindgen"] }
hex = "0.4.2"
pairing = "0.22"
ff = { version = "0.12.0", default-features = false }
nova-snark = { version = "0.13.0" }
nova-snark = { version = "0.20.3" }
zokrates_interpreter = { version = "0.1", path = "../zokrates_interpreter" }
serde = { version = "1.0", features = ["derive"] }

View file

@ -100,7 +100,10 @@ fn main() {
type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine<G2>;
type S1 = nova_snark::spartan::RelaxedR1CSSNARK<G1, EE1>;
type S2 = nova_snark::spartan::RelaxedR1CSSNARK<G2, EE2>;
let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &recursive_snark);
let (pk, vk) = CompressedSNARK::<_, _, _, _, S1, S2>::setup(&pp).unwrap();
let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &pk, &recursive_snark);
println!(
"CompressedSNARK::prove: {:?}, took {:?}",
res.is_ok(),
@ -112,7 +115,7 @@ fn main() {
// verify the compressed SNARK
println!("Verifying a CompressedSNARK...");
let start = Instant::now();
let res = compressed_snark.verify(&pp, num_steps, z0_primary, z0_secondary);
let res = compressed_snark.verify(&vk, num_steps, z0_primary, z0_secondary);
println!(
"CompressedSNARK::verify: {:?}, took {:?}",
res.is_ok(),

View file

@ -11,6 +11,7 @@ use nova_snark::traits::Group;
use nova_snark::CompressedSNARK as GCompressedSNARK;
pub use nova_snark::PublicParams as GPublicParams;
pub use nova_snark::RecursiveSNARK as GRecursiveSNARK;
use nova_snark::VerifierKey as GVerifierKey;
use serde::{Deserialize, Serialize};
use std::fmt;
use zokrates_ast::ir::*;
@ -116,8 +117,8 @@ pub fn verify<T: NovaField>(
#[derive(Serialize, Debug, Deserialize)]
pub struct RecursiveSNARKWithStepCount<'ast, T: NovaField> {
#[serde(bound = "T: NovaField")]
proof: RecursiveSNARK<'ast, T>,
steps: usize,
pub proof: RecursiveSNARK<'ast, T>,
pub steps: usize,
}
type EE1<T> = nova_snark::provider::ipa_pc::EvaluationEngine<G1<T>>;
@ -125,13 +126,34 @@ type EE2<T> = nova_snark::provider::ipa_pc::EvaluationEngine<G2<T>>;
type S1<T> = nova_snark::spartan::RelaxedR1CSSNARK<G1<T>, EE1<T>>;
type S2<T> = nova_snark::spartan::RelaxedR1CSSNARK<G2<T>, EE2<T>>;
type CompressedSNARK<'ast, T> = GCompressedSNARK<G1<T>, G2<T>, C1<'ast, T>, C2<T>, S1<T>, S2<T>>;
pub type CompressedSNARK<'ast, T> =
GCompressedSNARK<G1<T>, G2<T>, C1<'ast, T>, C2<T>, S1<T>, S2<T>>;
pub type VerifierKey<'ast, T> = GVerifierKey<G1<T>, G2<T>, C1<'ast, T>, C2<T>, S1<T>, S2<T>>;
pub fn compress<'ast, T: NovaField>(
public_parameters: &PublicParams<'ast, T>,
instance: RecursiveSNARKWithStepCount<'ast, T>,
) -> CompressedSNARK<'ast, T> {
CompressedSNARK::prove(public_parameters, &instance.proof).unwrap()
) -> (CompressedSNARK<'ast, T>, VerifierKey<'ast, T>) {
let (pk, vk) = CompressedSNARK::<'ast, T>::setup(public_parameters).unwrap();
(
CompressedSNARK::prove(public_parameters, &pk, &instance.proof).unwrap(),
vk,
)
}
pub fn verify_compressed<'ast, T: NovaField>(
proof: &CompressedSNARK<'ast, T>,
vk: &VerifierKey<'ast, T>,
arguments: Vec<T>,
step_count: usize,
) -> bool {
let z0_primary: Vec<_> = arguments.into_iter().map(|a| a.into_bellperson()).collect();
let z0_secondary = vec![<<T as Cycle>::Point as Group>::Base::one()];
proof
.verify(vk, step_count, z0_primary, z0_secondary)
.is_ok()
}
pub fn prove<'ast, T: NovaField>(

View file

@ -52,6 +52,12 @@ Once we're done, we compress the proof to a compressed snark:
zokrates nova compress
```
Finally, we can verify this proof
```
zokrates nova verify
```
### Limitations
- The step circuit must be compiled with `--curve pallas`

View file

@ -39,6 +39,16 @@ pub fn subcommand() -> App<'static, 'static> {
.required(false)
.default_value(cli_constants::JSON_PROOF_PATH),
)
.arg(
Arg::with_name("verification-key-path")
.short("v")
.long("verification-key-path")
.help("Path of the generated verification key file")
.value_name("FILE")
.takes_value(true)
.required(false)
.default_value(cli_constants::VERIFICATION_KEY_DEFAULT_PATH),
)
}
pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
@ -66,8 +76,9 @@ fn cli_nova_compress<T: NovaField>(
.map_err(|why| format!("Could not deserialize {}: {}", params_path.display(), why))?;
let proof_path = Path::new(sub_matches.value_of("proof-path").unwrap());
let verification_key_path = Path::new(sub_matches.value_of("verification-key-path").unwrap());
let proof = nova::compress(&params, instance);
let (proof, vk) = nova::compress(&params, instance);
let proof_json = serde_json::to_string_pretty(&proof).unwrap();
@ -78,5 +89,32 @@ fn cli_nova_compress<T: NovaField>(
.write(proof_json.as_bytes())
.map_err(|why| format!("Could not write to {}: {}", proof_path.display(), why))?;
println!("Compressed SNARK written to '{}'", proof_path.display());
let verification_key_json = serde_json::to_string_pretty(&vk).unwrap();
let mut verification_key_file = File::create(verification_key_path).map_err(|why| {
format!(
"Could not create {}: {}",
verification_key_path.display(),
why
)
})?;
verification_key_file
.write(verification_key_json.as_bytes())
.map_err(|why| {
format!(
"Could not write to {}: {}",
verification_key_path.display(),
why
)
})?;
println!(
"Verification key written to '{}'",
verification_key_path.display()
);
Ok(())
}

View file

@ -3,6 +3,7 @@ use clap::{App, AppSettings, ArgMatches, SubCommand};
pub mod compress;
pub mod prove;
pub mod setup;
pub mod verify;
pub fn subcommand() -> App<'static, 'static> {
SubCommand::with_name("nova")
@ -12,6 +13,7 @@ pub fn subcommand() -> App<'static, 'static> {
setup::subcommand().display_order(1),
prove::subcommand().display_order(2),
compress::subcommand().display_order(3),
verify::subcommand().display_order(4),
])
}
@ -20,6 +22,7 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
("setup", Some(sub_matches)) => setup::exec(sub_matches),
("prove", Some(sub_matches)) => prove::exec(sub_matches),
("compress", Some(sub_matches)) => compress::exec(sub_matches),
("verify", Some(sub_matches)) => verify::exec(sub_matches),
_ => unreachable!(),
}
}

View file

@ -13,7 +13,7 @@ use zokrates_bellperson::nova::{self, NovaField};
pub fn subcommand() -> App<'static, 'static> {
SubCommand::with_name("prove")
.about("Proves a many steps of an incremental computation")
.about("Proves many steps of an incremental computation")
.arg(
Arg::with_name("init")
.long("init")
@ -99,7 +99,9 @@ fn cli_nova_prove_step<'ast, T: NovaField, I: Iterator<Item = ir::Statement<'ast
) -> Result<(), String> {
let proof_path = Path::new(sub_matches.value_of("instance-path").unwrap());
println!("Reading step program...");
let program = program.collect();
println!("Done");
let path = Path::new(sub_matches.value_of("abi-spec").unwrap());
let file =
@ -153,6 +155,8 @@ fn cli_nova_prove_step<'ast, T: NovaField, I: Iterator<Item = ir::Statement<'ast
serde_json::from_reader(BufReader::new(File::open(path).unwrap())).unwrap()
});
println!("Reading parameters...");
let params_path = Path::new(sub_matches.value_of("params-path").unwrap());
let params_file = File::open(params_path)
.map_err(|why| format!("Could not open {}: {}", params_path.display(), why))?;
@ -161,6 +165,8 @@ fn cli_nova_prove_step<'ast, T: NovaField, I: Iterator<Item = ir::Statement<'ast
let params = serde_cbor::from_reader(params_reader)
.map_err(|why| format!("Could not deserialize {}: {}", params_path.display(), why))?;
println!("Done");
let mut proof_file = File::create(proof_path)
.map_err(|why| format!("Could not create {}: {}", proof_path.display(), why))?;
@ -168,6 +174,8 @@ fn cli_nova_prove_step<'ast, T: NovaField, I: Iterator<Item = ir::Statement<'ast
let proof = nova::prove(&params, &program, init.clone(), from, steps)
.map_err(|e| format!("Error `{:#?}` during proving", e))?;
println!("Done");
let proof_json = serde_json::to_string_pretty(&proof).unwrap();
proof_file

View file

@ -0,0 +1,125 @@
use crate::cli_constants::{self, NOVA_PUBLIC_INIT};
use clap::{App, Arg, ArgMatches, SubCommand};
use serde_json::from_reader;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use zokrates_abi::{parse_value, Encode};
use zokrates_ast::typed::abi::Abi;
use zokrates_bellperson::nova::{
self, CompressedSNARK, NovaField, RecursiveSNARKWithStepCount, VerifierKey,
};
use zokrates_field::PallasField;
pub fn subcommand() -> App<'static, 'static> {
SubCommand::with_name("verify")
.about("Verifies a Nova compressed proof")
.arg(
Arg::with_name("init")
.long("init")
.help("Path to the initial value of the public input")
.takes_value(true)
.default_value(NOVA_PUBLIC_INIT),
)
.arg(
Arg::with_name("proof-path")
.short("j")
.long("proof-path")
.help("Path of the JSON compressed proof path")
.value_name("FILE")
.takes_value(true)
.required(false)
.default_value(cli_constants::JSON_PROOF_PATH),
)
.arg(
Arg::with_name("verification-key-path")
.short("v")
.long("verification-key-path")
.help("Path of the generated verification key file")
.value_name("FILE")
.takes_value(true)
.required(false)
.default_value(cli_constants::VERIFICATION_KEY_DEFAULT_PATH),
)
.arg(
Arg::with_name("instance-path")
.long("instance-path")
.help("Path of the JSON running instance file")
.value_name("FILE")
.takes_value(true)
.required(false)
.default_value(cli_constants::JSON_NOVA_RUNNING_INSTANCE),
)
.arg(
Arg::with_name("abi-spec")
.short("s")
.long("abi-spec")
.help("Path of the ABI specification")
.value_name("FILE")
.takes_value(true)
.required(false)
.default_value(cli_constants::ABI_SPEC_DEFAULT_PATH),
)
}
pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
let proof_path = sub_matches.value_of("proof-path").unwrap();
let proof_file =
File::open(proof_path).map_err(|why| format!("Could not open {}: {}", proof_path, why))?;
let proof_reader = BufReader::new(proof_file);
let verification_key_path = sub_matches.value_of("verification-key-path").unwrap();
let verification_key_file = File::open(verification_key_path)
.map_err(|why| format!("Could not open {}: {}", verification_key_path, why))?;
let verification_key_reader = BufReader::new(verification_key_file);
let proof: CompressedSNARK<PallasField> = serde_json::from_reader(proof_reader).unwrap();
let vk: VerifierKey<PallasField> = serde_json::from_reader(verification_key_reader).unwrap();
cli_nova_verify(proof, vk, sub_matches)
}
fn cli_nova_verify<'ast, T: NovaField>(
proof: CompressedSNARK<'ast, T>,
vk: VerifierKey<'ast, T>,
sub_matches: &ArgMatches,
) -> Result<(), String> {
let path = Path::new(sub_matches.value_of("abi-spec").unwrap());
let file =
File::open(path).map_err(|why| format!("Could not open {}: {}", path.display(), why))?;
let mut reader = BufReader::new(file);
let abi: Abi = from_reader(&mut reader).map_err(|why| why.to_string())?;
let signature = abi.signature();
let init_type = signature.inputs[0].clone();
let init = {
let path = Path::new(sub_matches.value_of("init").unwrap());
let file = File::open(path).unwrap();
let reader = BufReader::new(file);
parse_value(serde_json::from_reader(reader).unwrap(), init_type)
.unwrap()
.encode()
};
let instance_path = Path::new(sub_matches.value_of("instance-path").unwrap());
let instance: RecursiveSNARKWithStepCount<'ast, T> =
serde_json::from_reader(BufReader::new(File::open(instance_path).unwrap())).unwrap();
let steps = instance.steps;
if nova::verify_compressed(&proof, &vk, init, steps) {
println!("Compressed proof succesfully verified");
} else {
eprintln!("Compressed proof verification failed");
}
Ok(())
}

View file

@ -28,7 +28,7 @@ bellperson = { version = "0.24", default-features = false, optional = true }
pairing = { version = "0.22", default-features = false, optional = true }
ff = { version = "0.12.0", default-features = false, optional = true }
pasta_curves = { version = "0.5.2", features = ["repr-c", "serde"], package = "fil_pasta_curves", optional = true }
nova-snark = { version = "0.13.0", optional = true }
nova-snark = { version = "0.20.3", optional = true }
# ark
ark-ff = { version = "^0.3.0", default-features = false }

View file

@ -6,6 +6,7 @@
#[cfg(feature = "bellman_extensions")]
use bellman_ce::pairing::{ff::ScalarEngine, Engine};
use nova_snark::provider::pedersen::CommitmentEngine;
use num_bigint::BigUint;
use num_traits::{CheckedDiv, One, Zero};
use serde::{Deserialize, Serialize};
@ -27,7 +28,10 @@ pub trait Pow<RHS> {
#[cfg(feature = "bellperson_extensions")]
pub trait Cycle {
type Other: Field + BellpersonFieldExtensions + Cycle<Other = Self>;
type Point: Group<Base = <<Self::Other as Cycle>::Point as Group>::Scalar>;
type Point: Group<
Base = <<Self::Other as Cycle>::Point as Group>::Scalar,
CE = CommitmentEngine<Self::Point>,
>;
}
#[cfg(feature = "bellman_extensions")]