Skip to main content

p3_challenger/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3
4extern crate alloc;
5
6// Why: a drop-time check must not panic while another panic unwinds.
7//
8//     unwinding exists  ->  `std` links  ->  `thread::panicking()` is observable
9//
10// A target without unwinding aborts on the first panic, so no second one can follow it.
11#[cfg(panic = "unwind")]
12extern crate std;
13
14mod duplex_challenger;
15pub mod fs;
16mod grinding_challenger;
17mod hash_challenger;
18mod multi_field_challenger;
19mod serializing_challenger;
20#[cfg(any(test, feature = "test-utils"))]
21pub mod testing;
22
23use alloc::vec::Vec;
24use core::array;
25
26pub use duplex_challenger::*;
27pub use grinding_challenger::*;
28pub use hash_challenger::*;
29pub use multi_field_challenger::*;
30pub use p3_field::UniformSamplingField;
31use p3_field::{Algebra, BasedVectorSpace, Field};
32pub use serializing_challenger::*;
33
34/// A generic trait for absorbing elements into the transcript.
35///
36/// Absorbed elements update the internal sponge state,
37/// preparing it to deterministically produce future challenges.
38pub trait CanObserve<T> {
39    /// Absorb a single value into the transcript.
40    fn observe(&mut self, value: T);
41
42    /// Absorb a slice of values into the transcript.
43    fn observe_slice(&mut self, values: &[T])
44    where
45        T: Clone,
46    {
47        for value in values {
48            self.observe(value.clone());
49        }
50    }
51}
52
53/// A trait for sampling challenge elements from the Fiat-Shamir transcript.
54///
55/// Sampling produces pseudo-random elements deterministically derived
56/// from the absorbed inputs and the sponge state.
57pub trait CanSample<T> {
58    /// Sample a single challenge value from the transcript.
59    fn sample(&mut self) -> T;
60
61    /// Fill an existing buffer with consecutive samples.
62    fn sample_into_slice(&mut self, values: &mut [T]) {
63        for value in values {
64            *value = self.sample();
65        }
66    }
67
68    /// Sample an array of `N` challenge values from the transcript.
69    fn sample_array<const N: usize>(&mut self) -> [T; N] {
70        array::from_fn(|_| self.sample())
71    }
72
73    /// Sample a `Vec` of `n` challenge values from the transcript.
74    fn sample_vec(&mut self, n: usize) -> Vec<T> {
75        (0..n).map(|_| self.sample()).collect()
76    }
77}
78
79/// A trait for sampling random bitstrings from the Fiat-Shamir transcript.
80pub trait CanSampleBits<T> {
81    /// Sample a random `bits`-bit integer from the transcript.
82    ///
83    /// The distribution should be reasonably close to uniform.
84    /// (In practice, a small bias may arise when bit-decomposing a uniformly
85    /// sampled field element)
86    ///
87    /// Guarantees that the returned value fits within the requested bit width.
88    fn sample_bits(&mut self, bits: usize) -> T;
89}
90
91/// Uniform bit sampling interface.
92///
93/// This trait provides a method for drawing uniformly distributed bitstrings
94/// from a Fiat–Shamir transcript. The goal is to obtain an integer supported
95/// on the range $[0, 2^{bits})$ with each value having equal probability.
96pub trait CanSampleUniformBits<F> {
97    /// Sample a random `bits`-bit integer from the transcript with a guarantee of
98    /// uniformly sampled bits.
99    ///
100    /// Performance overhead depends on the field and number of bits requested.
101    /// E.g. for KoalaBear sampling up to 24 bits uniformly is essentially free.
102    ///
103    /// If `RESAMPLE` is set to true then this function will sample multiple field
104    /// elements until it finds one which will produce uniform bits.
105    /// If `RESAMPLE` is set to false then this function will sample a single field
106    /// element and produce an error if the value would produce non-uniform bits.
107    ///
108    /// The probability of a panic or a resample is about 1/P for most fields.
109    /// See `UniformSamplingField` implementation for each field for details.
110    fn sample_uniform_bits<const RESAMPLE: bool>(
111        &mut self,
112        bits: usize,
113    ) -> Result<usize, ResamplingError>;
114}
115
116/// A high-level trait combining observation and sampling over a finite field.
117pub trait FieldChallenger<F: Field>:
118    CanObserve<F> + CanSample<F> + CanSampleBits<usize> + Sync
119{
120    /// Absorb an element from a vector space over the base field.
121    ///
122    /// Decomposes the element into its basis coefficients and absorbs each.
123    #[inline(always)]
124    fn observe_algebra_element<A: BasedVectorSpace<F>>(&mut self, alg_elem: A) {
125        self.observe_slice(alg_elem.as_basis_coefficients_slice());
126    }
127
128    /// Absorb a slice of elements from a vector space over the base field.
129    ///
130    /// Decomposes each element into its basis coefficients and absorbs them.
131    #[inline(always)]
132    fn observe_algebra_slice<A: BasedVectorSpace<F> + Clone>(&mut self, alg_elems: &[A]) {
133        for alg_elem in alg_elems {
134            self.observe_algebra_element(alg_elem.clone());
135        }
136    }
137
138    /// Sample an element of a vector space over the base field.
139    ///
140    /// Constructs the element by sampling basis coefficients.
141    #[inline(always)]
142    fn sample_algebra_element<A: BasedVectorSpace<F>>(&mut self) -> A {
143        A::from_basis_coefficients_fn(|_| self.sample())
144    }
145
146    /// Observe base field elements as extension field elements for recursion-friendly transcripts.
147    ///
148    /// This simplifies recursive verifier circuits by using a uniform extension field challenger.
149    /// Instead of observing a mix of base and extension field elements, we convert all base field
150    /// observations (metadata, public values) to extension field elements before passing to the challenger.
151    ///
152    /// # Recursion Benefits
153    ///
154    /// In recursive proof systems, the verifier circuit needs to verify the inner proof. Since STARK
155    /// verification operates entirely in the extension field (challenges, opened values, constraint
156    /// evaluation), having a challenger that only observes extension field elements significantly
157    /// simplifies the recursive circuit implementation.
158    #[inline(always)]
159    fn observe_base_as_algebra_element<EF>(&mut self, val: F)
160    where
161        EF: Algebra<F> + BasedVectorSpace<F>,
162    {
163        self.observe_algebra_element(EF::from(val));
164    }
165}
166
167impl<C, T> CanObserve<T> for &mut C
168where
169    C: CanObserve<T>,
170{
171    #[inline(always)]
172    fn observe(&mut self, value: T) {
173        (*self).observe(value);
174    }
175
176    #[inline(always)]
177    fn observe_slice(&mut self, values: &[T])
178    where
179        T: Clone,
180    {
181        (*self).observe_slice(values);
182    }
183}
184
185impl<C, T> CanSample<T> for &mut C
186where
187    C: CanSample<T>,
188{
189    #[inline(always)]
190    fn sample(&mut self) -> T {
191        (*self).sample()
192    }
193
194    #[inline(always)]
195    fn sample_into_slice(&mut self, values: &mut [T]) {
196        (*self).sample_into_slice(values);
197    }
198
199    #[inline(always)]
200    fn sample_array<const N: usize>(&mut self) -> [T; N] {
201        (*self).sample_array()
202    }
203
204    #[inline(always)]
205    fn sample_vec(&mut self, n: usize) -> Vec<T> {
206        (*self).sample_vec(n)
207    }
208}
209
210impl<C, T> CanSampleBits<T> for &mut C
211where
212    C: CanSampleBits<T>,
213{
214    #[inline(always)]
215    fn sample_bits(&mut self, bits: usize) -> T {
216        (*self).sample_bits(bits)
217    }
218}
219
220impl<C, F: Field> FieldChallenger<F> for &mut C where C: FieldChallenger<F> {}
221
222impl<C, F> CanSampleUniformBits<F> for &mut C
223where
224    C: CanSampleUniformBits<F>,
225{
226    fn sample_uniform_bits<const RESAMPLE: bool>(
227        &mut self,
228        bits: usize,
229    ) -> Result<usize, ResamplingError> {
230        (*self).sample_uniform_bits::<RESAMPLE>(bits)
231    }
232}
233
234/// Extract a binding commitment to the full transcript state.
235///
236/// Consumes the challenger, producing a digest that commits to all
237/// previously observed values.
238///
239/// ## Contract
240///
241/// Implementations must satisfy the following properties:
242///
243/// - **Determinism**: identical sequences of observations and samples produce identical digests.
244/// - **Observation sensitivity**: different observed values produce different digests.
245pub trait CanFinalizeDigest {
246    /// The type of digest produced by finalization.
247    type Digest;
248
249    /// Finalize the transcript and produce a binding digest.
250    fn finalize(self) -> Self::Digest;
251}