add compressed proof verification, upgrade nova to latest
This commit is contained in:
parent
8015912eab
commit
46685b1076
12 changed files with 225 additions and 34 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -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
25
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"] }
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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(¶ms, instance);
|
||||
let (proof, vk) = nova::compress(¶ms, 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(())
|
||||
}
|
||||
|
|
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(¶ms, &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
|
||||
|
|
125
zokrates_cli/src/ops/nova/verify.rs
Normal file
125
zokrates_cli/src/ops/nova/verify.rs
Normal 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(())
|
||||
}
|
|
@ -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 }
|
||||
|
|
|
@ -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")]
|
||||
|
|
Loading…
Reference in a new issue