mod evm_verifier;
use libzeropool::{
POOL_PARAMS,
circuit::tree::{tree_update, CTreePub, CTreeSec},
circuit::tx::{c_transfer, CTransferPub, CTransferSec},
clap::Clap,
};
use core::panic;
use std::{fs::File, io::Write};
use fawkes_crypto::engines::bn256::Fr;
use fawkes_crypto::backend::bellman_groth16::engines::Bn256;
use fawkes_crypto::ff_uint::Num;
use fawkes_crypto::backend::bellman_groth16::{verifier::{VK, verify}, prover::{Proof, prove}, setup::setup, Parameters};
use evm_verifier::generate_sol_data;
use fawkes_crypto::circuit::cs::CS;
use fawkes_crypto::rand::rngs::OsRng;
use libzeropool::helpers::sample_data::State;
use convert_case::{Case, Casing};
#[derive(Clap)]
struct Opts {
#[clap(subcommand)]
command: SubCommand,
}
#[derive(Clap)]
enum SubCommand {
Prove(ProveOpts),
Verify(VerifyOpts),
Setup(SetupOpts),
GenerateVerifier(GenerateVerifierOpts),
GenerateTestData(GenerateTestDataOpts),
}
#[derive(Clap)]
struct ProveOpts {
#[clap(short = "c", long = "circuit", default_value = "transfer")]
circuit: String,
#[clap(short = "p", long = "params")]
params: Option<String>,
#[clap(short = "o", long = "object")]
object: Option<String>,
#[clap(short = "r", long = "proof")]
proof: Option<String>,
#[clap(short = "i", long = "inputs")]
inputs: Option<String>,
}
#[derive(Clap)]
struct VerifyOpts {
#[clap(short = "c", long = "circuit", default_value = "transfer")]
circuit: String,
#[clap(short = "v", long = "vk")]
vk: Option<String>,
#[clap(short = "r", long = "proof")]
proof: Option<String>,
#[clap(short = "i", long = "inputs")]
inputs: Option<String>,
}
#[derive(Clap)]
struct SetupOpts {
#[clap(short = "c", long = "circuit", default_value = "transfer")]
circuit: String,
#[clap(short = "p", long = "params")]
params: Option<String>,
#[clap(short = "v", long = "vk")]
vk: Option<String>,
}
#[derive(Clap)]
struct GenerateVerifierOpts {
#[clap(short = "c", long = "circuit", default_value = "transfer")]
circuit: String,
#[clap(short = "v", long = "vk")]
vk: Option<String>,
#[clap(short = "n", long = "name")]
contract_name: Option<String>,
#[clap(short = "s", long = "solidity")]
solidity: Option<String>,
}
#[derive(Clap)]
struct GenerateTestDataOpts {
#[clap(short = "c", long = "circuit", default_value = "transfer")]
circuit: String,
#[clap(short = "o", long = "object")]
object: Option<String>
}
fn tree_circuit<C:CS<Fr=Fr>>(public: CTreePub<C>, secret: CTreeSec<C>) {
tree_update(&public, &secret, &*POOL_PARAMS);
}
fn tx_circuit<C:CS<Fr=Fr>>(public: CTransferPub<C>, secret: CTransferSec<C>) {
c_transfer(&public, &secret, &*POOL_PARAMS);
}
fn cli_setup(o:SetupOpts) {
let params_path = o.params.unwrap_or(format!("{}_params.bin", o.circuit));
let vk_path = o.vk.unwrap_or(format!("{}_verification_key.json", o.circuit));
let params = match o.circuit.as_str() {
"tree_update" => setup::<Bn256, _, _, _>(tree_circuit),
"transfer" => setup::<Bn256, _, _, _>(tx_circuit),
_ => panic!("Wrong cicruit parameter")
};
let vk = params.get_vk();
let vk_str = serde_json::to_string_pretty(&vk).unwrap();
let mut fp = File::create(params_path).unwrap();
params.write(&mut fp).unwrap();
std::fs::write(vk_path, &vk_str.into_bytes()).unwrap();
println!("setup OK");
}
fn cli_generate_verifier(o: GenerateVerifierOpts) {
let circuit = o.circuit.clone();
let vk_path = o.vk.unwrap_or(format!("{}_verification_key.json", circuit));
let contract_name = o.contract_name.unwrap_or(format!("{}_verifier", circuit).to_case(Case::Pascal));
let solidity_path = o.solidity.unwrap_or(format!("{}_verifier.sol", circuit));
let vk_str = std::fs::read_to_string(vk_path).unwrap();
let vk :VK<Bn256> = serde_json::from_str(&vk_str).unwrap();
let sol_str = generate_sol_data(&vk, contract_name);
File::create(solidity_path).unwrap().write(&sol_str.into_bytes()).unwrap();
println!("solidity verifier generated")
}
fn cli_verify(o:VerifyOpts) {
let proof_path = o.proof.unwrap_or(format!("{}_proof.json", o.circuit));
let vk_path = o.vk.unwrap_or(format!("{}_verification_key.json", o.circuit));
let inputs_path = o.inputs.unwrap_or(format!("{}_inputs.json", o.circuit));
let vk_str = std::fs::read_to_string(vk_path).unwrap();
let proof_str = std::fs::read_to_string(proof_path).unwrap();
let public_inputs_str = std::fs::read_to_string(inputs_path).unwrap();
let vk:VK<Bn256> = serde_json::from_str(&vk_str).unwrap();
let proof:Proof<Bn256> = serde_json::from_str(&proof_str).unwrap();
let public_inputs:Vec<Num<Fr>> = serde_json::from_str(&public_inputs_str).unwrap();
println!("Verify result is {}.", verify(&vk, &proof, &public_inputs))
}
fn cli_generate_test_data(o:GenerateTestDataOpts) {
let object_path = o.object.unwrap_or(format!("{}_object.json", o.circuit));
match o.circuit.as_str() {
"transfer" => {
let mut rng = OsRng::default();
let state = State::random_sample_state(&mut rng, &*POOL_PARAMS);
let data = state.random_sample_transfer(&mut rng, &*POOL_PARAMS);
let data_str = serde_json::to_string_pretty(&data).unwrap();
std::fs::write(object_path, &data_str.into_bytes()).unwrap();
},
"tree_update" => std::unimplemented!(),
_ => panic!("Wrong cicruit parameter")
}
println!("Test data generated")
}
fn cli_prove(o:ProveOpts) {
let params_path = o.params.unwrap_or(format!("{}_params.bin", o.circuit));
let object_path = o.object.unwrap_or(format!("{}_object.json", o.circuit));
let proof_path = o.proof.unwrap_or(format!("{}_proof.json", o.circuit));
let inputs_path = o.inputs.unwrap_or(format!("{}_inputs.json", o.circuit));
let params_data = std::fs::read(params_path).unwrap();
let mut params_data_cur = ¶ms_data[..];
let params = Parameters::<Bn256>::read(&mut params_data_cur, false, false).unwrap();
let object_str = std::fs::read_to_string(object_path).unwrap();
let (inputs, snark_proof) = if o.circuit.eq("tree_update") {
let (public, secret) = serde_json::from_str(&object_str).unwrap();
prove(¶ms, &public, &secret, tree_circuit)
} else {
let (public, secret) = serde_json::from_str(&object_str).unwrap();
prove(¶ms, &public, &secret, tx_circuit)
};
let proof_str = serde_json::to_string_pretty(&snark_proof).unwrap();
let inputs_str = serde_json::to_string_pretty(&inputs).unwrap();
std::fs::write(proof_path, &proof_str.into_bytes()).unwrap();
std::fs::write(inputs_path, &inputs_str.into_bytes()).unwrap();
println!("Proved")
}
pub fn main() {
let opts: Opts = Opts::parse();
match opts.command {
SubCommand::Prove(o) => cli_prove(o),
SubCommand::Verify(o) => cli_verify(o),
SubCommand::Setup(o) => cli_setup(o),
SubCommand::GenerateVerifier(o) => cli_generate_verifier(o),
SubCommand::GenerateTestData(o) => cli_generate_test_data(o)
}
}