-
Notifications
You must be signed in to change notification settings - Fork 3
feat: add RecursiveChallenger for in-circuit FS challenge sampling
#143
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
Merged
Merged
Changes from 6 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
d083480
feat: add TargetAllocator for structured PI alloc
Nashtare 54595cb
tweak
Nashtare 93d31cf
fmt
Nashtare 7fd3abf
Minor
Nashtare 9d974b6
feat: Add RecursiveChallenger object
Nashtare 022cb02
minor
Nashtare d179339
Make TargetAllocator global within CircuitBuilder
Nashtare 8e97fce
Merge robin/target_allocator into robin/recursive_challenger
Nashtare d565162
fmt
Nashtare f227b75
Merge origin/main into robin/recursive_challenger
Nashtare 7f49a56
minor
Nashtare 0543b6e
Apply Hamy's suggestions
Nashtare b3b5eae
Merge remote-tracking branch 'origin/main' into robin/recursive_chall…
Nashtare 139b187
Merge remote-tracking branch 'origin/main' into robin/recursive_chall…
Nashtare b59b536
fix
Nashtare File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| //! Module defining hash operations for circuit builder. | ||
| //! | ||
| //! Provides methods for absorbing and squeezing elements using a sponge | ||
| //! construction within the circuit. | ||
|
|
||
| use p3_field::PrimeCharacteristicRing; | ||
|
|
||
| use crate::builder::{CircuitBuilder, CircuitBuilderError}; | ||
| use crate::op::NonPrimitiveOpType; | ||
| use crate::types::ExprId; | ||
|
|
||
| /// Hash operations trait for `CircuitBuilder`. | ||
| pub trait HashOps<F: Clone + PrimeCharacteristicRing + Eq + core::hash::Hash> { | ||
| /// Absorb field elements into the sponge state. | ||
| /// | ||
| /// # Arguments | ||
| /// | ||
| /// * `inputs` - The `ExprId`s to absorb | ||
| /// * `reset` - Whether to reset the sponge state before absorbing | ||
| fn add_hash_absorb( | ||
| &mut self, | ||
| inputs: &[ExprId], | ||
| reset: bool, | ||
| ) -> Result<(), CircuitBuilderError>; | ||
|
|
||
| /// Squeeze field elements from the sponge state. | ||
| /// | ||
| /// # Arguments | ||
| /// | ||
| /// * `outputs` - The `ExprId`s to store squeezed values in | ||
| fn add_hash_squeeze(&mut self, outputs: &[ExprId]) -> Result<(), CircuitBuilderError>; | ||
| } | ||
|
|
||
| impl<F> HashOps<F> for CircuitBuilder<F> | ||
| where | ||
| F: Clone + PrimeCharacteristicRing + Eq + core::hash::Hash, | ||
| { | ||
| fn add_hash_absorb( | ||
| &mut self, | ||
| inputs: &[ExprId], | ||
| reset: bool, | ||
| ) -> Result<(), CircuitBuilderError> { | ||
| self.ensure_op_enabled(NonPrimitiveOpType::HashAbsorb { reset })?; | ||
|
|
||
| self.push_non_primitive_op(NonPrimitiveOpType::HashAbsorb { reset }, inputs.to_vec()); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| fn add_hash_squeeze(&mut self, outputs: &[ExprId]) -> Result<(), CircuitBuilderError> { | ||
| self.ensure_op_enabled(NonPrimitiveOpType::HashSqueeze)?; | ||
|
|
||
| self.push_non_primitive_op(NonPrimitiveOpType::HashSqueeze, outputs.to_vec()); | ||
|
|
||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use p3_baby_bear::BabyBear; | ||
| use p3_field::PrimeCharacteristicRing; | ||
|
|
||
| use super::*; | ||
| use crate::op::NonPrimitiveOpConfig; | ||
|
|
||
| #[test] | ||
| fn test_hash_absorb() { | ||
| let mut circuit = CircuitBuilder::<BabyBear>::new(); | ||
| circuit.enable_op( | ||
| NonPrimitiveOpType::HashAbsorb { reset: true }, | ||
| NonPrimitiveOpConfig::None, | ||
| ); | ||
|
|
||
| let input1 = circuit.add_const(BabyBear::ONE); | ||
| let input2 = circuit.add_const(BabyBear::TWO); | ||
|
|
||
| circuit.add_hash_absorb(&[input1, input2], true).unwrap(); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_hash_squeeze() { | ||
| let mut circuit = CircuitBuilder::<BabyBear>::new(); | ||
| circuit.enable_op(NonPrimitiveOpType::HashSqueeze, NonPrimitiveOpConfig::None); | ||
|
|
||
| let output = circuit.add_public_input(); | ||
|
|
||
| circuit.add_hash_squeeze(&[output]).unwrap(); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_hash_absorb_squeeze_sequence() { | ||
| let mut circuit = CircuitBuilder::<BabyBear>::new(); | ||
| circuit.enable_op( | ||
| NonPrimitiveOpType::HashAbsorb { reset: true }, | ||
| NonPrimitiveOpConfig::None, | ||
| ); | ||
| circuit.enable_op(NonPrimitiveOpType::HashSqueeze, NonPrimitiveOpConfig::None); | ||
|
|
||
| // Absorb | ||
| let input = circuit.add_const(BabyBear::ONE); | ||
| circuit.add_hash_absorb(&[input], true).unwrap(); | ||
|
|
||
| // Squeeze | ||
| let output = circuit.add_public_input(); | ||
| circuit.add_hash_squeeze(&[output]).unwrap(); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_hash_absorb_not_enabled() { | ||
| let mut circuit = CircuitBuilder::<BabyBear>::new(); | ||
|
|
||
| let input = circuit.add_const(BabyBear::ONE); | ||
| let result = circuit.add_hash_absorb(&[input], true); | ||
|
|
||
| assert!(result.is_err()); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| pub mod fri; | ||
| pub mod hash; | ||
| pub mod mmcs; | ||
|
|
||
| pub use fri::FriOps; | ||
| pub use hash::HashOps; | ||
| pub use mmcs::MmcsOps; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| //! Challenge target structures for STARK verification circuits. | ||
| //! | ||
| //! This module provides structured allocation of challenge targets, | ||
| //! encapsulating the Fiat-Shamir ordering and making challenge generation | ||
| //! more maintainable. | ||
|
|
||
| use alloc::vec; | ||
| use alloc::vec::Vec; | ||
|
|
||
| use p3_circuit::CircuitBuilder; | ||
| use p3_field::PrimeCharacteristicRing; | ||
| use p3_uni_stark::StarkGenericConfig; | ||
|
|
||
| use crate::Target; | ||
| use crate::circuit_challenger::CircuitChallenger; | ||
| use crate::circuit_verifier::ObservableCommitment; | ||
| use crate::recursive_challenger::RecursiveChallenger; | ||
| use crate::recursive_traits::{ProofTargets, Recursive}; | ||
|
|
||
| /// Base STARK challenges (independent of PCS choice). | ||
| /// | ||
| /// These are the fundamental challenges needed for any STARK verification: | ||
| /// - Alpha: for folding constraint polynomials | ||
| /// - Zeta, Zeta_next: for out-of-domain evaluation | ||
| #[derive(Debug, Clone)] | ||
| pub struct StarkChallenges { | ||
| /// Alpha: challenge for folding all constraint polynomials | ||
| pub alpha: Target, | ||
| /// Zeta: out-of-domain evaluation point | ||
| pub zeta: Target, | ||
| /// Zeta next: evaluation point for next row (zeta * g in the trace domain) | ||
| pub zeta_next: Target, | ||
| } | ||
|
|
||
| impl StarkChallenges { | ||
| /// Allocate base STARK challenge targets using Fiat-Shamir transform. | ||
| /// | ||
| /// It will mutate the challenger state. | ||
| /// | ||
| /// # Fiat-Shamir Ordering | ||
| /// 1. Observe domain parameters (degree_bits, log_quotient_degree) | ||
| /// 2. Observe trace commitment | ||
| /// 3. Observe public values | ||
| /// 4. **Sample alpha** (for constraint folding) | ||
| /// 5. Observe quotient chunks commitment | ||
| /// 6. Observe random commitment (if ZK mode) | ||
| /// 7. **Sample zeta** (OOD evaluation point) | ||
| /// 8. **Sample zeta_next** (next row evaluation point) | ||
| /// 9. Return challenger for PCS to continue sampling (betas, query indices) | ||
| pub fn allocate<SC, Comm, OpeningProof>( | ||
| circuit: &mut CircuitBuilder<SC::Challenge>, | ||
| challenger: &mut CircuitChallenger, | ||
| proof_targets: &ProofTargets<SC, Comm, OpeningProof>, | ||
| public_values: &[Target], | ||
| log_quotient_degree: usize, | ||
| ) -> Self | ||
| where | ||
| SC: StarkGenericConfig, | ||
| SC::Challenge: PrimeCharacteristicRing, | ||
| Comm: Recursive<SC::Challenge> + ObservableCommitment, | ||
| OpeningProof: Recursive<SC::Challenge>, | ||
| { | ||
| // Extract commitment targets from proof | ||
| let trace_comm_targets = proof_targets | ||
| .commitments_targets | ||
| .trace_targets | ||
| .to_observation_targets(); | ||
| let quotient_comm_targets = proof_targets | ||
| .commitments_targets | ||
| .quotient_chunks_targets | ||
| .to_observation_targets(); | ||
| let random_comm_targets = proof_targets | ||
| .commitments_targets | ||
| .random_commit | ||
| .as_ref() | ||
| .map(|c| c.to_observation_targets()); | ||
|
|
||
| // Observe domain parameters | ||
| let degree_bits_target = | ||
| circuit.add_const(SC::Challenge::from_usize(proof_targets.degree_bits)); | ||
| let log_quotient_degree_target = | ||
| circuit.add_const(SC::Challenge::from_usize(log_quotient_degree)); | ||
| challenger.observe(circuit, degree_bits_target); | ||
| challenger.observe(circuit, log_quotient_degree_target); | ||
|
|
||
| // Observe trace commitment | ||
| challenger.observe_slice(circuit, &trace_comm_targets); | ||
|
|
||
| // Observe public values | ||
| challenger.observe_slice(circuit, public_values); | ||
|
|
||
| // Sample alpha challenge | ||
| let alpha = challenger.sample(circuit); | ||
|
|
||
| // Observe quotient chunks commitment | ||
| challenger.observe_slice(circuit, "ient_comm_targets); | ||
|
|
||
| // Observe random commitment if in ZK mode | ||
| if let Some(random_comm) = random_comm_targets { | ||
| challenger.observe_slice(circuit, &random_comm); | ||
| } | ||
|
|
||
| // Sample zeta and zeta_next challenges | ||
| let zeta = challenger.sample(circuit); | ||
| let zeta_next = challenger.sample(circuit); | ||
|
|
||
| Self { | ||
| alpha, | ||
| zeta, | ||
| zeta_next, | ||
| } | ||
| } | ||
|
|
||
| /// Convert to flat vector: [alpha, zeta, zeta_next] | ||
| pub fn to_vec(&self) -> Vec<Target> { | ||
| vec![self.alpha, self.zeta, self.zeta_next] | ||
| } | ||
|
|
||
| /// Get individual challenge targets. | ||
| pub fn alpha(&self) -> Target { | ||
| self.alpha | ||
| } | ||
|
|
||
| pub fn zeta(&self) -> Target { | ||
| self.zeta | ||
| } | ||
|
|
||
| pub fn zeta_next(&self) -> Target { | ||
| self.zeta_next | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use p3_circuit::ExprId; | ||
|
|
||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn test_stark_challenges_to_vec() { | ||
| let challenges = StarkChallenges { | ||
| alpha: ExprId(1), | ||
| zeta: ExprId(2), | ||
| zeta_next: ExprId(3), | ||
| }; | ||
|
|
||
| let vec = challenges.to_vec(); | ||
| assert_eq!(vec.len(), 3); | ||
| assert_eq!(vec[0], challenges.alpha); | ||
| assert_eq!(vec[1], challenges.zeta); | ||
| assert_eq!(vec[2], challenges.zeta_next); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it need you would need to potentially separately allow with
reset = trueandreset = false?