Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unified Recursion Circuit for Multi-Degree Starky Proof Verification #1635

Merged
merged 39 commits into from
Nov 25, 2024

Conversation

sai-deng
Copy link
Contributor

@sai-deng sai-deng commented Nov 1, 2024

This PR introduces an enhanced recursion circuit that supports Starky proof verification across multiple degrees. This work significantly reduces the number of recursion circuits needed for zkVM/zkEVM proof recursion. Although the new circuit adds approximately 1000-4000 more gates compared to the previous fixed-degree recursion circuit, the padded circuit size remains unchanged for commonly used cases within the 2^22 - 2^26 range (fibonacci_stark). Additionally, the extra gates added for the more complex recursive circuit (e.g. Keccak STARK) can be considered negligible.

Circuit Size Comparison (fibonacci_stark):

degree bits (old) circuit size (old) circuit size after padding (old) degree bits (new) circuit size (new) circuit size after padding (new)
14 4668 8192 4-14 5781 8192
18 6889 8192 4-18 8488 16384
22 9417 16384 4-22 12126 16384
26 12289 16384 4-26 15633 16384
30 15502 16384 4-30 19480 32768

Example: In the previous setup, the recursion circuit supported proof verification at degree bits 22 with a final degree of 16384. The updated recursion circuit supports proof verification across degree bits from 4 to 22 while retaining the final degree of 16384.

Old Circuit Details:

[DEBUG plonky2::util::context_tree] 9416 gates to root
[DEBUG plonky2::util::context_tree] | 112 gates to verify one (of 84) query rounds
[DEBUG plonky2::plonk::circuit_builder] Total gate counts:
[DEBUG plonky2::plonk::circuit_builder] - 31 instances of MulExtensionGate { num_ops: 13 }
[DEBUG plonky2::plonk::circuit_builder] - 84 instances of ArithmeticExtensionGate { num_ops: 10 }
[DEBUG plonky2::plonk::circuit_builder] - 226 instances of ArithmeticGate { num_ops: 20 }
[DEBUG plonky2::plonk::circuit_builder] - 336 instances of CosetInterpolationGate { subgroup_bits: 4, degree: 6, barycentric_weights: [17293822565076172801, 256, 1048576, 4294967296, 17592186044416, 72057594037927936, 68719476720, 281474976645120, 1152921504338411520, 18446744069414584065, 18446744069413535745, 18446744065119617025, 18446726477228539905, 18374686475376656385, 18446744000695107601, 18446462594437939201], _phantom: PhantomData<plonky2_field::goldilocks_field::GoldilocksField> }<D=2>
[DEBUG plonky2::plonk::circuit_builder] - 168 instances of ReducingExtensionGate { num_coeffs: 32 }
[DEBUG plonky2::plonk::circuit_builder] - 672 instances of RandomAccessGate { bits: 4, num_copies: 4, num_extra_constants: 2, _phantom: PhantomData<plonky2_field::goldilocks_field::GoldilocksField> }<D=2>
[DEBUG plonky2::plonk::circuit_builder] - 7638 instances of PoseidonGate(PhantomData<plonky2_field::goldilocks_field::GoldilocksField>)<WIDTH=12>
[DEBUG plonky2::plonk::circuit_builder] - 174 instances of BaseSumGate { num_limbs: 63 } + Base: 2
[DEBUG plonky2::plonk::circuit_builder] - 87 instances of ExponentiationGate { num_power_bits: 66, _phantom: PhantomData<plonky2_field::goldilocks_field::GoldilocksField> }<D=2>
[DEBUG plonky2::plonk::circuit_builder] Degree before blinding & padding: 9417
[DEBUG plonky2::plonk::circuit_builder] Degree after blinding & padding: 16384

New Circuit Details:

[DEBUG plonky2::util::context_tree] 12125 gates to root
[DEBUG plonky2::util::context_tree] | 143 gates to verify one (of 84) query rounds
[DEBUG plonky2::plonk::circuit_builder] Total gate counts:
[DEBUG plonky2::plonk::circuit_builder] - 31 instances of MulExtensionGate { num_ops: 13 }
[DEBUG plonky2::plonk::circuit_builder] - 151 instances of ArithmeticExtensionGate { num_ops: 10 }
[DEBUG plonky2::plonk::circuit_builder] - 1608 instances of ArithmeticGate { num_ops: 20 }
[DEBUG plonky2::plonk::circuit_builder] - 336 instances of CosetInterpolationGate { subgroup_bits: 4, degree: 6, barycentric_weights: [17293822565076172801, 256, 1048576, 4294967296, 17592186044416, 72057594037927936, 68719476720, 281474976645120, 1152921504338411520, 18446744069414584065, 18446744069413535745, 18446744065119617025, 18446726477228539905, 18374686475376656385, 18446744000695107601, 18446462594437939201], _phantom: PhantomData<plonky2_field::goldilocks_field::GoldilocksField> }<D=2>
[DEBUG plonky2::plonk::circuit_builder] - 168 instances of ReducingExtensionGate { num_coeffs: 32 }
[DEBUG plonky2::plonk::circuit_builder] - 1092 instances of RandomAccessGate { bits: 5, num_copies: 2, num_extra_constants: 2, _phantom: PhantomData<plonky2_field::goldilocks_field::GoldilocksField> }<D=2>
[DEBUG plonky2::plonk::circuit_builder] - 7638 instances of PoseidonGate(PhantomData<plonky2_field::goldilocks_field::GoldilocksField>)<WIDTH=12>
[DEBUG plonky2::plonk::circuit_builder] - 255 instances of ExponentiationGate { num_power_bits: 66, _phantom: PhantomData<plonky2_field::goldilocks_field::GoldilocksField> }<D=2>
[DEBUG plonky2::plonk::circuit_builder] - 174 instances of BaseSumGate { num_limbs: 63 } + Base: 2
[DEBUG plonky2::plonk::circuit_builder] - 672 instances of RandomAccessGate { bits: 4, num_copies: 4, num_extra_constants: 2, _phantom: PhantomData<plonky2_field::goldilocks_field::GoldilocksField> }<D=2>
[DEBUG plonky2::plonk::circuit_builder] Degree before blinding & padding: 12126
[DEBUG plonky2::plonk::circuit_builder] Degree after blinding & padding: 16384

@sai-deng sai-deng self-assigned this Nov 7, 2024
@sai-deng sai-deng changed the title [wip] Sai/multi degrees recursion circuit One recursion circuit for all degrees Nov 12, 2024
@sai-deng sai-deng changed the title One recursion circuit for all degrees Unified Recursion Circuit for Multi-Degree Proof Verification Nov 12, 2024
@sai-deng sai-deng changed the title Unified Recursion Circuit for Multi-Degree Proof Verification Unified Recursion Circuit for Multi-Degree Starky Proof Verification Nov 12, 2024
@sai-deng sai-deng marked this pull request as ready for review November 12, 2024 23:42
@sai-deng sai-deng requested a review from hratoanina November 12, 2024 23:42
@sai-deng sai-deng force-pushed the sai/multi_degrees_recursion_circuit branch from 870bbe7 to 1e3c8b9 Compare November 15, 2024 17:59
@sai-deng
Copy link
Contributor Author

sai-deng commented Nov 15, 2024

I would like to provide some clarification based on my offline discussion with @hratoanina

Currently, we only support maximum degree bits of {30, 26, 22, 18, …} in the recursive verifier circuit, as they generate the max final polynomial length when using the default configuration ConstantArityBits(4, 5). This ensures that for other degrees, the final proof polynomial will not be longer than the circuit’s final polynomial length.

We will always pad the proof with smaller degree bits (than the circuit’s degree bits) with zeros to Merkle proofs and final polynomials and "skip" some FRI steps or hashing steps in Merkle verification. Compared to the previous method, degree bits are now variables instead of constants.

This PR does not affect the speed of the provers or alter any security assumptions of STARK.

@sai-deng sai-deng requested a review from 4l0n50 November 18, 2024 17:24
Copy link
Contributor

@LindaGuiga LindaGuiga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Globally, I think it would be better to have more comments, and I think it would be beneficial to have one big paragraph explaining how the multiple degrees work. So it would be nice to explain the verifier has a max number of folding steps and some are skipped if the actual degree is lower.

In addition, saying the FRI strategy has to be the same across proofs using this multi-degree circuit would be nice.

Finally, I think the degrees you mentioned above (and in the test) are off by one, aren't they? (Shouldn't it be 29, 25, ... instead of 30, 26, ...?)

Thanks for this work though, it is really great!

let zero_cap = vec![F::ZERO; cap_len];
for _ in fri_params.reduction_arity_bits.len()..step_count {
challenger.observe_elements(&zero_cap);
challenger.get_extension_challenge::<D>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? We just need to observe, but we don't need a challenge, do we?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we need to observe and get a challenge here, as we want the challenger to be in sync with the verifier.

let log_n = params.config.rate_bits + params.degree_bits;
let mut current_log_n = self.constant(F::from_canonical_usize(params.config.rate_bits));
current_log_n = self.add(current_log_n, current_degree_bits);
let min_log_n_to_support = log_n - (params.degree_bits - min_degree_bits_to_support);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think it would be a tad clearer to write it like this instead:

Suggested change
let min_log_n_to_support = log_n - (params.degree_bits - min_degree_bits_to_support);
let min_log_n_to_support = params.config.rate_bits + min_degree_bits_to_support;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


let mut index_in_degree_sub_one_bits_vec = {
let mut degree_bits_len = degree_sub_one_bits_vec.len();
for artity_bits in &params.reduction_arity_bits {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
for artity_bits in &params.reduction_arity_bits {
for arity_bits in &params.reduction_arity_bits {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -39,6 +41,20 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
claimed_element
}

/// Like `random_access`, but padding `v` with the last element to a power of two.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add that this is necessary because random_access requires a power of two? (I'm not sure whether it would be better to add it here or to random_access directly)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, I modified random_access directly.

@@ -28,6 +32,8 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
pow_witness: F,
degree_bits: usize,
config: &FriConfig,
final_poly_coeff_len: Option<usize>,
query_round_step_count: Option<usize>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this name unclear: maybe say that it's the max number of steps? So something like max_number_steps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed to max_num_query_steps

Copy link
Contributor

@hratoanina hratoanina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as Linda, very nice work!

I have a general question about the method: it seems that an arbitrary reduction strategy for the multi-verifier is supported, as long as the prover correctly follows the same strategy, and skips steps as needed. Is it in the scope of this PR, or do you want to restrict it to ConstantArityBits strategies? In any case this should be clearly stated somewhere in the code.

@sai-deng
Copy link
Contributor Author

Same comments as Linda, very nice work!

I have a general question about the method: it seems that an arbitrary reduction strategy for the multi-verifier is supported, as long as the prover correctly follows the same strategy, and skips steps as needed. Is it in the scope of this PR, or do you want to restrict it to ConstantArityBits strategies? In any case this should be clearly stated somewhere in the code.

We don’t have to, but I added checks to restrict it to ConstantArityBits, mainly because it is widely used and I want to avoid making this PR more complex.

@sai-deng
Copy link
Contributor Author

Globally, I think it would be better to have more comments, and I think it would be beneficial to have one big paragraph explaining how the multiple degrees work. So it would be nice to explain the verifier has a max number of folding steps and some are skipped if the actual degree is lower.

In addition, saying the FRI strategy has to be the same across proofs using this multi-degree circuit would be nice.

Finally, I think the degrees you mentioned above (and in the test) are off by one, aren't they? (Shouldn't it be 29, 25, ... instead of 30, 26, ...?)

Thanks for this work though, it is really great!

The degrees are correct, as the final poly's degree_bits need to account for an additional bit due to the rate_bits.

@sai-deng
Copy link
Contributor Author

Thanks for the reviews! All comments have been addressed. Additionally, we already have a fri_param check in the prover to ensure that the proof can be verified in a circuit with multi-degree support.

@Nashtare Nashtare merged commit 7203b7a into main Nov 25, 2024
5 checks passed
@Nashtare Nashtare deleted the sai/multi_degrees_recursion_circuit branch November 25, 2024 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants