use alloc::vec::Vec;
use core::iter::repeat;
use core::marker::PhantomData;
use p3_challenger::{
CanObserve, CanSample, CanSampleBits, CanSampleUniformBits, FieldChallenger,
GrindingChallenger, HashChallenger, ResamplingError,
};
use p3_field::BasedVectorSpace;
use p3_maybe_rayon::prelude::*;
use p3_symmetric::{CryptographicHasher, Hash, MerkleCap};
use tracing::instrument;
use crate::tower::TowerLevel;
const BITS_SAMPLE_BYTES: usize = 8;
const GRIND_MARGIN_BITS: usize = 8;
#[derive(Clone, Debug)]
pub struct BinaryChallenger<F, Inner> {
inner: Inner,
_marker: PhantomData<F>,
}
impl<F, Inner> BinaryChallenger<F, Inner> {
pub const fn new(inner: Inner) -> Self {
Self {
inner,
_marker: PhantomData,
}
}
}
impl<F, H> BinaryChallenger<F, HashChallenger<u8, H, 32>>
where
H: CryptographicHasher<u8, [u8; 32]>,
{
pub const fn from_hasher(initial_state: Vec<u8>, hasher: H) -> Self {
Self::new(HashChallenger::new(initial_state, hasher))
}
}
impl<F: TowerLevel, Inner: CanObserve<u8>> CanObserve<F> for BinaryChallenger<F, Inner> {
fn observe(&mut self, value: F) {
let mut bytes = [0; 16];
for (slot, byte) in bytes.iter_mut().zip(value.into_bytes()) {
*slot = byte;
}
self.inner.observe_slice(&bytes[..F::NUM_BYTES]);
}
}
impl<F, G, const N: usize, Inner: CanObserve<u8>> CanObserve<Hash<G, u8, N>>
for BinaryChallenger<F, Inner>
{
fn observe(&mut self, values: Hash<G, u8, N>) {
self.inner.observe_slice(values.as_ref());
}
}
impl<F, G, const N: usize, Inner: CanObserve<u8>> CanObserve<&MerkleCap<G, [u8; N]>>
for BinaryChallenger<F, Inner>
{
fn observe(&mut self, cap: &MerkleCap<G, [u8; N]>) {
for digest in cap.roots() {
self.inner.observe_slice(digest);
}
}
}
impl<F, G, const N: usize, Inner: CanObserve<u8>> CanObserve<MerkleCap<G, [u8; N]>>
for BinaryChallenger<F, Inner>
{
fn observe(&mut self, cap: MerkleCap<G, [u8; N]>) {
self.observe(&cap);
}
}
impl<F, G, const N: usize, Inner: CanObserve<u8>> CanObserve<Hash<G, u64, N>>
for BinaryChallenger<F, Inner>
{
fn observe(&mut self, values: Hash<G, u64, N>) {
for value in values {
self.inner.observe_slice(&value.to_le_bytes());
}
}
}
impl<F, G, const N: usize, Inner: CanObserve<u8>> CanObserve<&MerkleCap<G, [u64; N]>>
for BinaryChallenger<F, Inner>
{
fn observe(&mut self, cap: &MerkleCap<G, [u64; N]>) {
for digest in cap.roots() {
for value in digest {
self.inner.observe_slice(&value.to_le_bytes());
}
}
}
}
impl<F, G, const N: usize, Inner: CanObserve<u8>> CanObserve<MerkleCap<G, [u64; N]>>
for BinaryChallenger<F, Inner>
{
fn observe(&mut self, cap: MerkleCap<G, [u64; N]>) {
self.observe(&cap);
}
}
impl<F, EF, Inner> CanSample<EF> for BinaryChallenger<F, Inner>
where
F: TowerLevel,
EF: BasedVectorSpace<F>,
Inner: CanSample<u8>,
{
fn sample(&mut self) -> EF {
let inner = &mut self.inner;
EF::from_basis_coefficients_fn(|_| {
let mut bytes = [0; 16];
inner.sample_into_slice(&mut bytes[..F::NUM_BYTES]);
F::from_le_byte_iter(bytes.into_iter())
})
}
}
impl<F, Inner> CanSampleBits<usize> for BinaryChallenger<F, Inner>
where
F: TowerLevel,
Inner: CanSample<u8>,
{
fn sample_bits(&mut self, bits: usize) -> usize {
assert!(
bits < (usize::BITS as usize),
"requested bit count must fit within a usize"
);
let mut bytes = [0; BITS_SAMPLE_BYTES];
self.inner.sample_into_slice(&mut bytes);
let rand_u64 = u64::from_le_bytes(bytes);
(rand_u64 & ((1u64 << bits) - 1)) as usize
}
}
impl<F, Inner> CanSampleUniformBits<F> for BinaryChallenger<F, Inner>
where
F: TowerLevel,
Inner: CanSample<u8>,
{
fn sample_uniform_bits<const RESAMPLE: bool>(
&mut self,
bits: usize,
) -> Result<usize, ResamplingError> {
Ok(self.sample_bits(bits))
}
}
#[inline]
fn candidate<F: TowerLevel>(index: u64) -> F {
F::from_le_byte_iter(index.to_le_bytes().into_iter().chain(repeat(0)))
}
impl<F, Inner> GrindingChallenger for BinaryChallenger<F, Inner>
where
F: TowerLevel,
Inner: CanSample<u8> + CanObserve<u8> + Clone + Send + Sync,
{
type Witness = F;
#[instrument(name = "grind for proof-of-work witness", skip_all)]
fn grind(&mut self, bits: usize) -> Self::Witness {
if bits == 0 {
return F::ZERO;
}
assert!(bits < (usize::BITS as usize));
assert!(
bits + GRIND_MARGIN_BITS <= F::bits().min(u64::BITS as usize),
"requested bit count leaves too small a margin against the witness field's size"
);
let num_candidates = if F::bits() >= u64::BITS as usize {
u64::MAX
} else {
1u64 << F::bits()
};
let witness = (0..num_candidates)
.into_par_iter()
.map_init(
|| self.clone(),
|worker, index| {
worker.inner.clone_from(&self.inner);
let witness = candidate(index);
worker.check_witness(bits, witness).then_some(witness)
},
)
.find_map_any(core::convert::identity)
.expect("failed to find witness");
assert!(self.check_witness(bits, witness));
witness
}
}
impl<F, Inner> FieldChallenger<F> for BinaryChallenger<F, Inner>
where
F: TowerLevel,
Inner: CanSample<u8> + CanObserve<u8> + Clone + Send + Sync,
{
}
#[cfg(test)]
mod tests {
use alloc::vec;
use alloc::vec::Vec;
use p3_challenger::{
CanObserve, CanSample, CanSampleBits, CanSampleUniformBits, FieldChallenger,
GrindingChallenger, HashChallenger,
};
use p3_field::{BasedVectorSpace, Field, PrimeCharacteristicRing, RawDataSerializable};
use p3_keccak::Keccak256Hash;
use p3_symmetric::{Hash, MerkleCap};
use crate::challenger::{BITS_SAMPLE_BYTES, BinaryChallenger};
use crate::tower::TowerLevel;
use crate::{BinaryField8, BinaryField32, BinaryField128, Gf2, Ghash128};
type Inner = HashChallenger<u8, Keccak256Hash, 32>;
fn mk<F>() -> BinaryChallenger<F, Inner> {
BinaryChallenger::from_hasher(vec![], Keccak256Hash)
}
fn mk_inner() -> Inner {
HashChallenger::new(vec![], Keccak256Hash)
}
fn a128() -> BinaryField128 {
BinaryField128::from_le_bytes([
0x21, 0x43, 0x65, 0x87, 0xa9, 0xcb, 0xed, 0x0f, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56,
0x34, 0x12,
])
}
#[test]
fn the_polynomial_basis_field_shares_the_transcript_with_the_tower() {
let element = a128();
let mut tower = mk::<BinaryField128>();
tower.observe(element);
let drawn: BinaryField128 = tower.sample();
let mut ghash = mk::<Ghash128>();
ghash.observe(Ghash128::from_le_byte_iter(
element.into_bytes().into_iter(),
));
let mirrored: Ghash128 = ghash.sample();
assert_eq!(
drawn.into_bytes().into_iter().collect::<Vec<_>>(),
mirrored.into_bytes().into_iter().collect::<Vec<_>>()
);
}
#[test]
fn sampling_is_deterministic_and_observation_dependent() {
let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
let (x, y): (BinaryField128, BinaryField128) = (a.sample(), b.sample());
assert_eq!(x, y);
let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
a.observe(a128());
b.observe(a128());
let (x, y): (BinaryField128, BinaryField128) = (a.sample(), b.sample());
assert_eq!(x, y);
let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
a.observe(BinaryField128::ONE);
b.observe(BinaryField128::ZERO);
let (x, y): (BinaryField128, BinaryField128) = (a.sample(), b.sample());
assert_ne!(x, y);
let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
b.observe(BinaryField128::ZERO);
let (x, y): (BinaryField128, BinaryField128) = (a.sample(), b.sample());
assert_ne!(x, y);
let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
let xs: [BinaryField128; 4] = a.sample_array();
let ys: [BinaryField128; 4] = b.sample_array();
assert_eq!(xs, ys);
assert_ne!(xs[0], xs[1]);
assert_ne!(xs[1], xs[2]);
assert_ne!(xs[2], xs[3]);
}
#[test]
fn observing_an_element_absorbs_its_little_endian_bytes() {
let a = a128();
let mut challenger = mk::<BinaryField128>();
challenger.observe(a);
let mut inner = mk_inner();
inner.observe_slice(&a.into_bytes().into_iter().collect::<Vec<_>>());
for _ in 0..32 {
let observed: u8 = challenger.inner.sample();
let expected: u8 = inner.sample();
assert_eq!(observed, expected);
}
}
#[test]
fn a_sampled_element_is_read_from_the_little_endian_byte_stream() {
let mut challenger = mk::<BinaryField128>();
let sampled: BinaryField128 = challenger.sample();
let mut inner = mk_inner();
let bytes: [u8; 16] = core::array::from_fn(|_| inner.sample());
assert_eq!(sampled, BinaryField128::from_le_bytes(bytes));
}
#[test]
fn a_sampled_element_of_a_narrow_level_reads_only_its_own_bytes() {
let mut challenger = mk::<BinaryField32>();
let sampled: BinaryField32 = challenger.sample();
let mut inner = mk_inner();
let bytes: [u8; 4] = core::array::from_fn(|_| inner.sample());
assert_eq!(sampled, BinaryField32::from_le_bytes(bytes));
}
#[test]
fn sampling_an_extension_element_reads_its_basis_coefficients_in_order() {
let mut challenger = mk::<BinaryField8>();
let sampled: BinaryField128 = challenger.sample();
let mut coefficients = mk::<BinaryField8>();
let coeffs: [BinaryField8; 16] = core::array::from_fn(|_| coefficients.sample());
assert_eq!(
<BinaryField128 as BasedVectorSpace<BinaryField8>>::from_basis_coefficients_fn(|i| {
coeffs[i]
}),
sampled
);
}
#[test]
fn observing_digests_matches_observing_their_bytes() {
let digest = [1_u8, 2, 3, 4];
let mut from_hash = mk::<BinaryField128>();
from_hash.observe(Hash::<BinaryField128, u8, 4>::from(digest));
let cap = MerkleCap::<BinaryField128, [u8; 4]>::new(vec![digest, [5, 6, 7, 8]]);
let mut from_cap_ref = mk::<BinaryField128>();
from_cap_ref.observe(&cap);
let mut from_cap = mk::<BinaryField128>();
from_cap.observe(cap);
let mut hash_bytes = mk_inner();
hash_bytes.observe_slice(&digest);
let mut cap_bytes = mk_inner();
cap_bytes.observe_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
for _ in 0..32 {
let expected: u8 = hash_bytes.sample();
assert_eq!(from_hash.inner.sample(), expected);
let expected: u8 = cap_bytes.sample();
assert_eq!(from_cap_ref.inner.sample(), expected);
assert_eq!(from_cap.inner.sample(), expected);
}
}
#[test]
fn observing_word_digests_matches_observing_their_little_endian_bytes() {
let digest = [0x0807_0605_0403_0201_u64, 0x100f_0e0d_0c0b_0a09];
let second = [0x1817_1615_1413_1211_u64, 0x201f_1e1d_1c1b_1a19];
let mut from_hash = mk::<BinaryField128>();
from_hash.observe(Hash::<BinaryField128, u64, 2>::from(digest));
let cap = MerkleCap::<BinaryField128, [u64; 2]>::new(vec![digest, second]);
let mut from_cap_ref = mk::<BinaryField128>();
from_cap_ref.observe(&cap);
let mut from_cap = mk::<BinaryField128>();
from_cap.observe(cap);
let mut hash_bytes = mk_inner();
hash_bytes.observe_slice(&core::array::from_fn::<u8, 16, _>(|i| i as u8 + 1));
let mut cap_bytes = mk_inner();
cap_bytes.observe_slice(&core::array::from_fn::<u8, 32, _>(|i| i as u8 + 1));
for _ in 0..32 {
let expected: u8 = hash_bytes.sample();
assert_eq!(from_hash.inner.sample(), expected);
let expected: u8 = cap_bytes.sample();
assert_eq!(from_cap_ref.inner.sample(), expected);
assert_eq!(from_cap.inner.sample(), expected);
}
}
#[test]
fn sampled_bits_fit_the_requested_width_and_are_reproducible() {
let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
for bits in 0..32 {
let value = a.sample_bits(bits);
assert!(value < (1 << bits), "{value} does not fit in {bits} bits");
assert_eq!(value, b.sample_bits(bits));
}
let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
a.observe(BinaryField128::ONE);
assert_ne!(a.sample_bits(31), b.sample_bits(31));
}
#[test]
fn uniform_bits_agree_with_plain_bits_and_never_reject() {
let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
for bits in [0, 1, 7, 8, 16, 31] {
assert_eq!(
a.sample_uniform_bits::<true>(bits).unwrap(),
b.sample_bits(bits)
);
}
let (mut a, mut b) = (mk::<BinaryField128>(), mk::<BinaryField128>());
for bits in [0, 1, 7, 8, 16, 31] {
assert_eq!(
a.sample_uniform_bits::<false>(bits).unwrap(),
b.sample_bits(bits)
);
}
}
#[test]
fn sample_bits_is_not_limited_by_the_width_of_the_field() {
assert_eq!(<BinaryField8 as Field>::bits(), 8);
let mut challenger = mk::<BinaryField8>();
let sampled = challenger.sample_bits(24);
assert!(sampled < (1 << 24));
let mut inner = mk_inner();
let bytes: [u8; BITS_SAMPLE_BYTES] = core::array::from_fn(|_| inner.sample());
assert_eq!(sampled, (u64::from_le_bytes(bytes) & 0xff_ffff) as usize);
let mut challenger = mk::<BinaryField8>();
assert_eq!(challenger.sample_uniform_bits::<true>(24).unwrap(), sampled);
let mut challenger = mk::<BinaryField8>();
assert_eq!(
challenger.sample_uniform_bits::<false>(24).unwrap(),
sampled
);
let widest = usize::BITS as usize - 1;
let mut challenger = mk::<BinaryField8>();
assert!(challenger.sample_bits(widest) < (1 << widest));
}
#[test]
#[should_panic = "requested bit count must fit within a usize"]
fn sample_bits_rejects_a_request_wider_than_a_usize() {
let mut challenger = mk::<BinaryField128>();
let _ = challenger.sample_bits(usize::BITS as usize);
}
#[test]
#[should_panic = "requested bit count leaves too small a margin"]
fn grind_rejects_a_request_wider_than_the_field() {
let mut challenger = mk::<BinaryField32>();
let _ = challenger.grind(32);
}
#[test]
#[should_panic = "requested bit count leaves too small a margin"]
fn grind_rejects_a_request_inside_the_margin() {
let mut challenger = mk::<BinaryField32>();
let _ = challenger.grind(25);
}
#[derive(Clone)]
struct AlwaysZeroInner;
impl CanObserve<u8> for AlwaysZeroInner {
fn observe(&mut self, _value: u8) {}
}
impl CanSample<u8> for AlwaysZeroInner {
fn sample(&mut self) -> u8 {
0
}
}
#[test]
#[should_panic = "requested bit count leaves too small a margin"]
fn grind_rejects_a_request_the_u64_cap_no_longer_allows() {
let mut challenger = mk::<BinaryField128>();
let _ = challenger.grind(63);
}
#[test]
fn grind_accepts_the_widest_request_the_u64_cap_allows() {
let mut challenger: BinaryChallenger<BinaryField128, AlwaysZeroInner> =
BinaryChallenger::new(AlwaysZeroInner);
let bits = 56.min(usize::BITS as usize - 1);
let witness = challenger.grind(bits);
assert!(challenger.check_witness(bits, witness));
}
#[test]
fn a_bitstring_sample_reads_a_fixed_number_of_bytes() {
let mut challenger = mk::<BinaryField128>();
let sampled = challenger.sample_bits(31);
let mut inner = mk_inner();
let bytes: [u8; BITS_SAMPLE_BYTES] = core::array::from_fn(|_| inner.sample());
assert_eq!(BITS_SAMPLE_BYTES, 8);
assert_eq!(
sampled,
(u64::from_le_bytes(bytes) & ((1 << 31) - 1)) as usize
);
let next = challenger.sample_bits(8);
let bytes: [u8; BITS_SAMPLE_BYTES] = core::array::from_fn(|_| inner.sample());
assert_eq!(next, (u64::from_le_bytes(bytes) & 0xff) as usize);
}
#[test]
fn grind_satisfies_check() {
let mut challenger = mk::<BinaryField128>();
let witness = challenger.clone().grind(8);
assert!(challenger.check_witness(8, witness));
}
#[test]
fn grind_over_a_narrow_level_satisfies_check() {
let mut challenger = mk::<BinaryField32>();
let witness = challenger.clone().grind(8);
assert!(challenger.check_witness(8, witness));
}
#[test]
fn grind_of_zero_bits_is_free() {
let mut challenger = mk::<BinaryField128>();
let mut shadow = challenger.clone();
let witness = challenger.grind(0);
assert_eq!(witness, BinaryField128::ZERO);
for _ in 0..32 {
let expected: u8 = shadow.inner.sample();
assert_eq!(challenger.inner.sample(), expected);
}
}
#[test]
fn grind_of_zero_bits_is_free_on_a_level_narrower_than_the_margin() {
let mut challenger = mk::<Gf2>();
let mut shadow = challenger.clone();
let witness = challenger.grind(0);
assert_eq!(witness, Gf2::ZERO);
for _ in 0..32 {
let expected: u8 = shadow.inner.sample();
assert_eq!(challenger.inner.sample(), expected);
}
}
#[test]
fn a_wrong_witness_fails_the_check() {
let mut challenger = mk::<BinaryField128>();
let witness = challenger.clone().grind(8);
let mut wrong = challenger.clone();
assert!(!wrong.check_witness(8, witness + BinaryField128::ONE));
assert!(challenger.check_witness(8, witness));
}
#[test]
fn field_challenger_algebra_helpers_round_trip() {
let mut challenger = mk::<BinaryField8>();
challenger.observe_algebra_element(a128());
let sampled: BinaryField128 = challenger.sample_algebra_element();
let mut expected = mk::<BinaryField8>();
expected
.observe_slice(BasedVectorSpace::<BinaryField8>::as_basis_coefficients_slice(&a128()));
let expected: BinaryField128 = expected.sample();
assert_eq!(sampled, expected);
}
}