From 256d76ae3434604b977e2fa5187762d7cff81f64 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Tue, 24 Jun 2025 17:28:58 +0200 Subject: [PATCH] add zk config, enabled by a feature (on by default) (#306) * add zk config, enabled by a feature (on by default) * Update src/backends/plonky2/recursion/circuit.rs Co-authored-by: Ahmad Afuni --------- Co-authored-by: Ahmad Afuni --- Cargo.toml | 3 +- src/backends/plonky2/emptypod.rs | 5 ++ src/backends/plonky2/recursion/circuit.rs | 94 ++++++++++++++++++----- 3 files changed, 82 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e4420de..dfea0c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,8 @@ pretty_assertions = "1.4.1" jsonschema = "0.30.0" [features] -default = ["backend_plonky2"] +default = ["backend_plonky2", "zk"] backend_plonky2 = ["plonky2"] +zk = [] metrics = [] time = [] diff --git a/src/backends/plonky2/emptypod.rs b/src/backends/plonky2/emptypod.rs index b8b15a3..81bd11d 100644 --- a/src/backends/plonky2/emptypod.rs +++ b/src/backends/plonky2/emptypod.rs @@ -88,7 +88,12 @@ pub static STANDARD_EMPTY_POD_DATA: LazyLock<(EmptyPodVerifyTarget, CircuitData) fn build() -> Result<(EmptyPodVerifyTarget, CircuitData)> { let params = &*DEFAULT_PARAMS; + + #[cfg(not(feature = "zk"))] let config = CircuitConfig::standard_recursion_config(); + #[cfg(feature = "zk")] + let config = CircuitConfig::standard_recursion_zk_config(); + let mut builder = CircuitBuilder::::new(config); let empty_pod_verify_target = EmptyPodVerifyCircuit { params: params.clone(), diff --git a/src/backends/plonky2/recursion/circuit.rs b/src/backends/plonky2/recursion/circuit.rs index 0126510..9553ff1 100644 --- a/src/backends/plonky2/recursion/circuit.rs +++ b/src/backends/plonky2/recursion/circuit.rs @@ -164,8 +164,12 @@ impl RecursiveCircuit { /// builds the targets and returns also a ProverCircuitData pub fn build(params: &RecursiveParams, inner_params: &I::Params) -> Result { + #[cfg(not(feature = "zk"))] let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::new(config.clone()); + #[cfg(feature = "zk")] + let config = CircuitConfig::standard_recursion_zk_config(); + + let mut builder = CircuitBuilder::new(config); let targets: RecursiveCircuitTarget = Self::build_targets( &mut builder, @@ -275,7 +279,11 @@ impl RecursiveCircuit { ); // build the actual RecursiveCircuit circuit data + #[cfg(not(feature = "zk"))] let config = CircuitConfig::standard_recursion_config(); + #[cfg(feature = "zk")] + let config = CircuitConfig::standard_recursion_zk_config(); + let mut builder = CircuitBuilder::new(config); let target = timed!( @@ -295,7 +303,11 @@ impl RecursiveCircuit { inner_params: &I::Params, ) -> Result<(RecursiveCircuitTarget, CircuitData)> { // build the actual RecursiveCircuit circuit data + #[cfg(not(feature = "zk"))] let config = CircuitConfig::standard_recursion_config(); + #[cfg(feature = "zk")] + let config = CircuitConfig::standard_recursion_zk_config(); + let mut builder = CircuitBuilder::new(config); let target = timed!( @@ -398,9 +410,20 @@ fn standard_gates(config: &CircuitConfig) -> Vec> { /// Estimate the number of gates to verify a proof of `degree_bits` that uses the /// `standard_gates(&standard_recursion_config)` fn estimate_verif_num_gates(degree_bits: usize) -> usize { - // Formula obtained via linear regression using `test_measure_recursion` results with - // `standard_recursion_config`. - let num_gates: usize = 236 * degree_bits + 1171; + let num_gates: usize; + + #[cfg(feature = "zk")] + { + // Formula obtained via linear regression using + // `test_measure_zk_recursion` results with `standard_recursion_zk_config`. + num_gates = 244 * degree_bits + 1127; + } + #[cfg(not(feature = "zk"))] + { + // Formula obtained via linear regression using `test_measure_recursion` + // results with `standard_recursion_config`. + let num_gates: usize = 236 * degree_bits + 1171; + } // Add 2% for error because the results are not a clean line num_gates * 102 / 100 } @@ -417,6 +440,21 @@ fn estimate_gates_after_zk(degree_bits: usize) -> usize { } } +// how many blinding gates are in this zk circuit +fn blinding_gates(degree_bits: usize) -> usize { + // Table data obtained using `test_measure_zk_recursion`, and printing + // `regular_poly_openings + 2 * z_openings` at method `blind` of the file + // `plonky2/plonky2/src/plonk/circuit_builder.rs`. + match degree_bits { + 0..=12 => 8326, + 13..=14 => 8998, + 15 => 10342, + 16 => 13030, + 17 => 10846, + _ => panic!("not supported"), + } +} + pub fn common_data_for_recursion( arity: usize, num_public_inputs: usize, @@ -448,13 +486,22 @@ pub fn common_data_for_recursion( let mut degree_bits = log2_ceil(inner_num_gates); loop { let verif_num_gates = estimate_verif_num_gates(degree_bits); - // Leave space for public input hashing, a `PublicInputGate` and some `ConstantGate`s (that's - // MAX_CONSTANT_GATES*2 constants in the standard_recursion_config). - let total_num_gates = inner_num_gates + + // Leave space for public input hashing, a `PublicInputGate` and some + // `ConstantGate`s (that's MAX_CONSTANT_GATES*2 constants in the + // standard_recursion_config). And if the zk feature is enabled, add + // space for the blinding gates. + let mut total_num_gates = inner_num_gates + verif_num_gates * arity + circuit_data.common.num_public_inputs.div_ceil(8) + 1 + MAX_CONSTANT_GATES; + + #[cfg(feature = "zk")] + { + total_num_gates += blinding_gates(degree_bits); + } + if total_num_gates < (1 << degree_bits) { break; } @@ -464,6 +511,11 @@ pub fn common_data_for_recursion( let mut common_data = circuit_data.common.clone(); common_data.fri_params.degree_bits = degree_bits; common_data.fri_params.reduction_arity_bits = vec![4, 4, 4]; + #[cfg(feature = "zk")] + { + common_data.fri_params.hiding = true; + common_data.config.zero_knowledge = true; + } Ok(common_data) } @@ -471,12 +523,6 @@ pub fn common_data_for_recursion( pub fn pad_circuit(builder: &mut CircuitBuilder, common_data: &CommonCircuitData) { assert_eq!(common_data.config, builder.config); assert_eq!(common_data.num_public_inputs, builder.num_public_inputs()); - // TODO: We need to figure this out once we enable zero-knowledge - // https://github.com/0xPARC/pod2/issues/248 - assert!( - !common_data.config.zero_knowledge, - "Degree calculation can be off if zero-knowledge is on." - ); let degree = common_data.degree(); // Need to account for public input hashing, a `PublicInputGate` and MAX_CONSTANT_GATES @@ -484,7 +530,17 @@ pub fn pad_circuit(builder: &mut CircuitBuilder, common_data: &CommonCircu // have been registered, so we can't know exactly how many `ConstantGates` will be required. // We hope that no more than MAX_CONSTANT_GATES*2 constants are used :pray:. Maybe we should // make a PR to plonky2 to expose this? - let num_gates = degree - common_data.num_public_inputs.div_ceil(8) - 1 - MAX_CONSTANT_GATES; + let mut num_gates = degree - common_data.num_public_inputs.div_ceil(8) - 1 - MAX_CONSTANT_GATES; + #[cfg(feature = "zk")] + { + // in the zk config case, account for the blinding gates, to avoid + // padding to them too, because then plonky2's in the builder.build() + // phase would add new blinding gates on top of the ones that we already + // accounted for in the `common_data_for_recursion`, increasing (w.h.p.) + // the degree of the circuit. + num_gates -= blinding_gates(log2_ceil(degree)); + } + assert!( builder.num_gates() < num_gates, "builder has more gates ({}) than the padding target ({})", @@ -678,22 +734,22 @@ mod tests { } #[test] - fn test_circuit_i() -> Result<()> { + fn test_inner_circuit_i() -> Result<()> { let inner_params = (); let inp = HashOut::::ZERO; let inner_inputs = (inp, circuit1_io(inp)); - test_circuit_i_opt::(&inner_params, inner_inputs)?; + test_inner_circuit_i_opt::(&inner_params, inner_inputs)?; let inner_inputs = (inp, circuit2_io(inp)); - test_circuit_i_opt::(&inner_params, inner_inputs)?; + test_inner_circuit_i_opt::(&inner_params, inner_inputs)?; let inner_inputs = (inp, circuit3_io(inp)); - test_circuit_i_opt::(&inner_params, inner_inputs)?; + test_inner_circuit_i_opt::(&inner_params, inner_inputs)?; Ok(()) } - fn test_circuit_i_opt( + fn test_inner_circuit_i_opt( inner_params: &IC::Params, inner_inputs: IC::Input, ) -> Result<()> {