From b06acb55e4c48454e1921949046bf06695716f0c Mon Sep 17 00:00:00 2001 From: dark64 Date: Thu, 17 Dec 2020 21:03:45 +0100 Subject: [PATCH 1/3] cli commands in separate files --- Cargo.lock | 2 + zokrates_cli/Cargo.toml | 2 + zokrates_cli/src/bin.rs | 1077 +----------------- zokrates_cli/src/constants.rs | 17 + zokrates_cli/src/helpers.rs | 3 +- zokrates_cli/src/ops/check.rs | 94 ++ zokrates_cli/src/ops/compile.rs | 162 +++ zokrates_cli/src/ops/compute_witness.rs | 189 +++ zokrates_cli/src/ops/export_verifier.rs | 119 ++ zokrates_cli/src/ops/generate_proof.rs | 180 +++ zokrates_cli/src/ops/mod.rs | 12 + zokrates_cli/src/ops/print_proof.rs | 68 ++ zokrates_cli/src/ops/setup.rs | 172 +++ zokrates_cli/src/ops/verify.rs | 133 +++ zokrates_core/src/proof_system/scheme/mod.rs | 4 + zokrates_js/Cargo.lock | 123 -- 16 files changed, 1193 insertions(+), 1164 deletions(-) create mode 100644 zokrates_cli/src/ops/check.rs create mode 100644 zokrates_cli/src/ops/compile.rs create mode 100644 zokrates_cli/src/ops/compute_witness.rs create mode 100644 zokrates_cli/src/ops/export_verifier.rs create mode 100644 zokrates_cli/src/ops/generate_proof.rs create mode 100644 zokrates_cli/src/ops/mod.rs create mode 100644 zokrates_cli/src/ops/print_proof.rs create mode 100644 zokrates_cli/src/ops/setup.rs create mode 100644 zokrates_cli/src/ops/verify.rs diff --git a/Cargo.lock b/Cargo.lock index b1c0fb00..5785b550 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2050,6 +2050,8 @@ dependencies = [ "dirs", "fs_extra", "glob 0.2.11", + "lazy_static", + "log", "regex", "serde_json", "tempdir", diff --git a/zokrates_cli/Cargo.toml b/zokrates_cli/Cargo.toml index 7942ac22..1385eb40 100644 --- a/zokrates_cli/Cargo.toml +++ b/zokrates_cli/Cargo.toml @@ -22,6 +22,8 @@ zokrates_core = { version = "0.5", path = "../zokrates_core", default-features = zokrates_fs_resolver = { version = "0.5", path = "../zokrates_fs_resolver"} serde_json = "1.0" dirs = "3.0.1" +log = "0.4.11" +lazy_static = "1.4.0" [dev-dependencies] glob = "0.2.11" diff --git a/zokrates_cli/src/bin.rs b/zokrates_cli/src/bin.rs index dd797d20..21c1221d 100644 --- a/zokrates_cli/src/bin.rs +++ b/zokrates_cli/src/bin.rs @@ -4,41 +4,15 @@ // @author Dennis Kuhnert // @date 2017 +#[macro_use] +extern crate lazy_static; + mod constants; mod helpers; +mod ops; -use constants::*; -use helpers::*; - -use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; -use serde_json::{from_reader, to_writer_pretty, Value}; -use std::convert::TryFrom; -use std::env; -use std::fs::File; -use std::io::{stdin, BufReader, BufWriter, Read, Write}; -use std::path::{Path, PathBuf}; -use std::string::String; -use zokrates_abi::Encode; -use zokrates_core::compile::{check, compile, CompilationArtifacts, CompileError}; -use zokrates_core::ir::{self, ProgEnum}; - -use zokrates_core::proof_system::{ - gm17::GM17, groth16::G16, pghr13::PGHR13, SolidityCompatibleField, -}; -use zokrates_core::proof_system::{SolidityAbi, SolidityCompatibleScheme}; -use zokrates_core::typed_absy::abi::Abi; -use zokrates_core::typed_absy::{types::Signature, Type}; -use zokrates_field::{Bls12_377Field, Bls12_381Field, Bn128Field, Bw6_761Field, Field}; -use zokrates_fs_resolver::FileSystemResolver; - -#[cfg(feature = "ark")] -use zokrates_core::proof_system::ark::Ark; -#[cfg(feature = "bellman")] -use zokrates_core::proof_system::bellman::Bellman; -#[cfg(feature = "libsnark")] -use zokrates_core::proof_system::libsnark::Libsnark; -#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] -use zokrates_core::proof_system::{Backend, Scheme}; +use clap::{App, AppSettings}; +use ops::*; fn main() { cli().unwrap_or_else(|e| { @@ -47,1021 +21,39 @@ fn main() { }) } -#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] -fn cli_generate_proof, B: Backend>( - program: ir::Prog, - sub_matches: &ArgMatches, -) -> Result<(), String> { - println!("Generating proof..."); - - // deserialize witness - let witness_path = Path::new(sub_matches.value_of("witness").unwrap()); - let witness_file = match File::open(&witness_path) { - Ok(file) => file, - Err(why) => panic!("Couldn't open {}: {}", witness_path.display(), why), - }; - - let witness = ir::Witness::read(witness_file) - .map_err(|why| format!("Could not load witness: {:?}", why))?; - - let pk_path = Path::new(sub_matches.value_of("proving-key-path").unwrap()); - let proof_path = Path::new(sub_matches.value_of("proof-path").unwrap()); - - let pk_file = File::open(&pk_path) - .map_err(|why| format!("Couldn't open {}: {}", pk_path.display(), why))?; - - let mut pk: Vec = Vec::new(); - let mut pk_reader = BufReader::new(pk_file); - pk_reader - .read_to_end(&mut pk) - .map_err(|why| format!("Couldn't read {}: {}", pk_path.display(), why))?; - - let proof = B::generate_proof(program, witness, pk); - let mut proof_file = File::create(proof_path).unwrap(); - - let proof = serde_json::to_string_pretty(&proof).unwrap(); - proof_file - .write(proof.as_bytes()) - .map_err(|why| format!("Couldn't write to {}: {}", proof_path.display(), why))?; - - println!("Proof:\n{}", format!("{}", proof)); - - Ok(()) -} - -fn cli_export_verifier>( - sub_matches: &ArgMatches, -) -> Result<(), String> { - println!("Exporting verifier..."); - - // read vk file - let input_path = Path::new(sub_matches.value_of("input").unwrap()); - let input_file = File::open(&input_path) - .map_err(|why| format!("Couldn't open {}: {}", input_path.display(), why))?; - let reader = BufReader::new(input_file); - - let vk = serde_json::from_reader(reader) - .map_err(|why| format!("Couldn't deserialize verifying key: {}", why))?; - - let abi = SolidityAbi::from(sub_matches.value_of("solidity-abi").unwrap())?; - - let verifier = S::export_solidity_verifier(vk, abi); - - //write output file - let output_path = Path::new(sub_matches.value_of("output").unwrap()); - let output_file = File::create(&output_path) - .map_err(|why| format!("Couldn't create {}: {}", output_path.display(), why))?; - - let mut writer = BufWriter::new(output_file); - - writer - .write_all(&verifier.as_bytes()) - .map_err(|_| "Failed writing output to file.".to_string())?; - - println!("Finished exporting verifier."); - Ok(()) -} - -#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] -fn cli_setup, B: Backend>( - program: ir::Prog, - sub_matches: &ArgMatches, -) -> Result<(), String> { - println!("Performing setup..."); - - // print deserialized flattened program - if !sub_matches.is_present("light") { - println!("{}", program); - } - - // get paths for proving and verification keys - let pk_path = Path::new(sub_matches.value_of("proving-key-path").unwrap()); - let vk_path = Path::new(sub_matches.value_of("verification-key-path").unwrap()); - - // run setup phase - let keypair = B::setup(program); - - // write verification key - let mut vk_file = File::create(vk_path) - .map_err(|why| format!("couldn't create {}: {}", vk_path.display(), why))?; - vk_file - .write( - serde_json::to_string_pretty(&keypair.vk) - .unwrap() - .as_bytes(), - ) - .map_err(|why| format!("couldn't write to {}: {}", vk_path.display(), why))?; - - // write proving key - let mut pk_file = File::create(pk_path) - .map_err(|why| format!("couldn't create {}: {}", pk_path.display(), why))?; - pk_file - .write(keypair.pk.as_ref()) - .map_err(|why| format!("couldn't write to {}: {}", pk_path.display(), why))?; - - println!("Setup completed."); - - Ok(()) -} - -fn cli_compute(ir_prog: ir::Prog, sub_matches: &ArgMatches) -> Result<(), String> { - println!("Computing witness..."); - - // print deserialized flattened program - if !sub_matches.is_present("light") { - println!("{}", ir_prog); - } - - let is_stdin = sub_matches.is_present("stdin"); - let is_abi = sub_matches.is_present("abi"); - - if !is_stdin && is_abi { - return Err("ABI input as inline argument is not supported. Please use `--stdin`.".into()); - } - - let signature = match is_abi { - true => { - let path = Path::new(sub_matches.value_of("abi_spec").unwrap()); - let file = File::open(&path) - .map_err(|why| format!("couldn't open {}: {}", path.display(), why))?; - let mut reader = BufReader::new(file); - - let abi: Abi = from_reader(&mut reader).map_err(|why| why.to_string())?; - - abi.signature() - } - false => Signature::new() - .inputs(vec![Type::FieldElement; ir_prog.main.arguments.len()]) - .outputs(vec![Type::FieldElement; ir_prog.main.returns.len()]), - }; - - use zokrates_abi::Inputs; - - // get arguments - let arguments = match is_stdin { - // take inline arguments - false => { - let arguments = sub_matches.values_of("arguments"); - arguments - .map(|a| { - a.map(|x| T::try_from_dec_str(x).map_err(|_| x.to_string())) - .collect::, _>>() - }) - .unwrap_or(Ok(vec![])) - .map(|v| Inputs::Raw(v)) - } - // take stdin arguments - true => { - let mut stdin = stdin(); - let mut input = String::new(); - - match is_abi { - true => match stdin.read_to_string(&mut input) { - Ok(_) => { - use zokrates_abi::parse_strict; - - parse_strict(&input, signature.inputs) - .map(|parsed| Inputs::Abi(parsed)) - .map_err(|why| why.to_string()) - } - Err(_) => Err(String::from("???")), - }, - false => match ir_prog.arguments_count() { - 0 => Ok(Inputs::Raw(vec![])), - _ => match stdin.read_to_string(&mut input) { - Ok(_) => { - input.retain(|x| x != '\n'); - input - .split(" ") - .map(|x| T::try_from_dec_str(x).map_err(|_| x.to_string())) - .collect::, _>>() - .map(|v| Inputs::Raw(v)) - } - Err(_) => Err(String::from("???")), - }, - }, - } - } - } - .map_err(|e| format!("Could not parse argument: {}", e))?; - - let interpreter = ir::Interpreter::default(); - - let witness = interpreter - .execute(&ir_prog, &arguments.encode()) - .map_err(|e| format!("Execution failed: {}", e))?; - - use zokrates_abi::Decode; - - let results_json_value: serde_json::Value = - zokrates_abi::CheckedValues::decode(witness.return_values(), signature.outputs).into(); - - println!("\nWitness: \n\n{}", results_json_value); - - // write witness to file - let output_path = Path::new(sub_matches.value_of("output").unwrap()); - let output_file = File::create(&output_path) - .map_err(|why| format!("couldn't create {}: {}", output_path.display(), why))?; - - let writer = BufWriter::new(output_file); - - witness - .write(writer) - .map_err(|why| format!("could not save witness: {:?}", why))?; - - Ok(()) -} - -fn cli_compile(sub_matches: &ArgMatches) -> Result<(), String> { - println!("Compiling {}\n", sub_matches.value_of("input").unwrap()); - let path = PathBuf::from(sub_matches.value_of("input").unwrap()); - - let light = sub_matches.occurrences_of("light") > 0; - - let bin_output_path = Path::new(sub_matches.value_of("output").unwrap()); - - let abi_spec_path = Path::new(sub_matches.value_of("abi_spec").unwrap()); - - let hr_output_path = bin_output_path.to_path_buf().with_extension("ztf"); - - let file = File::open(path.clone()) - .map_err(|why| format!("Couldn't open input file {}: {}", path.display(), why))?; - - let mut reader = BufReader::new(file); - let mut source = String::new(); - reader.read_to_string(&mut source).unwrap(); - - let fmt_error = |e: &CompileError| { - let file = e.file().canonicalize().unwrap(); - format!( - "{}:{}", - file.strip_prefix(std::env::current_dir().unwrap()) - .unwrap_or(file.as_path()) - .display(), - e.value() - ) - }; - - let resolver = - FileSystemResolver::with_stdlib_root(sub_matches.value_of("stdlib-path").unwrap()); - let artifacts: CompilationArtifacts = - compile(source, path, Some(&resolver)).map_err(|e| { - format!( - "Compilation failed:\n\n{}", - e.0.iter() - .map(|e| fmt_error(e)) - .collect::>() - .join("\n\n") - ) - })?; - - let program_flattened = artifacts.prog(); - - // number of constraints the flattened program will translate to. - let num_constraints = program_flattened.constraint_count(); - - // serialize flattened program and write to binary file - let bin_output_file = File::create(&bin_output_path) - .map_err(|why| format!("Couldn't create {}: {}", bin_output_path.display(), why))?; - - let mut writer = BufWriter::new(bin_output_file); - - program_flattened.serialize(&mut writer); - - // serialize ABI spec and write to JSON file - let abi_spec_file = File::create(&abi_spec_path) - .map_err(|why| format!("Couldn't create {}: {}", abi_spec_path.display(), why))?; - - let abi = artifacts.abi(); - - let mut writer = BufWriter::new(abi_spec_file); - - to_writer_pretty(&mut writer, &abi).map_err(|_| "Unable to write data to file.".to_string())?; - - if !light { - // write human-readable output file - let hr_output_file = File::create(&hr_output_path) - .map_err(|why| format!("Couldn't create {}: {}", hr_output_path.display(), why))?; - - let mut hrofb = BufWriter::new(hr_output_file); - write!(&mut hrofb, "{}\n", program_flattened) - .map_err(|_| "Unable to write data to file.".to_string())?; - hrofb - .flush() - .map_err(|_| "Unable to flush buffer.".to_string())?; - } - - if !light { - // debugging output - println!("Compiled program:\n{}", program_flattened); - } - - println!("Compiled code written to '{}'", bin_output_path.display()); - - if !light { - println!("Human readable code to '{}'", hr_output_path.display()); - } - - println!("Number of constraints: {}", num_constraints); - Ok(()) -} - -fn cli_check(sub_matches: &ArgMatches) -> Result<(), String> { - println!("Checking {}\n", sub_matches.value_of("input").unwrap()); - let path = PathBuf::from(sub_matches.value_of("input").unwrap()); - - let file = File::open(path.clone()) - .map_err(|why| format!("Couldn't open input file {}: {}", path.display(), why))?; - - let mut reader = BufReader::new(file); - let mut source = String::new(); - reader.read_to_string(&mut source).unwrap(); - - let fmt_error = |e: &CompileError| { - let file = e.file().canonicalize().unwrap(); - format!( - "{}:{}", - file.strip_prefix(std::env::current_dir().unwrap()) - .unwrap_or(file.as_path()) - .display(), - e.value() - ) - }; - - let resolver = - FileSystemResolver::with_stdlib_root(sub_matches.value_of("stdlib-path").unwrap()); - let _ = check::(source, path, Some(&resolver)).map_err(|e| { - format!( - "Check failed:\n\n{}", - e.0.iter() - .map(|e| fmt_error(e)) - .collect::>() - .join("\n\n") - ) - })?; - - println!("Program checked, no errors found."); - - Ok(()) -} - -#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] -fn cli_verify, B: Backend>( - sub_matches: &ArgMatches, -) -> Result<(), String> { - let vk_path = Path::new(sub_matches.value_of("verification-key-path").unwrap()); - let vk_file = File::open(&vk_path) - .map_err(|why| format!("Couldn't open {}: {}", vk_path.display(), why))?; - - let vk_reader = BufReader::new(vk_file); - let vk = serde_json::from_reader(vk_reader) - .map_err(|why| format!("Couldn't deserialize verification key: {}", why))?; - - let proof_path = Path::new(sub_matches.value_of("proof-path").unwrap()); - let proof_file = File::open(&proof_path) - .map_err(|why| format!("Couldn't open {}: {}", proof_path.display(), why))?; - - let proof_reader = BufReader::new(proof_file); - let proof = serde_json::from_reader(proof_reader) - .map_err(|why| format!("Couldn't deserialize proof: {}", why))?; - - println!("Performing verification..."); - println!( - "The verification result is: {}", - match B::verify(vk, proof) { - true => "PASS", - false => "FAIL", - } - ); - - Ok(()) -} - fn cli() -> Result<(), String> { - const FLATTENED_CODE_DEFAULT_PATH: &str = "out"; - const ABI_SPEC_DEFAULT_PATH: &str = "abi.json"; - const VERIFICATION_KEY_DEFAULT_PATH: &str = "verification.key"; - const PROVING_KEY_DEFAULT_PATH: &str = "proving.key"; - const VERIFICATION_CONTRACT_DEFAULT_PATH: &str = "verifier.sol"; - const WITNESS_DEFAULT_PATH: &str = "witness"; - const JSON_PROOF_PATH: &str = "proof.json"; - let default_curve = env::var("ZOKRATES_CURVE").unwrap_or(constants::BN128.into()); - let default_backend = env::var("ZOKRATES_BACKEND").unwrap_or(constants::BELLMAN.into()); - let default_scheme = env::var("ZOKRATES_PROVING_SCHEME").unwrap_or(constants::G16.into()); - let default_solidity_abi = "v1"; - let default_stdlib_path = dirs::home_dir() - .map(|p| p.join(".zokrates/stdlib")) - .unwrap(); - // cli specification using clap library let matches = App::new("ZoKrates") - .setting(AppSettings::SubcommandRequiredElseHelp) - .version(env!("CARGO_PKG_VERSION")) - .author("Jacob Eberhardt, Thibaut Schaeffer, Stefan Deml") - .about("Supports generation of zkSNARKs from high level language code including Smart Contracts for proof verification on the Ethereum Blockchain.\n'I know that I show nothing!'") - .subcommand(SubCommand::with_name("compile") - .about("Compiles into flattened conditions. Produces two files: human-readable '.ztf' file for debugging and binary file") - .arg(Arg::with_name("input") - .short("i") - .long("input") - .help("Path of the source code") - .value_name("FILE") - .takes_value(true) - .required(true) - ).arg(Arg::with_name("stdlib-path") - .long("stdlib-path") - .help("Path to the standard library") - .value_name("PATH") - .takes_value(true) - .required(false) - .env("ZOKRATES_STDLIB") - .default_value(default_stdlib_path.to_str().unwrap_or("")) - ).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(ABI_SPEC_DEFAULT_PATH) - ).arg(Arg::with_name("output") - .short("o") - .long("output") - .help("Path of the output binary") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(FLATTENED_CODE_DEFAULT_PATH) - ).arg(Arg::with_name("curve") - .short("c") - .long("curve") - .help("Curve to be used in the compilation") - .takes_value(true) - .required(false) - .possible_values(CURVES) - .default_value(&default_curve) - ).arg(Arg::with_name("light") - .long("light") - .help("Skip logs and human readable output") - .required(false) - ) - ) - .subcommand(SubCommand::with_name("check") - .about("Checks a program for errors") - .arg(Arg::with_name("input") - .short("i") - .long("input") - .help("Path of the source code") - .value_name("FILE") - .takes_value(true) - .required(true) - ).arg(Arg::with_name("stdlib-path") - .long("stdlib-path") - .help("Path to the standard library") - .value_name("PATH") - .takes_value(true) - .required(false) - .env("ZOKRATES_STDLIB") - .default_value(default_stdlib_path.to_str().unwrap_or("")) - ).arg(Arg::with_name("curve") - .short("c") - .long("curve") - .help("Curve to be used in the compilation") - .takes_value(true) - .required(false) - .possible_values(CURVES) - .default_value(&default_curve) - ) - ) - .subcommand(SubCommand::with_name("setup") - .about("Performs a trusted setup for a given constraint system") - .arg(Arg::with_name("input") - .short("i") - .long("input") - .help("Path of the binary") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(FLATTENED_CODE_DEFAULT_PATH) - ).arg(Arg::with_name("proving-key-path") - .short("p") - .long("proving-key-path") - .help("Path of the generated proving key file") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(PROVING_KEY_DEFAULT_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(VERIFICATION_KEY_DEFAULT_PATH) - ).arg(Arg::with_name("backend") - .short("b") - .long("backend") - .help("Backend to use") - .takes_value(true) - .required(false) - .possible_values(BACKENDS) - .default_value(&default_backend) - ).arg(Arg::with_name("proving-scheme") - .short("s") - .long("proving-scheme") - .help("Proving scheme to use in the setup") - .takes_value(true) - .required(false) - .possible_values(SCHEMES) - .default_value(&default_scheme) - ).arg(Arg::with_name("light") - .long("light") - .help("Skip logging the human-readable program and writing it to a file") - .required(false) - ) - ) - .subcommand(SubCommand::with_name("export-verifier") - .about("Exports a verifier as Solidity smart contract") - .arg(Arg::with_name("input") - .short("i") - .long("input") - .help("Path of the verifier") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(VERIFICATION_KEY_DEFAULT_PATH) - ).arg(Arg::with_name("output") - .short("o") - .long("output") - .help("Path of the output file") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(VERIFICATION_CONTRACT_DEFAULT_PATH) - ).arg(Arg::with_name("curve") - .short("c") - .long("curve") - .help("Curve to be used to export the verifier") - .takes_value(true) - .required(false) - .possible_values(CURVES) - .default_value(&default_curve) - ).arg(Arg::with_name("proving-scheme") - .short("s") - .long("proving-scheme") - .help("Proving scheme to use to export the verifier") - .value_name("FILE") - .takes_value(true) - .required(false) - .possible_values(SCHEMES) - .default_value(&default_scheme) - ).arg(Arg::with_name("solidity-abi") - .short("a") - .long("solidity-abi") - .help("Flag for setting the version of the ABI Encoder used in the contract") - .takes_value(true) - .possible_values(&["v1", "v2"]) - .default_value(&default_solidity_abi) - .required(false) - ) - ) - .subcommand(SubCommand::with_name("compute-witness") - .about("Calculates a witness for a given constraint system") - .arg(Arg::with_name("input") - .short("i") - .long("input") - .help("Path of the binary") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(FLATTENED_CODE_DEFAULT_PATH) - ).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(ABI_SPEC_DEFAULT_PATH) - ).arg(Arg::with_name("output") - .short("o") - .long("output") - .help("Path of the output file") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(WITNESS_DEFAULT_PATH) - ).arg(Arg::with_name("arguments") - .short("a") - .long("arguments") - .help("Arguments for the program's main function, when not using ABI encoding. Expects a space-separated list of field elements like `-a 1 2 3`") - .takes_value(true) - .multiple(true) // allows multiple values - .required(false) - .conflicts_with("abi") - .conflicts_with("stdin") - ).arg(Arg::with_name("abi") - .long("abi") - .help("Use ABI encoding. Arguments are expected as a JSON object as specified at zokrates.github.io/toolbox/abi.html#abi-input-format") - .conflicts_with("arguments") - .required(false) - ).arg(Arg::with_name("stdin") - .long("stdin") - .help("Read arguments from stdin") - .conflicts_with("arguments") - .required(false) - ).arg(Arg::with_name("light") - .long("light") - .help("Skip logging the human-readable program") - .required(false) - ) - ) - .subcommand(SubCommand::with_name("generate-proof") - .about("Calculates a proof for a given constraint system and witness") - .arg(Arg::with_name("witness") - .short("w") - .long("witness") - .help("Path of the witness file") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(WITNESS_DEFAULT_PATH) - ).arg(Arg::with_name("proving-key-path") - .short("p") - .long("proving-key-path") - .help("Path of the proving key file") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(PROVING_KEY_DEFAULT_PATH) - ).arg(Arg::with_name("proof-path") - .short("j") - .long("proof-path") - .help("Path of the JSON proof file") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(JSON_PROOF_PATH) - ).arg(Arg::with_name("input") - .short("i") - .long("input") - .help("Path of the binary") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(FLATTENED_CODE_DEFAULT_PATH) - ).arg(Arg::with_name("backend") - .short("b") - .long("backend") - .help("Backend to use") - .takes_value(true) - .required(false) - .possible_values(BACKENDS) - .default_value(&default_backend) - ).arg(Arg::with_name("proving-scheme") - .short("s") - .long("proving-scheme") - .help("Proving scheme to use to generate the proof") - .value_name("FILE") - .takes_value(true) - .required(false) - .possible_values(SCHEMES) - .default_value(&default_scheme) - ) - ) - .subcommand(SubCommand::with_name("print-proof") - .about("Prints proof in the chosen format") - .arg(Arg::with_name("proof-path") - .short("j") - .long("proof-path") - .help("Path of the JSON proof file") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(JSON_PROOF_PATH) - ).arg(Arg::with_name("format") - .short("f") - .long("format") - .value_name("FORMAT") - .help("Format in which the proof should be printed") - .takes_value(true) - .possible_values(&["remix", "json"]) - .required(true) - ) - ) - .subcommand(SubCommand::with_name("verify") - .about("Verifies a given proof with the given verification key") - .arg(Arg::with_name("proof-path") - .short("j") - .long("proof-path") - .help("Path of the JSON proof file") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(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(VERIFICATION_KEY_DEFAULT_PATH) - ).arg(Arg::with_name("backend") - .short("b") - .long("backend") - .help("Backend to use") - .takes_value(true) - .required(false) - .possible_values(BACKENDS) - .default_value(&default_backend) - ).arg(Arg::with_name("proving-scheme") - .short("s") - .long("proving-scheme") - .help("Proving scheme to use in the setup. Available options are G16 (default), PGHR13 and GM17") - .value_name("FILE") - .takes_value(true) - .required(false) - .default_value(&default_scheme) - ).arg(Arg::with_name("curve") - .short("c") - .long("curve") - .help("Curve to be used in the verification") - .takes_value(true) - .required(false) - .possible_values(CURVES) - .default_value(&default_curve) - ) - ) - .get_matches(); + .setting(AppSettings::SubcommandRequiredElseHelp) + .version(env!("CARGO_PKG_VERSION")) + .author("Jacob Eberhardt, Thibaut Schaeffer, Stefan Deml") + .about("Supports generation of zkSNARKs from high level language code including Smart Contracts for proof verification on the Ethereum Blockchain.\n'I know that I show nothing!'") + .subcommands(vec![ + compile::subcommand(), + check::subcommand(), + compute_witness::subcommand(), + setup::subcommand(), + export_verifier::subcommand(), + generate_proof::subcommand(), + print_proof::subcommand(), + verify::subcommand()]) + .get_matches(); match matches.subcommand() { - ("compile", Some(sub_matches)) => { - let curve = CurveParameter::try_from(sub_matches.value_of("curve").unwrap())?; - match curve { - CurveParameter::Bn128 => cli_compile::(sub_matches)?, - CurveParameter::Bls12_377 => cli_compile::(sub_matches)?, - CurveParameter::Bls12_381 => cli_compile::(sub_matches)?, - CurveParameter::Bw6_761 => cli_compile::(sub_matches)?, - } - } - ("check", Some(sub_matches)) => { - let curve = CurveParameter::try_from(sub_matches.value_of("curve").unwrap())?; - match curve { - CurveParameter::Bn128 => cli_check::(sub_matches)?, - CurveParameter::Bls12_377 => cli_check::(sub_matches)?, - CurveParameter::Bls12_381 => cli_check::(sub_matches)?, - CurveParameter::Bw6_761 => cli_check::(sub_matches)?, - } - } - ("compute-witness", Some(sub_matches)) => { - // read compiled program - let path = Path::new(sub_matches.value_of("input").unwrap()); - let file = File::open(&path) - .map_err(|why| format!("Couldn't open {}: {}", path.display(), why))?; - - let mut reader = BufReader::new(file); - - match ProgEnum::deserialize(&mut reader)? { - ProgEnum::Bn128Program(p) => cli_compute(p, sub_matches)?, - ProgEnum::Bls12_377Program(p) => cli_compute(p, sub_matches)?, - ProgEnum::Bls12_381Program(p) => cli_compute(p, sub_matches)?, - ProgEnum::Bw6_761Program(p) => cli_compute(p, sub_matches)?, - } - } + ("compile", Some(sub_matches)) => compile::exec(sub_matches)?, + ("check", Some(sub_matches)) => check::exec(sub_matches)?, + ("compute-witness", Some(sub_matches)) => compute_witness::exec(sub_matches)?, #[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] - ("setup", Some(sub_matches)) => { - // read compiled program - let path = Path::new(sub_matches.value_of("input").unwrap()); - let file = File::open(&path) - .map_err(|why| format!("Couldn't open {}: {}", path.display(), why))?; - - let mut reader = BufReader::new(file); - let prog = ProgEnum::deserialize(&mut reader)?; - - let parameters = Parameters::try_from(( - sub_matches.value_of("backend").unwrap(), - match prog { - ProgEnum::Bn128Program(_) => constants::BN128, - ProgEnum::Bls12_377Program(_) => constants::BLS12_377, - ProgEnum::Bls12_381Program(_) => constants::BLS12_381, - ProgEnum::Bw6_761Program(_) => constants::BW6_761, - }, - sub_matches.value_of("proving-scheme").unwrap(), - ))?; - - match parameters { - #[cfg(feature = "bellman")] - Parameters(BackendParameter::Bellman, _, SchemeParameter::G16) => match prog { - ProgEnum::Bn128Program(p) => cli_setup::<_, G16, Bellman>(p, sub_matches), - ProgEnum::Bls12_381Program(p) => cli_setup::<_, G16, Bellman>(p, sub_matches), - _ => unreachable!(), - }, - #[cfg(feature = "ark")] - Parameters(BackendParameter::Ark, _, SchemeParameter::GM17) => match prog { - ProgEnum::Bls12_377Program(p) => cli_setup::<_, GM17, Ark>(p, sub_matches), - ProgEnum::Bw6_761Program(p) => cli_setup::<_, GM17, Ark>(p, sub_matches), - ProgEnum::Bn128Program(p) => cli_setup::<_, GM17, Ark>(p, sub_matches), - _ => unreachable!(), - }, - #[cfg(feature = "libsnark")] - Parameters( - BackendParameter::Libsnark, - CurveParameter::Bn128, - SchemeParameter::GM17, - ) => match prog { - ProgEnum::Bn128Program(p) => cli_setup::<_, GM17, Libsnark>(p, sub_matches), - _ => unreachable!(), - }, - #[cfg(feature = "libsnark")] - Parameters( - BackendParameter::Libsnark, - CurveParameter::Bn128, - SchemeParameter::PGHR13, - ) => match prog { - ProgEnum::Bn128Program(p) => cli_setup::<_, PGHR13, Libsnark>(p, sub_matches), - _ => unreachable!(), - }, - _ => unreachable!(), - }? - } - ("export-verifier", Some(sub_matches)) => { - let curve = sub_matches.value_of("curve").unwrap(); - let scheme = sub_matches.value_of("proving-scheme").unwrap(); - let curve_parameter = CurveParameter::try_from(curve)?; - let scheme_parameter = SchemeParameter::try_from(scheme)?; - - match (curve_parameter, scheme_parameter) { - (CurveParameter::Bn128, SchemeParameter::G16) => { - cli_export_verifier::(sub_matches) - } - (CurveParameter::Bn128, SchemeParameter::GM17) => { - cli_export_verifier::(sub_matches) - } - (CurveParameter::Bn128, SchemeParameter::PGHR13) => { - cli_export_verifier::(sub_matches) - } - _ => Err(format!("Could not export verifier with given parameters (curve: {}, scheme: {}): not supported", curve, scheme)) - }? - } + ("setup", Some(sub_matches)) => setup::exec(sub_matches)?, + ("export-verifier", Some(sub_matches)) => export_verifier::exec(sub_matches)?, #[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] - ("generate-proof", Some(sub_matches)) => { - let program_path = Path::new(sub_matches.value_of("input").unwrap()); - let program_file = File::open(&program_path) - .map_err(|why| format!("Couldn't open {}: {}", program_path.display(), why))?; - - let mut reader = BufReader::new(program_file); - let prog = ProgEnum::deserialize(&mut reader)?; - - let parameters = Parameters::try_from(( - sub_matches.value_of("backend").unwrap(), - match prog { - ProgEnum::Bn128Program(_) => constants::BN128, - ProgEnum::Bls12_381Program(_) => constants::BLS12_381, - ProgEnum::Bls12_377Program(_) => constants::BLS12_377, - ProgEnum::Bw6_761Program(_) => constants::BW6_761, - }, - sub_matches.value_of("proving-scheme").unwrap(), - ))?; - - match parameters { - #[cfg(feature = "bellman")] - Parameters(BackendParameter::Bellman, _, SchemeParameter::G16) => match prog { - ProgEnum::Bn128Program(p) => { - cli_generate_proof::<_, G16, Bellman>(p, sub_matches) - } - ProgEnum::Bls12_381Program(p) => { - cli_generate_proof::<_, G16, Bellman>(p, sub_matches) - } - _ => unreachable!(), - }, - #[cfg(feature = "ark")] - Parameters(BackendParameter::Ark, _, SchemeParameter::GM17) => match prog { - ProgEnum::Bls12_377Program(p) => { - cli_generate_proof::<_, GM17, Ark>(p, sub_matches) - } - ProgEnum::Bw6_761Program(p) => { - cli_generate_proof::<_, GM17, Ark>(p, sub_matches) - } - ProgEnum::Bn128Program(p) => cli_generate_proof::<_, GM17, Ark>(p, sub_matches), - _ => unreachable!(), - }, - #[cfg(feature = "libsnark")] - Parameters( - BackendParameter::Libsnark, - CurveParameter::Bn128, - SchemeParameter::GM17, - ) => match prog { - ProgEnum::Bn128Program(p) => { - cli_generate_proof::<_, GM17, Libsnark>(p, sub_matches) - } - _ => unreachable!(), - }, - #[cfg(feature = "libsnark")] - Parameters( - BackendParameter::Libsnark, - CurveParameter::Bn128, - SchemeParameter::PGHR13, - ) => match prog { - ProgEnum::Bn128Program(p) => { - cli_generate_proof::<_, PGHR13, Libsnark>(p, sub_matches) - } - _ => unreachable!(), - }, - _ => unreachable!(), - }? - } - ("print-proof", Some(sub_matches)) => { - let format = sub_matches.value_of("format").unwrap(); - let path = Path::new(sub_matches.value_of("proof-path").unwrap()); - - let file = File::open(&path) - .map_err(|why| format!("Couldn't open {}: {}", path.display(), why))?; - - let proof_object: Value = - serde_json::from_reader(file).map_err(|why| format!("{:?}", why))?; - - match format { - "json" => { - println!("~~~~~~~~ Copy the output below for valid ABIv2 format ~~~~~~~~"); - println!(); - print!("{}", proof_object["proof"]); - print!(","); - println!("{}", proof_object["inputs"]); - println!(); - println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - } - "remix" => { - println!("~~~~~~~~ Copy the output below for valid ABIv1 format ~~~~~~~~"); - println!(); - - for (_, value) in proof_object["proof"].as_object().unwrap().iter() { - print!("{}", value); - print!(","); - } - - println!("{}", proof_object["inputs"]); - println!(); - println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - } - _ => unreachable!(), - } - } + ("generate-proof", Some(sub_matches)) => generate_proof::exec(sub_matches)?, + ("print-proof", Some(sub_matches)) => print_proof::exec(sub_matches)?, #[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] - ("verify", Some(sub_matches)) => { - let parameters = Parameters::try_from(( - sub_matches.value_of("backend").unwrap(), - sub_matches.value_of("curve").unwrap(), - sub_matches.value_of("proving-scheme").unwrap(), - ))?; - - match parameters { - #[cfg(feature = "bellman")] - Parameters( - BackendParameter::Bellman, - CurveParameter::Bn128, - SchemeParameter::G16, - ) => cli_verify::(sub_matches), - #[cfg(feature = "bellman")] - Parameters( - BackendParameter::Bellman, - CurveParameter::Bls12_381, - SchemeParameter::G16, - ) => cli_verify::(sub_matches), - #[cfg(feature = "ark")] - Parameters( - BackendParameter::Ark, - CurveParameter::Bls12_377, - SchemeParameter::GM17, - ) => cli_verify::(sub_matches), - #[cfg(feature = "ark")] - Parameters( - BackendParameter::Ark, - CurveParameter::Bw6_761, - SchemeParameter::GM17, - ) => cli_verify::(sub_matches), - #[cfg(feature = "ark")] - Parameters(BackendParameter::Ark, CurveParameter::Bn128, SchemeParameter::GM17) => { - cli_verify::(sub_matches) - } - #[cfg(feature = "libsnark")] - Parameters( - BackendParameter::Libsnark, - CurveParameter::Bn128, - SchemeParameter::GM17, - ) => cli_verify::(sub_matches), - #[cfg(feature = "libsnark")] - Parameters( - BackendParameter::Libsnark, - CurveParameter::Bn128, - SchemeParameter::PGHR13, - ) => cli_verify::(sub_matches), - _ => unreachable!(), - }? - } + ("verify", Some(sub_matches)) => verify::exec(sub_matches)?, _ => unreachable!(), - } + }; + Ok(()) } @@ -1069,7 +61,14 @@ fn cli() -> Result<(), String> { mod tests { extern crate glob; use self::glob::glob; - use super::*; + + use std::fs::File; + use std::io::{BufReader, Read}; + use std::string::String; + use zokrates_core::compile::{compile, CompilationArtifacts}; + use zokrates_core::ir; + use zokrates_field::Bn128Field; + use zokrates_fs_resolver::FileSystemResolver; #[test] fn compile_examples() { diff --git a/zokrates_cli/src/constants.rs b/zokrates_cli/src/constants.rs index fba1b3e0..1af664ab 100644 --- a/zokrates_cli/src/constants.rs +++ b/zokrates_cli/src/constants.rs @@ -1,7 +1,24 @@ +pub const FLATTENED_CODE_DEFAULT_PATH: &str = "out"; +pub const ABI_SPEC_DEFAULT_PATH: &str = "abi.json"; +pub const VERIFICATION_KEY_DEFAULT_PATH: &str = "verification.key"; +pub const PROVING_KEY_DEFAULT_PATH: &str = "proving.key"; +pub const VERIFICATION_CONTRACT_DEFAULT_PATH: &str = "verifier.sol"; +pub const WITNESS_DEFAULT_PATH: &str = "witness"; +pub const JSON_PROOF_PATH: &str = "proof.json"; + pub const BELLMAN: &str = "bellman"; pub const LIBSNARK: &str = "libsnark"; pub const ARK: &str = "ark"; +lazy_static! { + pub static ref DEFAULT_STDLIB_PATH: String = dirs::home_dir() + .map(|p| p.join(".zokrates/stdlib")) + .unwrap() + .into_os_string() + .into_string() + .unwrap(); +} + pub const BACKENDS: &[&str] = if cfg!(feature = "libsnark") { if cfg!(feature = "ark") { if cfg!(feature = "bellman") { diff --git a/zokrates_cli/src/helpers.rs b/zokrates_cli/src/helpers.rs index a8cc0d6a..bc379951 100644 --- a/zokrates_cli/src/helpers.rs +++ b/zokrates_cli/src/helpers.rs @@ -1,6 +1,5 @@ -use core::convert::TryFrom; - use crate::constants::*; +use core::convert::TryFrom; #[derive(Debug)] pub enum CurveParameter { diff --git a/zokrates_cli/src/ops/check.rs b/zokrates_cli/src/ops/check.rs new file mode 100644 index 00000000..36c3f744 --- /dev/null +++ b/zokrates_cli/src/ops/check.rs @@ -0,0 +1,94 @@ +use crate::constants; +use crate::helpers::CurveParameter; +use clap::{App, Arg, ArgMatches, SubCommand}; +use std::convert::TryFrom; +use std::fs::File; +use std::io::{BufReader, Read}; +use std::path::PathBuf; +use zokrates_core::compile::{check, CompileError}; +use zokrates_field::{Bls12_377Field, Bls12_381Field, Bn128Field, Bw6_761Field, Field}; +use zokrates_fs_resolver::FileSystemResolver; + +pub fn subcommand() -> App<'static, 'static> { + SubCommand::with_name("check") + .about("Checks a program for errors") + .arg( + Arg::with_name("input") + .short("i") + .long("input") + .help("Path of the source code") + .value_name("FILE") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("stdlib-path") + .long("stdlib-path") + .help("Path to the standard library") + .value_name("PATH") + .takes_value(true) + .required(false) + .env("ZOKRATES_STDLIB") + .default_value(constants::DEFAULT_STDLIB_PATH.as_str()), + ) + .arg( + Arg::with_name("curve") + .short("c") + .long("curve") + .help("Curve to be used in the compilation") + .takes_value(true) + .required(false) + .possible_values(constants::CURVES) + .default_value(constants::BN128), + ) +} + +pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> { + let curve = CurveParameter::try_from(sub_matches.value_of("curve").unwrap())?; + match curve { + CurveParameter::Bn128 => cli_check::(sub_matches), + CurveParameter::Bls12_377 => cli_check::(sub_matches), + CurveParameter::Bls12_381 => cli_check::(sub_matches), + CurveParameter::Bw6_761 => cli_check::(sub_matches), + } +} + +fn cli_check(sub_matches: &ArgMatches) -> Result<(), String> { + println!("Checking {}\n", sub_matches.value_of("input").unwrap()); + let path = PathBuf::from(sub_matches.value_of("input").unwrap()); + + let file = File::open(path.clone()) + .map_err(|why| format!("Couldn't open input file {}: {}", path.display(), why))?; + + let mut reader = BufReader::new(file); + let mut source = String::new(); + reader.read_to_string(&mut source).unwrap(); + + let fmt_error = |e: &CompileError| { + let file = e.file().canonicalize().unwrap(); + format!( + "{}:{}", + file.strip_prefix(std::env::current_dir().unwrap()) + .unwrap_or(file.as_path()) + .display(), + e.value() + ) + }; + + let stdlib_path = sub_matches.value_of("stdlib-path").unwrap(); + let resolver = FileSystemResolver::with_stdlib_root(stdlib_path); + + let _ = check::(source, path, Some(&resolver)).map_err(|e| { + format!( + "Check failed:\n\n{}", + e.0.iter() + .map(|e| fmt_error(e)) + .collect::>() + .join("\n\n") + ) + })?; + + println!("Program checked, no errors found."); + + Ok(()) +} diff --git a/zokrates_cli/src/ops/compile.rs b/zokrates_cli/src/ops/compile.rs new file mode 100644 index 00000000..77cdf385 --- /dev/null +++ b/zokrates_cli/src/ops/compile.rs @@ -0,0 +1,162 @@ +use crate::constants; +use crate::helpers::CurveParameter; +use clap::{App, Arg, ArgMatches, SubCommand}; +use serde_json::to_writer_pretty; +use std::convert::TryFrom; +use std::fs::File; +use std::io::{BufReader, BufWriter, Read, Write}; +use std::path::{Path, PathBuf}; +use zokrates_core::compile::{compile, CompilationArtifacts, CompileError}; +use zokrates_field::{Bls12_377Field, Bls12_381Field, Bn128Field, Bw6_761Field, Field}; +use zokrates_fs_resolver::FileSystemResolver; + +pub fn subcommand() -> App<'static, 'static> { + SubCommand::with_name("compile") + .about("Compiles into flattened conditions. Produces two files: human-readable '.ztf' file for debugging and binary file") + .arg(Arg::with_name("input") + .short("i") + .long("input") + .help("Path of the source code") + .value_name("FILE") + .takes_value(true) + .required(true) + ).arg(Arg::with_name("stdlib-path") + .long("stdlib-path") + .help("Path to the standard library") + .value_name("PATH") + .takes_value(true) + .required(false) + .env("ZOKRATES_STDLIB") + .default_value(constants::DEFAULT_STDLIB_PATH.as_str()) + ).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(constants::ABI_SPEC_DEFAULT_PATH) + ).arg(Arg::with_name("output") + .short("o") + .long("output") + .help("Path of the output binary") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(constants::FLATTENED_CODE_DEFAULT_PATH) + ).arg(Arg::with_name("curve") + .short("c") + .long("curve") + .help("Curve to be used in the compilation") + .takes_value(true) + .required(false) + .possible_values(constants::CURVES) + .default_value(constants::BN128) + ).arg(Arg::with_name("light") + .long("light") + .help("Skip logs and human readable output") + .required(false) + ) +} + +pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> { + let curve = CurveParameter::try_from(sub_matches.value_of("curve").unwrap())?; + match curve { + CurveParameter::Bn128 => cli_compile::(sub_matches), + CurveParameter::Bls12_377 => cli_compile::(sub_matches), + CurveParameter::Bls12_381 => cli_compile::(sub_matches), + CurveParameter::Bw6_761 => cli_compile::(sub_matches), + } +} + +fn cli_compile(sub_matches: &ArgMatches) -> Result<(), String> { + println!("Compiling {}\n", sub_matches.value_of("input").unwrap()); + + let path = PathBuf::from(sub_matches.value_of("input").unwrap()); + let light = sub_matches.occurrences_of("light") > 0; + let bin_output_path = Path::new(sub_matches.value_of("output").unwrap()); + let abi_spec_path = Path::new(sub_matches.value_of("abi_spec").unwrap()); + let hr_output_path = bin_output_path.to_path_buf().with_extension("ztf"); + + let file = File::open(path.clone()) + .map_err(|why| format!("Couldn't open input file {}: {}", path.display(), why))?; + + let mut reader = BufReader::new(file); + let mut source = String::new(); + reader.read_to_string(&mut source).unwrap(); + + let fmt_error = |e: &CompileError| { + let file = e.file().canonicalize().unwrap(); + format!( + "{}:{}", + file.strip_prefix(std::env::current_dir().unwrap()) + .unwrap_or(file.as_path()) + .display(), + e.value() + ) + }; + + let stdlib_path = sub_matches.value_of("stdlib-path").unwrap(); + let resolver = FileSystemResolver::with_stdlib_root(stdlib_path); + + let artifacts: CompilationArtifacts = + compile(source, path, Some(&resolver)).map_err(|e| { + format!( + "Compilation failed:\n\n{}", + e.0.iter() + .map(|e| fmt_error(e)) + .collect::>() + .join("\n\n") + ) + })?; + + let program_flattened = artifacts.prog(); + + // number of constraints the flattened program will translate to. + let num_constraints = program_flattened.constraint_count(); + + // serialize flattened program and write to binary file + let bin_output_file = File::create(&bin_output_path) + .map_err(|why| format!("Couldn't create {}: {}", bin_output_path.display(), why))?; + + let mut writer = BufWriter::new(bin_output_file); + + program_flattened.serialize(&mut writer); + + // serialize ABI spec and write to JSON file + let abi_spec_file = File::create(&abi_spec_path) + .map_err(|why| format!("Couldn't create {}: {}", abi_spec_path.display(), why))?; + + let abi = artifacts.abi(); + + let mut writer = BufWriter::new(abi_spec_file); + + to_writer_pretty(&mut writer, &abi).map_err(|_| "Unable to write data to file.".to_string())?; + + if !light { + // write human-readable output file + let hr_output_file = File::create(&hr_output_path) + .map_err(|why| format!("Couldn't create {}: {}", hr_output_path.display(), why))?; + + let mut hrofb = BufWriter::new(hr_output_file); + write!(&mut hrofb, "{}\n", program_flattened) + .map_err(|_| "Unable to write data to file".to_string())?; + hrofb + .flush() + .map_err(|_| "Unable to flush buffer".to_string())?; + } + + if !light { + // debugging output + println!("Compiled program:\n{}", program_flattened); + } + + println!("Compiled code written to '{}'", bin_output_path.display()); + + if !light { + println!("Human readable code to '{}'", hr_output_path.display()); + } + + println!("Number of constraints: {}", num_constraints); + Ok(()) +} diff --git a/zokrates_cli/src/ops/compute_witness.rs b/zokrates_cli/src/ops/compute_witness.rs new file mode 100644 index 00000000..f1657f2d --- /dev/null +++ b/zokrates_cli/src/ops/compute_witness.rs @@ -0,0 +1,189 @@ +use crate::constants::{ABI_SPEC_DEFAULT_PATH, FLATTENED_CODE_DEFAULT_PATH, WITNESS_DEFAULT_PATH}; +use clap::{App, Arg, ArgMatches, SubCommand}; +use serde_json::from_reader; +use std::fs::File; +use std::io::{stdin, BufReader, BufWriter, Read}; +use std::path::Path; +use zokrates_abi::Encode; +use zokrates_core::ir; +use zokrates_core::ir::ProgEnum; +use zokrates_core::typed_absy::abi::Abi; +use zokrates_core::typed_absy::{Signature, Type}; +use zokrates_field::Field; + +pub fn subcommand() -> App<'static, 'static> { + SubCommand::with_name("compute-witness") + .about("Calculates a witness for a given constraint system") + .arg(Arg::with_name("input") + .short("i") + .long("input") + .help("Path of the binary") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(FLATTENED_CODE_DEFAULT_PATH) + ).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(ABI_SPEC_DEFAULT_PATH) + ).arg(Arg::with_name("output") + .short("o") + .long("output") + .help("Path of the output file") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(WITNESS_DEFAULT_PATH) + ).arg(Arg::with_name("arguments") + .short("a") + .long("arguments") + .help("Arguments for the program's main function, when not using ABI encoding. Expects a space-separated list of field elements like `-a 1 2 3`") + .takes_value(true) + .multiple(true) // allows multiple values + .required(false) + .conflicts_with("abi") + .conflicts_with("stdin") + ).arg(Arg::with_name("abi") + .long("abi") + .help("Use ABI encoding. Arguments are expected as a JSON object as specified at zokrates.github.io/toolbox/abi.html#abi-input-format") + .conflicts_with("arguments") + .required(false) + ).arg(Arg::with_name("stdin") + .long("stdin") + .help("Read arguments from stdin") + .conflicts_with("arguments") + .required(false) + ).arg(Arg::with_name("light") + .long("light") + .help("Skip logging the human-readable program") + .required(false) + ) +} + +pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> { + // read compiled program + let path = Path::new(sub_matches.value_of("input").unwrap()); + let file = + File::open(&path).map_err(|why| format!("Couldn't open {}: {}", path.display(), why))?; + + let mut reader = BufReader::new(file); + + match ProgEnum::deserialize(&mut reader)? { + ProgEnum::Bn128Program(p) => cli_compute(p, sub_matches), + ProgEnum::Bls12_377Program(p) => cli_compute(p, sub_matches), + ProgEnum::Bls12_381Program(p) => cli_compute(p, sub_matches), + ProgEnum::Bw6_761Program(p) => cli_compute(p, sub_matches), + } +} + +fn cli_compute(ir_prog: ir::Prog, sub_matches: &ArgMatches) -> Result<(), String> { + println!("Computing witness..."); + + // print deserialized flattened program + if !sub_matches.is_present("light") { + println!("{}", ir_prog); + } + + let is_stdin = sub_matches.is_present("stdin"); + let is_abi = sub_matches.is_present("abi"); + + if !is_stdin && is_abi { + return Err("ABI input as inline argument is not supported. Please use `--stdin`.".into()); + } + + let signature = match is_abi { + true => { + let path = Path::new(sub_matches.value_of("abi_spec").unwrap()); + let file = File::open(&path) + .map_err(|why| format!("couldn't open {}: {}", path.display(), why))?; + let mut reader = BufReader::new(file); + + let abi: Abi = from_reader(&mut reader).map_err(|why| why.to_string())?; + + abi.signature() + } + false => Signature::new() + .inputs(vec![Type::FieldElement; ir_prog.main.arguments.len()]) + .outputs(vec![Type::FieldElement; ir_prog.main.returns.len()]), + }; + + use zokrates_abi::Inputs; + + // get arguments + let arguments = match is_stdin { + // take inline arguments + false => { + let arguments = sub_matches.values_of("arguments"); + arguments + .map(|a| { + a.map(|x| T::try_from_dec_str(x).map_err(|_| x.to_string())) + .collect::, _>>() + }) + .unwrap_or(Ok(vec![])) + .map(|v| Inputs::Raw(v)) + } + // take stdin arguments + true => { + let mut stdin = stdin(); + let mut input = String::new(); + + match is_abi { + true => match stdin.read_to_string(&mut input) { + Ok(_) => { + use zokrates_abi::parse_strict; + + parse_strict(&input, signature.inputs) + .map(|parsed| Inputs::Abi(parsed)) + .map_err(|why| why.to_string()) + } + Err(_) => Err(String::from("???")), + }, + false => match ir_prog.arguments_count() { + 0 => Ok(Inputs::Raw(vec![])), + _ => match stdin.read_to_string(&mut input) { + Ok(_) => { + input.retain(|x| x != '\n'); + input + .split(" ") + .map(|x| T::try_from_dec_str(x).map_err(|_| x.to_string())) + .collect::, _>>() + .map(|v| Inputs::Raw(v)) + } + Err(_) => Err(String::from("???")), + }, + }, + } + } + } + .map_err(|e| format!("Could not parse argument: {}", e))?; + + let interpreter = ir::Interpreter::default(); + + let witness = interpreter + .execute(&ir_prog, &arguments.encode()) + .map_err(|e| format!("Execution failed: {}", e))?; + + use zokrates_abi::Decode; + + let results_json_value: serde_json::Value = + zokrates_abi::CheckedValues::decode(witness.return_values(), signature.outputs).into(); + + println!("\nWitness: \n\n{}", results_json_value); + + // write witness to file + let output_path = Path::new(sub_matches.value_of("output").unwrap()); + let output_file = File::create(&output_path) + .map_err(|why| format!("couldn't create {}: {}", output_path.display(), why))?; + + let writer = BufWriter::new(output_file); + + witness + .write(writer) + .map_err(|why| format!("could not save witness: {:?}", why))?; + + Ok(()) +} diff --git a/zokrates_cli/src/ops/export_verifier.rs b/zokrates_cli/src/ops/export_verifier.rs new file mode 100644 index 00000000..b4658c62 --- /dev/null +++ b/zokrates_cli/src/ops/export_verifier.rs @@ -0,0 +1,119 @@ +use crate::constants; +use crate::helpers::{CurveParameter, SchemeParameter}; +use clap::{App, Arg, ArgMatches, SubCommand}; +use std::convert::TryFrom; +use std::fs::File; +use std::io::{BufReader, BufWriter, Write}; +use std::path::Path; +use zokrates_core::proof_system::*; +use zokrates_field::Bn128Field; + +pub fn subcommand() -> App<'static, 'static> { + SubCommand::with_name("export-verifier") + .about("Exports a verifier as Solidity smart contract") + .arg( + Arg::with_name("input") + .short("i") + .long("input") + .help("Path of the verifier") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(constants::VERIFICATION_KEY_DEFAULT_PATH), + ) + .arg( + Arg::with_name("output") + .short("o") + .long("output") + .help("Path of the output file") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(constants::VERIFICATION_CONTRACT_DEFAULT_PATH), + ) + .arg( + Arg::with_name("curve") + .short("c") + .long("curve") + .help("Curve to be used to export the verifier") + .takes_value(true) + .required(false) + .possible_values(constants::CURVES) + .default_value(constants::BN128), + ) + .arg( + Arg::with_name("proving-scheme") + .short("s") + .long("proving-scheme") + .help("Proving scheme to use to export the verifier") + .value_name("FILE") + .takes_value(true) + .required(false) + .possible_values(constants::SCHEMES) + .default_value(constants::G16), + ) + .arg( + Arg::with_name("solidity-abi") + .short("a") + .long("solidity-abi") + .help("Flag for setting the version of the ABI Encoder used in the contract") + .takes_value(true) + .possible_values(&["v1", "v2"]) + .default_value("v1") + .required(false), + ) +} + +pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> { + let curve = sub_matches.value_of("curve").unwrap(); + let scheme = sub_matches.value_of("proving-scheme").unwrap(); + + let curve_parameter = CurveParameter::try_from(curve)?; + let scheme_parameter = SchemeParameter::try_from(scheme)?; + + match (curve_parameter, scheme_parameter) { + (CurveParameter::Bn128, SchemeParameter::G16) => { + cli_export_verifier::(sub_matches) + } + (CurveParameter::Bn128, SchemeParameter::GM17) => { + cli_export_verifier::(sub_matches) + } + (CurveParameter::Bn128, SchemeParameter::PGHR13) => { + cli_export_verifier::(sub_matches) + } + _ => Err(format!("Could not export verifier with given parameters (curve: {}, scheme: {}): not supported", curve, scheme)) + } +} + +fn cli_export_verifier>( + sub_matches: &ArgMatches, +) -> Result<(), String> { + println!("Exporting verifier..."); + + // read vk file + let input_path = Path::new(sub_matches.value_of("input").unwrap()); + let input_file = File::open(&input_path) + .map_err(|why| format!("Couldn't open {}: {}", input_path.display(), why))?; + let reader = BufReader::new(input_file); + + let vk = serde_json::from_reader(reader) + .map_err(|why| format!("Couldn't deserialize verifying key: {}", why))?; + + let abi = SolidityAbi::from(sub_matches.value_of("solidity-abi").unwrap())?; + + let verifier = S::export_solidity_verifier(vk, abi); + + //write output file + let output_path = Path::new(sub_matches.value_of("output").unwrap()); + let output_file = File::create(&output_path) + .map_err(|why| format!("Couldn't create {}: {}", output_path.display(), why))?; + + let mut writer = BufWriter::new(output_file); + + writer + .write_all(&verifier.as_bytes()) + .map_err(|_| "Failed writing output to file".to_string())?; + + println!("Finished exporting verifier"); + Ok(()) +} diff --git a/zokrates_cli/src/ops/generate_proof.rs b/zokrates_cli/src/ops/generate_proof.rs new file mode 100644 index 00000000..3118a7d0 --- /dev/null +++ b/zokrates_cli/src/ops/generate_proof.rs @@ -0,0 +1,180 @@ +use crate::constants; +use crate::helpers::*; +use clap::{App, Arg, ArgMatches, SubCommand}; +use std::convert::TryFrom; +use std::fs::File; +use std::io::{BufReader, Read, Write}; +use std::path::Path; +use zokrates_core::ir; +use zokrates_core::ir::ProgEnum; +#[cfg(feature = "ark")] +use zokrates_core::proof_system::ark::Ark; +#[cfg(feature = "bellman")] +use zokrates_core::proof_system::bellman::Bellman; +#[cfg(feature = "libsnark")] +use zokrates_core::proof_system::libsnark::Libsnark; +#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] +use zokrates_core::proof_system::*; +use zokrates_field::Field; + +pub fn subcommand() -> App<'static, 'static> { + SubCommand::with_name("generate-proof") + .about("Calculates a proof for a given constraint system and witness") + .arg( + Arg::with_name("witness") + .short("w") + .long("witness") + .help("Path of the witness file") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(constants::WITNESS_DEFAULT_PATH), + ) + .arg( + Arg::with_name("proving-key-path") + .short("p") + .long("proving-key-path") + .help("Path of the proving key file") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(constants::PROVING_KEY_DEFAULT_PATH), + ) + .arg( + Arg::with_name("proof-path") + .short("j") + .long("proof-path") + .help("Path of the JSON proof file") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(constants::JSON_PROOF_PATH), + ) + .arg( + Arg::with_name("input") + .short("i") + .long("input") + .help("Path of the binary") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(constants::FLATTENED_CODE_DEFAULT_PATH), + ) + .arg( + Arg::with_name("backend") + .short("b") + .long("backend") + .help("Backend to use") + .takes_value(true) + .required(false) + .possible_values(constants::BACKENDS) + .default_value(constants::BELLMAN), + ) + .arg( + Arg::with_name("proving-scheme") + .short("s") + .long("proving-scheme") + .help("Proving scheme to use to generate the proof") + .value_name("FILE") + .takes_value(true) + .required(false) + .possible_values(constants::SCHEMES) + .default_value(constants::G16), + ) +} + +pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> { + let program_path = Path::new(sub_matches.value_of("input").unwrap()); + let program_file = File::open(&program_path) + .map_err(|why| format!("Couldn't open {}: {}", program_path.display(), why))?; + + let mut reader = BufReader::new(program_file); + let prog = ProgEnum::deserialize(&mut reader)?; + + let parameters = Parameters::try_from(( + sub_matches.value_of("backend").unwrap(), + match prog { + ProgEnum::Bn128Program(_) => constants::BN128, + ProgEnum::Bls12_381Program(_) => constants::BLS12_381, + ProgEnum::Bls12_377Program(_) => constants::BLS12_377, + ProgEnum::Bw6_761Program(_) => constants::BW6_761, + }, + sub_matches.value_of("proving-scheme").unwrap(), + ))?; + + match parameters { + #[cfg(feature = "bellman")] + Parameters(BackendParameter::Bellman, _, SchemeParameter::G16) => match prog { + ProgEnum::Bn128Program(p) => cli_generate_proof::<_, G16, Bellman>(p, sub_matches), + ProgEnum::Bls12_381Program(p) => cli_generate_proof::<_, G16, Bellman>(p, sub_matches), + _ => unreachable!(), + }, + #[cfg(feature = "ark")] + Parameters(BackendParameter::Ark, _, SchemeParameter::GM17) => match prog { + ProgEnum::Bls12_377Program(p) => cli_generate_proof::<_, GM17, Ark>(p, sub_matches), + ProgEnum::Bw6_761Program(p) => cli_generate_proof::<_, GM17, Ark>(p, sub_matches), + ProgEnum::Bn128Program(p) => cli_generate_proof::<_, GM17, Ark>(p, sub_matches), + _ => unreachable!(), + }, + #[cfg(feature = "libsnark")] + Parameters(BackendParameter::Libsnark, CurveParameter::Bn128, SchemeParameter::GM17) => { + match prog { + ProgEnum::Bn128Program(p) => { + cli_generate_proof::<_, GM17, Libsnark>(p, sub_matches) + } + _ => unreachable!(), + } + } + #[cfg(feature = "libsnark")] + Parameters(BackendParameter::Libsnark, CurveParameter::Bn128, SchemeParameter::PGHR13) => { + match prog { + ProgEnum::Bn128Program(p) => { + cli_generate_proof::<_, PGHR13, Libsnark>(p, sub_matches) + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } +} + +fn cli_generate_proof, B: Backend>( + program: ir::Prog, + sub_matches: &ArgMatches, +) -> Result<(), String> { + println!("Generating proof..."); + + // deserialize witness + let witness_path = Path::new(sub_matches.value_of("witness").unwrap()); + let witness_file = match File::open(&witness_path) { + Ok(file) => file, + Err(why) => panic!("Couldn't open {}: {}", witness_path.display(), why), + }; + + let witness = ir::Witness::read(witness_file) + .map_err(|why| format!("Could not load witness: {:?}", why))?; + + let pk_path = Path::new(sub_matches.value_of("proving-key-path").unwrap()); + let proof_path = Path::new(sub_matches.value_of("proof-path").unwrap()); + + let pk_file = File::open(&pk_path) + .map_err(|why| format!("Couldn't open {}: {}", pk_path.display(), why))?; + + let mut pk: Vec = Vec::new(); + let mut pk_reader = BufReader::new(pk_file); + pk_reader + .read_to_end(&mut pk) + .map_err(|why| format!("Couldn't read {}: {}", pk_path.display(), why))?; + + let proof = B::generate_proof(program, witness, pk); + let mut proof_file = File::create(proof_path).unwrap(); + + let proof = serde_json::to_string_pretty(&proof).unwrap(); + proof_file + .write(proof.as_bytes()) + .map_err(|why| format!("Couldn't write to {}: {}", proof_path.display(), why))?; + + println!("Proof:\n{}", format!("{}", proof)); + + Ok(()) +} diff --git a/zokrates_cli/src/ops/mod.rs b/zokrates_cli/src/ops/mod.rs new file mode 100644 index 00000000..bb1748c8 --- /dev/null +++ b/zokrates_cli/src/ops/mod.rs @@ -0,0 +1,12 @@ +pub mod check; +pub mod compile; +pub mod compute_witness; +#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] +pub mod export_verifier; +#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] +pub mod generate_proof; +pub mod print_proof; +#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] +pub mod setup; +#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] +pub mod verify; diff --git a/zokrates_cli/src/ops/print_proof.rs b/zokrates_cli/src/ops/print_proof.rs new file mode 100644 index 00000000..190aee93 --- /dev/null +++ b/zokrates_cli/src/ops/print_proof.rs @@ -0,0 +1,68 @@ +use crate::constants::JSON_PROOF_PATH; +use clap::{App, Arg, ArgMatches, SubCommand}; +use serde_json::Value; +use std::fs::File; +use std::path::Path; + +pub fn subcommand() -> App<'static, 'static> { + SubCommand::with_name("print-proof") + .about("Prints proof in the chosen format") + .arg( + Arg::with_name("proof-path") + .short("j") + .long("proof-path") + .help("Path of the JSON proof file") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(JSON_PROOF_PATH), + ) + .arg( + Arg::with_name("format") + .short("f") + .long("format") + .value_name("FORMAT") + .help("Format in which the proof should be printed") + .takes_value(true) + .possible_values(&["remix", "json"]) + .required(true), + ) +} + +pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> { + let format = sub_matches.value_of("format").unwrap(); + let path = Path::new(sub_matches.value_of("proof-path").unwrap()); + + let file = + File::open(&path).map_err(|why| format!("Couldn't open {}: {}", path.display(), why))?; + + let proof_object: Value = serde_json::from_reader(file).map_err(|why| format!("{:?}", why))?; + + match format { + "json" => { + println!("~~~~~~~~ Copy the output below for valid ABIv2 format ~~~~~~~~"); + println!(); + print!("{}", proof_object["proof"]); + print!(","); + println!("{}", proof_object["inputs"]); + println!(); + println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + } + "remix" => { + println!("~~~~~~~~ Copy the output below for valid ABIv1 format ~~~~~~~~"); + println!(); + + for (_, value) in proof_object["proof"].as_object().unwrap().iter() { + print!("{}", value); + print!(","); + } + + println!("{}", proof_object["inputs"]); + println!(); + println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + } + _ => unreachable!(), + } + + Ok(()) +} diff --git a/zokrates_cli/src/ops/setup.rs b/zokrates_cli/src/ops/setup.rs new file mode 100644 index 00000000..693b7819 --- /dev/null +++ b/zokrates_cli/src/ops/setup.rs @@ -0,0 +1,172 @@ +use crate::constants; +use crate::helpers::*; +use clap::{App, Arg, ArgMatches, SubCommand}; +use std::convert::TryFrom; +use std::fs::File; +use std::io::{BufReader, Write}; +use std::path::Path; +use zokrates_core::ir; +use zokrates_core::ir::ProgEnum; +#[cfg(feature = "ark")] +use zokrates_core::proof_system::ark::Ark; +#[cfg(feature = "bellman")] +use zokrates_core::proof_system::bellman::Bellman; +#[cfg(feature = "libsnark")] +use zokrates_core::proof_system::libsnark::Libsnark; +#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] +use zokrates_core::proof_system::*; +use zokrates_field::Field; + +pub fn subcommand() -> App<'static, 'static> { + SubCommand::with_name("setup") + .about("Performs a trusted setup for a given constraint system") + .arg( + Arg::with_name("input") + .short("i") + .long("input") + .help("Path of the binary") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(constants::FLATTENED_CODE_DEFAULT_PATH), + ) + .arg( + Arg::with_name("proving-key-path") + .short("p") + .long("proving-key-path") + .help("Path of the generated proving key file") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(constants::PROVING_KEY_DEFAULT_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(constants::VERIFICATION_KEY_DEFAULT_PATH), + ) + .arg( + Arg::with_name("backend") + .short("b") + .long("backend") + .help("Backend to use") + .takes_value(true) + .required(false) + .possible_values(constants::BACKENDS) + .default_value(constants::BELLMAN), + ) + .arg( + Arg::with_name("proving-scheme") + .short("s") + .long("proving-scheme") + .help("Proving scheme to use in the setup") + .takes_value(true) + .required(false) + .possible_values(constants::SCHEMES) + .default_value(constants::G16), + ) + .arg( + Arg::with_name("light") + .long("light") + .help("Skip logging the human-readable program and writing it to a file") + .required(false), + ) +} + +pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> { + // read compiled program + let path = Path::new(sub_matches.value_of("input").unwrap()); + let file = + File::open(&path).map_err(|why| format!("Couldn't open {}: {}", path.display(), why))?; + + let mut reader = BufReader::new(file); + let prog = ProgEnum::deserialize(&mut reader)?; + + let parameters = Parameters::try_from(( + sub_matches.value_of("backend").unwrap(), + match prog { + ProgEnum::Bn128Program(_) => constants::BN128, + ProgEnum::Bls12_377Program(_) => constants::BLS12_377, + ProgEnum::Bls12_381Program(_) => constants::BLS12_381, + ProgEnum::Bw6_761Program(_) => constants::BW6_761, + }, + sub_matches.value_of("proving-scheme").unwrap(), + ))?; + + match parameters { + #[cfg(feature = "bellman")] + Parameters(BackendParameter::Bellman, _, SchemeParameter::G16) => match prog { + ProgEnum::Bn128Program(p) => cli_setup::<_, G16, Bellman>(p, sub_matches), + ProgEnum::Bls12_381Program(p) => cli_setup::<_, G16, Bellman>(p, sub_matches), + _ => unreachable!(), + }, + #[cfg(feature = "ark")] + Parameters(BackendParameter::Ark, _, SchemeParameter::GM17) => match prog { + ProgEnum::Bls12_377Program(p) => cli_setup::<_, GM17, Ark>(p, sub_matches), + ProgEnum::Bw6_761Program(p) => cli_setup::<_, GM17, Ark>(p, sub_matches), + ProgEnum::Bn128Program(p) => cli_setup::<_, GM17, Ark>(p, sub_matches), + _ => unreachable!(), + }, + #[cfg(feature = "libsnark")] + Parameters(BackendParameter::Libsnark, CurveParameter::Bn128, SchemeParameter::GM17) => { + match prog { + ProgEnum::Bn128Program(p) => cli_setup::<_, GM17, Libsnark>(p, sub_matches), + _ => unreachable!(), + } + } + #[cfg(feature = "libsnark")] + Parameters(BackendParameter::Libsnark, CurveParameter::Bn128, SchemeParameter::PGHR13) => { + match prog { + ProgEnum::Bn128Program(p) => cli_setup::<_, PGHR13, Libsnark>(p, sub_matches), + _ => unreachable!(), + } + } + _ => unreachable!(), + } +} + +fn cli_setup, B: Backend>( + program: ir::Prog, + sub_matches: &ArgMatches, +) -> Result<(), String> { + println!("Performing setup..."); + + // print deserialized flattened program + if !sub_matches.is_present("light") { + println!("{}", program); + } + + // get paths for proving and verification keys + let pk_path = Path::new(sub_matches.value_of("proving-key-path").unwrap()); + let vk_path = Path::new(sub_matches.value_of("verification-key-path").unwrap()); + + // run setup phase + let keypair = B::setup(program); + + // write verification key + let mut vk_file = File::create(vk_path) + .map_err(|why| format!("couldn't create {}: {}", vk_path.display(), why))?; + vk_file + .write( + serde_json::to_string_pretty(&keypair.vk) + .unwrap() + .as_bytes(), + ) + .map_err(|why| format!("couldn't write to {}: {}", vk_path.display(), why))?; + + // write proving key + let mut pk_file = File::create(pk_path) + .map_err(|why| format!("couldn't create {}: {}", pk_path.display(), why))?; + pk_file + .write(keypair.pk.as_ref()) + .map_err(|why| format!("couldn't write to {}: {}", pk_path.display(), why))?; + + println!("Setup completed."); + + Ok(()) +} diff --git a/zokrates_cli/src/ops/verify.rs b/zokrates_cli/src/ops/verify.rs new file mode 100644 index 00000000..aefd51b9 --- /dev/null +++ b/zokrates_cli/src/ops/verify.rs @@ -0,0 +1,133 @@ +use crate::constants; +use crate::helpers::*; +use clap::{App, Arg, ArgMatches, SubCommand}; +use std::convert::TryFrom; +use std::fs::File; +use std::io::BufReader; +use std::path::Path; +#[cfg(feature = "ark")] +use zokrates_core::proof_system::ark::Ark; +#[cfg(feature = "bellman")] +use zokrates_core::proof_system::bellman::Bellman; +#[cfg(feature = "libsnark")] +use zokrates_core::proof_system::libsnark::Libsnark; +#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))] +use zokrates_core::proof_system::*; +use zokrates_field::{Bls12_377Field, Bls12_381Field, Bn128Field, Bw6_761Field, Field}; + +pub fn subcommand() -> App<'static, 'static> { + SubCommand::with_name("verify") + .about("Verifies a given proof with the given verification key") + .arg(Arg::with_name("proof-path") + .short("j") + .long("proof-path") + .help("Path of the JSON proof file") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(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(constants::VERIFICATION_KEY_DEFAULT_PATH) + ).arg(Arg::with_name("backend") + .short("b") + .long("backend") + .help("Backend to use") + .takes_value(true) + .required(false) + .possible_values(constants::BACKENDS) + .default_value(constants::BELLMAN) + ).arg(Arg::with_name("proving-scheme") + .short("s") + .long("proving-scheme") + .help("Proving scheme to use in the setup. Available options are G16 (default), PGHR13 and GM17") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(constants::G16) + ).arg(Arg::with_name("curve") + .short("c") + .long("curve") + .help("Curve to be used in the verification") + .takes_value(true) + .required(false) + .possible_values(constants::CURVES) + .default_value(constants::BN128) + ) +} + +pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> { + let parameters = Parameters::try_from(( + sub_matches.value_of("backend").unwrap(), + sub_matches.value_of("curve").unwrap(), + sub_matches.value_of("proving-scheme").unwrap(), + ))?; + + match parameters { + #[cfg(feature = "bellman")] + Parameters(BackendParameter::Bellman, CurveParameter::Bn128, SchemeParameter::G16) => { + cli_verify::(sub_matches) + } + #[cfg(feature = "bellman")] + Parameters(BackendParameter::Bellman, CurveParameter::Bls12_381, SchemeParameter::G16) => { + cli_verify::(sub_matches) + } + #[cfg(feature = "ark")] + Parameters(BackendParameter::Ark, CurveParameter::Bls12_377, SchemeParameter::GM17) => { + cli_verify::(sub_matches) + } + #[cfg(feature = "ark")] + Parameters(BackendParameter::Ark, CurveParameter::Bw6_761, SchemeParameter::GM17) => { + cli_verify::(sub_matches) + } + #[cfg(feature = "ark")] + Parameters(BackendParameter::Ark, CurveParameter::Bn128, SchemeParameter::GM17) => { + cli_verify::(sub_matches) + } + #[cfg(feature = "libsnark")] + Parameters(BackendParameter::Libsnark, CurveParameter::Bn128, SchemeParameter::GM17) => { + cli_verify::(sub_matches) + } + #[cfg(feature = "libsnark")] + Parameters(BackendParameter::Libsnark, CurveParameter::Bn128, SchemeParameter::PGHR13) => { + cli_verify::(sub_matches) + } + _ => unreachable!(), + } +} + +fn cli_verify, B: Backend>( + sub_matches: &ArgMatches, +) -> Result<(), String> { + let vk_path = Path::new(sub_matches.value_of("verification-key-path").unwrap()); + let vk_file = File::open(&vk_path) + .map_err(|why| format!("Couldn't open {}: {}", vk_path.display(), why))?; + + let vk_reader = BufReader::new(vk_file); + let vk = serde_json::from_reader(vk_reader) + .map_err(|why| format!("Couldn't deserialize verification key: {}", why))?; + + let proof_path = Path::new(sub_matches.value_of("proof-path").unwrap()); + let proof_file = File::open(&proof_path) + .map_err(|why| format!("Couldn't open {}: {}", proof_path.display(), why))?; + + let proof_reader = BufReader::new(proof_file); + let proof = serde_json::from_reader(proof_reader) + .map_err(|why| format!("Couldn't deserialize proof: {}", why))?; + + println!("Performing verification..."); + println!( + "The verification result is: {}", + match B::verify(vk, proof) { + true => "PASS", + false => "FAIL", + } + ); + + Ok(()) +} diff --git a/zokrates_core/src/proof_system/scheme/mod.rs b/zokrates_core/src/proof_system/scheme/mod.rs index f3ed498b..b9a09b80 100644 --- a/zokrates_core/src/proof_system/scheme/mod.rs +++ b/zokrates_core/src/proof_system/scheme/mod.rs @@ -6,6 +6,10 @@ pub mod gm17; pub mod groth16; pub mod pghr13; +pub use self::gm17::GM17; +pub use self::groth16::G16; +pub use self::pghr13::PGHR13; + pub trait Scheme { type VerificationKey: Serialize + DeserializeOwned; type ProofPoints: Serialize + DeserializeOwned; diff --git a/zokrates_js/Cargo.lock b/zokrates_js/Cargo.lock index 06ab564a..f0aad642 100644 --- a/zokrates_js/Cargo.lock +++ b/zokrates_js/Cargo.lock @@ -24,86 +24,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ark-bls12-377" -version = "0.1.0" -source = "git+https://github.com/arkworks-rs/curves#23e87bf224c23be5c5bccc6084aae31fff8bb83f" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-bn254" -version = "0.1.0" -source = "git+https://github.com/arkworks-rs/curves#23e87bf224c23be5c5bccc6084aae31fff8bb83f" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-bw6-761" -version = "0.1.0" -source = "git+https://github.com/arkworks-rs/curves#23e87bf224c23be5c5bccc6084aae31fff8bb83f" -dependencies = [ - "ark-bls12-377", - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-ec" -version = "0.1.0" -source = "git+https://github.com/arkworks-rs/algebra#9bc541722707e1ac495e82fbf86446bbfde38a82" -dependencies = [ - "ark-ff", - "ark-serialize", - "ark-std", - "derivative", - "num-traits 0.2.12", - "rand 0.7.3", -] - -[[package]] -name = "ark-ff" -version = "0.1.0" -source = "git+https://github.com/arkworks-rs/algebra#9bc541722707e1ac495e82fbf86446bbfde38a82" -dependencies = [ - "ark-ff-asm", - "ark-serialize", - "ark-std", - "derivative", - "num-traits 0.2.12", - "rand 0.7.3", - "rustc_version", -] - -[[package]] -name = "ark-ff-asm" -version = "0.1.0" -source = "git+https://github.com/arkworks-rs/algebra#9bc541722707e1ac495e82fbf86446bbfde38a82" -dependencies = [ - "quote 1.0.7", - "syn 1.0.34", -] - -[[package]] -name = "ark-serialize" -version = "0.1.0" -source = "git+https://github.com/arkworks-rs/algebra#9bc541722707e1ac495e82fbf86446bbfde38a82" -dependencies = [ - "ark-std", -] - -[[package]] -name = "ark-std" -version = "0.1.0" -source = "git+https://github.com/arkworks-rs/utils#f6974ac72f59339b7ab798a728a84c5a7b8bac45" - [[package]] name = "autocfg" version = "1.0.0" @@ -256,17 +176,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "derivative" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" -dependencies = [ - "proc-macro2 1.0.18", - "quote 1.0.7", - "syn 1.0.34", -] - [[package]] name = "digest" version = "0.8.1" @@ -896,39 +805,12 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -[[package]] -name = "rustc_version" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65c94201b44764d6d1f7e37c15a8289ed55e546c1762c7f1d57f616966e0c181" -dependencies = [ - "semver", -] - [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428" -dependencies = [ - "pest", -] - [[package]] name = "serde" version = "1.0.114" @@ -1229,11 +1111,6 @@ dependencies = [ name = "zokrates_field" version = "0.3.7" dependencies = [ - "ark-bls12-377", - "ark-bn254", - "ark-bw6-761", - "ark-ec", - "ark-ff", "bellman_ce", "bincode 0.8.0", "lazy_static", From 448821576e4a20d962dbcee2eaf8e746f9058e67 Mon Sep 17 00:00:00 2001 From: dark64 Date: Thu, 17 Dec 2020 21:23:11 +0100 Subject: [PATCH 2/3] check validity of stdlib path --- Cargo.lock | 1 - zokrates_cli/Cargo.toml | 1 - zokrates_cli/src/ops/check.rs | 11 +++++++++-- zokrates_cli/src/ops/compile.rs | 9 ++++++++- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5785b550..03acbe3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2051,7 +2051,6 @@ dependencies = [ "fs_extra", "glob 0.2.11", "lazy_static", - "log", "regex", "serde_json", "tempdir", diff --git a/zokrates_cli/Cargo.toml b/zokrates_cli/Cargo.toml index 1385eb40..e698c441 100644 --- a/zokrates_cli/Cargo.toml +++ b/zokrates_cli/Cargo.toml @@ -22,7 +22,6 @@ zokrates_core = { version = "0.5", path = "../zokrates_core", default-features = zokrates_fs_resolver = { version = "0.5", path = "../zokrates_fs_resolver"} serde_json = "1.0" dirs = "3.0.1" -log = "0.4.11" lazy_static = "1.4.0" [dev-dependencies] diff --git a/zokrates_cli/src/ops/check.rs b/zokrates_cli/src/ops/check.rs index 36c3f744..99186b86 100644 --- a/zokrates_cli/src/ops/check.rs +++ b/zokrates_cli/src/ops/check.rs @@ -4,7 +4,7 @@ use clap::{App, Arg, ArgMatches, SubCommand}; use std::convert::TryFrom; use std::fs::File; use std::io::{BufReader, Read}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use zokrates_core::compile::{check, CompileError}; use zokrates_field::{Bls12_377Field, Bls12_381Field, Bn128Field, Bw6_761Field, Field}; use zokrates_fs_resolver::FileSystemResolver; @@ -76,8 +76,15 @@ fn cli_check(sub_matches: &ArgMatches) -> Result<(), String> { }; let stdlib_path = sub_matches.value_of("stdlib-path").unwrap(); - let resolver = FileSystemResolver::with_stdlib_root(stdlib_path); + match Path::new(stdlib_path).exists() { + true => Ok(()), + _ => Err(format!( + "Invalid standard library source path: {}", + stdlib_path + )), + }?; + let resolver = FileSystemResolver::with_stdlib_root(stdlib_path); let _ = check::(source, path, Some(&resolver)).map_err(|e| { format!( "Check failed:\n\n{}", diff --git a/zokrates_cli/src/ops/compile.rs b/zokrates_cli/src/ops/compile.rs index 77cdf385..2cefb70f 100644 --- a/zokrates_cli/src/ops/compile.rs +++ b/zokrates_cli/src/ops/compile.rs @@ -97,8 +97,15 @@ fn cli_compile(sub_matches: &ArgMatches) -> Result<(), String> { }; let stdlib_path = sub_matches.value_of("stdlib-path").unwrap(); - let resolver = FileSystemResolver::with_stdlib_root(stdlib_path); + match Path::new(stdlib_path).exists() { + true => Ok(()), + _ => Err(format!( + "Invalid standard library source path: {}", + stdlib_path + )), + }?; + let resolver = FileSystemResolver::with_stdlib_root(stdlib_path); let artifacts: CompilationArtifacts = compile(source, path, Some(&resolver)).map_err(|e| { format!( From 0b1435c966844d025f48858aae78d4b646b52f64 Mon Sep 17 00:00:00 2001 From: dark64 Date: Mon, 21 Dec 2020 12:04:09 +0100 Subject: [PATCH 3/3] fix import --- zokrates_cli/src/helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zokrates_cli/src/helpers.rs b/zokrates_cli/src/helpers.rs index bc379951..295b749b 100644 --- a/zokrates_cli/src/helpers.rs +++ b/zokrates_cli/src/helpers.rs @@ -1,5 +1,5 @@ use crate::constants::*; -use core::convert::TryFrom; +use std::convert::TryFrom; #[derive(Debug)] pub enum CurveParameter {