use alloc::vec::Vec;
use p3_challenger::CanObserve;
use p3_challenger::fs::{TranscriptError, TranscriptField, TypeTag};
use crate::{
BinaryField8, BinaryField16, BinaryField32, BinaryField64, BinaryField128, Poly64, TowerLevel,
};
macro_rules! impl_transcript_field {
($name:ty, $repr:ty, $bits:literal, |$degree:ident, $basis:ident| $tag:expr) => {
impl TranscriptField for $name {
fn algebra_tag($degree: usize, $basis: [u8; 32]) -> TypeTag {
$tag
}
fn observe_seed<C: CanObserve<Self>>(challenger: &mut C, bytes: &[u8]) {
const WIDTH: usize = $bits / 8;
let element = |chunk: &[u8]| {
let mut padded = [0u8; WIDTH];
padded[..chunk.len()].copy_from_slice(chunk);
<$name>::from_repr(<$repr>::from_le_bytes(padded))
};
for chunk in (bytes.len() as u64).to_le_bytes().chunks(WIDTH) {
challenger.observe(element(chunk));
}
for chunk in bytes.chunks(WIDTH) {
challenger.observe(element(chunk));
}
}
fn wire_len() -> usize {
$bits / 8
}
fn encode(value: &Self, out: &mut Vec<u8>) {
out.extend_from_slice(&value.to_repr().to_be_bytes());
}
fn decode(bytes: &[u8]) -> Result<Self, TranscriptError> {
let prefix = bytes
.get(..$bits / 8)
.ok_or(TranscriptError::BadProofShape {
reason: "not enough bytes for a canonical binary field encoding",
})?;
Ok(Self::from_repr(<$repr>::from_be_bytes(
prefix.try_into().unwrap(),
)))
}
}
};
}
impl_transcript_field!(BinaryField8, u8, 8, |degree, basis| TypeTag::BinaryTower {
bits: 8,
degree,
basis,
});
impl_transcript_field!(BinaryField16, u16, 16, |degree, basis| {
TypeTag::BinaryTower {
bits: 16,
degree,
basis,
}
});
impl_transcript_field!(BinaryField32, u32, 32, |degree, basis| {
TypeTag::BinaryTower {
bits: 32,
degree,
basis,
}
});
impl_transcript_field!(BinaryField64, u64, 64, |degree, basis| {
TypeTag::BinaryTower {
bits: 64,
degree,
basis,
}
});
impl_transcript_field!(BinaryField128, u128, 128, |degree, basis| {
TypeTag::BinaryTower {
bits: 128,
degree,
basis,
}
});
impl_transcript_field!(Poly64, u64, 64, |degree, basis| TypeTag::BinaryPolynomial {
bits: 64,
modulus: 0x1b,
degree,
basis,
});
#[cfg(test)]
mod tests {
use alloc::vec;
use alloc::vec::Vec;
use p3_challenger::testing::Recorder;
use super::*;
const SEED_LEN_BYTES: usize = 8;
fn packed<F: TranscriptField>(bytes: &[u8]) -> Vec<F> {
let mut recorder = Recorder::default();
F::observe_seed(&mut recorder, bytes);
recorder.into_absorbed()
}
#[test]
fn a_seed_carries_its_length_ahead_of_its_bytes() {
let seen = packed::<BinaryField8>(&[1, 2, 3]);
assert_eq!(seen.len(), SEED_LEN_BYTES + 3);
assert_eq!(seen[0], BinaryField8::from_repr(3));
assert_eq!(&seen[SEED_LEN_BYTES..], &packed_bytes(&[1, 2, 3])[..]);
}
fn packed_bytes(bytes: &[u8]) -> Vec<BinaryField8> {
bytes.iter().map(|&b| BinaryField8::from_repr(b)).collect()
}
#[test]
fn a_shorter_string_is_never_a_prefix_of_a_longer_one() {
assert_ne!(
packed::<BinaryField8>(&[1]),
packed::<BinaryField8>(&[1, 0])
);
assert_ne!(
packed::<BinaryField16>(&[1]),
packed::<BinaryField16>(&[1, 0])
);
assert_ne!(
packed::<BinaryField32>(&[1]),
packed::<BinaryField32>(&[1, 0])
);
assert_ne!(
packed::<BinaryField64>(&[1]),
packed::<BinaryField64>(&[1, 0])
);
assert_ne!(
packed::<BinaryField128>(&[1]),
packed::<BinaryField128>(&[1, 0])
);
}
#[test]
fn the_widest_level_packs_a_seed_by_the_same_rule_as_the_narrow_ones() {
let bytes: Vec<u8> = (0..20).collect();
let seen = packed::<BinaryField128>(&bytes);
assert_eq!(seen.len(), 3);
assert_eq!(seen[0], BinaryField128::from_repr(20));
let mut first = [0u8; 16];
first.copy_from_slice(&bytes[..16]);
assert_eq!(
seen[1],
BinaryField128::from_repr(u128::from_le_bytes(first))
);
let mut last = [0u8; 16];
last[..4].copy_from_slice(&bytes[16..]);
assert_eq!(
seen[2],
BinaryField128::from_repr(u128::from_le_bytes(last))
);
}
#[test]
fn a_wire_encoding_round_trips_at_every_level() {
let mut out = Vec::new();
BinaryField8::encode(&BinaryField8::from_repr(0xA5), &mut out);
assert_eq!(out, vec![0xA5]);
assert_eq!(
BinaryField8::decode(&out).unwrap(),
BinaryField8::from_repr(0xA5)
);
let mut out = Vec::new();
BinaryField16::encode(&BinaryField16::from_repr(0xA5C3), &mut out);
assert_eq!(out, vec![0xA5, 0xC3]);
assert_eq!(
BinaryField16::decode(&out).unwrap(),
BinaryField16::from_repr(0xA5C3)
);
let mut out = Vec::new();
BinaryField32::encode(&BinaryField32::from_repr(0x0123_4567), &mut out);
assert_eq!(
BinaryField32::decode(&out).unwrap(),
BinaryField32::from_repr(0x0123_4567)
);
let mut out = Vec::new();
BinaryField64::encode(&BinaryField64::from_repr(0x0123_4567_89AB_CDEF), &mut out);
assert_eq!(
BinaryField64::decode(&out).unwrap(),
BinaryField64::from_repr(0x0123_4567_89AB_CDEF)
);
let mut out = Vec::new();
Poly64::encode(&Poly64::new(0xFEDC_BA98_7654_3210), &mut out);
assert_eq!(
Poly64::decode(&out).unwrap(),
Poly64::new(0xFEDC_BA98_7654_3210)
);
}
#[test]
fn a_short_wire_encoding_is_rejected() {
assert!(BinaryField64::decode(&[0; 7]).is_err());
assert!(BinaryField32::decode(&[0; 3]).is_err());
assert!(BinaryField16::decode(&[0; 1]).is_err());
assert!(BinaryField8::decode(&[]).is_err());
assert!(Poly64::decode(&[0; 7]).is_err());
}
#[test]
fn each_level_names_its_own_width() {
assert_eq!(
BinaryField8::algebra_tag(1, [0; 32]),
TypeTag::BinaryTower {
bits: 8,
degree: 1,
basis: [0; 32]
}
);
assert_ne!(
BinaryField8::algebra_tag(1, [0; 32]),
BinaryField16::algebra_tag(1, [0; 32])
);
assert_ne!(
BinaryField32::algebra_tag(1, [0; 32]),
BinaryField64::algebra_tag(1, [0; 32])
);
assert_ne!(
BinaryField64::algebra_tag(1, [0; 32]),
BinaryField128::algebra_tag(1, [0; 32])
);
assert_ne!(
BinaryField64::algebra_tag(1, [0; 32]),
Poly64::algebra_tag(1, [0; 32])
);
}
}