diff --git a/.gitignore b/.gitignore index dbf3d8e..ceafe9f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ target/ Cargo.lock .vscode *.log +data \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 86f03f2..6bc9758 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,18 @@ [workspace] members = [ - "crates/*" + "crates/bellman", + "crates/boojum", + "crates/cs_derive", + "crates/ff", + "crates/ff_derive", + "crates/franklin-crypto", + "crates/pairing", + "crates/plonk-verifier-codegen", + "crates/plonk-verifier-codegen-bin", + "crates/rescue-poseidon", + "crates/snark-wrapper", ] + resolver = "2" [workspace.package] @@ -14,8 +25,8 @@ version = "0.29.0" # Local dependencies bellman = { version = "=0.29.0", path = "crates/bellman", package = "bellman_ce" } boojum = { version = "=0.29.0", path = "crates/boojum" } -zksync_solidity_vk_codegen = { version = "=0.29.0", path = "crates/codegen" } -codegen-bin = { version = "=0.29.0", path = "crates/codegen-bin" } +plonk_verifier_codegen = { version = "=0.29.0", path = "crates/plonk-verifier-codegen", package = "plonk-verifier-codegen" } +plonk_verifier_codegen_bin = { version = "=0.29.0", path = "crates/plonk-verifier-codegen-bin", package = "plonk-verifier-codegen-bin" } cs_derive = { version = "=0.29.0", path = "crates/cs_derive" } ff = { version = "=0.29.0", path = "crates/ff", package = "ff_ce" } ff_derive_ce = { version = "=0.29.0", path = "crates/ff_derive" } diff --git a/crates/codegen-bin/src/main.rs b/crates/codegen-bin/src/main.rs deleted file mode 100644 index 3e5ad4b..0000000 --- a/crates/codegen-bin/src/main.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::path::PathBuf; -use structopt::StructOpt; -use zksync_solidity_vk_codegen::{generate, Encoding}; - -const DEFAULT_OUTPUT_FILE: &str = "./hardhat/contracts"; - -const PAIRING_BN_254_FILE_PATH: &str = "PairingsBn254.sol"; -const TRANSCRIPT_LIB_FILE_PATH: &str = "TranscriptLib.sol"; -const UNCHECKED_MATH_FILE_PATH: &str = "UncheckedMath.sol"; -const PLONK_4_VERIFIER_FILE_PATH: &str = "Plonk4VerifierWithAccessToDNext.sol"; -const VEERIFIER_TEMPLATE_FILE_PATH: &str = "Verifier.sol"; - -#[derive(StructOpt, Debug)] -pub struct Opts { - /// Path to verification key (required) - #[structopt(long, parse(from_os_str))] - verification_key: PathBuf, - /// Path to the folder with templates (required) - /// Should point to the `codegen/template` directory - #[structopt(long, parse(from_os_str))] - templates_dir: PathBuf, - /// Output directory - #[structopt(long, parse(from_os_str), default_value = DEFAULT_OUTPUT_FILE)] - output: PathBuf, - - #[structopt(long)] - encoding: Option, -} - -fn main() { - let opts = Opts::from_args(); - println!("{:#?}", opts); - - let Opts { - verification_key, - output, - templates_dir, - encoding, - } = opts; - - let encoding = match encoding { - Some(encoding) => match encoding.as_str() { - "json" => Encoding::Json, - _ => Encoding::Default, - }, - None => Encoding::Default, - }; - - let template_path = |file_name: &str| templates_dir.join(file_name).to_string_lossy().into_owned(); - - generate( - verification_key, - output.clone(), - encoding, - vec![ - template_path(VEERIFIER_TEMPLATE_FILE_PATH).as_ref(), - template_path(PLONK_4_VERIFIER_FILE_PATH).as_ref(), - template_path(TRANSCRIPT_LIB_FILE_PATH).as_ref(), - template_path(PAIRING_BN_254_FILE_PATH).as_ref(), - template_path(UNCHECKED_MATH_FILE_PATH).as_ref(), - ], - ); - - eprintln!("Success!"); -} diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml deleted file mode 100644 index b3ba5a1..0000000 --- a/crates/codegen/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "zksync_solidity_vk_codegen" -version.workspace = true -edition = "2018" -license = "MIT OR Apache-2.0" -description = "ZKsync solidity codegen for vks" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -franklin-crypto = { workspace = true, features = ["plonk"] } -rescue_poseidon.workspace = true - -handlebars = "3.5.5" -serde = "1" -serde_derive = "1" -serde_json = "1" - -hex = "0.4.3" -paste = "1.0" -ethereum-types = "=0.14.1" diff --git a/crates/codegen/src/circuits.rs b/crates/codegen/src/circuits.rs deleted file mode 100644 index b918228..0000000 --- a/crates/codegen/src/circuits.rs +++ /dev/null @@ -1,150 +0,0 @@ -use franklin_crypto::{ - bellman::{ - plonk::better_better_cs::{ - cs::{Circuit, ConstraintSystem, Gate, GateInternal, LookupTableApplication, PolyIdentifier, Width4MainGateWithDNext}, - gates::selector_optimized_with_d_next::SelectorOptimizedWidth4MainGateWithDNext, - }, - Engine, Field, PrimeField, SynthesisError, - }, - plonk::circuit::{ - allocated_num::{AllocatedNum, Num}, - boolean::Boolean, - custom_rescue_gate::Rescue5CustomGate, - }, -}; - -use rescue_poseidon::{circuit_generic_hash, CustomGate, HashParams, RescueParams}; - -use paste::paste; - -macro_rules! circuit_inner { - ($id:ident, $main_gate:ty, $declare_rescue:expr, $( $synth:tt ),*) => { - #[derive(Clone, Debug)] - pub struct $id; - - impl Circuit for $id { - type MainGate = $main_gate; - - fn synthesize>( - &self, - cs: &mut CS, - ) -> Result<(), SynthesisError> { - inner_circuit_main_gate_part(cs)?; - $( - $synth(cs)?; - )* - - Ok(()) - } - - fn declare_used_gates() -> Result>>, SynthesisError> { - let has_rescue = $declare_rescue; - if has_rescue{ - Ok(vec![ - Self::MainGate::default().into_internal(), - Rescue5CustomGate::default().into_internal(), - ]) - }else{ - Ok(vec![ - Self::MainGate::default().into_internal(), - ]) - } - } - } - }; -} - -#[macro_export] -macro_rules! circuit { - ($id:ident, $main_gate:ty) => { - circuit_inner!($id, $main_gate, false, inner_circuit_main_gate_part); - paste! { - circuit_inner!([<$id WithLookup>], $main_gate, false, inner_circuit_lookup_part); - } - paste! { - circuit_inner!([<$id WithRescue>], $main_gate, true, inner_circuit_rescue_part); - } - paste! { - circuit_inner!([<$id WithLookupAndRescue>], $main_gate, true, inner_circuit_lookup_part, inner_circuit_rescue_part); - } - }; -} - -circuit!(DummyCircuit, Width4MainGateWithDNext); -circuit!(SelectorOptimizedDummyCircuit, SelectorOptimizedWidth4MainGateWithDNext); - -fn inner_circuit_main_gate_part>(cs: &mut CS) -> Result<(), SynthesisError> { - for _ in 0..32 { - let a = Num::alloc(cs, Some(E::Fr::one()))?; - let b = Num::alloc(cs, Some(E::Fr::zero()))?; - let flag = Boolean::alloc(cs, Some(true))?; - let c = Num::conditionally_select(cs, &flag, &a, &b)?; - let is_equal = Num::equals(cs, &a, &c)?; - - Boolean::enforce_equal(cs, &is_equal, &Boolean::Constant(true))?; - } - - Ok(()) -} - -fn inner_circuit_lookup_part>(cs: &mut CS) -> Result<(), SynthesisError> { - // add dummy lookup table queries - let dummy = CS::get_dummy_variable(); - dbg!("HAS LOOKUP"); - // need to create a table (any) - let columns = vec![PolyIdentifier::VariablesPolynomial(0), PolyIdentifier::VariablesPolynomial(1), PolyIdentifier::VariablesPolynomial(2)]; - let range_table = LookupTableApplication::new_range_table_of_width_3(2, columns.clone())?; - let _range_table_name = range_table.functional_name(); - - let xor_table = LookupTableApplication::new_xor_table(2, columns.clone())?; - let _xor_table_name = xor_table.functional_name(); - - let and_table = LookupTableApplication::new_and_table(2, columns)?; - let and_table_name = and_table.functional_name(); - - cs.add_table(range_table)?; - cs.add_table(xor_table)?; - cs.add_table(and_table)?; - - let binary_x_value = E::Fr::from_str("3").unwrap(); - let binary_y_value = E::Fr::from_str("1").unwrap(); - - let t = AllocatedNum::zero(cs); - let tt = AllocatedNum::one(cs); - let ttt = t.mul(cs, &tt)?; - ttt.inputize(cs)?; - - let binary_x = cs.alloc(|| Ok(binary_x_value))?; - - let binary_y = cs.alloc(|| Ok(binary_y_value))?; - - let table = cs.get_table(&and_table_name)?; - let num_keys_and_values = table.width(); - - let and_result_value = table.query(&[binary_x_value, binary_y_value])?[0]; - - let binary_z = cs.alloc(|| Ok(and_result_value))?; - - cs.begin_gates_batch_for_step()?; - - let vars = [binary_x, binary_y, binary_z, dummy]; - cs.allocate_variables_without_gate(&vars, &[])?; - - cs.apply_single_lookup_gate(&vars[..num_keys_and_values], table)?; - - cs.end_gates_batch_for_step()?; - - Ok(()) -} - -fn inner_circuit_rescue_part>(cs: &mut CS) -> Result<(), SynthesisError> { - dbg!("HAS RESCUE"); - // make single rescue hash to satisfy gate requirements of declaration - let mut params = RescueParams::default(); - params.use_custom_gate(CustomGate::QuinticWidth4); - - let elem = Num::alloc(cs, Some(E::Fr::from_str("42").unwrap()))?; - let _ = circuit_generic_hash::<_, _, _, 2, 3, 2>(cs, &[elem, elem], ¶ms, None)?; - - Ok(()) -} diff --git a/crates/codegen/src/generate.rs b/crates/codegen/src/generate.rs deleted file mode 100644 index 63fa902..0000000 --- a/crates/codegen/src/generate.rs +++ /dev/null @@ -1,253 +0,0 @@ -use franklin_crypto::bellman::plonk::better_better_cs::gates::selector_optimized_with_d_next::SelectorOptimizedWidth4MainGateWithDNext; -use franklin_crypto::bellman::{ - pairing::bn256::{Bn256, Fr}, - plonk::{ - better_better_cs::{cs::Width4MainGateWithDNext, setup::VerificationKey}, - domains::Domain, - }, - CurveAffine, Engine, -}; -use handlebars::*; -use serde_json::Map; -use std::io::Write; -use std::path::PathBuf; - -use serde::ser::Serialize; - -use crate::{ - circuits::DummyCircuit, - serialize::{FieldElement, G1Point, G2Point}, -}; - -pub enum MainGateType { - Standard, - SelectorOptimized, -} - -struct TemplateVars { - num_gates: usize, - has_rescue_custom_gate: bool, - has_lookup: bool, - is_selector_optimized_main_gate: bool, - num_main_gate_selectors: usize, - ab_coeff_idx: usize, - ac_coeff_idx: usize, - constant_coeff_idx: usize, - d_next_coeff_idx: usize, -} - -pub enum Encoding { - Json, - Default, -} - -pub fn generate(vk_path: PathBuf, output_dir: PathBuf, encoding_type: Encoding, template_files_path: Vec<&str>) { - let mut reader = std::fs::File::open(vk_path).expect("vk file"); - - let vk = match encoding_type { - Encoding::Json => serde_json::from_reader(reader).expect("read vk from json encoded data"), - Encoding::Default => VerificationKey::::read(&mut reader).expect("read vk from default encoded data"), - }; - // we know from the fact that vk belongs to a - // - standart main gate when there are 7 selectors - // - selector optimized main gate when there are 8 selectors - let num_selectors_of_main_gate = vk.gate_setup_commitments.len(); - let main_gate = if num_selectors_of_main_gate == 7 { - MainGateType::Standard - } else if num_selectors_of_main_gate == 8 { - MainGateType::SelectorOptimized - } else { - unimplemented!() - }; - - let num_gates = if vk.gate_selectors_commitments.len() == 0 { 1 } else { vk.gate_selectors_commitments.len() }; - - let has_rescue_custom_gate = if num_gates > 1 { true } else { false }; - - let has_lookup = if vk.total_lookup_entries_length > 0 { - assert!(vk.lookup_selector_commitment.is_some()); - assert!(vk.lookup_tables_commitments.len() > 0); - assert!(vk.lookup_table_type_commitment.is_some()); - true - } else { - assert!(vk.lookup_selector_commitment.is_none()); - assert!(vk.lookup_tables_commitments.len() == 0); - assert!(vk.lookup_table_type_commitment.is_none()); - false - }; - - let (num_main_gate_selectors, ab_coeff_idx, constant_coeff_idx, d_next_coeff_idx, ac_coeff_idx) = match main_gate { - MainGateType::Standard => ( - 7, - Width4MainGateWithDNext::AB_MULTIPLICATION_TERM_COEFF_INDEX, - Width4MainGateWithDNext::CONSTANT_TERM_COEFF_INDEX, - Width4MainGateWithDNext::D_NEXT_TERM_COEFF_INDEX, - None, - ), - MainGateType::SelectorOptimized => ( - 8, - SelectorOptimizedWidth4MainGateWithDNext::AB_MULTIPLICATION_TERM_COEFF_INDEX, - SelectorOptimizedWidth4MainGateWithDNext::CONSTANT_TERM_COEFF_INDEX, - SelectorOptimizedWidth4MainGateWithDNext::D_NEXT_TERM_COEFF_INDEX, - Some(SelectorOptimizedWidth4MainGateWithDNext::AC_MULTIPLICATION_TERM_COEFF_INDEX), - ), - }; - - let is_selector_optimized_main_gate = ac_coeff_idx.is_some(); - let ac_coeff_idx = if let Some(coeff) = ac_coeff_idx { coeff } else { 0 }; - - let vars = TemplateVars { - num_gates, - has_rescue_custom_gate, - has_lookup, - is_selector_optimized_main_gate, - num_main_gate_selectors, - ab_coeff_idx, - ac_coeff_idx, - constant_coeff_idx, - d_next_coeff_idx, - }; - - render(vars, vk, output_dir, template_files_path) -} - -fn render(vars: TemplateVars, vk: VerificationKey, output_dir: PathBuf, template_files_path: Vec<&str>) { - let mut map = MapWrapper::new(); - let mut handlebars = Handlebars::new(); - - map.insert("is_selector_optimized_main_gate", vars.is_selector_optimized_main_gate); - // main gate + custom rescue - map.insert("NUM_GATES", vars.num_gates); - map.insert("has_lookup", vars.has_lookup); - map.insert("has_rescue_custom_gate", vars.has_rescue_custom_gate); - map.insert("MAIN_GATE_AB_COEFF_IDX", vars.ab_coeff_idx); - map.insert("CONSTANT_TERM_COEFF_INDEX", vars.constant_coeff_idx); - map.insert("D_NEXT_TERM_COEFF_INDEX", vars.d_next_coeff_idx); - assert_eq!(vars.ab_coeff_idx, 4); - map.insert("MAIN_GATE_AC_COEFF_IDX", vars.ac_coeff_idx); - assert_eq!(vk.gate_setup_commitments.len(), vars.num_main_gate_selectors); - map.insert("NUM_MAIN_GATE_SELECTORS", vars.num_main_gate_selectors); - // a, b, c, d - println!("VK STATE WIDTH {}", vk.state_width); - map.insert("STATE_WIDTH", vk.state_width); - map.insert("DNEXT_INDEX", vk.state_width - 1); - map.insert("NUM_G2_ELS", vk.g2_elements.len()); - map.insert("NUM_LOOKUP_TABLES", vk.lookup_tables_commitments.len()); - map.insert("SERIALIZED_PROOF_LENGTH", 44); - let mut num_commitments_at_z = 2 + 4 + 3; - let mut num_commitments_at_z_omega = 1 + 2; - - let mut num_alpha_challenges = 1 + 2; - if vars.has_rescue_custom_gate { - num_commitments_at_z += 1; - num_alpha_challenges += 3; - } - if vars.has_lookup { - num_commitments_at_z += 3; - num_alpha_challenges += 3; - num_commitments_at_z_omega += 3; - } - - map.insert("rescue_alpha_idx", 1); - map.insert("num_commitments_at_z", num_commitments_at_z); - map.insert("num_commitments_at_z_omega", num_commitments_at_z_omega); - map.insert("NUM_ALPHA_CHALLENGES", num_alpha_challenges); - if vars.has_rescue_custom_gate { - map.insert("copy_permutation_alpha_idx", 4); - map.insert("lookup_alpha_idx", 6); - } else { - map.insert("copy_permutation_alpha_idx", 1); - map.insert("lookup_alpha_idx", 3); - } - - // domain - map.insert("num_inputs".into(), vk.num_inputs); - assert!(vk.num_inputs > 0); - let domain: Domain = Domain::new_for_size(vk.n as u64).expect("a domain"); - map.insert("domain_size".into(), domain.size); - map.insert("domain_generator".into(), FieldElement::from(domain.generator)); - - // G1Points - let mut gate_setup_commitments = vec![]; - for cmt in vk.gate_setup_commitments.iter() { - gate_setup_commitments.push(G1Point::from_affine_point(cmt.clone())) - } - map.insert("gate_setup_commitments", gate_setup_commitments); - - let mut gate_selectors_commitments = vec![]; - for cmt in vk.gate_selectors_commitments.iter() { - gate_selectors_commitments.push(G1Point::from_affine_point(cmt.clone())) - } - map.insert("gate_selectors_commitments", gate_selectors_commitments); - - let mut permutation_commitments = vec![]; - for cmt in vk.permutation_commitments.iter() { - permutation_commitments.push(G1Point::from_affine_point(cmt.clone())) - } - map.insert("permutation_commitments", permutation_commitments); - - if vk.total_lookup_entries_length > 0 { - assert!(vk.lookup_selector_commitment.is_some()); - assert!(vk.lookup_tables_commitments.len() > 0); - assert!(vk.lookup_table_type_commitment.is_some()); - - map.insert("has_lookup", true); - map.insert( - "lookup_selector_commitment", - G1Point::from_affine_point(vk.lookup_selector_commitment.unwrap_or(::G1Affine::zero())), - ); - if vk.total_lookup_entries_length > 0 { - assert!(vk.lookup_selector_commitment.is_some()); - } - let mut lookup_tables_commitments = vec![]; - for cmt in vk.lookup_tables_commitments.iter() { - lookup_tables_commitments.push(G1Point::from_affine_point(cmt.clone())) - } - map.insert("lookup_tables_commitments", lookup_tables_commitments); - map.insert( - "lookup_table_type_commitment", - G1Point::from_affine_point(vk.lookup_table_type_commitment.unwrap_or(::G1Affine::zero())), - ); - } - - // non residues - let mut non_residues = vec![]; - for el in vk.non_residues.iter() { - non_residues.push(FieldElement::from(el.clone())); - } - map.insert("non_residues", non_residues); - - // pairing g2 elements - let mut g2_elements = vec![]; - for point in vk.g2_elements.iter() { - g2_elements.push(G2Point::from_affine_point(point.clone())); - } - map.insert("g2_elements", g2_elements); - - for template_file_path in template_files_path { - let mut output_path = output_dir.clone(); - output_path.push(template_file_path.split('/').last().unwrap()); - let mut writer = std::fs::File::create(output_path).expect("output file"); - // register template from a file and assign a name to it - handlebars - .register_template_file("contract", template_file_path.clone()) - .expect(&format!("must read the template at path {}", template_file_path)); - - let rendered = handlebars.render("contract", &map.inner).unwrap(); - - writer.write(rendered.as_bytes()).expect("must write to file"); - } -} - -struct MapWrapper { - inner: Map, -} -impl MapWrapper { - fn new() -> Self { - Self { inner: Map::new() } - } - - fn insert(&mut self, key: &str, value: T) -> Option { - self.inner.insert(key.into(), to_json(value)) - } -} diff --git a/crates/codegen-bin/Cargo.toml b/crates/plonk-verifier-codegen-bin/Cargo.toml similarity index 65% rename from crates/codegen-bin/Cargo.toml rename to crates/plonk-verifier-codegen-bin/Cargo.toml index 82bc52c..83a0bbc 100644 --- a/crates/codegen-bin/Cargo.toml +++ b/crates/plonk-verifier-codegen-bin/Cargo.toml @@ -1,14 +1,17 @@ [package] -name = "codegen-bin" +name = "plonk-verifier-codegen-bin" version.workspace = true edition = "2018" license = "MIT OR Apache-2.0" -publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -zksync_solidity_vk_codegen.workspace = true +plonk_verifier_codegen.workspace = true + structopt = "0.3" dialoguer = "0.8" +[[bin]] +name = "generate" +path = "src/main.rs" diff --git a/crates/plonk-verifier-codegen-bin/src/main.rs b/crates/plonk-verifier-codegen-bin/src/main.rs new file mode 100644 index 0000000..6d2d1ce --- /dev/null +++ b/crates/plonk-verifier-codegen-bin/src/main.rs @@ -0,0 +1,65 @@ +use plonk_verifier_codegen::{generate, Encoding}; +use std::path::PathBuf; +use structopt::StructOpt; + +const DEFAULT_OUTPUT_FILE: &str = "./crates/plonk-verifier-foundry"; + +const PAIRING_BN_254_FILE_PATH: &str = "./crates/plonk-verifier-codegen/template/PairingsBn254.sol"; +const TRANSCRIPT_LIB_FILE_PATH: &str = "./crates/plonk-verifier-codegen/template/TranscriptLib.sol"; +const UNCHECKED_MATH_FILE_PATH: &str = "./crates/plonk-verifier-codegen/template/UncheckedMath.sol"; +const PLONK_4_VERIFIER_FILE_PATH: &str = "./crates/plonk-verifier-codegen/template/Plonk4VerifierWithAccessToDNext.sol"; +const VEERIFIER_TEMPLATE_FILE_PATH: &str = "./crates/plonk-verifier-codegen/template/Verifier.sol"; +const PROOF_TEST_TEMPLATE_FILE_PATH: &str = "./crates/plonk-verifier-codegen/template/HardcodedValues.sol"; + +#[derive(StructOpt, Debug)] +pub struct Opts { + /// Path to verification key(required) + #[structopt(long, parse(from_os_str))] + verification_key: PathBuf, + /// Path to proof key(optional) + #[structopt(long, parse(from_os_str))] + proof: Option, + /// Output directory + #[structopt(long, parse(from_os_str), default_value = DEFAULT_OUTPUT_FILE)] + output: PathBuf, + + #[structopt(long)] + encoding: Option, +} + +fn main() { + let opts = Opts::from_args(); + println!("{:#?}", opts); + + let Opts { + verification_key, + proof, + output, + encoding, + } = opts; + + let encoding = match encoding { + Some(encoding) => match encoding.as_str() { + "json" => Encoding::Json, + _ => Encoding::Default, + }, + None => Encoding::Default, + }; + + generate( + verification_key, + proof, + output.clone(), + encoding, + vec![ + VEERIFIER_TEMPLATE_FILE_PATH, + PLONK_4_VERIFIER_FILE_PATH, + TRANSCRIPT_LIB_FILE_PATH, + PAIRING_BN_254_FILE_PATH, + UNCHECKED_MATH_FILE_PATH, + PROOF_TEST_TEMPLATE_FILE_PATH, + ], + ); + + eprintln!("Success!"); +} diff --git a/crates/codegen/.gitignore b/crates/plonk-verifier-codegen/.gitignore similarity index 100% rename from crates/codegen/.gitignore rename to crates/plonk-verifier-codegen/.gitignore diff --git a/crates/plonk-verifier-codegen/Cargo.toml b/crates/plonk-verifier-codegen/Cargo.toml new file mode 100644 index 0000000..40c32d8 --- /dev/null +++ b/crates/plonk-verifier-codegen/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "plonk-verifier-codegen" +version.workspace = true +edition = "2018" +license = "MIT OR Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +handlebars = "*" +serde = "*" +serde_derive = "*" +serde_json = "*" +hex = "*" +paste = "1.0" +ethereum-types = "=0.14.1" +rescue_poseidon = {package = "rescue_poseidon", path = "../rescue-poseidon"} +# rescue-poseidon = {package = "rescue_poseidon", git = "https://github.com/matter-labs/rescue-poseidon"} diff --git a/crates/plonk-verifier-codegen/src/circuits.rs b/crates/plonk-verifier-codegen/src/circuits.rs new file mode 100644 index 0000000..732c403 --- /dev/null +++ b/crates/plonk-verifier-codegen/src/circuits.rs @@ -0,0 +1,271 @@ +use rescue_poseidon::franklin_crypto::{ + bellman::{ + bn256::{Bn256, Fr}, + kate_commitment::{Crs, CrsForMonomialForm}, + plonk::{ + better_better_cs::{ + cs::{ + ArithmeticTerm, Circuit, ConstraintSystem, Gate, GateInternal, LookupTableApplication, MainGate, MainGateTerm, PlonkConstraintSystemParams, + PlonkCsWidth4WithNextStepAndCustomGatesParams, PlonkCsWidth4WithNextStepParams, PolyIdentifier, TrivialAssembly, VerificationKey, Width4MainGateWithDNext, + }, + gates::selector_optimized_with_d_next::SelectorOptimizedWidth4MainGateWithDNext, + proof::Proof, + }, + commitments::transcript::{keccak_transcript::RollingKeccakTranscript, Transcript}, + }, + worker::Worker, + Engine, Field, PrimeField, SynthesisError, + }, + plonk::circuit::{ + allocated_num::{AllocatedNum, Num}, + boolean::Boolean, + custom_rescue_gate::Rescue5CustomGate, + linear_combination::LinearCombination, + Width4WithCustomGates, + }, +}; + +use rescue_poseidon::{circuit_generic_hash, CustomGate, HashParams, RescueParams}; + +use paste::paste; + +macro_rules! circuit_inner { + ($id:ident, $main_gate:ty, $declare_rescue:expr, $( $synth:tt ),*) => { + #[derive(Clone, Debug)] + pub struct $id; + + impl Circuit for $id { + type MainGate = $main_gate; + + fn synthesize>( + &self, + cs: &mut CS, + ) -> Result<(), SynthesisError> { + inner_circuit_main_gate_part(cs)?; + $( + $synth(cs)?; + )* + + Ok(()) + } + + fn declare_used_gates() -> Result>>, SynthesisError> { + let has_rescue = $declare_rescue; + if has_rescue{ + Ok(vec![ + Self::MainGate::default().into_internal(), + Rescue5CustomGate::default().into_internal(), + ]) + }else{ + Ok(vec![ + Self::MainGate::default().into_internal(), + ]) + } + } + } + }; +} + +#[macro_export] +macro_rules! circuit { + ($id:ident, $main_gate:ty) => { + circuit_inner!($id, $main_gate, false, inner_circuit_main_gate_part); + paste! { + circuit_inner!([<$id WithLookup>], $main_gate, false, inner_circuit_lookup_part); + } + paste! { + circuit_inner!([<$id WithRescue>], $main_gate, true, inner_circuit_rescue_part); + } + paste! { + circuit_inner!([<$id WithLookupAndRescue>], $main_gate, true, inner_circuit_lookup_part, inner_circuit_rescue_part); + } + }; +} + +circuit!(MockCircuit, Width4MainGateWithDNext); +circuit!(MockCircuitSelectorOptimized, SelectorOptimizedWidth4MainGateWithDNext); + +fn inner_circuit_main_gate_part>(cs: &mut CS) -> Result<(), SynthesisError> { + for _ in 0..32 { + let a = Num::alloc(cs, Some(E::Fr::one()))?; + let b = Num::alloc(cs, Some(E::Fr::zero()))?; + let flag = Boolean::alloc(cs, Some(true))?; + let c = Num::conditionally_select(cs, &flag, &a, &b)?; + let is_equal = Num::equals(cs, &a, &c)?; + Boolean::enforce_equal(cs, &is_equal, &Boolean::Constant(true))?; + + let mut lc = LinearCombination::zero(); + for idx in 0..6 { + let el = E::Fr::from_str(&idx.to_string()).unwrap(); + let allocated = Num::alloc(cs, Some(el))?; + lc.add_assign_number_with_coeff(&allocated, E::Fr::one()); + } + let sum = Num::alloc(cs, Some(E::Fr::from_str(&20.to_string()).unwrap()))?; + let mut minus_one = E::Fr::one(); + minus_one.negate(); + lc.add_assign_number_with_coeff(&sum, minus_one); + } + let a = Num::alloc(cs, Some(E::Fr::one()))?; + let b = Num::alloc(cs, Some(E::Fr::one()))?; + let expected = cs.alloc_input(|| Ok(E::Fr::from_str(&2.to_string()).unwrap()))?; + let actual = a.add(cs, &b)?; + + let mut term = MainGateTerm::::new(); + term.add_assign(ArithmeticTerm::from_variable(expected)); + term.sub_assign(ArithmeticTerm::from_variable(actual.get_variable().get_variable())); + cs.allocate_main_gate(term)?; + + Ok(()) +} + +fn inner_circuit_lookup_part>(cs: &mut CS) -> Result<(), SynthesisError> { + // add dummy lookup table queries + let dummy = CS::get_dummy_variable(); + // need to create a table (any) + let columns = vec![PolyIdentifier::VariablesPolynomial(0), PolyIdentifier::VariablesPolynomial(1), PolyIdentifier::VariablesPolynomial(2)]; + let range_table = LookupTableApplication::new_range_table_of_width_3(2, columns.clone())?; + let _range_table_name = range_table.functional_name(); + + let xor_table = LookupTableApplication::new_xor_table(2, columns.clone())?; + let _xor_table_name = xor_table.functional_name(); + + let and_table = LookupTableApplication::new_and_table(2, columns)?; + let and_table_name = and_table.functional_name(); + + cs.add_table(range_table)?; + cs.add_table(xor_table)?; + cs.add_table(and_table)?; + + let binary_x_value = E::Fr::from_str("3").unwrap(); + let binary_y_value = E::Fr::from_str("1").unwrap(); + + let t = AllocatedNum::zero(cs); + let tt = AllocatedNum::one(cs); + let ttt = t.mul(cs, &tt)?; + ttt.inputize(cs)?; + + let binary_x = cs.alloc(|| Ok(binary_x_value))?; + + let binary_y = cs.alloc(|| Ok(binary_y_value))?; + + let table = cs.get_table(&and_table_name)?; + let num_keys_and_values = table.width(); + + let and_result_value = table.query(&[binary_x_value, binary_y_value])?[0]; + + let binary_z = cs.alloc(|| Ok(and_result_value))?; + + cs.begin_gates_batch_for_step()?; + + let vars = [binary_x, binary_y, binary_z, dummy]; + cs.allocate_variables_without_gate(&vars, &[])?; + + cs.apply_single_lookup_gate(&vars[..num_keys_and_values], table)?; + + cs.end_gates_batch_for_step()?; + + Ok(()) +} + +fn inner_circuit_rescue_part>(cs: &mut CS) -> Result<(), SynthesisError> { + // make single rescue hash to satisfy gate requirements of declaration + let mut params = RescueParams::default(); + params.use_custom_gate(CustomGate::QuinticWidth4); + + let elem = Num::alloc(cs, Some(E::Fr::from_str("42").unwrap()))?; + let _ = circuit_generic_hash::<_, _, _, 2, 3, 2>(cs, &[elem, elem], ¶ms, None)?; + + Ok(()) +} + +#[test] +#[ignore] +fn test_create_proof_for_all_circuits() { + type T = RollingKeccakTranscript; + + let base_dir = std::env::var("PLONK_VERIFIER_DATA_DIR").expect("output dir for output files"); + let out_dir = format!("{}/std", base_dir,); + println!("out dir {}", out_dir); + println!("Std main gate"); + create_proof_for_circuit::<_, PlonkCsWidth4WithNextStepParams, Width4MainGateWithDNext, T>(MockCircuit {}, &format!("{}/std", &out_dir)); + println!("Std main gate with lookup"); + create_proof_for_circuit::<_, PlonkCsWidth4WithNextStepParams, Width4MainGateWithDNext, T>(MockCircuitWithLookup {}, &format!("{}/std_with_lookup", &out_dir)); + println!("Std main gate with sbox custom gate"); + create_proof_for_circuit::<_, PlonkCsWidth4WithNextStepAndCustomGatesParams, Width4MainGateWithDNext, T>(MockCircuitWithRescue {}, &format!("{}/std_with_rescue", &out_dir)); + println!("Std main gate with lookup and sbox custom gate"); + create_proof_for_circuit::<_, PlonkCsWidth4WithNextStepAndCustomGatesParams, Width4MainGateWithDNext, T>(MockCircuitWithLookupAndRescue {}, &format!("{}/std_with_lookup_and_rescue", &out_dir)); + + let out_dir = format!("{}/optimized", base_dir,); + println!("SelectorOptimized main gate"); + create_proof_for_circuit::<_, PlonkCsWidth4WithNextStepAndCustomGatesParams, SelectorOptimizedWidth4MainGateWithDNext, T>(MockCircuitSelectorOptimized {}, &format!("{}/optimized", &out_dir)); + println!("SelectorOptimized main gate with lookup"); + create_proof_for_circuit::<_, PlonkCsWidth4WithNextStepAndCustomGatesParams, SelectorOptimizedWidth4MainGateWithDNext, T>( + MockCircuitSelectorOptimizedWithLookup {}, + &format!("{}/optimized_with_lookup", &out_dir), + ); + println!("SelectorOptimized main gate with sbox custom gate"); + create_proof_for_circuit::<_, PlonkCsWidth4WithNextStepAndCustomGatesParams, SelectorOptimizedWidth4MainGateWithDNext, T>( + MockCircuitSelectorOptimizedWithRescue {}, + &format!("{}/optimized_with_rescue", &out_dir), + ); + println!("SelectorOptimized main gate with lookup and sbox custom gate"); + create_proof_for_circuit::<_, PlonkCsWidth4WithNextStepAndCustomGatesParams, SelectorOptimizedWidth4MainGateWithDNext, T>( + MockCircuitSelectorOptimizedWithLookupAndRescue {}, + &format!("{}/optimized_with_lookup_and_rescue", &out_dir), + ); +} + +fn init_crs(worker: &Worker, domain_size: usize) -> Crs { + let mon_crs = if let Ok(crs_file_path) = std::env::var("CRS_FILE") { + println!("using crs file at {crs_file_path}"); + let crs_file = std::fs::File::open(&crs_file_path).expect(&format!("crs file at {}", crs_file_path)); + let mon_crs = Crs::::read(crs_file).expect(&format!("read crs file at {}", crs_file_path)); + assert!(domain_size <= mon_crs.g1_bases.len()); + + mon_crs + } else { + Crs::::crs_42(domain_size, &worker) + }; + + mon_crs +} + +fn create_proof_for_circuit, P: PlonkConstraintSystemParams + 'static, MG: MainGate, T: Transcript>( + circuit: C, + out_dir: &str, +) -> (VerificationKey, Proof) { + let worker = Worker::new(); + println!("Synthesizing circuit"); + let mut assembly = TrivialAssembly::::new(); + circuit.synthesize(&mut assembly).unwrap(); + assert!(assembly.is_satisfied()); + assembly.finalize(); + + let domain_size = assembly.n() + 1; + assert!(domain_size.is_power_of_two()); + println!("Generating setup"); + let setup = assembly.create_setup::(&worker).unwrap(); + + let crs = init_crs(&worker, domain_size); + println!("Generating Vk"); + let vk = VerificationKey::from_setup(&setup, &worker, &crs).expect("vk from setup"); + + println!("Generating proof"); + let proof = assembly.create_proof::(&worker, &setup, &crs, None).expect("proof"); + + save_proof_and_vk_into_file(&proof, &vk, &out_dir); + + (vk, proof) +} + +pub fn save_proof_and_vk_into_file>(proof: &Proof, vk: &VerificationKey, output_path: &str) { + let proof_file_path = format!("{}_proof.json", output_path); + let proof_file = std::fs::File::create(&proof_file_path).unwrap(); + serde_json::to_writer(proof_file, &proof).unwrap(); + println!("proof saved at {proof_file_path}"); + + let vk_file_path = format!("{}_vk.json", output_path); + let vk_file = std::fs::File::create(&vk_file_path).unwrap(); + serde_json::to_writer(vk_file, &vk).unwrap(); + println!("vk saved at {vk_file_path}"); +} diff --git a/crates/plonk-verifier-codegen/src/generate.rs b/crates/plonk-verifier-codegen/src/generate.rs new file mode 100644 index 0000000..f384c8b --- /dev/null +++ b/crates/plonk-verifier-codegen/src/generate.rs @@ -0,0 +1,579 @@ +use rescue_poseidon::franklin_crypto::bellman::bn256::G1Affine; +use rescue_poseidon::franklin_crypto::bellman::plonk::better_better_cs::gates::selector_optimized_with_d_next::SelectorOptimizedWidth4MainGateWithDNext; +use rescue_poseidon::franklin_crypto::bellman::plonk::better_better_cs::proof::{self, Proof}; + +use handlebars::*; +use rescue_poseidon::franklin_crypto::bellman::plonk::commitments::transcript::keccak_transcript::RollingKeccakTranscript; +use rescue_poseidon::franklin_crypto::bellman::PrimeField; +use rescue_poseidon::franklin_crypto::bellman::{ + pairing::bn256::{Bn256, Fr}, + plonk::{ + better_better_cs::{cs::Width4MainGateWithDNext, setup::VerificationKey}, + domains::Domain, + }, + CurveAffine, Engine, +}; +use serde_json::Map; +use std::io::Write; +use std::path::PathBuf; + +use serde::ser::Serialize; + +use crate::circuits::{ + MockCircuitSelectorOptimized, MockCircuitSelectorOptimizedWithLookup, MockCircuitSelectorOptimizedWithLookupAndRescue, MockCircuitSelectorOptimizedWithRescue, MockCircuitWithLookup, + MockCircuitWithLookupAndRescue, MockCircuitWithRescue, +}; +use crate::{ + circuits::MockCircuit, + serialize::{FieldElement, G1Point, G2Point}, +}; + +pub enum MainGateType { + Standard, + SelectorOptimized, +} + +struct TemplateVars { + has_rescue_custom_gate: bool, + has_lookup: bool, + is_selector_optimized_main_gate: bool, + num_main_gate_selectors: usize, + ab_coeff_idx: usize, + ac_coeff_idx: usize, + constant_coeff_idx: usize, + d_next_coeff_idx: usize, +} + +pub enum Encoding { + Json, + Default, +} + +// Generally it is okey to use MockCircuit in type definitions +// and then transmute it to the corresponding type of circuit +// by inspecting some values +pub fn generate(vk_path: PathBuf, proof_path: Option, output_dir: PathBuf, encoding_type: Encoding, mut template_files_path: Vec<&str>) { + let mut reader = std::fs::File::open(vk_path).expect("vk file"); + + let vk = match encoding_type { + Encoding::Json => serde_json::from_reader(reader).expect("read vk from json encoded data"), + Encoding::Default => VerificationKey::::read(&mut reader).expect("read vk from default encoded data"), + }; + + // we know from the fact that vk belongs to a + // - standart main gate when there are 7 selectors + // - selector optimized main gate when there are 8 selectors + let num_selectors_of_main_gate = vk.gate_setup_commitments.len(); + let main_gate = if num_selectors_of_main_gate == 7 { + MainGateType::Standard + } else if num_selectors_of_main_gate == 8 { + MainGateType::SelectorOptimized + } else { + unimplemented!() + }; + + let has_rescue_custom_gate = if vk.gate_selectors_commitments.len() > 1 { + assert_eq!(vk.gate_selectors_commitments.len(), 2, "only sbox custom gate is supported"); + true + } else { + assert!(vk.gate_selectors_commitments.is_empty()); + false + }; + + let has_lookup = if vk.total_lookup_entries_length > 0 { + assert!(vk.lookup_selector_commitment.is_some()); + assert!(vk.lookup_tables_commitments.len() > 0); + assert!(vk.lookup_table_type_commitment.is_some()); + true + } else { + assert!(vk.lookup_selector_commitment.is_none()); + assert!(vk.lookup_tables_commitments.len() == 0); + assert!(vk.lookup_table_type_commitment.is_none()); + false + }; + + let (num_main_gate_selectors, ab_coeff_idx, constant_coeff_idx, d_next_coeff_idx, ac_coeff_idx) = match main_gate { + MainGateType::Standard => ( + 7, + Width4MainGateWithDNext::AB_MULTIPLICATION_TERM_COEFF_INDEX, + Width4MainGateWithDNext::CONSTANT_TERM_COEFF_INDEX, + Width4MainGateWithDNext::D_NEXT_TERM_COEFF_INDEX, + None, + ), + MainGateType::SelectorOptimized => ( + 8, + SelectorOptimizedWidth4MainGateWithDNext::AB_MULTIPLICATION_TERM_COEFF_INDEX, + SelectorOptimizedWidth4MainGateWithDNext::CONSTANT_TERM_COEFF_INDEX, + SelectorOptimizedWidth4MainGateWithDNext::D_NEXT_TERM_COEFF_INDEX, + Some(SelectorOptimizedWidth4MainGateWithDNext::AC_MULTIPLICATION_TERM_COEFF_INDEX), + ), + }; + + let is_selector_optimized_main_gate = ac_coeff_idx.is_some(); + let ac_coeff_idx = if let Some(coeff) = ac_coeff_idx { coeff } else { 0 }; + + let vars = TemplateVars { + has_rescue_custom_gate, + has_lookup, + is_selector_optimized_main_gate, + num_main_gate_selectors, + ab_coeff_idx, + ac_coeff_idx, + constant_coeff_idx, + d_next_coeff_idx, + }; + + let proof_template_dir = template_files_path.pop().unwrap(); + + render_verifier(vars, &vk, &output_dir, &template_files_path); + + if let Some(proof_path) = proof_path { + let mut reader = std::fs::File::open(proof_path).expect("proof file"); + let proof = match encoding_type { + Encoding::Json => serde_json::from_reader(reader).expect("read proof from json encoded data"), + Encoding::Default => Proof::::read(&mut reader).expect("read proof from default encoded data"), + }; + unsafe { verify_proof(&vk, &proof, main_gate) }; + render_expected_proofs(proof, proof_template_dir, &output_dir, has_rescue_custom_gate, has_lookup); + } +} + +unsafe fn verify_proof(vk: &VerificationKey, proof: &Proof, main_gate_type: MainGateType) { + use rescue_poseidon::franklin_crypto::bellman::plonk::better_better_cs::verifier::verify; + + assert_eq!(vk.n, proof.n); + assert_eq!(vk.num_inputs, proof.inputs.len()); + + let has_lookup = vk.total_lookup_entries_length > 0; + assert_eq!(has_lookup, proof.lookup_grand_product_commitment.is_some()); + assert_eq!(has_lookup, proof.lookup_s_poly_commitment.is_some()); + assert_eq!(has_lookup, proof.lookup_grand_product_opening_at_z_omega.is_some()); + assert_eq!(has_lookup, proof.lookup_s_poly_opening_at_z_omega.is_some()); + assert_eq!(has_lookup, proof.lookup_selector_poly_opening_at_z.is_some()); + assert_eq!(has_lookup, proof.lookup_t_poly_opening_at_z.is_some()); + assert_eq!(has_lookup, proof.lookup_t_poly_opening_at_z_omega.is_some()); + assert_eq!(has_lookup, proof.lookup_table_type_poly_opening_at_z.is_some()); + + let has_custom_gate = vk.gate_selectors_commitments.len() > 0; + + let is_valid = match main_gate_type { + MainGateType::Standard => { + if !has_lookup && !has_custom_gate { + verify::>(std::mem::transmute(vk), std::mem::transmute(proof), None).unwrap() + } else if has_lookup && !has_custom_gate { + verify::>(std::mem::transmute(vk), std::mem::transmute(proof), None).unwrap() + } else if has_custom_gate && !has_lookup { + verify::>(std::mem::transmute(vk), std::mem::transmute(proof), None).unwrap() + } else { + assert!(has_lookup); + assert!(has_custom_gate); + verify::>(std::mem::transmute(vk), std::mem::transmute(proof), None).unwrap() + } + } + MainGateType::SelectorOptimized => { + if !has_lookup && !has_custom_gate { + verify::>(std::mem::transmute(vk), std::mem::transmute(proof), None).unwrap() + } else if has_lookup && !has_custom_gate { + verify::>(std::mem::transmute(vk), std::mem::transmute(proof), None).unwrap() + } else if has_custom_gate && !has_lookup { + verify::>(std::mem::transmute(vk), std::mem::transmute(proof), None).unwrap() + } else { + assert!(has_lookup); + assert!(has_custom_gate); + verify::>(std::mem::transmute(vk), std::mem::transmute(proof), None).unwrap() + } + } + }; + + assert!(is_valid, "proof verification failed at codegen"); +} + +fn length_of_serialized_proof_from_vk(vk: &VerificationKey, has_custom_gate: bool, has_lookup: bool, has_dnext: bool, quotient_degree: usize) -> usize { + assert_eq!(vk.state_width, quotient_degree); + + let mut num_commitments = vk.state_width; // trace + num_commitments += 1; // copy-perm z(x) + num_commitments += quotient_degree; + num_commitments += 2; // opening proofs + + if has_lookup { + num_commitments += 2; // lookup z(x) + lookup s(x) + } + + // openings + let mut num_openings = vk.state_width; // trace + if has_dnext { + num_openings += 1; + } + + if has_custom_gate { + num_openings += 1; // main gate selector + } + + num_openings += vk.state_width - 1; // sigmas + // copy-perm z(z) is part of linearizaton + num_openings += 1; // copy-perm z(z*w) + + if has_lookup { + // - s(z*w) + // - z(z*w) + // - t(z) + // - t(z*w) + // - selector(z) + // - type(z) + num_openings += 6; + } + + num_openings += 1; // quotient + num_openings += 1; // linearization + + 2 * num_commitments + num_openings +} + +fn compute_quotient_degree(state_width: usize, has_custom_gate: bool, has_lookup: bool) -> usize { + let mut main_gate_quotient_degree = 2; + if has_custom_gate { + main_gate_quotient_degree += 1; + } + + let copy_perm_quotient_degree = state_width; + + let lookup_quotient_degree = if has_lookup { 2 } else { 0 }; + + [main_gate_quotient_degree, copy_perm_quotient_degree, lookup_quotient_degree].iter().cloned().max().unwrap() +} + +fn render_verifier(vars: TemplateVars, vk: &VerificationKey, output_dir: &PathBuf, template_files_path: &[&str]) { + let mut map = MapWrapper::new(); + let mut handlebars = Handlebars::new(); + + map.insert("is_selector_optimized_main_gate", vars.is_selector_optimized_main_gate); + // main gate + custom rescue + map.insert("has_lookup", vars.has_lookup); + map.insert("has_rescue_custom_gate", vars.has_rescue_custom_gate); + map.insert("MAIN_GATE_AB_COEFF_IDX", vars.ab_coeff_idx); + map.insert("CONSTANT_TERM_COEFF_INDEX", vars.constant_coeff_idx); + map.insert("D_NEXT_TERM_COEFF_INDEX", vars.d_next_coeff_idx); + assert_eq!(vars.ab_coeff_idx, 4); + map.insert("MAIN_GATE_AC_COEFF_IDX", vars.ac_coeff_idx); + assert_eq!(vk.gate_setup_commitments.len(), vars.num_main_gate_selectors); + map.insert("NUM_MAIN_GATE_SELECTORS", vars.num_main_gate_selectors); + // a, b, c, d + println!("VK STATE WIDTH {}", vk.state_width); + map.insert("STATE_WIDTH", vk.state_width); + map.insert("DNEXT_INDEX", vk.state_width - 1); + map.insert("NUM_G2_ELS", vk.g2_elements.len()); + map.insert("NUM_LOOKUP_TABLES", vk.lookup_tables_commitments.len()); + let quotient_degree = compute_quotient_degree(vk.state_width, vars.has_rescue_custom_gate, vars.has_lookup); + let serialized_proof_length = length_of_serialized_proof_from_vk(vk, vars.has_rescue_custom_gate, vars.has_lookup, vars.d_next_coeff_idx > 0, quotient_degree); + map.insert("SERIALIZED_PROOF_LENGTH", serialized_proof_length); + let mut num_commitments_at_z = 2 + 4 + 3; + let mut num_commitments_at_z_omega = 1 + 2; + + let mut num_alpha_challenges = 1 + 2; + if vars.has_rescue_custom_gate { + num_commitments_at_z += 1; + num_alpha_challenges += 3; + } + if vars.has_lookup { + num_commitments_at_z += 3; + num_alpha_challenges += 3; + num_commitments_at_z_omega += 3; + } + + map.insert("rescue_alpha_idx", 1); + map.insert("num_commitments_at_z", num_commitments_at_z); + map.insert("num_commitments_at_z_omega", num_commitments_at_z_omega); + map.insert("NUM_ALPHA_CHALLENGES", num_alpha_challenges); + if vars.has_rescue_custom_gate { + map.insert("copy_permutation_alpha_idx", 4); + map.insert("lookup_alpha_idx", 6); + } else { + map.insert("copy_permutation_alpha_idx", 1); + map.insert("lookup_alpha_idx", 3); + } + + // domain + map.insert("num_inputs".into(), vk.num_inputs); + // assert!(vk.num_inputs > 0); + let domain: Domain = Domain::new_for_size(vk.n as u64).expect("a domain"); + map.insert("domain_size".into(), domain.size); + map.insert("domain_generator".into(), FieldElement::from(domain.generator)); + + // G1Points + let mut gate_setup_commitments = vec![]; + for cmt in vk.gate_setup_commitments.iter() { + gate_setup_commitments.push(G1Point::from_affine_point(cmt.clone())) + } + map.insert("gate_setup_commitments", gate_setup_commitments); + + let mut gate_selectors_commitments = vec![]; + for cmt in vk.gate_selectors_commitments.iter() { + gate_selectors_commitments.push(G1Point::from_affine_point(cmt.clone())) + } + map.insert("gate_selectors_commitments", gate_selectors_commitments); + + let mut permutation_commitments = vec![]; + for cmt in vk.permutation_commitments.iter() { + permutation_commitments.push(G1Point::from_affine_point(cmt.clone())) + } + map.insert("permutation_commitments", permutation_commitments); + + if vk.total_lookup_entries_length > 0 { + assert!(vk.lookup_selector_commitment.is_some()); + assert!(vk.lookup_tables_commitments.len() > 0); + assert!(vk.lookup_table_type_commitment.is_some()); + + map.insert("has_lookup", true); + map.insert( + "lookup_selector_commitment", + G1Point::from_affine_point(vk.lookup_selector_commitment.unwrap_or(::G1Affine::zero())), + ); + if vk.total_lookup_entries_length > 0 { + assert!(vk.lookup_selector_commitment.is_some()); + } + let mut lookup_tables_commitments = vec![]; + for cmt in vk.lookup_tables_commitments.iter() { + lookup_tables_commitments.push(G1Point::from_affine_point(cmt.clone())) + } + map.insert("lookup_tables_commitments", lookup_tables_commitments); + map.insert( + "lookup_table_type_commitment", + G1Point::from_affine_point(vk.lookup_table_type_commitment.unwrap_or(::G1Affine::zero())), + ); + } + + // non residues + let mut non_residues = vec![]; + for el in vk.non_residues.iter() { + non_residues.push(FieldElement::from(el.clone())); + } + map.insert("non_residues", non_residues); + + // pairing g2 elements + let mut g2_elements = vec![]; + for point in vk.g2_elements.iter() { + g2_elements.push(G2Point::from_affine_point(point.clone())); + } + map.insert("g2_elements", g2_elements); + + let mut src_dir = output_dir.clone(); + src_dir.push("src"); + for template_file_path in template_files_path { + let mut output_path = src_dir.clone(); + output_path.push(template_file_path.split('/').last().unwrap()); + let mut writer = std::fs::File::create(output_path).expect("output file"); + // register template from a file and assign a name to it + handlebars + .register_template_file("contract", template_file_path) + .expect(&format!("must read the template at path {}", template_file_path)); + + let rendered = handlebars.render("contract", &map.inner).unwrap(); + + writer.write(rendered.as_bytes()).expect("must write to file"); + } +} + +fn render_expected_proofs(proof: Proof, proof_template_dir: &str, output_dir: &PathBuf, has_custom_gate: bool, has_lookup: bool) { + let output_file = format!("{}/test/HardcodedValues.sol", output_dir.to_string_lossy()); + let mut writer = std::fs::File::create(output_file).expect("output file"); + let mut handlebars = Handlebars::new(); + handlebars + .register_template_file("contract", proof_template_dir) + .expect(&format!("must read the template at path {}", proof_template_dir)); + let json_proof = transform_proof_into_json(proof); + let serialized_inputs = json_proof.inputs.clone(); + let serialized_proof = serialize_proof(json_proof.clone(), has_custom_gate, has_lookup); + + let num_inputs = serialized_inputs.len(); + let serialized_proof_length = serialized_proof.len(); + let hardcoded_values = HardcodedProofValues { + proof: json_proof, + serialized_inputs, + serialized_proof, + num_inputs, + serialized_proof_length, + has_custom_gate, + has_lookup, + }; + let rendered = handlebars.render("contract", &hardcoded_values).unwrap(); + + writer.write(rendered.as_bytes()).expect("must write to file"); +} + +#[derive(Clone, Default, serde::Serialize)] +pub struct HardcodedProofValues { + proof: JsonProof, + serialized_inputs: Vec, + serialized_proof: Vec, + num_inputs: usize, + serialized_proof_length: usize, + has_lookup: bool, + has_custom_gate: bool, +} + +#[derive(Clone, Default, serde::Serialize)] +pub struct JsonProof { + pub n: usize, + pub inputs: Vec, + pub state_polys_commitments: Vec<[String; 2]>, + pub copy_permutation_grand_product_commitment: [String; 2], + + pub lookup_s_poly_commitment: [String; 2], + pub lookup_grand_product_commitment: [String; 2], + + pub quotient_poly_parts_commitments: Vec<[String; 2]>, + + pub state_polys_openings_at_z: Vec, + pub state_polys_openings_at_z_omega: Vec, + + pub gate_setup_openings_at_z: Vec, + pub gate_selectors_openings_at_z: Vec, + + pub copy_permutation_polys_openings_at_z: Vec, + pub copy_permutation_grand_product_opening_at_z_omega: String, + + pub lookup_s_poly_opening_at_z_omega: String, + pub lookup_grand_product_opening_at_z_omega: String, + + pub lookup_t_poly_opening_at_z: String, + pub lookup_t_poly_opening_at_z_omega: String, + + pub lookup_selector_poly_opening_at_z: String, + pub lookup_table_type_poly_opening_at_z: String, + + pub quotient_poly_opening_at_z: String, + + pub linearization_poly_opening_at_z: String, + + pub opening_proof_at_z: [String; 2], + pub opening_proof_at_z_omega: [String; 2], +} + +pub fn to_hex(el: &F) -> String { + format!("0x{}", rescue_poseidon::franklin_crypto::bellman::to_hex(el)) +} + +pub fn point_into_xy(point: &G1Affine) -> [String; 2] { + let (x, y) = point.as_xy(); + + [to_hex(x), to_hex(y)] +} + +pub fn transform_proof_into_json(proof: Proof) -> JsonProof { + let mut json_proof = JsonProof::default(); + json_proof.n = proof.n; + json_proof.inputs = proof.inputs.iter().map(|v| to_hex(v)).collect(); + json_proof.state_polys_commitments = proof.state_polys_commitments.iter().map(|p| point_into_xy(p)).collect(); + + json_proof.copy_permutation_grand_product_commitment = point_into_xy(&proof.copy_permutation_grand_product_commitment); + + json_proof.lookup_s_poly_commitment = proof.lookup_s_poly_commitment.as_ref().map(|p| point_into_xy(p)).unwrap_or_default(); + json_proof.lookup_grand_product_commitment = proof.lookup_grand_product_commitment.as_ref().map(|p| point_into_xy(p)).unwrap_or_default(); + + json_proof.quotient_poly_parts_commitments = proof.quotient_poly_parts_commitments.iter().map(|p| point_into_xy(p)).collect(); + + json_proof.opening_proof_at_z = point_into_xy(&proof.opening_proof_at_z); + json_proof.opening_proof_at_z_omega = point_into_xy(&proof.opening_proof_at_z_omega); + + json_proof.state_polys_openings_at_z = proof.state_polys_openings_at_z.iter().map(|v| to_hex(v)).collect(); + assert_eq!(proof.state_polys_openings_at_dilations.len(), 1, "only one dilation is allowed"); + json_proof.state_polys_openings_at_z_omega = proof.state_polys_openings_at_dilations.iter().map(|(_, _, v)| to_hex(v)).collect(); + json_proof.gate_setup_openings_at_z = proof.gate_setup_openings_at_z.iter().map(|(_, _, v)| to_hex(v)).collect(); + json_proof.gate_selectors_openings_at_z = proof.gate_selectors_openings_at_z.iter().map(|(_, v)| to_hex(v)).collect(); + json_proof.copy_permutation_polys_openings_at_z = proof.copy_permutation_polys_openings_at_z.iter().map(|v| to_hex(v)).collect(); + json_proof.copy_permutation_grand_product_opening_at_z_omega = to_hex(&proof.copy_permutation_grand_product_opening_at_z_omega); + + json_proof.lookup_s_poly_opening_at_z_omega = proof.lookup_s_poly_opening_at_z_omega.as_ref().map(|v| to_hex(v)).unwrap_or_default(); + + json_proof.lookup_grand_product_opening_at_z_omega = proof.lookup_grand_product_opening_at_z_omega.as_ref().map(|v| to_hex(v)).unwrap_or_default(); + + json_proof.lookup_t_poly_opening_at_z = proof.lookup_t_poly_opening_at_z.as_ref().map(|v| to_hex(v)).unwrap_or_default(); + json_proof.lookup_t_poly_opening_at_z_omega = proof.lookup_t_poly_opening_at_z_omega.as_ref().map(|v| to_hex(v)).unwrap_or_default(); + json_proof.lookup_selector_poly_opening_at_z = proof.lookup_selector_poly_opening_at_z.as_ref().map(|v| to_hex(v)).unwrap_or_default(); + json_proof.lookup_table_type_poly_opening_at_z = proof.lookup_table_type_poly_opening_at_z.as_ref().map(|v| to_hex(v)).unwrap_or_default(); + + json_proof.quotient_poly_opening_at_z = to_hex(&proof.quotient_poly_opening_at_z); + json_proof.linearization_poly_opening_at_z = to_hex(&proof.linearization_poly_opening_at_z); + + json_proof +} + +pub fn serialize_proof(proof: JsonProof, has_custom_gate: bool, has_lookup: bool) -> Vec { + let JsonProof { + state_polys_commitments, + copy_permutation_grand_product_commitment, + lookup_s_poly_commitment, + lookup_grand_product_commitment, + quotient_poly_parts_commitments, + state_polys_openings_at_z, + state_polys_openings_at_z_omega, + gate_setup_openings_at_z, + gate_selectors_openings_at_z, + copy_permutation_polys_openings_at_z, + copy_permutation_grand_product_opening_at_z_omega, + lookup_s_poly_opening_at_z_omega, + lookup_grand_product_opening_at_z_omega, + lookup_t_poly_opening_at_z, + lookup_t_poly_opening_at_z_omega, + lookup_selector_poly_opening_at_z, + lookup_table_type_poly_opening_at_z, + quotient_poly_opening_at_z, + linearization_poly_opening_at_z, + opening_proof_at_z, + opening_proof_at_z_omega, + .. + } = proof; + + let mut serialized_proof = vec![]; + serialized_proof.extend(state_polys_commitments.iter().flat_map(|inner| inner.iter().cloned())); + + serialized_proof.extend(copy_permutation_grand_product_commitment); + if has_lookup { + serialized_proof.extend(lookup_s_poly_commitment); + serialized_proof.extend(lookup_grand_product_commitment); + } + + serialized_proof.extend(quotient_poly_parts_commitments.iter().flat_map(|inner| inner.iter().cloned())); + + serialized_proof.extend(state_polys_openings_at_z); + serialized_proof.extend(state_polys_openings_at_z_omega); + serialized_proof.extend(gate_setup_openings_at_z); + if has_custom_gate { + // linearization includes gate selector of the sbox gate + assert_eq!(gate_selectors_openings_at_z.len(), 1); + } else { + assert!(gate_selectors_openings_at_z.is_empty()); + } + serialized_proof.extend(gate_selectors_openings_at_z); + serialized_proof.extend(copy_permutation_polys_openings_at_z); + serialized_proof.push(copy_permutation_grand_product_opening_at_z_omega); + if has_lookup { + serialized_proof.push(lookup_s_poly_opening_at_z_omega); + serialized_proof.push(lookup_grand_product_opening_at_z_omega); + serialized_proof.push(lookup_t_poly_opening_at_z); + serialized_proof.push(lookup_t_poly_opening_at_z_omega); + serialized_proof.push(lookup_selector_poly_opening_at_z); + serialized_proof.push(lookup_table_type_poly_opening_at_z); + } + + serialized_proof.push(quotient_poly_opening_at_z); + serialized_proof.push(linearization_poly_opening_at_z); + + serialized_proof.extend(opening_proof_at_z); + serialized_proof.extend(opening_proof_at_z_omega); + + serialized_proof +} +struct MapWrapper { + inner: Map, +} +impl MapWrapper { + fn new() -> Self { + Self { inner: Map::new() } + } + + fn insert(&mut self, key: &str, value: T) -> Option { + self.inner.insert(key.into(), to_json(value)) + } +} diff --git a/crates/codegen/src/lib.rs b/crates/plonk-verifier-codegen/src/lib.rs similarity index 80% rename from crates/codegen/src/lib.rs rename to crates/plonk-verifier-codegen/src/lib.rs index 7094724..12d99cf 100644 --- a/crates/codegen/src/lib.rs +++ b/crates/plonk-verifier-codegen/src/lib.rs @@ -2,18 +2,17 @@ mod circuits; mod generate; mod serialize; +use std::str::FromStr; + +use circuits::MockCircuit; pub use generate::{generate, Encoding, MainGateType}; use ethereum_types::U256; -use franklin_crypto::bellman::pairing::bn256::{Bn256, Fr}; -use franklin_crypto::bellman::pairing::ff::*; -use franklin_crypto::bellman::pairing::*; -use franklin_crypto::bellman::plonk::better_better_cs::cs::Circuit; -use franklin_crypto::bellman::plonk::better_better_cs::gates::selector_optimized_with_d_next::SelectorOptimizedWidth4MainGateWithDNext; -use franklin_crypto::bellman::plonk::better_better_cs::proof::Proof; -use franklin_crypto::bellman::plonk::better_better_cs::setup::VerificationKey; - -use crate::circuits::DummyCircuit; +use rescue_poseidon::franklin_crypto::bellman::pairing::bn256::{Bn256, Fr}; +use rescue_poseidon::franklin_crypto::bellman::pairing::ff::*; +use rescue_poseidon::franklin_crypto::bellman::pairing::*; +use rescue_poseidon::franklin_crypto::bellman::plonk::better_better_cs::cs::{Circuit, VerificationKey}; +use rescue_poseidon::franklin_crypto::bellman::plonk::better_better_cs::proof::Proof; fn render_scalar_to_hex(el: &F) -> String { let mut buff = vec![]; @@ -145,28 +144,3 @@ pub fn serialize_proof>(proof: &Proof) -> (Vec (inputs, serialized_proof) } - -#[test] -#[ignore] // TODO(ignored-test): Failure. -fn render_simple_proof() { - use franklin_crypto::bellman::pairing::bn256::*; - - let mut reader = std::io::BufReader::with_capacity(1 << 24, std::fs::File::open("../data/optimized/scheduler_proof.key").unwrap()); - let proof = Proof::::read(&mut reader).unwrap(); - let (inputs, proof) = serialize_proof(&proof); - - println!("Inputs"); - let mut vec = vec![]; - for i in inputs.into_iter() { - vec.push(format!("\"{}\"", i)); - } - println!("[{}]", vec.join(",")); - - println!("Proof"); - let mut vec = vec![]; - for i in proof.into_iter() { - vec.push(format!("\"{}\"", i)); - } - - println!("[{}]", vec.join(",")); -} diff --git a/crates/codegen/src/serialize.rs b/crates/plonk-verifier-codegen/src/serialize.rs similarity index 98% rename from crates/codegen/src/serialize.rs rename to crates/plonk-verifier-codegen/src/serialize.rs index 4eaedb6..5c78754 100644 --- a/crates/codegen/src/serialize.rs +++ b/crates/plonk-verifier-codegen/src/serialize.rs @@ -1,4 +1,4 @@ -use franklin_crypto::bellman::{ +use rescue_poseidon::franklin_crypto::bellman::{ pairing::bn256::{Bn256, Fq, Fq2, Fr}, CurveAffine, Engine, PrimeField, PrimeFieldRepr, }; diff --git a/crates/codegen/src/tests.rs b/crates/plonk-verifier-codegen/src/tests.rs similarity index 100% rename from crates/codegen/src/tests.rs rename to crates/plonk-verifier-codegen/src/tests.rs diff --git a/crates/plonk-verifier-codegen/template/HardcodedValues.sol b/crates/plonk-verifier-codegen/template/HardcodedValues.sol new file mode 100644 index 0000000..b26e7ba --- /dev/null +++ b/crates/plonk-verifier-codegen/template/HardcodedValues.sol @@ -0,0 +1,64 @@ +pragma solidity >=0.8.25 <0.9.0; +import {Proof} from "../src/Plonk4VerifierWithAccessToDNext.sol"; + +library HardcodedValues{ + function hardcoded_proof() internal pure returns(Proof memory proof){ + {{#each proof.inputs}} + proof.input_values[{{@index}}] = {{this}}; + {{/each}} + {{#each proof.state_polys_commitments}} + proof.state_polys_commitments[{{@index}}].X = {{this.[0]}}; + proof.state_polys_commitments[{{@index}}].Y = {{this.[1]}}; + {{/each}} + proof.copy_permutation_grand_product_commitment.X = {{proof.copy_permutation_grand_product_commitment.[0]}}; + proof.copy_permutation_grand_product_commitment.Y = {{proof.copy_permutation_grand_product_commitment.[1]}}; + {{#each proof.quotient_poly_parts_commitments}} + proof.quotient_poly_parts_commitments[{{@index}}].X = {{this.[0]}}; + proof.quotient_poly_parts_commitments[{{@index}}].Y = {{this.[1]}}; + {{/each}} + {{#if has_lookup}} + proof.lookup_s_poly_commitment.X = {{proof.lookup_s_poly_commitment.[0]}}; + proof.lookup_s_poly_commitment.Y = {{proof.lookup_s_poly_commitment.[1]}}; + proof.lookup_grand_product_commitment.X = {{proof.lookup_grand_product_commitment.[0]}}; + proof.lookup_grand_product_commitment.Y = {{proof.lookup_grand_product_commitment.[1]}}; + {{/if}} + proof.opening_proof_at_z.X = {{proof.opening_proof_at_z.[0]}}; + proof.opening_proof_at_z.Y = {{proof.opening_proof_at_z.[1]}}; + proof.opening_proof_at_z_omega.X = {{proof.opening_proof_at_z_omega.[0]}}; + proof.opening_proof_at_z_omega.Y = {{proof.opening_proof_at_z_omega.[1]}}; + + {{#each proof.state_polys_openings_at_z}} + proof.state_polys_openings_at_z[{{@index}}].value = {{this}}; + {{/each}} + {{#each proof.state_polys_openings_at_z_omega}} + proof.state_polys_openings_at_z_omega[{{@index}}].value = {{this}}; + {{/each}} + {{#each proof.gate_selectors_openings_at_z}} + proof.gate_selectors_openings_at_z[{{@index}}].value = {{this}}; + {{/each}} + {{#each proof.copy_permutation_polys_openings_at_z}} + proof.copy_permutation_polys_openings_at_z[{{@index}}].value = {{this}}; + {{/each}} + proof.copy_permutation_grand_product_opening_at_z_omega.value = {{proof.copy_permutation_grand_product_opening_at_z_omega}}; + proof.quotient_poly_opening_at_z.value = {{proof.quotient_poly_opening_at_z}}; + proof.linearization_poly_opening_at_z.value = {{proof.linearization_poly_opening_at_z}}; + {{#if has_lookup}} + proof.lookup_s_poly_opening_at_z_omega.value = {{proof.lookup_s_poly_opening_at_z_omega}}; + proof.lookup_grand_product_opening_at_z_omega.value = {{proof.lookup_grand_product_opening_at_z_omega}}; + proof.lookup_t_poly_opening_at_z.value = {{proof.lookup_t_poly_opening_at_z}}; + proof.lookup_selector_poly_opening_at_z.value = {{proof.lookup_selector_poly_opening_at_z}}; + proof.lookup_table_type_poly_opening_at_z.value = {{proof.lookup_table_type_poly_opening_at_z}}; + {{/if}} + } + + function hardcoded_serialized_proof() public pure returns(uint256[] memory serializedInputs, uint256[] memory serializedProof){ + serializedInputs = new uint256[]({{num_inputs}}); + {{#each serialized_inputs}} + serializedInputs[{{@index}}] = {{this}}; + {{/each}} + serializedProof = new uint256[]({{serialized_proof_length}}); + {{#each serialized_proof}} + serializedProof[{{@index}}] = {{this}}; + {{/each}} + } +} diff --git a/crates/codegen/template/PairingsBn254.sol b/crates/plonk-verifier-codegen/template/PairingsBn254.sol similarity index 100% rename from crates/codegen/template/PairingsBn254.sol rename to crates/plonk-verifier-codegen/template/PairingsBn254.sol diff --git a/crates/codegen/template/Plonk4VerifierWithAccessToDNext.sol b/crates/plonk-verifier-codegen/template/Plonk4VerifierWithAccessToDNext.sol similarity index 94% rename from crates/codegen/template/Plonk4VerifierWithAccessToDNext.sol rename to crates/plonk-verifier-codegen/template/Plonk4VerifierWithAccessToDNext.sol index 1af4d24..dc9d45f 100644 --- a/crates/codegen/template/Plonk4VerifierWithAccessToDNext.sol +++ b/crates/plonk-verifier-codegen/template/Plonk4VerifierWithAccessToDNext.sol @@ -11,7 +11,9 @@ struct VerificationKey { uint256 domain_size; uint256 num_inputs; PairingsBn254.Fr omega; - PairingsBn254.G1Point[{{NUM_GATES}}] gate_selectors_commitments; + {{#if has_rescue_custom_gate}} + PairingsBn254.G1Point[2] gate_selectors_commitments; + {{/if}} PairingsBn254.G1Point[{{NUM_MAIN_GATE_SELECTORS}}] gate_setup_commitments; PairingsBn254.G1Point[STATE_WIDTH] permutation_commitments; {{#if has_lookup}} @@ -25,6 +27,41 @@ struct VerificationKey { } +struct Proof { + uint256[] input_values; + // commitments + PairingsBn254.G1Point[STATE_WIDTH] state_polys_commitments; + PairingsBn254.G1Point copy_permutation_grand_product_commitment; + PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_parts_commitments; + + // openings + PairingsBn254.Fr[STATE_WIDTH] state_polys_openings_at_z; + PairingsBn254.Fr[1] state_polys_openings_at_z_omega; + {{#if has_rescue_custom_gate}} + PairingsBn254.Fr[1] gate_selectors_openings_at_z; + {{/if}} + PairingsBn254.Fr[STATE_WIDTH-1] copy_permutation_polys_openings_at_z; + PairingsBn254.Fr copy_permutation_grand_product_opening_at_z_omega; + PairingsBn254.Fr quotient_poly_opening_at_z; + PairingsBn254.Fr linearization_poly_opening_at_z; + + {{#if has_lookup}} + // lookup commitments + PairingsBn254.G1Point lookup_s_poly_commitment; + PairingsBn254.G1Point lookup_grand_product_commitment; + // lookup openings + PairingsBn254.Fr lookup_s_poly_opening_at_z_omega; + PairingsBn254.Fr lookup_grand_product_opening_at_z_omega; + PairingsBn254.Fr lookup_t_poly_opening_at_z; + PairingsBn254.Fr lookup_t_poly_opening_at_z_omega; + PairingsBn254.Fr lookup_selector_poly_opening_at_z; + PairingsBn254.Fr lookup_table_type_poly_opening_at_z; + {{/if}} + PairingsBn254.G1Point opening_proof_at_z; + PairingsBn254.G1Point opening_proof_at_z_omega; +} + + contract Plonk4VerifierWithAccessToDNext { using PairingsBn254 for PairingsBn254.G1Point; using PairingsBn254 for PairingsBn254.G2Point; @@ -34,40 +71,6 @@ contract Plonk4VerifierWithAccessToDNext { using UncheckedMath for uint256; - struct Proof { - uint256[] input_values; - // commitments - PairingsBn254.G1Point[STATE_WIDTH] state_polys_commitments; - PairingsBn254.G1Point copy_permutation_grand_product_commitment; - PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_parts_commitments; - - // openings - PairingsBn254.Fr[STATE_WIDTH] state_polys_openings_at_z; - PairingsBn254.Fr[1] state_polys_openings_at_z_omega; - {{#if has_rescue_custom_gate}} - PairingsBn254.Fr[1] gate_selectors_openings_at_z; - {{/if}} - PairingsBn254.Fr[STATE_WIDTH-1] copy_permutation_polys_openings_at_z; - PairingsBn254.Fr copy_permutation_grand_product_opening_at_z_omega; - PairingsBn254.Fr quotient_poly_opening_at_z; - PairingsBn254.Fr linearization_poly_opening_at_z; - - {{#if has_lookup}} - // lookup commitments - PairingsBn254.G1Point lookup_s_poly_commitment; - PairingsBn254.G1Point lookup_grand_product_commitment; - // lookup openings - PairingsBn254.Fr lookup_s_poly_opening_at_z_omega; - PairingsBn254.Fr lookup_grand_product_opening_at_z_omega; - PairingsBn254.Fr lookup_t_poly_opening_at_z; - PairingsBn254.Fr lookup_t_poly_opening_at_z_omega; - PairingsBn254.Fr lookup_selector_poly_opening_at_z; - PairingsBn254.Fr lookup_table_type_poly_opening_at_z; - {{/if}} - PairingsBn254.G1Point opening_proof_at_z; - PairingsBn254.G1Point opening_proof_at_z_omega; - } - struct PartialVerifierState { PairingsBn254.Fr zero; PairingsBn254.Fr alpha; @@ -251,8 +254,12 @@ contract Plonk4VerifierWithAccessToDNext { current_z.mul_assign(z_in_domain_size); } } - + {{#if has_lookup}} Queries memory queries = prepare_queries(vk, proof, state); + {{else}} + Queries memory queries = prepare_queries(vk, proof); + {{/if}} + queries.commitments_at_z[0] = quotient_result; queries.values_at_z[0] = proof.quotient_poly_opening_at_z; queries.commitments_at_z[1] = aggregated_linearization_commitment(vk, proof, state); @@ -599,12 +606,18 @@ contract Plonk4VerifierWithAccessToDNext { PairingsBn254.G1Point[{{num_commitments_at_z_omega}}] commitments_at_z_omega; PairingsBn254.Fr[{{num_commitments_at_z_omega}}] values_at_z_omega; } - + {{#if has_lookup}} function prepare_queries( VerificationKey memory vk, - Proof memory proof, + Proof memory proof, PartialVerifierState memory state - ) public view returns(Queries memory queries){ + ) public view returns(Queries memory queries) + {{else}} + function prepare_queries( + VerificationKey memory vk, + Proof memory proof + ) public pure returns(Queries memory queries) + {{/if}}{ // we set first two items in calee side so start idx from 2 uint256 idx = 2; for(uint256 i = 0; i diff --git a/crates/plonk-verifier-foundry/README.md b/crates/plonk-verifier-foundry/README.md new file mode 100644 index 0000000..efac030 --- /dev/null +++ b/crates/plonk-verifier-foundry/README.md @@ -0,0 +1,39 @@ +#

Forge Template

+ +**Template repository for getting started quickly with Foundry projects** + +![Github Actions](https://github.com/foundry-rs/forge-template/workflows/CI/badge.svg) + +## Getting Started + +Click "Use this template" on [GitHub](https://github.com/foundry-rs/forge-template) to create a new repository with this repo as the initial state. + +Or, if your repo already exists, run: +```sh +forge init +forge build +forge test +``` + +## Writing your first test + +All you need is to `import forge-std/Test.sol` and then inherit it from your test contract. Forge-std's Test contract comes with a pre-instatiated [cheatcodes environment](https://book.getfoundry.sh/cheatcodes/), the `vm`. It also has support for [ds-test](https://book.getfoundry.sh/reference/ds-test.html)-style logs and assertions. Finally, it supports Hardhat's [console.log](https://github.com/brockelmore/forge-std/blob/master/src/console.sol). The logging functionalities require `-vvvv`. + +```solidity +pragma solidity 0.8.10; + +import "forge-std/Test.sol"; + +contract ContractTest is Test { + function testExample() public { + vm.roll(100); + console.log(1); + emit log("hi"); + assertTrue(true); + } +} +``` + +## Development + +This project uses [Foundry](https://getfoundry.sh). See the [book](https://book.getfoundry.sh/getting-started/installation.html) for instructions on how to install and use Foundry. diff --git a/crates/plonk-verifier-foundry/bun.lockb b/crates/plonk-verifier-foundry/bun.lockb new file mode 100755 index 0000000..18290a2 Binary files /dev/null and b/crates/plonk-verifier-foundry/bun.lockb differ diff --git a/crates/plonk-verifier-foundry/foundry.toml b/crates/plonk-verifier-foundry/foundry.toml new file mode 100644 index 0000000..21897df --- /dev/null +++ b/crates/plonk-verifier-foundry/foundry.toml @@ -0,0 +1,58 @@ +# Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config + +[profile.default] + auto_detect_solc = false + block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT + bytecode_hash = "none" + evm_version = "shanghai" + fuzz = { runs = 1_000 } + gas_reports = ["*"] + optimizer = true + optimizer_runs = 10_000 + out = "out" + script = "script" + solc = "0.8.25" + src = "src" + test = "test" + fs_permissions = [{ access = "read", path = "../../"}] +libs = ["node_modules", "lib"] + +[profile.ci] + fuzz = { runs = 10_000 } + verbosity = 4 + +[etherscan] + arbitrum = { key = "${API_KEY_ARBISCAN}" } + avalanche = { key = "${API_KEY_SNOWTRACE}" } + base = { key = "${API_KEY_BASESCAN}" } + bnb_smart_chain = { key = "${API_KEY_BSCSCAN}" } + gnosis_chain = { key = "${API_KEY_GNOSISSCAN}" } + goerli = { key = "${API_KEY_ETHERSCAN}" } + mainnet = { key = "${API_KEY_ETHERSCAN}" } + optimism = { key = "${API_KEY_OPTIMISTIC_ETHERSCAN}" } + polygon = { key = "${API_KEY_POLYGONSCAN}" } + sepolia = { key = "${API_KEY_ETHERSCAN}" } + +[fmt] + bracket_spacing = true + int_types = "long" + line_length = 120 + multiline_func_header = "all" + number_underscore = "thousands" + quote_style = "double" + tab_width = 4 + wrap_comments = true + +[rpc_endpoints] + arbitrum = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}" + avalanche = "https://avalanche-mainnet.infura.io/v3/${API_KEY_INFURA}" + base = "https://mainnet.base.org" + bnb_smart_chain = "https://bsc-dataseed.binance.org" + gnosis_chain = "https://rpc.gnosischain.com" + goerli = "https://goerli.infura.io/v3/${API_KEY_INFURA}" + localhost = "http://localhost:8545" + mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}" + optimism = "https://optimism-mainnet.infura.io/v3/${API_KEY_INFURA}" + polygon = "https://polygon-mainnet.infura.io/v3/${API_KEY_INFURA}" + sepolia = "https://sepolia.infura.io/v3/${API_KEY_INFURA}" + diff --git a/crates/plonk-verifier-foundry/index.ts b/crates/plonk-verifier-foundry/index.ts new file mode 100644 index 0000000..f67b2c6 --- /dev/null +++ b/crates/plonk-verifier-foundry/index.ts @@ -0,0 +1 @@ +console.log("Hello via Bun!"); \ No newline at end of file diff --git a/crates/plonk-verifier-foundry/package.json b/crates/plonk-verifier-foundry/package.json new file mode 100644 index 0000000..80569b6 --- /dev/null +++ b/crates/plonk-verifier-foundry/package.json @@ -0,0 +1,38 @@ +{ + "name": "@prb/foundry-template", + "description": "Foundry-based template for developing Solidity smart contracts", + "version": "1.0.0", + "author": { + "name": "Paul Razvan Berg", + "url": "https://github.com/PaulRBerg" + }, + "dependencies": { + "@openzeppelin/contracts": "^5.0.1" + }, + "devDependencies": { + "forge-std": "github:foundry-rs/forge-std#v1.8.1", + "prettier": "^3.0.0", + "solhint": "^3.6.2" + }, + "keywords": [ + "blockchain", + "ethereum", + "forge", + "foundry", + "smart-contracts", + "solidity", + "template" + ], + "private": true, + "scripts": { + "clean": "rm -rf cache out", + "build": "forge build", + "lint": "bun run lint:sol && bun run prettier:check", + "lint:sol": "forge fmt --check && bun solhint {script,src,test}/**/*.sol", + "prettier:check": "prettier --check \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"", + "prettier:write": "prettier --write \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"", + "test": "forge test", + "test:coverage": "forge coverage", + "test:coverage:report": "forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage" + } +} diff --git a/crates/plonk-verifier-foundry/remappings.txt b/crates/plonk-verifier-foundry/remappings.txt new file mode 100644 index 0000000..550f908 --- /dev/null +++ b/crates/plonk-verifier-foundry/remappings.txt @@ -0,0 +1,2 @@ +@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ +forge-std/=node_modules/forge-std/ diff --git a/crates/plonk-verifier-foundry/src/PairingsBn254.sol b/crates/plonk-verifier-foundry/src/PairingsBn254.sol new file mode 100644 index 0000000..3f19a32 --- /dev/null +++ b/crates/plonk-verifier-foundry/src/PairingsBn254.sol @@ -0,0 +1,272 @@ +pragma solidity ^0.8.0; + +library PairingsBn254 { + uint256 constant q_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 constant bn254_b_coeff = 3; + + struct G1Point { + uint256 X; + uint256 Y; + } + + struct Fr { + uint256 value; + } + + function new_fr(uint256 fr) internal pure returns (Fr memory) { + require(fr < r_mod); + return Fr({value: fr}); + } + + function copy(Fr memory self) internal pure returns (Fr memory n) { + n.value = self.value; + } + + function assign(Fr memory self, Fr memory other) internal pure { + self.value = other.value; + } + + function inverse(Fr memory fr) internal view returns (Fr memory) { + require(fr.value != 0); + return pow(fr, r_mod-2); + } + + function add_assign(Fr memory self, Fr memory other) internal pure { + self.value = addmod(self.value, other.value, r_mod); + } + + function sub_assign(Fr memory self, Fr memory other) internal pure { + self.value = addmod(self.value, r_mod - other.value, r_mod); + } + + function mul_assign(Fr memory self, Fr memory other) internal pure { + self.value = mulmod(self.value, other.value, r_mod); + } + + function pow(Fr memory self, uint256 power) internal view returns (Fr memory) { + uint256[6] memory input = [32, 32, 32, self.value, power, r_mod]; + uint256[1] memory result; + bool success; + assembly { + success := staticcall(gas(), 0x05, input, 0xc0, result, 0x20) + } + require(success); + return Fr({value: result[0]}); + } + + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint[2] X; + uint[2] Y; + } + + function P1() internal pure returns (G1Point memory) { + return G1Point(1, 2); + } + + function new_g1(uint256 x, uint256 y) internal pure returns (G1Point memory) { + return G1Point(x, y); + } + + // function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) { + function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) { + if (x == 0 && y == 0) { + // point of infinity is (0,0) + return G1Point(x, y); + } + + // check encoding + require(x < q_mod, "x axis isn't valid"); + require(y < q_mod, "y axis isn't valid"); + // check on curve + uint256 lhs = mulmod(y, y, q_mod); // y^2 + + uint256 rhs = mulmod(x, x, q_mod); // x^2 + rhs = mulmod(rhs, x, q_mod); // x^3 + rhs = addmod(rhs, bn254_b_coeff, q_mod); // x^3 + b + require(lhs == rhs, "is not on curve"); + + return G1Point(x, y); + } + + function new_g2(uint256[2] memory x, uint256[2] memory y) internal pure returns (G2Point memory) { + return G2Point(x, y); + } + + function copy_g1(G1Point memory self) internal pure returns (G1Point memory result) { + result.X = self.X; + result.Y = self.Y; + } + + function P2() internal pure returns (G2Point memory) { + // for some reason ethereum expects to have c1*v + c0 form + + return G2Point( + [0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed], + [0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa] + ); + } + + function negate(G1Point memory self) internal pure { + // The prime q in the base field F_q for G1 + if (self.Y == 0) { + require(self.X == 0); + return; + } + + self.Y = q_mod - self.Y; + } + + function point_add(G1Point memory p1, G1Point memory p2) + internal view returns (G1Point memory r) + { + point_add_into_dest(p1, p2, r); + return r; + } + + function point_add_assign(G1Point memory p1, G1Point memory p2) + internal view + { + point_add_into_dest(p1, p2, p1); + } + + function point_add_into_dest(G1Point memory p1, G1Point memory p2, G1Point memory dest) + internal view + { + if (p2.X == 0 && p2.Y == 0) { + // we add zero, nothing happens + dest.X = p1.X; + dest.Y = p1.Y; + return; + } else if (p1.X == 0 && p1.Y == 0) { + // we add into zero, and we add non-zero point + dest.X = p2.X; + dest.Y = p2.Y; + return; + } else { + uint256[4] memory input; + + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + + bool success; + assembly { + success := staticcall(gas(), 6, input, 0x80, dest, 0x40) + } + require(success); + } + } + + function point_sub_assign(G1Point memory p1, G1Point memory p2) + internal view + { + point_sub_into_dest(p1, p2, p1); + } + + function point_sub_into_dest(G1Point memory p1, G1Point memory p2, G1Point memory dest) + internal view + { + if (p2.X == 0 && p2.Y == 0) { + // we subtracted zero, nothing happens + dest.X = p1.X; + dest.Y = p1.Y; + return; + } else if (p1.X == 0 && p1.Y == 0) { + // we subtract from zero, and we subtract non-zero point + dest.X = p2.X; + dest.Y = q_mod - p2.Y; + return; + } else { + uint256[4] memory input; + + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = q_mod - p2.Y; + + bool success = false; + assembly { + success := staticcall(gas(), 6, input, 0x80, dest, 0x40) + } + require(success); + } + } + + function point_mul(G1Point memory p, Fr memory s) + internal view returns (G1Point memory r) + { + // https://eips.ethereum.org/EIPS/eip-197 + // Elliptic curve points are encoded as a Jacobian pair (X, Y) where the point at infinity is encoded as (0, 0) + if(p.X == 0 && p.Y == 1){ + p.Y = 0; + } + point_mul_into_dest(p, s, r); + return r; + } + + function point_mul_assign(G1Point memory p, Fr memory s) + internal view + { + point_mul_into_dest(p, s, p); + } + + function point_mul_into_dest(G1Point memory p, Fr memory s, G1Point memory dest) + internal view + { + uint[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s.value; + bool success; + assembly { + success := staticcall(gas(), 7, input, 0x60, dest, 0x40) + } + require(success); + } + + function pairing(G1Point[] memory p1, G2Point[] memory p2) + internal view returns (bool) + { + require(p1.length == p2.length); + uint elements = p1.length; + uint inputSize = elements * 6; + uint[] memory input = new uint[](inputSize); + for (uint i = 0; i < elements; ) + { + input[i * 6 + 0] = p1[i].X; + input[i * 6 + 1] = p1[i].Y; + input[i * 6 + 2] = p2[i].X[0]; + input[i * 6 + 3] = p2[i].X[1]; + input[i * 6 + 4] = p2[i].Y[0]; + input[i * 6 + 5] = p2[i].Y[1]; + unchecked { + ++i; + } + } + uint[1] memory out; + bool success; + assembly { + success := staticcall(gas(), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) + } + require(success); + return out[0] != 0; + } + + /// Convenience method for a pairing check for two pairs. + function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) + internal view returns (bool) + { + G1Point[] memory p1 = new G1Point[](2); + G2Point[] memory p2 = new G2Point[](2); + p1[0] = a1; + p1[1] = b1; + p2[0] = a2; + p2[1] = b2; + return pairing(p1, p2); + } +} diff --git a/crates/plonk-verifier-foundry/src/Plonk4VerifierWithAccessToDNext.sol b/crates/plonk-verifier-foundry/src/Plonk4VerifierWithAccessToDNext.sol new file mode 100644 index 0000000..def6895 --- /dev/null +++ b/crates/plonk-verifier-foundry/src/Plonk4VerifierWithAccessToDNext.sol @@ -0,0 +1,536 @@ +pragma solidity ^0.8.0; + +import "./PairingsBn254.sol"; +import "./TranscriptLib.sol"; +import "./UncheckedMath.sol"; + +uint256 constant STATE_WIDTH = 4; +uint256 constant NUM_G2_ELS = 2; + +struct VerificationKey { + uint256 domain_size; + uint256 num_inputs; + PairingsBn254.Fr omega; + + PairingsBn254.G1Point[2] gate_selectors_commitments; + + PairingsBn254.G1Point[8] gate_setup_commitments; + PairingsBn254.G1Point[STATE_WIDTH] permutation_commitments; + + + PairingsBn254.Fr[STATE_WIDTH-1] non_residues; + PairingsBn254.G2Point[NUM_G2_ELS] g2_elements; +} + + +struct Proof { + uint256[] input_values; + // commitments + PairingsBn254.G1Point[STATE_WIDTH] state_polys_commitments; + PairingsBn254.G1Point copy_permutation_grand_product_commitment; + PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_parts_commitments; + + // openings + PairingsBn254.Fr[STATE_WIDTH] state_polys_openings_at_z; + PairingsBn254.Fr[1] state_polys_openings_at_z_omega; + + PairingsBn254.Fr[1] gate_selectors_openings_at_z; + + PairingsBn254.Fr[STATE_WIDTH-1] copy_permutation_polys_openings_at_z; + PairingsBn254.Fr copy_permutation_grand_product_opening_at_z_omega; + PairingsBn254.Fr quotient_poly_opening_at_z; + PairingsBn254.Fr linearization_poly_opening_at_z; + + + PairingsBn254.G1Point opening_proof_at_z; + PairingsBn254.G1Point opening_proof_at_z_omega; +} + + +contract Plonk4VerifierWithAccessToDNext { + using PairingsBn254 for PairingsBn254.G1Point; + using PairingsBn254 for PairingsBn254.G2Point; + using PairingsBn254 for PairingsBn254.Fr; + + using TranscriptLib for TranscriptLib.Transcript; + + using UncheckedMath for uint256; + + struct PartialVerifierState { + PairingsBn254.Fr zero; + PairingsBn254.Fr alpha; + PairingsBn254.Fr beta; + PairingsBn254.Fr gamma; + PairingsBn254.Fr[6] alpha_values; + + PairingsBn254.Fr v; + PairingsBn254.Fr u; + PairingsBn254.Fr z; + PairingsBn254.Fr z_omega; + PairingsBn254.Fr z_minus_last_omega; + PairingsBn254.Fr l_0_at_z; + PairingsBn254.Fr l_n_minus_one_at_z; + PairingsBn254.Fr t; + PairingsBn254.G1Point tp; + } + + function evaluate_l0_at_point( + uint256 domain_size, + PairingsBn254.Fr memory at + ) internal view returns (PairingsBn254.Fr memory num) { + PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); + + PairingsBn254.Fr memory size_fe = PairingsBn254.new_fr(domain_size); + PairingsBn254.Fr memory den = at.copy(); + den.sub_assign(one); + den.mul_assign(size_fe); + + den = den.inverse(); + + num = at.pow(domain_size); + num.sub_assign(one); + num.mul_assign(den); + } + + function evaluate_lagrange_poly_out_of_domain( + uint256 poly_num, + uint256 domain_size, + PairingsBn254.Fr memory omega, + PairingsBn254.Fr memory at + ) internal view returns (PairingsBn254.Fr memory res) { + // (omega^i / N) / (X - omega^i) * (X^N - 1) + require(poly_num < domain_size); + PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); + PairingsBn254.Fr memory omega_power = omega.pow(poly_num); + res = at.pow(domain_size); + res.sub_assign(one); + require(res.value != 0); // Vanishing polynomial can not be zero at point `at` + res.mul_assign(omega_power); + + PairingsBn254.Fr memory den = PairingsBn254.copy(at); + den.sub_assign(omega_power); + den.mul_assign(PairingsBn254.new_fr(domain_size)); + + den = den.inverse(); + + res.mul_assign(den); + } + + function evaluate_vanishing( + uint256 domain_size, + PairingsBn254.Fr memory at + ) internal view returns (PairingsBn254.Fr memory res) { + res = at.pow(domain_size); + res.sub_assign(PairingsBn254.new_fr(1)); + } + + function initialize_transcript(Proof memory proof, VerificationKey memory vk) internal pure returns (PartialVerifierState memory state) { + TranscriptLib.Transcript memory transcript = TranscriptLib.new_transcript(); + + for(uint256 i =0; i < vk.num_inputs; i = i.uncheckedInc()){ + transcript.update_with_u256(proof.input_values[i]); + } + + for(uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()){ + transcript.update_with_g1(proof.state_polys_commitments[i]); + } + + + + state.beta = transcript.get_challenge(); + state.gamma = transcript.get_challenge(); + + transcript.update_with_g1(proof.copy_permutation_grand_product_commitment); + + state.alpha = transcript.get_challenge(); + + for(uint256 i =0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()){ + transcript.update_with_g1(proof.quotient_poly_parts_commitments[i]); + } + state.z = transcript.get_challenge(); + + transcript.update_with_fr(proof.quotient_poly_opening_at_z); + + for(uint256 i =0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()){ + transcript.update_with_fr(proof.state_polys_openings_at_z[i]); + } + + for(uint256 i =0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()){ + transcript.update_with_fr(proof.state_polys_openings_at_z_omega[i]); + } + + for(uint256 i =0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()){ + transcript.update_with_fr(proof.gate_selectors_openings_at_z[i]); + } + + for(uint256 i =0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()){ + transcript.update_with_fr(proof.copy_permutation_polys_openings_at_z[i]); + } + + state.z_omega = state.z.copy(); + state.z_omega.mul_assign(vk.omega); + + transcript.update_with_fr(proof.copy_permutation_grand_product_opening_at_z_omega); + + + transcript.update_with_fr(proof.linearization_poly_opening_at_z); + + state.v = transcript.get_challenge(); + + transcript.update_with_g1(proof.opening_proof_at_z); + transcript.update_with_g1(proof.opening_proof_at_z_omega); + + state.u = transcript.get_challenge(); + } + + // compute some powers of challenge alpha([alpha^1, .. alpha^8]) + function compute_powers_of_alpha(PartialVerifierState memory state) public pure { + require(state.alpha.value != 0); + state.alpha_values[0] = PairingsBn254.new_fr(1); + state.alpha_values[1] = state.alpha.copy(); + PairingsBn254.Fr memory current_alpha = state.alpha.copy(); + for(uint256 i= 2; i < state.alpha_values.length; i = i.uncheckedInc()){ + current_alpha.mul_assign(state.alpha); + state.alpha_values[i] = current_alpha.copy(); + } + } + + function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) { + // we initialize all challenges beforehand, we can draw each challenge in its own place + PartialVerifierState memory state = initialize_transcript(proof, vk); + if(verify_quotient_evaluation(vk, proof, state)== false){ + return false; + } + require(proof.state_polys_openings_at_z_omega.length == 1); + + + PairingsBn254.G1Point memory quotient_result = proof.quotient_poly_parts_commitments[0].copy_g1(); + { + // block scope + PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size); + PairingsBn254.Fr memory current_z = z_in_domain_size.copy(); + PairingsBn254.G1Point memory tp; + // start from i =1 + for(uint256 i = 1; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) { + tp = proof.quotient_poly_parts_commitments[i].copy_g1(); + tp.point_mul_assign(current_z); + quotient_result.point_add_assign(tp); + + current_z.mul_assign(z_in_domain_size); + } + } + + Queries memory queries = prepare_queries(vk, proof); + + + queries.commitments_at_z[0] = quotient_result; + queries.values_at_z[0] = proof.quotient_poly_opening_at_z; + queries.commitments_at_z[1] = aggregated_linearization_commitment(vk, proof, state); + queries.values_at_z[1] = proof.linearization_poly_opening_at_z; + + require(queries.commitments_at_z.length == queries.values_at_z.length); + + PairingsBn254.G1Point memory aggregated_commitment_at_z = queries.commitments_at_z[0]; + + PairingsBn254.Fr memory aggregated_opening_at_z = queries.values_at_z[0]; + PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1); + PairingsBn254.G1Point memory scaled; + for(uint256 i = 1; i < queries.commitments_at_z.length; i = i.uncheckedInc()){ + aggregation_challenge.mul_assign(state.v); + scaled = queries.commitments_at_z[i].point_mul(aggregation_challenge); + aggregated_commitment_at_z.point_add_assign(scaled); + + state.t = queries.values_at_z[i]; + state.t.mul_assign(aggregation_challenge); + aggregated_opening_at_z.add_assign(state.t); + } + + aggregation_challenge.mul_assign(state.v); + + PairingsBn254.G1Point memory aggregated_commitment_at_z_omega = queries.commitments_at_z_omega[0].point_mul(aggregation_challenge); + PairingsBn254.Fr memory aggregated_opening_at_z_omega = queries.values_at_z_omega[0]; + aggregated_opening_at_z_omega.mul_assign(aggregation_challenge); + for(uint256 i = 1; i < queries.commitments_at_z_omega.length; i = i.uncheckedInc()){ + aggregation_challenge.mul_assign(state.v); + + scaled = queries.commitments_at_z_omega[i].point_mul(aggregation_challenge); + aggregated_commitment_at_z_omega.point_add_assign(scaled); + + state.t = queries.values_at_z_omega[i]; + state.t.mul_assign(aggregation_challenge); + aggregated_opening_at_z_omega.add_assign(state.t); + } + + return final_pairing( + vk.g2_elements, + proof, + state, + aggregated_commitment_at_z, + aggregated_commitment_at_z_omega, + aggregated_opening_at_z, + aggregated_opening_at_z_omega + ); + + } + + function verify_quotient_evaluation(VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state) internal view returns(bool){ + uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs); + for (uint256 i = 0; i < lagrange_poly_numbers.length; i = i.uncheckedInc()) { + lagrange_poly_numbers[i] = i; + } + require(vk.num_inputs > 0); + + PairingsBn254.Fr memory inputs_term = PairingsBn254.new_fr(0); + for(uint256 i =0; i < vk.num_inputs; i = i.uncheckedInc()) { + state.t = evaluate_lagrange_poly_out_of_domain(i, vk.domain_size, vk.omega, state.z); + state.t.mul_assign(PairingsBn254.new_fr(proof.input_values[i])); + inputs_term.add_assign(state.t); + } + + inputs_term.mul_assign(proof.gate_selectors_openings_at_z[0]); + + PairingsBn254.Fr memory result = proof.linearization_poly_opening_at_z.copy(); + result.add_assign(inputs_term); + + // compute powers of alpha + compute_powers_of_alpha(state); + PairingsBn254.Fr memory factor = state.alpha_values[4].copy(); + factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega); + + // - alpha_0 * (a + perm(z) * beta + gamma)*()*(d + gamma) * z(z*omega) + require(proof.copy_permutation_polys_openings_at_z.length == STATE_WIDTH-1); + PairingsBn254.Fr memory t; // TMP; + for(uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()){ + t = proof.copy_permutation_polys_openings_at_z[i].copy(); + t.mul_assign(state.beta); + t.add_assign(proof.state_polys_openings_at_z[i]); + t.add_assign(state.gamma); + + factor.mul_assign(t); + } + + t = proof.state_polys_openings_at_z[3].copy(); + t.add_assign(state.gamma); + factor.mul_assign(t); + result.sub_assign(factor); + + // - L_0(z) * alpha_1 + PairingsBn254.Fr memory l_0_at_z = evaluate_l0_at_point(vk.domain_size, state.z); + l_0_at_z.mul_assign(state.alpha_values[4 + 1]); + result.sub_assign(l_0_at_z); + + + + PairingsBn254.Fr memory lhs = proof.quotient_poly_opening_at_z.copy(); + lhs.mul_assign(evaluate_vanishing(vk.domain_size, state.z)); + return lhs.value == result.value; + } + + function aggregated_linearization_commitment(VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state) internal view returns(PairingsBn254.G1Point memory result){ + // qMain*(Q_a * A + Q_b * B + Q_c * C + Q_d * D + Q_m * A*B + Q_const + Q_dNext * D_next) + result = PairingsBn254.new_g1(0, 0); + // Q_a * A + PairingsBn254.G1Point memory scaled = vk.gate_setup_commitments[0].point_mul(proof.state_polys_openings_at_z[0]); + result.point_add_assign(scaled); + // Q_b * B + scaled = vk.gate_setup_commitments[1].point_mul(proof.state_polys_openings_at_z[1]); + result.point_add_assign(scaled); + // Q_c * C + scaled = vk.gate_setup_commitments[2].point_mul(proof.state_polys_openings_at_z[2]); + result.point_add_assign(scaled); + // Q_d * D + scaled = vk.gate_setup_commitments[3].point_mul(proof.state_polys_openings_at_z[3]); + result.point_add_assign(scaled); + // Q_m* A*B or Q_ab*A*B + PairingsBn254.Fr memory t = proof.state_polys_openings_at_z[0].copy(); + t.mul_assign(proof.state_polys_openings_at_z[1]); + scaled = vk.gate_setup_commitments[4].point_mul(t); + result.point_add_assign(scaled); + + // Q_AC* A*C + t = proof.state_polys_openings_at_z[0].copy(); + t.mul_assign(proof.state_polys_openings_at_z[2]); + scaled = vk.gate_setup_commitments[5].point_mul(t); + result.point_add_assign(scaled); + + // Q_const + result.point_add_assign(vk.gate_setup_commitments[6]); + // Q_dNext * D_next + scaled = vk.gate_setup_commitments[7].point_mul(proof.state_polys_openings_at_z_omega[0]); + result.point_add_assign(scaled); + + result.point_mul_assign(proof.gate_selectors_openings_at_z[0]); + + PairingsBn254.G1Point memory rescue_custom_gate_linearization_contrib = rescue_custom_gate_linearization_contribution(vk, proof, state); + result.point_add_assign(rescue_custom_gate_linearization_contrib); + + require(vk.non_residues.length == STATE_WIDTH-1); + + PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); + PairingsBn254.Fr memory factor = state.alpha_values[4].copy(); + for(uint256 i = 0; i < proof.state_polys_openings_at_z.length; ){ + t = state.z.copy(); + if(i == 0){ + t.mul_assign(one); + }else{ + t.mul_assign(vk.non_residues[i-1]); + } + t.mul_assign(state.beta); + t.add_assign(state.gamma); + t.add_assign(proof.state_polys_openings_at_z[i]); + + factor.mul_assign(t); + unchecked { + ++i; + } + } + + scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor); + result.point_add_assign(scaled); + + // - (a(z) + beta*perm_a + gamma)*()*()*z(z*omega) * beta * perm_d(X) + factor = state.alpha_values[4].copy(); + factor.mul_assign(state.beta); + factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega); + for(uint256 i = 0; i < STATE_WIDTH-1; i = i.uncheckedInc()){ + t = proof.copy_permutation_polys_openings_at_z[i].copy(); + t.mul_assign(state.beta); + t.add_assign(state.gamma); + t.add_assign(proof.state_polys_openings_at_z[i]); + + factor.mul_assign(t); + } + scaled = vk.permutation_commitments[3].point_mul(factor); + result.point_sub_assign(scaled); + + // + L_0(z) * Z(x) + state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z); + require(state.l_0_at_z.value != 0); + factor = state.l_0_at_z.copy(); + factor.mul_assign(state.alpha_values[4 + 1]); + scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor); + result.point_add_assign(scaled); + + + + } + + function rescue_custom_gate_linearization_contribution(VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state) public view returns(PairingsBn254.G1Point memory result){ + PairingsBn254.Fr memory t; + PairingsBn254.Fr memory intermediate_result; + + // a^2 - b = 0 + t = proof.state_polys_openings_at_z[0].copy(); + t.mul_assign(t); + t.sub_assign(proof.state_polys_openings_at_z[1]); + // t.mul_assign(challenge1); + t.mul_assign(state.alpha_values[1]); + intermediate_result.add_assign(t); + + // b^2 - c = 0 + t = proof.state_polys_openings_at_z[1].copy(); + t.mul_assign(t); + t.sub_assign(proof.state_polys_openings_at_z[2]); + t.mul_assign(state.alpha_values[1+1]); + intermediate_result.add_assign(t); + + // c*a - d = 0; + t = proof.state_polys_openings_at_z[2].copy(); + t.mul_assign(proof.state_polys_openings_at_z[0]); + t.sub_assign(proof.state_polys_openings_at_z[3]); + t.mul_assign(state.alpha_values[1+2]); + intermediate_result.add_assign(t); + + result = vk.gate_selectors_commitments[1].point_mul(intermediate_result); + } + + + + + struct Queries { + PairingsBn254.G1Point[10] commitments_at_z; + PairingsBn254.Fr[10] values_at_z; + + PairingsBn254.G1Point[3] commitments_at_z_omega; + PairingsBn254.Fr[3] values_at_z_omega; + } + + function prepare_queries( + VerificationKey memory vk, + Proof memory proof + ) public pure returns(Queries memory queries) + { + // we set first two items in calee side so start idx from 2 + uint256 idx = 2; + for(uint256 i = 0; i=0.8.25 <0.9.0; +import {Proof} from "../src/Plonk4VerifierWithAccessToDNext.sol"; + +library HardcodedValues{ + function hardcoded_proof() internal pure returns(Proof memory proof){ + + proof.input_values[0] = 0x0000000000000000000000000000000000000000000000000000000000000002; + + + proof.state_polys_commitments[0].X = 0x125a96306030c34958b4ad672033277ea109a796adf28dc4976027a44c12ef22; + proof.state_polys_commitments[0].Y = 0x00946c4bb159e3d8576b2b0be5bf7b3fc0b61f1c2e72da4374f9f0ad26f293b7; + + proof.state_polys_commitments[1].X = 0x25bdc8e25058040ad05096a4fd2cd0a81cbad973deda5f7415840e6593439de9; + proof.state_polys_commitments[1].Y = 0x13d3fae4b771527d6283610904ee1c95f6d3e546ed861bd071f0c910af39d0e6; + + proof.state_polys_commitments[2].X = 0x0e0fb43d701a22c3fdd6670b69eca01b815ef2f6b3687191a4fb819d25c5e117; + proof.state_polys_commitments[2].Y = 0x22c377922737d710453e0e6efb6d8b5b6b6fc346f4bd811802db23d4f7fc8019; + + proof.state_polys_commitments[3].X = 0x2a0edd1ead0847b61d9b9e6e026c47a758afbe347ae119486b9ad1719f6c367d; + proof.state_polys_commitments[3].Y = 0x21e058312fc5a32072d821eadf55644320e7ca6e9ec236925edd632fc3ff463d; + + proof.copy_permutation_grand_product_commitment.X = 0x013bb8312203b14b634a689c6d4acef9d1f54e1068de1470e19279ede8bb991f; + proof.copy_permutation_grand_product_commitment.Y = 0x0c7b763a6bd20f6fd5295c9e85846c4b132b26bb5d5acf71bcb29ee2a42ed5be; + + proof.quotient_poly_parts_commitments[0].X = 0x1837c3c200f0aebb25d7e1a2af2685892fc0c701433996b3b80953505f4efd9c; + proof.quotient_poly_parts_commitments[0].Y = 0x1a9e03970fbb2ad6876f0f0bae91097eb19ac3face44eaae4396fc16ab9116ad; + + proof.quotient_poly_parts_commitments[1].X = 0x060df9de261271340128c4b3c77b2a41178e5b87db2355d3fe1930823abe56ac; + proof.quotient_poly_parts_commitments[1].Y = 0x022f38e477d4a7a8ad6d91d56443d53628bbfc2b5d48e577f8a28bbd0f9a90a7; + + proof.quotient_poly_parts_commitments[2].X = 0x1af9ec6291b5a9da75e943391bcd283a51cc8a7c1a4825c01395e6b840bd16b4; + proof.quotient_poly_parts_commitments[2].Y = 0x12b652f3ffe11917e28cd4731156df8a8143299d19c9a333a99e41e666c1baee; + + proof.quotient_poly_parts_commitments[3].X = 0x0daad57410d2430d709a9d5904c892b45189012336a110e6d89cf670d15a0b31; + proof.quotient_poly_parts_commitments[3].Y = 0x2c064d6c18d55c0d370f56cbb0548e21f7866f02a52e63ec774805f32bf5c0f0; + + + proof.opening_proof_at_z.X = 0x0d63c1990dcd7716af77977ce95bc0985cd416ca76bb65aebed69c79e85ae67f; + proof.opening_proof_at_z.Y = 0x11e0cd7c46cf5343986e816559536280ffcc362957c9074aa6b0cbce69cfd1a5; + proof.opening_proof_at_z_omega.X = 0x0c4d652ff36b77dc918b8df517d13a95c2150fd3163e03b19480bf733a6fdc39; + proof.opening_proof_at_z_omega.Y = 0x20ded5c2838da911842c0ca7e65095623e3d8e2dd177fc58377119834084022b; + + + proof.state_polys_openings_at_z[0].value = 0x260fb5a68c251e1d5fb776f1ae703a8c5e98ceb86cd57cfd03314b3a2dd78e44; + + proof.state_polys_openings_at_z[1].value = 0x22ca21b7257fb02fd9709e2c71c074e4fd8b2d3fbd969c2dc4624be529fd7cb2; + + proof.state_polys_openings_at_z[2].value = 0x1dc0e5fd84d3254929882dae104a0aa024caac4732894dedd1abb620c7d9f70a; + + proof.state_polys_openings_at_z[3].value = 0x28ae354f4b84775ea856462e682fda6e3489082b380423daadc97ed741a39eb9; + + + proof.state_polys_openings_at_z_omega[0].value = 0x2f926ea1197a008ab9a586b00063fd9a1c61627211d62932780ef7b3ff9c58dd; + + + proof.gate_selectors_openings_at_z[0].value = 0x0a6bf79bcd59025be43d0a022107876cf6adf94eb907448727c43ac75ec3c94e; + + + proof.copy_permutation_polys_openings_at_z[0].value = 0x2067b179640d9fbca42c4814286b9211e4de99fa74dcbf892d9cc88ae9b13bf9; + + proof.copy_permutation_polys_openings_at_z[1].value = 0x2094e6d2b8ae0afa2e548e05d3fda117da3e595a78f2818d54406627e85e69f6; + + proof.copy_permutation_polys_openings_at_z[2].value = 0x2c4b1065f0312db8067a3a627e06a07a72dbbcaadea183f7ab12b3cabc6611d8; + + proof.copy_permutation_grand_product_opening_at_z_omega.value = 0x06856e549033f929f5e4433bb0d2f7c80b2c27ce5c84cbb6745d315ea2db10a7; + proof.quotient_poly_opening_at_z.value = 0x201be4dd1d31bd3e0677b63dd095f21ecd194c240f9cbdb776ac3318fa1287cf; + proof.linearization_poly_opening_at_z.value = 0x02935a5e887039918f79ed4e98be063a48c6af224f8cbfde3e52ea936e524a6f; + + } + + function hardcoded_serialized_proof() public pure returns(uint256[] memory serializedInputs, uint256[] memory serializedProof){ + serializedInputs = new uint256[](1); + + serializedInputs[0] = 0x0000000000000000000000000000000000000000000000000000000000000002; + + serializedProof = new uint256[](34); + + serializedProof[0] = 0x125a96306030c34958b4ad672033277ea109a796adf28dc4976027a44c12ef22; + + serializedProof[1] = 0x00946c4bb159e3d8576b2b0be5bf7b3fc0b61f1c2e72da4374f9f0ad26f293b7; + + serializedProof[2] = 0x25bdc8e25058040ad05096a4fd2cd0a81cbad973deda5f7415840e6593439de9; + + serializedProof[3] = 0x13d3fae4b771527d6283610904ee1c95f6d3e546ed861bd071f0c910af39d0e6; + + serializedProof[4] = 0x0e0fb43d701a22c3fdd6670b69eca01b815ef2f6b3687191a4fb819d25c5e117; + + serializedProof[5] = 0x22c377922737d710453e0e6efb6d8b5b6b6fc346f4bd811802db23d4f7fc8019; + + serializedProof[6] = 0x2a0edd1ead0847b61d9b9e6e026c47a758afbe347ae119486b9ad1719f6c367d; + + serializedProof[7] = 0x21e058312fc5a32072d821eadf55644320e7ca6e9ec236925edd632fc3ff463d; + + serializedProof[8] = 0x013bb8312203b14b634a689c6d4acef9d1f54e1068de1470e19279ede8bb991f; + + serializedProof[9] = 0x0c7b763a6bd20f6fd5295c9e85846c4b132b26bb5d5acf71bcb29ee2a42ed5be; + + serializedProof[10] = 0x1837c3c200f0aebb25d7e1a2af2685892fc0c701433996b3b80953505f4efd9c; + + serializedProof[11] = 0x1a9e03970fbb2ad6876f0f0bae91097eb19ac3face44eaae4396fc16ab9116ad; + + serializedProof[12] = 0x060df9de261271340128c4b3c77b2a41178e5b87db2355d3fe1930823abe56ac; + + serializedProof[13] = 0x022f38e477d4a7a8ad6d91d56443d53628bbfc2b5d48e577f8a28bbd0f9a90a7; + + serializedProof[14] = 0x1af9ec6291b5a9da75e943391bcd283a51cc8a7c1a4825c01395e6b840bd16b4; + + serializedProof[15] = 0x12b652f3ffe11917e28cd4731156df8a8143299d19c9a333a99e41e666c1baee; + + serializedProof[16] = 0x0daad57410d2430d709a9d5904c892b45189012336a110e6d89cf670d15a0b31; + + serializedProof[17] = 0x2c064d6c18d55c0d370f56cbb0548e21f7866f02a52e63ec774805f32bf5c0f0; + + serializedProof[18] = 0x260fb5a68c251e1d5fb776f1ae703a8c5e98ceb86cd57cfd03314b3a2dd78e44; + + serializedProof[19] = 0x22ca21b7257fb02fd9709e2c71c074e4fd8b2d3fbd969c2dc4624be529fd7cb2; + + serializedProof[20] = 0x1dc0e5fd84d3254929882dae104a0aa024caac4732894dedd1abb620c7d9f70a; + + serializedProof[21] = 0x28ae354f4b84775ea856462e682fda6e3489082b380423daadc97ed741a39eb9; + + serializedProof[22] = 0x2f926ea1197a008ab9a586b00063fd9a1c61627211d62932780ef7b3ff9c58dd; + + serializedProof[23] = 0x0a6bf79bcd59025be43d0a022107876cf6adf94eb907448727c43ac75ec3c94e; + + serializedProof[24] = 0x2067b179640d9fbca42c4814286b9211e4de99fa74dcbf892d9cc88ae9b13bf9; + + serializedProof[25] = 0x2094e6d2b8ae0afa2e548e05d3fda117da3e595a78f2818d54406627e85e69f6; + + serializedProof[26] = 0x2c4b1065f0312db8067a3a627e06a07a72dbbcaadea183f7ab12b3cabc6611d8; + + serializedProof[27] = 0x06856e549033f929f5e4433bb0d2f7c80b2c27ce5c84cbb6745d315ea2db10a7; + + serializedProof[28] = 0x201be4dd1d31bd3e0677b63dd095f21ecd194c240f9cbdb776ac3318fa1287cf; + + serializedProof[29] = 0x02935a5e887039918f79ed4e98be063a48c6af224f8cbfde3e52ea936e524a6f; + + serializedProof[30] = 0x0d63c1990dcd7716af77977ce95bc0985cd416ca76bb65aebed69c79e85ae67f; + + serializedProof[31] = 0x11e0cd7c46cf5343986e816559536280ffcc362957c9074aa6b0cbce69cfd1a5; + + serializedProof[32] = 0x0c4d652ff36b77dc918b8df517d13a95c2150fd3163e03b19480bf733a6fdc39; + + serializedProof[33] = 0x20ded5c2838da911842c0ca7e65095623e3d8e2dd177fc58377119834084022b; + + } +} diff --git a/crates/plonk-verifier-foundry/test/Plonk.T.sol b/crates/plonk-verifier-foundry/test/Plonk.T.sol new file mode 100644 index 0000000..f75cd93 --- /dev/null +++ b/crates/plonk-verifier-foundry/test/Plonk.T.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.25 <0.9.0; + +import {Test, console} from "forge-std/src/Test.sol"; +import {Verifier} from "../src/Verifier.sol"; +import {HardcodedValues} from "./HardcodedValues.sol"; + +contract VerifierTest is Verifier { + Verifier public _verifier; + + function setUp() public { + _verifier = new Verifier(); + } + + function test1FullProtocol() public view { + uint256 _g0 = gasleft(); + ( + uint256[] memory serializedInputs, + uint256[] memory serializedProof + ) = HardcodedValues.hardcoded_serialized_proof(); + bool valid = verify_serialized_proof_helper( + serializedInputs, + serializedProof + ); + uint256 _g1 = gasleft(); + require(valid, "proof is valid"); + console.log("test8FullProtocol gas cost: %d", _g0 - _g1); + } + + function verify_serialized_proof_helper( + uint256[] memory serializedInputs, + uint256[] memory serializedProof + ) public view returns (bool) { + console.log("verifying proof"); + return this.verify_serialized_proof(serializedInputs, serializedProof); + } +} diff --git a/crates/plonk-verifier-foundry/tsconfig.json b/crates/plonk-verifier-foundry/tsconfig.json new file mode 100644 index 0000000..238655f --- /dev/null +++ b/crates/plonk-verifier-foundry/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/scripts/plonk-verifier/run-tests.sh b/scripts/plonk-verifier/run-tests.sh new file mode 100755 index 0000000..c8eab7b --- /dev/null +++ b/scripts/plonk-verifier/run-tests.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# set -e + +# install foundry + +if [[ ! -d $HOME/.foundry/bin ]]; then + curl -L https://foundry.paradigm.xyz | bash + . $HOME/.foundry/bin/foundryup +fi + +# run reference test first +PLONK_VERIFIER_DATA_DIR=$PWD/data/plonk-verifier +mkdir -p $PLONK_VERIFIER_DATA_DIR +cargo run --bin generate -- --verification-key $PLONK_VERIFIER_DATA_DIR/reference_block_20_keccak.key --proof $PLONK_VERIFIER_DATA_DIR/reference_block_20_keccak.proof + +cd crates/plonk-verifier-foundry +$HOME/.foundry/bin/forge test +cd - + +# then check sample proofs and vks +mkdir -p $PLONK_VERIFIER_DATA_DIR/std +rm -rf $PLONK_VERIFIER_DATA_DIR/std/* +mkdir -p $PLONK_VERIFIER_DATA_DIR/optimized +rm -rf $PLONK_VERIFIER_DATA_DIR/optimized/* + +PLONK_VERIFIER_DATA_DIR=$PLONK_VERIFIER_DATA_DIR cargo test circuits::test_create_proof_for_all_circuits --release -- --ignored --nocapture + +for main_gate in "std" "optimized" +do + for vk_file in $(ls $PLONK_VERIFIER_DATA_DIR/$main_gate/*_vk.json) + do + proof_file=$(echo $vk_file | sed s/_vk/_proof/g) + + cargo run --bin generate -- --encoding json --verification-key $vk_file --proof $proof_file + cd crates/plonk-verifier-foundry + $HOME/.foundry/bin/forge test + cd - + done +done