spongefish_circuit/baby_bear.rs
1//! [`spongefish::Unit`] support for `p3_baby_bear::BabyBear`.
2//!
3//! A direct `impl Unit for BabyBear` would violate the orphan rule, so the
4//! field is carried through the transparent [`BabyBearUnit`] wrapper.
5
6use alloc::vec::Vec;
7
8use p3_baby_bear::BabyBear;
9use p3_field::{integers::QuotientMap, PrimeCharacteristicRing};
10use spongefish::{EncodedSessionId, Unit};
11
12use crate::expr::Ring;
13
14/// Transparent [`Unit`] wrapper around [`BabyBear`].
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16#[repr(transparent)]
17pub struct BabyBearUnit(pub BabyBear);
18
19impl Unit for BabyBearUnit {
20 const ZERO: Self = Self(BabyBear::ZERO);
21}
22
23impl Ring for BabyBearUnit {
24 const ONE: Self = Self(BabyBear::ONE);
25
26 fn add(self, other: Self) -> Self {
27 self + other
28 }
29
30 fn mul(self, other: Self) -> Self {
31 self * other
32 }
33}
34
35impl core::ops::Add for BabyBearUnit {
36 type Output = Self;
37
38 fn add(self, rhs: Self) -> Self {
39 Self(self.0 + rhs.0)
40 }
41}
42
43impl core::ops::Mul for BabyBearUnit {
44 type Output = Self;
45
46 fn mul(self, rhs: Self) -> Self {
47 Self(self.0 * rhs.0)
48 }
49}
50
51impl From<BabyBear> for BabyBearUnit {
52 fn from(value: BabyBear) -> Self {
53 Self(value)
54 }
55}
56
57impl From<BabyBearUnit> for BabyBear {
58 fn from(value: BabyBearUnit) -> Self {
59 value.0
60 }
61}
62
63/// Reads a byte string as BabyBear units, one element per byte.
64///
65/// This is what lets a sponge over [`BabyBearUnit`] be seeded from a session
66/// identifier: [`DuplexSponge`][spongefish::DuplexSponge] implements
67/// [`DuplexSpongeInit`][spongefish::DuplexSpongeInit] over any alphabet that
68/// byte strings embed into. With it,
69/// [`ProverState::new`][spongefish::ProverState::new] works over BabyBear
70/// exactly as it does over bytes.
71///
72/// One element per byte rather than a denser packing: every element is then in
73/// `[0, 256)`, so the map is injective by inspection and there is no remainder
74/// case to get wrong. A 32-byte session identifier costs 32 units, absorbed
75/// once per proof.
76///
77/// # Security
78///
79/// Like the identity embedding on bytes, this map is injective but **not**
80/// prefix-free across lengths: `b"ab"` maps to a prefix of `b"abc"`. It is
81/// admissible where the byte length is fixed by the protocol — a session
82/// identifier is always 32 bytes — and a variable-length byte string must be
83/// length-prefixed by the caller.
84impl EncodedSessionId for BabyBearUnit {
85 fn encode_bytes(bytes: &[u8]) -> impl AsRef<[Self]> {
86 bytes
87 .iter()
88 .map(|&byte| Self(BabyBear::from_int(byte)))
89 .collect::<Vec<_>>()
90 }
91}
92
93impl core::ops::Deref for BabyBearUnit {
94 type Target = BabyBear;
95
96 fn deref(&self) -> &Self::Target {
97 &self.0
98 }
99}