1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// Copyright 2024 Ulvetanna Inc.

use super::field_challenger::{FieldChallenger, FieldChallengerHelper};
use binius_field::{Field, PackedField, PackedFieldIndexable};
use binius_hash::Hasher;
use std::{marker::PhantomData, slice};

#[derive(Debug, Clone)]
struct HashChallenger<F, H: Hasher<F>> {
	hasher: H,
	_marker: PhantomData<F>,
}

impl<F, H: Hasher<F>> Default for HashChallenger<F, H> {
	fn default() -> Self {
		Self {
			hasher: H::new(),
			_marker: PhantomData,
		}
	}
}

impl<F, H> FieldChallengerHelper<F> for HashChallenger<F, H>
where
	F: Field,
	H: Hasher<F>,
	H::Digest: PackedFieldIndexable<Scalar = F>,
{
	const RATE: usize = H::Digest::WIDTH;

	fn sample(&mut self, output: &mut [F]) {
		let digest = self.hasher.finalize_reset();
		let elems = H::Digest::unpack_scalars(slice::from_ref(&digest));

		// Chain values for the next sample call.
		self.hasher.update(elems);

		output.copy_from_slice(elems);
	}

	fn observe(&mut self, input: &[F]) {
		self.hasher.update(input);
	}
}

/// Construct a Fiat-Shamir challenger from a normal, collision-resistant hash function.
pub fn new<F, H>() -> FieldChallenger<F, impl FieldChallengerHelper<F> + Clone>
where
	F: Field,
	H: Hasher<F> + Clone,
	H::Digest: PackedFieldIndexable<Scalar = F>,
{
	FieldChallenger::<F, HashChallenger<F, H>>::default()
}

#[cfg(test)]
mod tests {
	use super::*;
	use binius_field::{BinaryField128b, BinaryField64b, BinaryField8b};
	use binius_hash::GroestlHasher;
	use p3_challenger::CanSample;

	#[test]
	fn test_groestl_challenger_can_sample_ext_field() {
		let mut challenger = new::<BinaryField8b, GroestlHasher<BinaryField8b>>();
		let _: BinaryField64b = challenger.sample();
		let _: BinaryField128b = challenger.sample();
		// This sample triggers a flush
		let _: BinaryField128b = challenger.sample();
	}
}