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 <root@ahmadafuni.com>

---------

Co-authored-by: Ahmad Afuni <root@ahmadafuni.com>
This commit is contained in:
arnaucube 2025-06-24 17:28:58 +02:00 committed by GitHub
parent d5da9d8593
commit 256d76ae34
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 82 additions and 20 deletions

View file

@ -43,7 +43,8 @@ pretty_assertions = "1.4.1"
jsonschema = "0.30.0" jsonschema = "0.30.0"
[features] [features]
default = ["backend_plonky2"] default = ["backend_plonky2", "zk"]
backend_plonky2 = ["plonky2"] backend_plonky2 = ["plonky2"]
zk = []
metrics = [] metrics = []
time = [] time = []

View file

@ -88,7 +88,12 @@ pub static STANDARD_EMPTY_POD_DATA: LazyLock<(EmptyPodVerifyTarget, CircuitData)
fn build() -> Result<(EmptyPodVerifyTarget, CircuitData)> { fn build() -> Result<(EmptyPodVerifyTarget, CircuitData)> {
let params = &*DEFAULT_PARAMS; let params = &*DEFAULT_PARAMS;
#[cfg(not(feature = "zk"))]
let config = CircuitConfig::standard_recursion_config(); let config = CircuitConfig::standard_recursion_config();
#[cfg(feature = "zk")]
let config = CircuitConfig::standard_recursion_zk_config();
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::<F, D>::new(config);
let empty_pod_verify_target = EmptyPodVerifyCircuit { let empty_pod_verify_target = EmptyPodVerifyCircuit {
params: params.clone(), params: params.clone(),

View file

@ -164,8 +164,12 @@ impl<I: InnerCircuit> RecursiveCircuit<I> {
/// builds the targets and returns also a ProverCircuitData /// builds the targets and returns also a ProverCircuitData
pub fn build(params: &RecursiveParams, inner_params: &I::Params) -> Result<Self> { pub fn build(params: &RecursiveParams, inner_params: &I::Params) -> Result<Self> {
#[cfg(not(feature = "zk"))]
let config = CircuitConfig::standard_recursion_config(); 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<I> = Self::build_targets( let targets: RecursiveCircuitTarget<I> = Self::build_targets(
&mut builder, &mut builder,
@ -275,7 +279,11 @@ impl<I: InnerCircuit> RecursiveCircuit<I> {
); );
// build the actual RecursiveCircuit circuit data // build the actual RecursiveCircuit circuit data
#[cfg(not(feature = "zk"))]
let config = CircuitConfig::standard_recursion_config(); let config = CircuitConfig::standard_recursion_config();
#[cfg(feature = "zk")]
let config = CircuitConfig::standard_recursion_zk_config();
let mut builder = CircuitBuilder::new(config); let mut builder = CircuitBuilder::new(config);
let target = timed!( let target = timed!(
@ -295,7 +303,11 @@ impl<I: InnerCircuit> RecursiveCircuit<I> {
inner_params: &I::Params, inner_params: &I::Params,
) -> Result<(RecursiveCircuitTarget<I>, CircuitData<F, C, D>)> { ) -> Result<(RecursiveCircuitTarget<I>, CircuitData<F, C, D>)> {
// build the actual RecursiveCircuit circuit data // build the actual RecursiveCircuit circuit data
#[cfg(not(feature = "zk"))]
let config = CircuitConfig::standard_recursion_config(); let config = CircuitConfig::standard_recursion_config();
#[cfg(feature = "zk")]
let config = CircuitConfig::standard_recursion_zk_config();
let mut builder = CircuitBuilder::new(config); let mut builder = CircuitBuilder::new(config);
let target = timed!( let target = timed!(
@ -398,9 +410,20 @@ fn standard_gates(config: &CircuitConfig) -> Vec<GateRef<F, D>> {
/// Estimate the number of gates to verify a proof of `degree_bits` that uses the /// Estimate the number of gates to verify a proof of `degree_bits` that uses the
/// `standard_gates(&standard_recursion_config)` /// `standard_gates(&standard_recursion_config)`
fn estimate_verif_num_gates(degree_bits: usize) -> usize { fn estimate_verif_num_gates(degree_bits: usize) -> usize {
// Formula obtained via linear regression using `test_measure_recursion` results with let num_gates: usize;
// `standard_recursion_config`.
let num_gates: usize = 236 * degree_bits + 1171; #[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 // Add 2% for error because the results are not a clean line
num_gates * 102 / 100 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<I: InnerCircuit>( pub fn common_data_for_recursion<I: InnerCircuit>(
arity: usize, arity: usize,
num_public_inputs: usize, num_public_inputs: usize,
@ -448,13 +486,22 @@ pub fn common_data_for_recursion<I: InnerCircuit>(
let mut degree_bits = log2_ceil(inner_num_gates); let mut degree_bits = log2_ceil(inner_num_gates);
loop { loop {
let verif_num_gates = estimate_verif_num_gates(degree_bits); 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). // Leave space for public input hashing, a `PublicInputGate` and some
let total_num_gates = inner_num_gates // `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 + verif_num_gates * arity
+ circuit_data.common.num_public_inputs.div_ceil(8) + circuit_data.common.num_public_inputs.div_ceil(8)
+ 1 + 1
+ MAX_CONSTANT_GATES; + MAX_CONSTANT_GATES;
#[cfg(feature = "zk")]
{
total_num_gates += blinding_gates(degree_bits);
}
if total_num_gates < (1 << degree_bits) { if total_num_gates < (1 << degree_bits) {
break; break;
} }
@ -464,6 +511,11 @@ pub fn common_data_for_recursion<I: InnerCircuit>(
let mut common_data = circuit_data.common.clone(); let mut common_data = circuit_data.common.clone();
common_data.fri_params.degree_bits = degree_bits; common_data.fri_params.degree_bits = degree_bits;
common_data.fri_params.reduction_arity_bits = vec![4, 4, 4]; 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) Ok(common_data)
} }
@ -471,12 +523,6 @@ pub fn common_data_for_recursion<I: InnerCircuit>(
pub fn pad_circuit(builder: &mut CircuitBuilder<F, D>, common_data: &CommonCircuitData<F, D>) { pub fn pad_circuit(builder: &mut CircuitBuilder<F, D>, common_data: &CommonCircuitData<F, D>) {
assert_eq!(common_data.config, builder.config); assert_eq!(common_data.config, builder.config);
assert_eq!(common_data.num_public_inputs, builder.num_public_inputs()); 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(); let degree = common_data.degree();
// Need to account for public input hashing, a `PublicInputGate` and MAX_CONSTANT_GATES // Need to account for public input hashing, a `PublicInputGate` and MAX_CONSTANT_GATES
@ -484,7 +530,17 @@ pub fn pad_circuit(builder: &mut CircuitBuilder<F, D>, common_data: &CommonCircu
// have been registered, so we can't know exactly how many `ConstantGates` will be required. // 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 // 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? // 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!( assert!(
builder.num_gates() < num_gates, builder.num_gates() < num_gates,
"builder has more gates ({}) than the padding target ({})", "builder has more gates ({}) than the padding target ({})",
@ -678,22 +734,22 @@ mod tests {
} }
#[test] #[test]
fn test_circuit_i() -> Result<()> { fn test_inner_circuit_i() -> Result<()> {
let inner_params = (); let inner_params = ();
let inp = HashOut::<F>::ZERO; let inp = HashOut::<F>::ZERO;
let inner_inputs = (inp, circuit1_io(inp)); let inner_inputs = (inp, circuit1_io(inp));
test_circuit_i_opt::<Circuit1>(&inner_params, inner_inputs)?; test_inner_circuit_i_opt::<Circuit1>(&inner_params, inner_inputs)?;
let inner_inputs = (inp, circuit2_io(inp)); let inner_inputs = (inp, circuit2_io(inp));
test_circuit_i_opt::<Circuit2>(&inner_params, inner_inputs)?; test_inner_circuit_i_opt::<Circuit2>(&inner_params, inner_inputs)?;
let inner_inputs = (inp, circuit3_io(inp)); let inner_inputs = (inp, circuit3_io(inp));
test_circuit_i_opt::<Circuit3>(&inner_params, inner_inputs)?; test_inner_circuit_i_opt::<Circuit3>(&inner_params, inner_inputs)?;
Ok(()) Ok(())
} }
fn test_circuit_i_opt<IC: InnerCircuit>( fn test_inner_circuit_i_opt<IC: InnerCircuit>(
inner_params: &IC::Params, inner_params: &IC::Params,
inner_inputs: IC::Input, inner_inputs: IC::Input,
) -> Result<()> { ) -> Result<()> {