Skip to main content

p3_binary_field/
challenger.rs

1//! A byte-sampling Fiat–Shamir challenger for the binary tower fields.
2
3use alloc::vec::Vec;
4use core::iter::{repeat, repeat_with};
5use core::marker::PhantomData;
6
7use p3_challenger::{
8    CanObserve, CanSample, CanSampleBits, CanSampleUniformBits, FieldChallenger,
9    GrindingChallenger, HashChallenger, ResamplingError,
10};
11use p3_field::BasedVectorSpace;
12use p3_maybe_rayon::prelude::*;
13use p3_symmetric::{CryptographicHasher, Hash, MerkleCap};
14use tracing::instrument;
15
16use crate::tower::TowerLevel;
17
18/// The number of transcript bytes consumed by one bitstring sample.
19///
20/// Fixing this at eight keeps the transcript independent of the target's pointer width.
21const BITS_SAMPLE_BYTES: usize = 8;
22
23/// The headroom, in bits, that a grinding request must leave below the width of the
24/// candidate space `grind` actually searches.
25///
26/// `grind` enumerates candidates with a 64-bit counter, so that space is `min(F::bits(),
27/// 64)` bits wide even for fields wider than `u64`. A candidate satisfies a `bits`-wide
28/// check with probability `2^-bits`, so a search space of `w` bits offers `2^(w - bits)`
29/// expected successes. Eight bits of headroom keep that expectation at `>= 256`, so an
30/// exhaustive search that finds nothing is astronomically unlikely rather than merely
31/// improbable.
32const GRIND_MARGIN_BITS: usize = 8;
33
34/// A challenger for a binary tower field, driven by a challenger over bytes.
35///
36/// A level of the Wiedemann tower is an exact number of bytes wide and every bit pattern of
37/// that width is a field element, so a transcript over the field is a transcript over its
38/// bytes: no encoding, decoding or rejection sampling is needed in either direction.
39///
40/// **Observing**: absorbs the little-endian bytes of the element into the inner challenger.
41///
42/// **Sampling**: reads the little-endian bytes of the element from the inner challenger.
43/// Since the field has exactly `2^(8·NUM_BYTES)` elements, uniform bytes give a uniform
44/// field element.
45///
46/// **Proof of work**: unlike the field and bit sampling above, which work at every level,
47/// grinding is only viable in the wider ones. [`GrindingChallenger::grind`] enumerates
48/// candidates with a 64-bit counter and demands `GRIND_MARGIN_BITS` of headroom below that
49/// space, which puts a ceiling of `min(F::bits(), 64) − GRIND_MARGIN_BITS` on the width of a
50/// single request:
51///
52/// | witness field | largest supported `bits` |
53/// | --- | --- |
54/// | [`BinaryField2`](crate::BinaryField2), [`BinaryField4`](crate::BinaryField4), [`BinaryField8`](crate::BinaryField8) | none: every nonzero request panics |
55/// | [`BinaryField16`](crate::BinaryField16) | 8 |
56/// | [`BinaryField32`](crate::BinaryField32) | 24 |
57/// | [`BinaryField64`](crate::BinaryField64) | 56 |
58/// | [`BinaryField128`](crate::BinaryField128) | 56, bounded by the counter rather than the field |
59///
60/// So the levels below `GF(2^32)` implement [`GrindingChallenger`] without being usable
61/// proof-of-work witness types, and a 128-bit witness buys no more width than a 64-bit one.
62#[derive(Clone, Debug)]
63pub struct BinaryChallenger<F, Inner> {
64    inner: Inner,
65    _marker: PhantomData<F>,
66}
67
68impl<F, Inner> BinaryChallenger<F, Inner> {
69    /// Wrap a challenger over bytes.
70    pub const fn new(inner: Inner) -> Self {
71        Self {
72            inner,
73            _marker: PhantomData,
74        }
75    }
76}
77
78impl<F, H> BinaryChallenger<F, HashChallenger<u8, H, 32>>
79where
80    H: CryptographicHasher<u8, [u8; 32]>,
81{
82    /// Wrap a fresh [`HashChallenger`] over the given hasher and initial transcript.
83    pub const fn from_hasher(initial_state: Vec<u8>, hasher: H) -> Self {
84        Self::new(HashChallenger::new(initial_state, hasher))
85    }
86}
87
88impl<F: TowerLevel, Inner: CanObserve<u8>> CanObserve<F> for BinaryChallenger<F, Inner> {
89    fn observe(&mut self, value: F) {
90        for byte in value.into_bytes() {
91            self.inner.observe(byte);
92        }
93    }
94}
95
96impl<F, const N: usize, Inner: CanObserve<u8>> CanObserve<Hash<F, u8, N>>
97    for BinaryChallenger<F, Inner>
98{
99    fn observe(&mut self, values: Hash<F, u8, N>) {
100        for value in values {
101            self.inner.observe(value);
102        }
103    }
104}
105
106impl<F, const N: usize, Inner: CanObserve<u8>> CanObserve<&MerkleCap<F, [u8; N]>>
107    for BinaryChallenger<F, Inner>
108{
109    fn observe(&mut self, cap: &MerkleCap<F, [u8; N]>) {
110        for digest in cap.roots() {
111            for value in digest {
112                self.inner.observe(*value);
113            }
114        }
115    }
116}
117
118impl<F, const N: usize, Inner: CanObserve<u8>> CanObserve<MerkleCap<F, [u8; N]>>
119    for BinaryChallenger<F, Inner>
120{
121    fn observe(&mut self, cap: MerkleCap<F, [u8; N]>) {
122        self.observe(&cap);
123    }
124}
125
126impl<F, EF, Inner> CanSample<EF> for BinaryChallenger<F, Inner>
127where
128    F: TowerLevel,
129    EF: BasedVectorSpace<F>,
130    Inner: CanSample<u8>,
131{
132    fn sample(&mut self) -> EF {
133        let inner = &mut self.inner;
134        // Every bit pattern is a field element, so reading `NUM_BYTES` uniform bytes per
135        // basis coefficient samples uniformly from `EF`.
136        EF::from_basis_coefficients_fn(|_| F::from_le_byte_iter(repeat_with(|| inner.sample())))
137    }
138}
139
140impl<F, Inner> CanSampleBits<usize> for BinaryChallenger<F, Inner>
141where
142    F: TowerLevel,
143    Inner: CanSample<u8>,
144{
145    /// Sample a bitstring by masking bytes taken from the inner stream.
146    ///
147    /// A challenger that bit-decomposes a sampled field element can only extract as many
148    /// bits as that element carries, and so has to bound the request by the field order.
149    /// This one instead reads its entropy straight off the inner byte stream, so how many
150    /// bits are available per call does not depend on `F`'s width at all: a challenger over
151    /// a narrow level can still draw a query index far wider than one of its own elements.
152    ///
153    /// The one real bound is that the result must fit in a `usize`, which also keeps it
154    /// within the `u64` the bytes are read into.
155    fn sample_bits(&mut self, bits: usize) -> usize {
156        assert!(
157            bits < (usize::BITS as usize),
158            "requested bit count must fit within a usize"
159        );
160        let bytes: [u8; BITS_SAMPLE_BYTES] = self.inner.sample_array();
161        let rand_u64 = u64::from_le_bytes(bytes);
162        (rand_u64 & ((1u64 << bits) - 1)) as usize
163    }
164}
165
166impl<F, Inner> CanSampleUniformBits<F> for BinaryChallenger<F, Inner>
167where
168    F: TowerLevel,
169    Inner: CanSample<u8>,
170{
171    /// Sample uniform bits by masking bytes from the inner stream.
172    ///
173    /// # Overview
174    ///
175    /// The inner stream emits cryptographic-hash bytes uniform on `[0, 2^8)`.
176    ///
177    /// Reading 8 bytes as a 64-bit integer and masking the low `bits` is exactly uniform
178    /// on `[0, 2^bits)`.
179    ///
180    /// No field-element decomposition occurs, so no rejection band exists. The const
181    /// generic is therefore inert: this function never errors and never resamples.
182    fn sample_uniform_bits<const RESAMPLE: bool>(
183        &mut self,
184        bits: usize,
185    ) -> Result<usize, ResamplingError> {
186        // Byte-sourced sampling is uniform without rejection, so the result is always
187        // valid and the error arm is unreachable.
188        Ok(self.sample_bits(bits))
189    }
190}
191
192/// The `index`-th grinding candidate: the element whose low bytes are those of `index`.
193///
194/// This is injective on the candidate range of every level: narrower levels stop before the
195/// counter wraps their bit width, and wider ones are zero-extended.
196#[inline]
197fn candidate<F: TowerLevel>(index: u64) -> F {
198    F::from_le_byte_iter(index.to_le_bytes().into_iter().chain(repeat(0)))
199}
200
201impl<F, Inner> GrindingChallenger for BinaryChallenger<F, Inner>
202where
203    F: TowerLevel,
204    Inner: CanSample<u8> + CanObserve<u8> + Clone + Send + Sync,
205{
206    type Witness = F;
207
208    /// # Panics
209    /// Panics unless `bits + GRIND_MARGIN_BITS <= min(F::bits(), 64)`. See the per-level
210    /// ceiling tabulated on [`BinaryChallenger`].
211    #[instrument(name = "grind for proof-of-work witness", skip_all)]
212    fn grind(&mut self, bits: usize) -> Self::Witness {
213        // Trivial case: 0 bits mean no PoW is required and any witness is valid.
214        if bits == 0 {
215            return F::ZERO;
216        }
217
218        assert!(bits < (usize::BITS as usize));
219        assert!(
220            bits + GRIND_MARGIN_BITS <= F::bits().min(u64::BITS as usize),
221            "requested bit count leaves too small a margin against the witness field's size"
222        );
223
224        // Candidates are enumerated by a 64-bit counter, which exhausts the field only for
225        // the levels narrower than `GF(2^64)`.
226        let num_candidates = if F::bits() >= u64::BITS as usize {
227            u64::MAX
228        } else {
229            1u64 << F::bits()
230        };
231
232        let witness = (0..num_candidates)
233            .into_par_iter()
234            .map(candidate)
235            .find_any(|witness| self.clone().check_witness(bits, *witness))
236            .expect("failed to find witness");
237        assert!(self.check_witness(bits, witness));
238        witness
239    }
240}
241
242impl<F, Inner> FieldChallenger<F> for BinaryChallenger<F, Inner>
243where
244    F: TowerLevel,
245    Inner: CanSample<u8> + CanObserve<u8> + Clone + Send + Sync,
246{
247}
248
249#[cfg(test)]
250mod tests {
251    use alloc::vec;
252    use alloc::vec::Vec;
253
254    use p3_challenger::{
255        CanObserve, CanSample, CanSampleBits, CanSampleUniformBits, FieldChallenger,
256        GrindingChallenger, HashChallenger,
257    };
258    use p3_field::{BasedVectorSpace, Field, PrimeCharacteristicRing, RawDataSerializable};
259    use p3_keccak::Keccak256Hash;
260    use p3_symmetric::{Hash, MerkleCap};
261
262    use crate::challenger::{BITS_SAMPLE_BYTES, BinaryChallenger};
263    use crate::{BinaryField8, BinaryField32, BinaryField128, Gf2};
264
265    type Inner = HashChallenger<u8, Keccak256Hash, 32>;
266
267    /// A challenger over an empty transcript.
268    fn mk<F>() -> BinaryChallenger<F, Inner> {
269        BinaryChallenger::from_hasher(vec![], Keccak256Hash)
270    }
271
272    /// An inner challenger over the same empty transcript, to compare byte streams against.
273    fn mk_inner() -> Inner {
274        HashChallenger::new(vec![], Keccak256Hash)
275    }
276
277    /// A fixed nonzero element of `GF(2^128)`.
278    fn a128() -> BinaryField128 {
279        BinaryField128::from_le_bytes([
280            0x21, 0x43, 0x65, 0x87, 0xa9, 0xcb, 0xed, 0x0f, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56,
281            0x34, 0x12,
282        ])
283    }
284
285    #[test]
286    fn sampling_is_deterministic_and_observation_dependent() {
287        // Identical (empty) histories produce identical challenges.
288        let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
289        let (x, y): (BinaryField128, BinaryField128) = (a.sample(), b.sample());
290        assert_eq!(x, y);
291
292        // Identical observations produce identical challenges.
293        let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
294        a.observe(a128());
295        b.observe(a128());
296        let (x, y): (BinaryField128, BinaryField128) = (a.sample(), b.sample());
297        assert_eq!(x, y);
298
299        // Different observations produce different challenges.
300        let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
301        a.observe(BinaryField128::ONE);
302        b.observe(BinaryField128::ZERO);
303        let (x, y): (BinaryField128, BinaryField128) = (a.sample(), b.sample());
304        assert_ne!(x, y);
305
306        // Observing at all changes what is sampled next.
307        let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
308        b.observe(BinaryField128::ZERO);
309        let (x, y): (BinaryField128, BinaryField128) = (a.sample(), b.sample());
310        assert_ne!(x, y);
311
312        // The transcript advances: consecutive challenges differ, and the sequence is
313        // reproducible from an identical history.
314        let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
315        let xs: [BinaryField128; 4] = a.sample_array();
316        let ys: [BinaryField128; 4] = b.sample_array();
317        assert_eq!(xs, ys);
318        assert_ne!(xs[0], xs[1]);
319        assert_ne!(xs[1], xs[2]);
320        assert_ne!(xs[2], xs[3]);
321    }
322
323    #[test]
324    fn observing_an_element_absorbs_its_little_endian_bytes() {
325        let a = a128();
326
327        let mut challenger = mk::<BinaryField128>();
328        challenger.observe(a);
329
330        let mut inner = mk_inner();
331        inner.observe_slice(&a.into_bytes().into_iter().collect::<Vec<_>>());
332
333        // Compare a full digest worth of the resulting byte streams.
334        for _ in 0..32 {
335            let observed: u8 = challenger.inner.sample();
336            let expected: u8 = inner.sample();
337            assert_eq!(observed, expected);
338        }
339    }
340
341    #[test]
342    fn a_sampled_element_is_read_from_the_little_endian_byte_stream() {
343        let mut challenger = mk::<BinaryField128>();
344        let sampled: BinaryField128 = challenger.sample();
345
346        let mut inner = mk_inner();
347        let bytes: [u8; 16] = core::array::from_fn(|_| inner.sample());
348
349        assert_eq!(sampled, BinaryField128::from_le_bytes(bytes));
350    }
351
352    #[test]
353    fn a_sampled_element_of_a_narrow_level_reads_only_its_own_bytes() {
354        let mut challenger = mk::<BinaryField32>();
355        let sampled: BinaryField32 = challenger.sample();
356
357        let mut inner = mk_inner();
358        let bytes: [u8; 4] = core::array::from_fn(|_| inner.sample());
359
360        assert_eq!(sampled, BinaryField32::from_le_bytes(bytes));
361    }
362
363    #[test]
364    fn sampling_an_extension_element_reads_its_basis_coefficients_in_order() {
365        let mut challenger = mk::<BinaryField8>();
366        let sampled: BinaryField128 = challenger.sample();
367
368        let mut coefficients = mk::<BinaryField8>();
369        let coeffs: [BinaryField8; 16] = core::array::from_fn(|_| coefficients.sample());
370
371        assert_eq!(
372            <BinaryField128 as BasedVectorSpace<BinaryField8>>::from_basis_coefficients_fn(|i| {
373                coeffs[i]
374            }),
375            sampled
376        );
377    }
378
379    #[test]
380    fn observing_digests_matches_observing_their_bytes() {
381        let digest = [1_u8, 2, 3, 4];
382
383        let mut from_hash = mk::<BinaryField128>();
384        from_hash.observe(Hash::<BinaryField128, u8, 4>::from(digest));
385
386        let cap = MerkleCap::<BinaryField128, [u8; 4]>::new(vec![digest, [5, 6, 7, 8]]);
387        let mut from_cap_ref = mk::<BinaryField128>();
388        from_cap_ref.observe(&cap);
389        let mut from_cap = mk::<BinaryField128>();
390        from_cap.observe(cap);
391
392        let mut hash_bytes = mk_inner();
393        hash_bytes.observe_slice(&digest);
394        let mut cap_bytes = mk_inner();
395        cap_bytes.observe_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
396
397        for _ in 0..32 {
398            let expected: u8 = hash_bytes.sample();
399            assert_eq!(from_hash.inner.sample(), expected);
400
401            let expected: u8 = cap_bytes.sample();
402            assert_eq!(from_cap_ref.inner.sample(), expected);
403            assert_eq!(from_cap.inner.sample(), expected);
404        }
405    }
406
407    #[test]
408    fn sampled_bits_fit_the_requested_width_and_are_reproducible() {
409        let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
410        for bits in 0..32 {
411            let value = a.sample_bits(bits);
412            assert!(value < (1 << bits), "{value} does not fit in {bits} bits");
413            assert_eq!(value, b.sample_bits(bits));
414        }
415
416        // The bit stream depends on the transcript.
417        let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
418        a.observe(BinaryField128::ONE);
419        assert_ne!(a.sample_bits(31), b.sample_bits(31));
420    }
421
422    #[test]
423    fn uniform_bits_agree_with_plain_bits_and_never_reject() {
424        let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
425        for bits in [0, 1, 7, 8, 16, 31] {
426            assert_eq!(
427                a.sample_uniform_bits::<true>(bits).unwrap(),
428                b.sample_bits(bits)
429            );
430        }
431
432        let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
433        for bits in [0, 1, 7, 8, 16, 31] {
434            assert_eq!(
435                a.sample_uniform_bits::<false>(bits).unwrap(),
436                b.sample_bits(bits)
437            );
438        }
439    }
440
441    /// The bits come from the inner byte stream, not from a sampled element, so a request
442    /// wider than the field itself is well defined and must be served.
443    #[test]
444    fn sample_bits_is_not_limited_by_the_width_of_the_field() {
445        // A `GF(2^8)` challenger sampling `GF(2^128)` challenges still has to produce
446        // query indices of a realistic width.
447        assert_eq!(<BinaryField8 as Field>::bits(), 8);
448        let mut challenger = mk::<BinaryField8>();
449        let sampled = challenger.sample_bits(24);
450        assert!(sampled < (1 << 24));
451
452        let mut inner = mk_inner();
453        let bytes: [u8; BITS_SAMPLE_BYTES] = core::array::from_fn(|_| inner.sample());
454        assert_eq!(sampled, (u64::from_le_bytes(bytes) & 0xff_ffff) as usize);
455
456        // The uniform-bit entry point inherits the same reach.
457        let mut challenger = mk::<BinaryField8>();
458        assert_eq!(challenger.sample_uniform_bits::<true>(24).unwrap(), sampled);
459        let mut challenger = mk::<BinaryField8>();
460        assert_eq!(
461            challenger.sample_uniform_bits::<false>(24).unwrap(),
462            sampled
463        );
464
465        // Right up to the widest request a `usize` can hold.
466        let widest = usize::BITS as usize - 1;
467        let mut challenger = mk::<BinaryField8>();
468        assert!(challenger.sample_bits(widest) < (1 << widest));
469    }
470
471    #[test]
472    #[should_panic = "requested bit count must fit within a usize"]
473    fn sample_bits_rejects_a_request_wider_than_a_usize() {
474        let mut challenger = mk::<BinaryField128>();
475        let _ = challenger.sample_bits(usize::BITS as usize);
476    }
477
478    #[test]
479    #[should_panic = "requested bit count leaves too small a margin"]
480    fn grind_rejects_a_request_wider_than_the_field() {
481        let mut challenger = mk::<BinaryField32>();
482        let _ = challenger.grind(32);
483    }
484
485    #[test]
486    #[should_panic = "requested bit count leaves too small a margin"]
487    fn grind_rejects_a_request_inside_the_margin() {
488        // Narrower than the field, but too close to it: only `2^7` expected witnesses.
489        let mut challenger = mk::<BinaryField32>();
490        let _ = challenger.grind(25);
491    }
492
493    /// An inner challenger that always samples zero bytes, so every candidate trivially
494    /// satisfies `check_witness`. This lets a test drive `grind` at bit widths whose real
495    /// exhaustive search would otherwise be intractably slow to actually run.
496    #[derive(Clone)]
497    struct AlwaysZeroInner;
498
499    impl CanObserve<u8> for AlwaysZeroInner {
500        fn observe(&mut self, _value: u8) {}
501    }
502
503    impl CanSample<u8> for AlwaysZeroInner {
504        fn sample(&mut self) -> u8 {
505            0
506        }
507    }
508
509    #[test]
510    #[should_panic = "requested bit count leaves too small a margin"]
511    fn grind_rejects_a_request_the_u64_cap_no_longer_allows() {
512        // `BinaryField128`'s candidates are enumerated by a 64-bit counter, so the real
513        // search space is 64 bits wide, not the field's 128. Before accounting for that
514        // cap, this request wrongly passed the margin check (`63 + 8 <= 128`) while the
515        // real candidate space only offered `2^(64 - 63) = 2` expected witnesses.
516        let mut challenger = mk::<BinaryField128>();
517        let _ = challenger.grind(63);
518    }
519
520    #[test]
521    fn grind_accepts_the_widest_request_the_u64_cap_allows() {
522        // The u64-capped search space is 64 bits wide, so `64 - GRIND_MARGIN_BITS = 56` is
523        // the widest request the margin should still allow on `BinaryField128`. An inner
524        // challenger that always samples zero bytes makes every candidate an accepted
525        // witness, so the search completes immediately regardless of how wide `bits` is,
526        // letting this exercise the real `grind` path instead of just its assertions.
527        let mut challenger: BinaryChallenger<BinaryField128, AlwaysZeroInner> =
528            BinaryChallenger::new(AlwaysZeroInner);
529        let witness = challenger.grind(56);
530        assert!(challenger.check_witness(56, witness));
531    }
532
533    #[test]
534    fn a_bitstring_sample_reads_a_fixed_number_of_bytes() {
535        // The width of one bitstring draw must not depend on the target's pointer width,
536        // or transcripts would not agree across targets.
537        let mut challenger = mk::<BinaryField128>();
538        let sampled = challenger.sample_bits(31);
539
540        let mut inner = mk_inner();
541        let bytes: [u8; BITS_SAMPLE_BYTES] = core::array::from_fn(|_| inner.sample());
542
543        assert_eq!(BITS_SAMPLE_BYTES, 8);
544        assert_eq!(
545            sampled,
546            (u64::from_le_bytes(bytes) & ((1 << 31) - 1)) as usize
547        );
548
549        // The next draw continues from where the first one stopped.
550        let next = challenger.sample_bits(8);
551        let bytes: [u8; BITS_SAMPLE_BYTES] = core::array::from_fn(|_| inner.sample());
552        assert_eq!(next, (u64::from_le_bytes(bytes) & 0xff) as usize);
553    }
554
555    #[test]
556    fn grind_satisfies_check() {
557        let mut challenger = mk::<BinaryField128>();
558        let witness = challenger.clone().grind(8);
559        assert!(challenger.check_witness(8, witness));
560    }
561
562    #[test]
563    fn grind_over_a_narrow_level_satisfies_check() {
564        let mut challenger = mk::<BinaryField32>();
565        let witness = challenger.clone().grind(8);
566        assert!(challenger.check_witness(8, witness));
567    }
568
569    #[test]
570    fn grind_of_zero_bits_is_free() {
571        let mut challenger = mk::<BinaryField128>();
572        let mut shadow = challenger.clone();
573
574        let witness = challenger.grind(0);
575        assert_eq!(witness, BinaryField128::ZERO);
576
577        // The transcript must be untouched.
578        for _ in 0..32 {
579            let expected: u8 = shadow.inner.sample();
580            assert_eq!(challenger.inner.sample(), expected);
581        }
582    }
583
584    #[test]
585    fn grind_of_zero_bits_is_free_on_a_level_narrower_than_the_margin() {
586        // `Gf2`'s single bit is narrower than `GRIND_MARGIN_BITS`: the zero-bits short
587        // circuit must run before the margin assertion, or this would panic.
588        let mut challenger = mk::<Gf2>();
589        let mut shadow = challenger.clone();
590
591        let witness = challenger.grind(0);
592        assert_eq!(witness, Gf2::ZERO);
593
594        // The transcript must be untouched.
595        for _ in 0..32 {
596            let expected: u8 = shadow.inner.sample();
597            assert_eq!(challenger.inner.sample(), expected);
598        }
599    }
600
601    #[test]
602    fn a_wrong_witness_fails_the_check() {
603        let mut challenger = mk::<BinaryField128>();
604        let witness = challenger.clone().grind(8);
605
606        // Flipping a bit of the witness invalidates it with probability 1 - 2^-8; the
607        // transcript is fixed, so this is a deterministic statement about this witness.
608        let mut wrong = challenger.clone();
609        assert!(!wrong.check_witness(8, witness + BinaryField128::ONE));
610        assert!(challenger.check_witness(8, witness));
611    }
612
613    /// The generic [`FieldChallenger`] helpers go through the base-field basis.
614    #[test]
615    fn field_challenger_algebra_helpers_round_trip() {
616        let mut challenger = mk::<BinaryField8>();
617        challenger.observe_algebra_element(a128());
618        let sampled: BinaryField128 = challenger.sample_algebra_element();
619
620        let mut expected = mk::<BinaryField8>();
621        expected
622            .observe_slice(BasedVectorSpace::<BinaryField8>::as_basis_coefficients_slice(&a128()));
623        let expected: BinaryField128 = expected.sample();
624
625        assert_eq!(sampled, expected);
626    }
627}