diff --git a/crates/fflonk/Cargo.toml b/crates/fflonk/Cargo.toml index e9dd836..a4886ec 100644 --- a/crates/fflonk/Cargo.toml +++ b/crates/fflonk/Cargo.toml @@ -5,12 +5,13 @@ edition = "2021" [dependencies] circuit_definitions = {git = "https://github.com/matter-labs/zksync-protocol", package = "circuit_definitions", branch = "si/fflonk"} -# circuit_definitions = {path = "../../../zksync-protocol/crates/circuit_definitions"} +# circuit_definitions = { path = "../../../zksync-protocol/crates/circuit_definitions" } +# circuit_definitions = "=0.150.5" # franklin_crypto = {path = "../franklin-crypto", package = "franklin-crypto"} -num-bigint = {version = "0.4", features = ["serde"]} +num-bigint = { version = "0.4", features = ["serde"] } num-traits = "0.2" rand = "0.4" -serde = {version = "1", features = ["derive", "rc"]} +serde = { version = "1", features = ["derive", "rc"] } serde_json = "1" serde_derive = "1" bincode = "1.3" @@ -18,5 +19,5 @@ byteorder = "1" ureq = "2.5" [features] -default = ["sanity"] -sanity = [] \ No newline at end of file +default = ["sanity"] +sanity = [] diff --git a/crates/fflonk/src/convenience.rs b/crates/fflonk/src/convenience.rs index 2dac308..048f0c8 100644 --- a/crates/fflonk/src/convenience.rs +++ b/crates/fflonk/src/convenience.rs @@ -6,6 +6,7 @@ use bellman::plonk::better_better_cs::{ }; use byteorder::{BigEndian, ReadBytesExt}; use circuit_definitions::circuit_definitions::aux_layer::compression_modes::{CompressionTranscriptForWrapper, CompressionTreeHasherForWrapper}; +use circuit_definitions::circuit_definitions::aux_layer::wrapper::ZkSyncCompressionWrapper; use circuit_definitions::circuit_definitions::recursion_layer::{ZkSyncRecursionProof, ZkSyncRecursionVerificationKey}; use circuit_definitions::{ circuit_definitions::aux_layer::{ @@ -48,20 +49,22 @@ type CompressionTranscript = GoldilocksPoisedon2Transcript; type CompressionTreeHasher = GoldilocksPoseidon2Sponge; pub const L1_VERIFIER_DOMAIN_SIZE_LOG: usize = 23; +pub const MAX_COMBINED_DEGREE_FACTOR: usize = 10; type F = GoldilocksField; type EXT = GoldilocksExt2; -pub fn init_crs(worker: &Worker, domain_size: usize, max_combined_degree: usize) -> Crs { +pub fn init_crs(worker: &Worker, domain_size: usize) -> Crs { assert!(domain_size <= 1 << L1_VERIFIER_DOMAIN_SIZE_LOG); + let num_points = MAX_COMBINED_DEGREE_FACTOR * domain_size; 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!(max_combined_degree <= mon_crs.g1_bases.len()); + assert!(num_points <= mon_crs.g1_bases.len()); mon_crs } else { - Crs::::non_power_of_two_crs_42(max_combined_degree, &worker) + Crs::::non_power_of_two_crs_42(num_points, &worker) }; mon_crs @@ -80,6 +83,52 @@ pub fn init_crs_for_vk() -> Crs { mon_crs } +pub fn init_snark_wrapper_circuit(path: &str) -> FflonkSnarkVerifierCircuit { + let compression_wrapper_mode = if let Ok(compression_wrapper_mode) = std::env::var("COMPRESSION_WRAPPER_MODE") { + compression_wrapper_mode.parse::().unwrap() + } else { + 5u8 + }; + println!("Compression mode {}", compression_wrapper_mode); + let compression_proof_file_path = if let Ok(file_path) = std::env::var("COMPRESSION_PROOF_FILE") { + file_path + } else { + format!("{}/compression_wrapper_{compression_wrapper_mode}_proof.json", path) + }; + println!("Reading proof file at {compression_proof_file_path}"); + let compression_vk_file_path = if let Ok(file_path) = std::env::var("COMPRESSION_VK_FILE") { + file_path + } else { + format!("{}/compression_wrapper_{compression_wrapper_mode}_vk.json", path) + }; + println!("Reading vk file at {compression_vk_file_path}"); + + let compression_proof_file = std::fs::File::open(compression_proof_file_path).unwrap(); + let compression_proof: ZkSyncCompressionProofForWrapper = serde_json::from_reader(&compression_proof_file).unwrap(); + + let compression_vk_file = std::fs::File::open(compression_vk_file_path).unwrap(); + let compression_vk: ZkSyncCompressionVerificationKeyForWrapper = serde_json::from_reader(&compression_vk_file).unwrap(); + + init_snark_wrapper_circuit_from_inputs(compression_wrapper_mode, compression_proof, compression_vk) +} + +pub fn init_snark_wrapper_circuit_from_inputs( + compression_wrapper_mode: u8, + input_proof: ZkSyncCompressionProofForWrapper, + input_vk: ZkSyncCompressionVerificationKeyForWrapper, +) -> FflonkSnarkVerifierCircuit { + let wrapper_function = ZkSyncCompressionWrapper::from_numeric_circuit_type(compression_wrapper_mode); + let fixed_parameters = input_vk.fixed_parameters.clone(); + + FflonkSnarkVerifierCircuit { + witness: Some(input_proof), + vk: input_vk, + fixed_parameters, + transcript_params: (), + wrapper_function, + } +} + pub fn precompute_and_save_setup_for_fflonk_snark_circuit(circuit: &FflonkSnarkVerifierCircuit, worker: &Worker, output_blob_path: &str) { let compression_wrapper_mode = circuit.wrapper_function.numeric_circuit_type(); println!("Compression mode: {compression_wrapper_mode}"); @@ -94,7 +143,7 @@ pub fn precompute_and_save_setup_for_fflonk_snark_circuit(circuit: &FflonkSnarkV assert!(domain_size.is_power_of_two()); assert!(domain_size <= 1 << L1_VERIFIER_DOMAIN_SIZE_LOG); - let mon_crs = init_crs(&worker, domain_size, max_combined_degree); + let mon_crs = init_crs(&worker, domain_size); let setup: FflonkSnarkVerifierCircuitSetup = FflonkSetup::create_setup(&setup_assembly, &worker, &mon_crs).expect("fflonk setup"); let vk = FflonkVerificationKey::from_setup(&setup, &mon_crs).unwrap(); @@ -121,9 +170,9 @@ pub fn prove_fflonk_snark_verifier_circuit_with_precomputation( assert!(domain_size <= 1 << L1_VERIFIER_DOMAIN_SIZE_LOG); - let mon_crs = init_crs(&worker, domain_size, max_combined_degree); + let mon_crs = init_crs(&worker, domain_size); - let proof = crate::prover::create_proof::<_, FflonkSnarkVerifierCircuit, _, _, _, RollingKeccakTranscript>(assembly, &worker, &precomputed_setup, &mon_crs, None).expect("proof"); + let proof = crate::prover::create_proof::<_, FflonkSnarkVerifierCircuit, _, _, _, RollingKeccakTranscript>(&assembly, &worker, &precomputed_setup, &mon_crs, None).expect("proof"); let valid = crate::verify::<_, _, RollingKeccakTranscript>(&vk, &proof, None).unwrap(); assert!(valid, "proof verification fails"); @@ -143,11 +192,11 @@ pub fn prove_fflonk_snark_verifier_circuit_single_shot(circuit: &FflonkSnarkVeri let max_combined_degree = compute_max_combined_degree_from_assembly::<_, _, _, _, FflonkSnarkVerifierCircuit>(&assembly); println!("Max degree is {}", max_combined_degree); - let mon_crs = init_crs(&worker, domain_size, max_combined_degree); + let mon_crs = init_crs(&worker, domain_size); let setup = FflonkSetup::create_setup(&assembly, &worker, &mon_crs).expect("setup"); let vk = FflonkVerificationKey::from_setup(&setup, &mon_crs).unwrap(); - let proof = crate::prover::create_proof::<_, FflonkSnarkVerifierCircuit, _, _, _, RollingKeccakTranscript>(assembly, &worker, &setup, &mon_crs, None).expect("proof"); + let proof = crate::prover::create_proof::<_, FflonkSnarkVerifierCircuit, _, _, _, RollingKeccakTranscript>(&assembly, &worker, &setup, &mon_crs, None).expect("proof"); let valid = crate::verify::<_, _, RollingKeccakTranscript>(&vk, &proof, None).unwrap(); assert!(valid, "proof verification fails"); @@ -1051,7 +1100,7 @@ fn download_file(url: &str, output_file: &str) -> Result<(), ureq::Error> { Ok(()) } -pub(crate) fn download_and_transform_ignition_transcripts(domain_size: usize) { +pub fn download_and_transform_ignition_transcripts(domain_size: usize) { let transcripts_dir = std::env::var("IGNITION_TRANSCRIPT_PATH").unwrap_or("./".to_string()); let chunk_size = 5_040_000usize; let num_chunks = domain_size.div_ceil(chunk_size); diff --git a/crates/fflonk/src/lib.rs b/crates/fflonk/src/lib.rs index 2c3c883..eab98fe 100644 --- a/crates/fflonk/src/lib.rs +++ b/crates/fflonk/src/lib.rs @@ -1,5 +1,6 @@ #![feature(generic_const_exprs)] #![feature(allocator_api)] +pub use circuit_definitions; pub use circuit_definitions::snark_wrapper::franklin_crypto; use circuit_definitions::snark_wrapper::franklin_crypto::bellman::plonk::better_better_cs::{cs::PlonkCsWidth3Params, gates::naive_main_gate::NaiveMainGate}; pub use franklin_crypto::bellman; @@ -31,7 +32,7 @@ pub use franklin_crypto::plonk::circuit::custom_rescue_gate::Rescue5CustomGate; mod definitions; pub use definitions::*; -mod prover; +pub mod prover; use prover::*; pub mod verifier; pub use utils::*; diff --git a/crates/fflonk/src/prover.rs b/crates/fflonk/src/prover.rs index 281e23d..005fc6c 100644 --- a/crates/fflonk/src/prover.rs +++ b/crates/fflonk/src/prover.rs @@ -1,7 +1,7 @@ use super::*; pub fn create_proof, P: PlonkConstraintSystemParams, MG: MainGate, S: SynthesisMode, T: Transcript<::Fr>>( - assembly: Assembly, + assembly: &Assembly, worker: &Worker, setup: &FflonkSetup, mon_crs: &Crs, diff --git a/crates/fflonk/src/test.rs b/crates/fflonk/src/test.rs index 8e102c7..077cdad 100644 --- a/crates/fflonk/src/test.rs +++ b/crates/fflonk/src/test.rs @@ -1,5 +1,6 @@ -use circuit_definitions::snark_wrapper::franklin_crypto::{ - bellman::{ +use circuit_definitions::{ + boojum::pairing::ff::Field, + snark_wrapper::franklin_crypto::bellman::{ bn256::{Bn256, Fr}, plonk::{ better_better_cs::{ @@ -7,10 +8,10 @@ use circuit_definitions::snark_wrapper::franklin_crypto::{ gates::naive_main_gate::NaiveMainGate, }, commitments::transcript::keccak_transcript::RollingKeccakTranscript, + polynomials::Polynomial, }, worker::Worker, }, - plonk::circuit::linear_combination::LinearCombination, }; use circuit_definitions::{ boojum::pairing::ff::PrimeField, @@ -22,6 +23,8 @@ use circuit_definitions::{ }, }; +use crate::FflonkTestCircuit; + use super::convenience::*; #[test] @@ -137,41 +140,7 @@ fn verify_compression_wrapper_proof() { #[ignore] fn test_snark_circuit_with_naive_main_gate() { let path = format!("./data/compression_schedule/hard"); - let compression_wrapper_mode = if let Ok(compression_wrapper_mode) = std::env::var("COMPRESSION_WRAPPER_MODE") { - compression_wrapper_mode.parse::().unwrap() - } else { - 5u8 - }; - println!("Compression mode {}", compression_wrapper_mode); - let compression_proof_file_path = if let Ok(file_path) = std::env::var("COMPRESSION_PROOF_FILE") { - file_path - } else { - format!("{}/compression_wrapper_{compression_wrapper_mode}_proof.json", path) - }; - println!("Reading proof file at {compression_proof_file_path}"); - let compression_vk_file_path = if let Ok(file_path) = std::env::var("COMPRESSION_VK_FILE") { - file_path - } else { - format!("{}/compression_wrapper_{compression_wrapper_mode}_vk.json", path) - }; - println!("Reading vk file at {compression_vk_file_path}"); - - let compression_proof_file = std::fs::File::open(compression_proof_file_path).unwrap(); - let compression_proof: ZkSyncCompressionProofForWrapper = serde_json::from_reader(&compression_proof_file).unwrap(); - - let compression_vk_file = std::fs::File::open(compression_vk_file_path).unwrap(); - let compression_vk: ZkSyncCompressionVerificationKeyForWrapper = serde_json::from_reader(&compression_vk_file).unwrap(); - - let wrapper_function = ZkSyncCompressionWrapper::from_numeric_circuit_type(compression_wrapper_mode); - let fixed_parameters = compression_vk.fixed_parameters.clone(); - - let circuit = FflonkSnarkVerifierCircuit { - witness: Some(compression_proof), - vk: compression_vk, - fixed_parameters, - transcript_params: (), - wrapper_function, - }; + let circuit = init_snark_wrapper_circuit(&path); let worker = Worker::new(); let (proof, vk) = prove_fflonk_snark_verifier_circuit_single_shot(&circuit, &worker); @@ -371,48 +340,12 @@ fn download_and_transform_crs() { compression_wrapper_mode ); // Step 1 - let max_degree = crate::compute_max_combined_degree_from_assembly::<_, _, _, _, FflonkSnarkVerifierCircuit>(&assembly); + // let max_degree = crate::compute_max_combined_degree_from_assembly::<_, _, _, _, FflonkSnarkVerifierCircuit>(&assembly); + let max_degree = MAX_COMBINED_DEGREE_FACTOR * domain_size; // Step 2-4 download_and_transform_ignition_transcripts(max_degree); } -struct FflonkTestCircuit; - -impl Circuit for FflonkTestCircuit { - type MainGate = NaiveMainGate; - - fn synthesize + 'static>( - &self, - cs: &mut CS, - ) -> Result<(), circuit_definitions::snark_wrapper::franklin_crypto::bellman::SynthesisError> { - use circuit_definitions::snark_wrapper::franklin_crypto::bellman::Field; - use circuit_definitions::snark_wrapper::franklin_crypto::plonk::circuit::allocated_num::Num; - let a = Fr::from_str(&65.to_string()).unwrap(); - let b = Fr::from_str(&66.to_string()).unwrap(); - let mut c = a; - c.add_assign(&b); - - let a_var = Num::alloc(cs, Some(a))?; - let b_var = Num::alloc(cs, Some(b))?; - let c_var = Num::alloc(cs, Some(c))?; - - for _ in 0..1 << 10 { - let mut lc = LinearCombination::zero(); - lc.add_assign_number_with_coeff(&a_var, Fr::one()); - lc.add_assign_number_with_coeff(&b_var, Fr::one()); - let mut minus_one = Fr::one(); - minus_one.negate(); - lc.add_assign_number_with_coeff(&c_var, minus_one); - - let _ = lc.into_num(cs)?; - } - - let _input = cs.alloc_input(|| Ok(Fr::one()))?; - - Ok(()) - } -} - #[test] #[ignore] fn test_test_circuit_with_naive_main_gate() { @@ -434,11 +367,56 @@ fn test_test_circuit_with_naive_main_gate() { let max_combined_degree = compute_max_combined_degree_from_assembly::<_, _, _, _, FflonkTestCircuit>(&assembly); println!("Max degree is {}", max_combined_degree); - let mon_crs = init_crs(&worker, domain_size, max_combined_degree); + let mon_crs = init_crs(&worker, domain_size); let setup = FflonkSetup::create_setup(&assembly, &worker, &mon_crs).expect("setup"); let vk = FflonkVerificationKey::from_setup(&setup, &mon_crs).unwrap(); + let vk_file = std::fs::File::create("/tmp/test_vk.json").unwrap(); + serde_json::to_writer(&vk_file, &vk).unwrap(); + println!("vk file saved"); - let proof = crate::prover::create_proof::<_, FflonkTestCircuit, _, _, _, RollingKeccakTranscript>(assembly, &worker, &setup, &mon_crs, None).expect("proof"); + let proof = crate::prover::create_proof::<_, FflonkTestCircuit, _, _, _, RollingKeccakTranscript>(&assembly, &worker, &setup, &mon_crs, None).expect("proof"); + dbg!(&proof.commitments); let valid = crate::verify::<_, _, RollingKeccakTranscript>(&vk, &proof, None).unwrap(); assert!(valid, "proof verification fails"); } + +#[test] +fn test_alternative_division() { + let worker = &Worker::new(); + let domain_size = 1 << 2; + let degree = 2 * domain_size; + + let expected_coeffs: Vec<_> = (0..degree).map(|el| Fr::from_str(&el.to_string()).unwrap()).collect(); + let mut actual = Polynomial::from_coeffs_unpadded(expected_coeffs.clone()).unwrap(); + + let mut minus_two = Fr::one(); + minus_two.negate(); + + let mut other = vec![Fr::zero(); 2 * domain_size]; + other[0] = minus_two; + other[domain_size - 1] = Fr::one(); + let other = Polynomial::from_coeffs(other).unwrap(); + + let coset_factor = Fr::multiplicative_generator(); + let mut other_evals = other.coset_fft_for_generator(worker, coset_factor); + other_evals.batch_inversion(worker).unwrap(); + + for chunk_coeffs in actual.as_mut().chunks_mut(domain_size) { + let mut num = vec![Fr::zero(); 2 * domain_size]; + num[..domain_size].copy_from_slice(chunk_coeffs); + + let chunk = Polynomial::from_coeffs(num).unwrap(); + let mut chunk_evals = chunk.coset_fft_for_generator(worker, coset_factor); + chunk_evals.mul_assign(worker, &other_evals); + // chunk_evals.bitreverse_enumeration(worker); + chunk_coeffs.copy_from_slice(chunk_evals.icoset_fft_for_generator(worker, &coset_factor).as_ref()); + } + + let mut actual_coeffs = vec![Fr::zero(); degree + 1]; + assert_eq!(actual_coeffs.len(), actual.as_ref().len() + 1); + + actual_coeffs[1..].copy_from_slice(actual.as_ref()); + actual_coeffs[0].add_assign(&minus_two); + + assert_eq!(expected_coeffs, actual_coeffs); +} diff --git a/crates/fflonk/src/utils.rs b/crates/fflonk/src/utils.rs index 44a58bb..ea00055 100644 --- a/crates/fflonk/src/utils.rs +++ b/crates/fflonk/src/utils.rs @@ -1880,7 +1880,7 @@ impl Circuit for FflonkTestCircuit { let b_var = Num::alloc(cs, Some(b))?; let c_var = Num::alloc(cs, Some(c))?; - for _ in 0..1 << 10 { + for _ in 0..1 << 5 { let mut lc = LinearCombination::zero(); lc.add_assign_number_with_coeff(&a_var, Fr::one()); lc.add_assign_number_with_coeff(&b_var, Fr::one());