Skip to main content

zinc_transcript/
traits.rs

1//
2// Transcribable and Transcript
3//
4
5use crypto_bigint::{Word, modular::ConstMontyParams};
6use crypto_primitives::{
7    BaseFieldConfig, ConstBaseField, ConstIntSemiring, FieldConfig, LiftElementWithConfig,
8    ProjectElementWithConfig, Semiring, boolean::Boolean, crypto_bigint_boxed_uint::BoxedUint,
9    crypto_bigint_const_monty::ConstMontyField, crypto_bigint_int::Int,
10    crypto_bigint_monty::MontyFieldElement, crypto_bigint_uint::Uint,
11};
12use itertools::Itertools;
13use zinc_primality::PrimalityTest;
14use zinc_utils::{from_ref::FromRef, mul};
15
16/// Common trait for both `Transcribable` and `ConstTranscribable` to avoid code
17/// duplication in their implementations.
18pub trait GenTranscribable: Sized {
19    /// Creates a new instance from a byte buffer.
20    /// The buffer must be exactly the expected length.
21    // TODO(alex): Return a Result instead of panicking
22    fn read_transcription_bytes_exact(bytes: &[u8]) -> Self;
23
24    /// Transcribes the current instance into a byte buffer.
25    /// The buffer must be exactly the expected length.
26    fn write_transcription_bytes_exact(&self, buf: &mut [u8]);
27}
28
29/// Trait for types that can be transcribed to and from a byte representation.
30/// Byte order is not specified, but it must be portable across platforms.
31pub trait Transcribable: GenTranscribable {
32    /// Number of bytes required to represent **length** of this type, could be
33    /// zero if known in advance.
34    // Defaults to 4 gigabytes - way more than we would ever want to handle in
35    // practice
36    const LENGTH_NUM_BYTES: usize = u32::NUM_BYTES;
37
38    /// Read number of bytes required to represent this type.
39    /// The buffer must be exactly `LENGTH_NUM_BYTES` long.
40    /// The buffer passed to `read_transcription_bytes` should be exactly the
41    /// length returned by this function.
42    fn read_num_bytes(bytes: &[u8]) -> usize {
43        usize::try_from(u32::read_transcription_bytes_exact(bytes))
44            .expect("num_bytes must fit into usize")
45    }
46
47    /// Returns the number of bytes required to represent this type.
48    /// The buffer passed to `write_transcription_bytes` should be exactly the
49    /// length returned by this function.
50    fn get_num_bytes(&self) -> usize;
51
52    /// Reads an instance of this type from the beginning of the byte slice, and
53    /// returns the instance along with the remaining byte slice.
54    fn read_transcription_bytes_subset(bytes: &[u8]) -> (Self, &[u8]) {
55        let (bytes_num_bytes, bytes_rem) = bytes.split_at(Self::LENGTH_NUM_BYTES);
56        let num_bytes = Self::read_num_bytes(bytes_num_bytes);
57        // TODO(alex): Return a Result instead of panicking
58        assert!(
59            bytes_rem.len() >= num_bytes,
60            "Byte slice length is not sufficient for reading Transcribable"
61        );
62        let (bytes_data, bytes_rem) = bytes_rem.split_at(num_bytes);
63        (Self::read_transcription_bytes_exact(bytes_data), bytes_rem)
64    }
65
66    /// Writes this instance, prefixed by length, into the beginning of the byte
67    /// buffer, and returns the remaining byte buffer.
68    fn write_transcription_bytes_subset<'a>(&self, mut buf: &'a mut [u8]) -> &'a mut [u8] {
69        let num_bytes = self.get_num_bytes();
70        if Self::LENGTH_NUM_BYTES > 0 {
71            buf[0..Self::LENGTH_NUM_BYTES]
72                .copy_from_slice(&num_bytes.to_le_bytes()[..Self::LENGTH_NUM_BYTES]);
73            buf = &mut buf[Self::LENGTH_NUM_BYTES..];
74        };
75        let (buf, rest) = buf.split_at_mut(num_bytes);
76        self.write_transcription_bytes_exact(buf);
77        rest
78    }
79}
80
81/// If number of bytes for `Transcribable` is known at compile time,
82/// there's no need to read and write them from a buffer.
83pub trait ConstTranscribable: GenTranscribable {
84    /// Number of bytes required to represent this type.
85    const NUM_BYTES: usize;
86    /// Number of bits actually used to store data.
87    const NUM_BITS: usize = Self::NUM_BYTES * 8;
88}
89
90impl<T: ConstTranscribable> Transcribable for T {
91    const LENGTH_NUM_BYTES: usize = 0;
92
93    fn read_num_bytes(bytes: &[u8]) -> usize {
94        assert_eq!(bytes.len(), 0);
95        Self::NUM_BYTES
96    }
97
98    fn get_num_bytes(&self) -> usize {
99        Self::NUM_BYTES
100    }
101
102    fn read_transcription_bytes_subset(bytes: &[u8]) -> (Self, &[u8]) {
103        assert!(
104            bytes.len() >= Self::NUM_BYTES,
105            "Byte slice length is not sufficient for reading Transcribable"
106        );
107        let (bytes_data, bytes_rem) = bytes.split_at(Self::NUM_BYTES);
108        (Self::read_transcription_bytes_exact(bytes_data), bytes_rem)
109    }
110}
111
112/// Should not be used directly — use [`delegate_transcribable!`] or
113/// [`delegate_const_transcribable!`] instead.
114#[macro_export]
115macro_rules! delegate_gen_transcribable {
116    ($wrapper:ident { $field:tt : $inner_ty:ty }) => {
117        impl $crate::traits::GenTranscribable for $wrapper {
118            fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
119                Self {
120                    $field: <$inner_ty as $crate::traits::GenTranscribable>::read_transcription_bytes_exact(bytes),
121                }
122            }
123
124            fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
125                $crate::traits::GenTranscribable::write_transcription_bytes_exact(&self.$field, buf)
126            }
127        }
128    };
129    ($wrapper:ident <$($gen:tt),+> { $field:tt : $inner_ty:ty } $(where $($bounds:tt)+)?) => {
130        impl<$($gen),+> $crate::traits::GenTranscribable for $wrapper<$($gen),+>
131        $(where $($bounds)+)?
132        {
133            fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
134                Self {
135                    $field: <$inner_ty as $crate::traits::GenTranscribable>::read_transcription_bytes_exact(bytes),
136                }
137            }
138
139            fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
140                $crate::traits::GenTranscribable::write_transcription_bytes_exact(&self.$field, buf)
141            }
142        }
143    };
144    ($wrapper:ident <const $cg_name:ident : $cg_ty:ty> { $field:tt : $inner_ty:ty } $(where $($bounds:tt)+)?) => {
145        impl<const $cg_name: $cg_ty> $crate::traits::GenTranscribable for $wrapper<$cg_name>
146        $(where $($bounds)+)?
147        {
148            fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
149                Self {
150                    $field: <$inner_ty as $crate::traits::GenTranscribable>::read_transcription_bytes_exact(bytes),
151                }
152            }
153
154            fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
155                $crate::traits::GenTranscribable::write_transcription_bytes_exact(&self.$field, buf)
156            }
157        }
158    };
159    ($wrapper:ident <$($gen:tt),+, const $cg_name:ident : $cg_ty:ty> { $field:tt : $inner_ty:ty } $(where $($bounds:tt)+)?) => {
160        impl<$($gen),+, const $cg_name: $cg_ty> $crate::traits::GenTranscribable for $wrapper<$($gen),+, $cg_name>
161        $(where $($bounds)+)?
162        {
163            fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
164                Self {
165                    $field: <$inner_ty as $crate::traits::GenTranscribable>::read_transcription_bytes_exact(bytes),
166                }
167            }
168
169            fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
170                $crate::traits::GenTranscribable::write_transcription_bytes_exact(&self.$field, buf)
171            }
172        }
173    };
174}
175
176/// Delegates `Transcribable` to the single inner field of a newtype.
177/// Use this instead of [`delegate_const_transcribable!`] when the inner type
178/// only implements `Transcribable` (e.g. `BoxedUint`).
179///
180/// Supports non-generic and generic types with optional `where` clauses:
181/// ```ignore
182/// delegate_transcribable!(MyTuple(InnerType));
183/// delegate_transcribable!(MyNamed { field: InnerType });
184/// delegate_transcribable!(MyGeneric<T> { field: Vec<T> } where T: SomeBound);
185/// delegate_transcribable!(MyConst<const N: usize>(SomeType));
186/// delegate_transcribable!(MyMixed<T, const N: usize> { f: [T; N] } where T: SomeBound);
187/// ```
188#[macro_export]
189macro_rules! delegate_transcribable {
190    ($wrapper:ident ($inner_ty:ty)) => {
191        $crate::delegate_transcribable!($wrapper { 0: $inner_ty });
192    };
193    ($wrapper:ident { $field:tt : $inner_ty:ty }) => {
194        $crate::delegate_gen_transcribable!($wrapper { $field: $inner_ty });
195        impl $crate::traits::Transcribable for $wrapper {
196            const LENGTH_NUM_BYTES: usize =
197                <$inner_ty as $crate::traits::Transcribable>::LENGTH_NUM_BYTES;
198
199            fn read_num_bytes(bytes: &[u8]) -> usize {
200                <$inner_ty as $crate::traits::Transcribable>::read_num_bytes(bytes)
201            }
202
203            fn get_num_bytes(&self) -> usize {
204                <$inner_ty as $crate::traits::Transcribable>::get_num_bytes(&self.$field)
205            }
206        }
207    };
208    ($wrapper:ident <$($gen:tt),+> ($inner_ty:ty) $(where $($bounds:tt)+)?) => {
209        $crate::delegate_transcribable!($wrapper <$($gen),+> { 0: $inner_ty } $(where $($bounds)+)?);
210    };
211    ($wrapper:ident <$($gen:tt),+> { $field:tt : $inner_ty:ty } $(where $($bounds:tt)+)?) => {
212        $crate::delegate_gen_transcribable!($wrapper <$($gen),+> { $field: $inner_ty } $(where $($bounds)+)?);
213        impl<$($gen),+> $crate::traits::Transcribable for $wrapper<$($gen),+>
214        $(where $($bounds)+)?
215        {
216            const LENGTH_NUM_BYTES: usize =
217                <$inner_ty as $crate::traits::Transcribable>::LENGTH_NUM_BYTES;
218
219            fn read_num_bytes(bytes: &[u8]) -> usize {
220                <$inner_ty as $crate::traits::Transcribable>::read_num_bytes(bytes)
221            }
222
223            fn get_num_bytes(&self) -> usize {
224                <$inner_ty as $crate::traits::Transcribable>::get_num_bytes(&self.$field)
225            }
226        }
227    };
228    ($wrapper:ident <const $cg_name:ident : $cg_ty:ty> ($inner_ty:ty) $(where $($bounds:tt)+)?) => {
229        $crate::delegate_transcribable!($wrapper <const $cg_name : $cg_ty> { 0: $inner_ty } $(where $($bounds)+)?);
230    };
231    ($wrapper:ident <const $cg_name:ident : $cg_ty:ty> { $field:tt : $inner_ty:ty } $(where $($bounds:tt)+)?) => {
232        $crate::delegate_gen_transcribable!($wrapper <const $cg_name : $cg_ty> { $field: $inner_ty } $(where $($bounds)+)?);
233        impl<const $cg_name: $cg_ty> $crate::traits::Transcribable for $wrapper<$cg_name>
234        $(where $($bounds)+)?
235        {
236            const LENGTH_NUM_BYTES: usize =
237                <$inner_ty as $crate::traits::Transcribable>::LENGTH_NUM_BYTES;
238
239            fn read_num_bytes(bytes: &[u8]) -> usize {
240                <$inner_ty as $crate::traits::Transcribable>::read_num_bytes(bytes)
241            }
242
243            fn get_num_bytes(&self) -> usize {
244                <$inner_ty as $crate::traits::Transcribable>::get_num_bytes(&self.$field)
245            }
246        }
247    };
248    ($wrapper:ident <$($gen:tt),+, const $cg_name:ident : $cg_ty:ty> ($inner_ty:ty) $(where $($bounds:tt)+)?) => {
249        $crate::delegate_transcribable!($wrapper <$($gen),+, const $cg_name : $cg_ty> { 0: $inner_ty } $(where $($bounds)+)?);
250    };
251    ($wrapper:ident <$($gen:tt),+, const $cg_name:ident : $cg_ty:ty> { $field:tt : $inner_ty:ty } $(where $($bounds:tt)+)?) => {
252        $crate::delegate_gen_transcribable!($wrapper <$($gen),+, const $cg_name : $cg_ty> { $field: $inner_ty } $(where $($bounds)+)?);
253        impl<$($gen),+, const $cg_name: $cg_ty> $crate::traits::Transcribable for $wrapper<$($gen),+, $cg_name>
254        $(where $($bounds)+)?
255        {
256            const LENGTH_NUM_BYTES: usize =
257                <$inner_ty as $crate::traits::Transcribable>::LENGTH_NUM_BYTES;
258
259            fn read_num_bytes(bytes: &[u8]) -> usize {
260                <$inner_ty as $crate::traits::Transcribable>::read_num_bytes(bytes)
261            }
262
263            fn get_num_bytes(&self) -> usize {
264                <$inner_ty as $crate::traits::Transcribable>::get_num_bytes(&self.$field)
265            }
266        }
267    };
268}
269
270/// Delegates `ConstTranscribable` to the single inner field of a newtype.
271/// `Transcribable` is obtained automatically via the blanket impl.
272///
273/// Supports non-generic and generic types with optional `where` clauses:
274/// ```ignore
275/// delegate_const_transcribable!(MyTuple(InnerType));
276/// delegate_const_transcribable!(MyNamed { field: InnerType });
277/// delegate_const_transcribable!(MyGeneric<T> { field: T } where T: SomeBound);
278/// delegate_const_transcribable!(MyConst<const N: usize>(SomeType));
279/// delegate_const_transcribable!(MyMixed<T, const N: usize> { f: [T; N] } where T: SomeBound);
280/// ```
281#[macro_export]
282macro_rules! delegate_const_transcribable {
283    ($wrapper:ident ($inner_ty:ty)) => {
284        $crate::delegate_const_transcribable!($wrapper { 0: $inner_ty });
285    };
286    ($wrapper:ident { $field:tt : $inner_ty:ty }) => {
287        $crate::delegate_gen_transcribable!($wrapper { $field: $inner_ty });
288        impl $crate::traits::ConstTranscribable for $wrapper {
289            const NUM_BYTES: usize = <$inner_ty as $crate::traits::ConstTranscribable>::NUM_BYTES;
290            const NUM_BITS: usize = <$inner_ty as $crate::traits::ConstTranscribable>::NUM_BITS;
291        }
292    };
293    ($wrapper:ident <$($gen:tt),+> ($inner_ty:ty) $(where $($bounds:tt)+)?) => {
294        $crate::delegate_const_transcribable!($wrapper <$($gen),+> { 0: $inner_ty } $(where $($bounds)+)?);
295    };
296    ($wrapper:ident <$($gen:tt),+> { $field:tt : $inner_ty:ty } $(where $($bounds:tt)+)?) => {
297        $crate::delegate_gen_transcribable!($wrapper <$($gen),+> { $field: $inner_ty } $(where $($bounds)+)?);
298        impl<$($gen),+> $crate::traits::ConstTranscribable for $wrapper<$($gen),+>
299        $(where $($bounds)+)?
300        {
301            const NUM_BYTES: usize = <$inner_ty as $crate::traits::ConstTranscribable>::NUM_BYTES;
302            const NUM_BITS: usize = <$inner_ty as $crate::traits::ConstTranscribable>::NUM_BITS;
303        }
304    };
305    ($wrapper:ident <const $cg_name:ident : $cg_ty:ty> ($inner_ty:ty) $(where $($bounds:tt)+)?) => {
306        $crate::delegate_const_transcribable!($wrapper <const $cg_name : $cg_ty> { 0: $inner_ty } $(where $($bounds)+)?);
307    };
308    ($wrapper:ident <const $cg_name:ident : $cg_ty:ty> { $field:tt : $inner_ty:ty } $(where $($bounds:tt)+)?) => {
309        $crate::delegate_gen_transcribable!($wrapper <const $cg_name : $cg_ty> { $field: $inner_ty } $(where $($bounds)+)?);
310        impl<const $cg_name: $cg_ty> $crate::traits::ConstTranscribable for $wrapper<$cg_name>
311        $(where $($bounds)+)?
312        {
313            const NUM_BYTES: usize = <$inner_ty as $crate::traits::ConstTranscribable>::NUM_BYTES;
314            const NUM_BITS: usize = <$inner_ty as $crate::traits::ConstTranscribable>::NUM_BITS;
315        }
316    };
317    ($wrapper:ident <$($gen:tt),+, const $cg_name:ident : $cg_ty:ty> ($inner_ty:ty) $(where $($bounds:tt)+)?) => {
318        $crate::delegate_const_transcribable!($wrapper <$($gen),+, const $cg_name : $cg_ty> { 0: $inner_ty } $(where $($bounds)+)?);
319    };
320    ($wrapper:ident <$($gen:tt),+, const $cg_name:ident : $cg_ty:ty> { $field:tt : $inner_ty:ty } $(where $($bounds:tt)+)?) => {
321        $crate::delegate_gen_transcribable!($wrapper <$($gen),+, const $cg_name : $cg_ty> { $field: $inner_ty } $(where $($bounds)+)?);
322        impl<$($gen),+, const $cg_name: $cg_ty> $crate::traits::ConstTranscribable for $wrapper<$($gen),+, $cg_name>
323        $(where $($bounds)+)?
324        {
325            const NUM_BYTES: usize = <$inner_ty as $crate::traits::ConstTranscribable>::NUM_BYTES;
326            const NUM_BITS: usize = <$inner_ty as $crate::traits::ConstTranscribable>::NUM_BITS;
327        }
328    };
329}
330
331pub trait Transcript {
332    /// Generates a pseudorandom transcribable value as a challenge based on the
333    /// current transcript state, updating it.
334    fn get_challenge<T: ConstTranscribable>(&mut self) -> T;
335
336    fn get_field_challenge<C>(&mut self, cfg: &C) -> C::Element
337    where
338        C: FieldConfig + ProjectElementWithConfig<C::Integer>,
339        C::Integer: ConstTranscribable,
340    {
341        let random_integer: C::Integer = self.get_challenge();
342        cfg.project(&random_integer)
343    }
344
345    /// Generates a pseudorandom transcribable values as challenges based on the
346    /// current transcript state, updating it.
347    // TODO(Alex): `get_field_challenge` is not efficient
348    //             to call in a batch because each call allocates its own buffer.
349    //             It might make sense to make a separate `get_challenge_with_buf`
350    //             alternative to `get_challenge`.
351    fn get_field_challenges<C>(&mut self, n: usize, cfg: &C) -> Vec<C::Element>
352    where
353        C: FieldConfig + ProjectElementWithConfig<C::Integer>,
354        C::Integer: ConstTranscribable,
355    {
356        (0..n).map(|_| self.get_field_challenge(cfg)).collect()
357    }
358
359    /// Generates a pseudorandom transcribable values as challenges based on the
360    /// current transcript state, updating it.
361    fn get_challenges<T: ConstTranscribable>(&mut self, n: usize) -> Vec<T> {
362        (0..n).map(|_| self.get_challenge()).collect()
363    }
364
365    fn get_prime<R: ConstIntSemiring + ConstTranscribable, T: PrimalityTest<R>>(&mut self) -> R;
366
367    fn get_random_field_cfg<C, FMod, T>(&mut self) -> C
368    where
369        C: BaseFieldConfig,
370        C::Integer: FromRef<FMod>,
371        FMod: ConstTranscribable + ConstIntSemiring,
372        T: PrimalityTest<FMod>,
373    {
374        let prime = self.get_prime::<FMod, T>();
375
376        C::new(&C::Integer::from_ref(&prime)).expect("prime is guaranteed to be prime")
377    }
378
379    /// Absorbs a byte slice into the hash sponge.
380    /// This updates the internal state of the hasher with the provided data.
381    /// Should not be used directly.
382    fn absorb_inner(&mut self, v: &[u8]);
383
384    /// Absorbs a byte slice into the transcript, delimited by
385    /// domain-separation tags.
386    fn absorb_bytes(&mut self, buf: &[u8]) {
387        self.absorb_inner(&[0x06]);
388        self.absorb_inner(buf);
389        self.absorb_inner(&[0x07]);
390    }
391
392    /// Absorbs a field element (its raw inner representation) into the
393    /// transcript.
394    ///
395    /// The field modulus is NOT absorbed here: it is bound into the transcript
396    /// separately, when the field is sampled.
397    fn absorb_field_element<C, I>(&mut self, cfg: &C, v: &C::Element, buf: &mut [u8])
398    where
399        C: FieldConfig + LiftElementWithConfig<I>,
400        I: Semiring + Transcribable,
401    {
402        self.absorb_int(&cfg.lift(v), buf);
403    }
404
405    /// Absorbs a slice of field element into the transcript.
406    fn absorb_field_element_slice<C, I>(&mut self, cfg: &C, v: &[C::Element], buf: &mut [u8])
407    where
408        C: FieldConfig + LiftElementWithConfig<I>,
409        I: Semiring + Transcribable,
410    {
411        v.iter()
412            .for_each(|x| self.absorb_field_element(cfg, x, buf));
413    }
414
415    /// Absorbs an integer into the transcript.
416    fn absorb_int<S>(&mut self, v: &S, buf: &mut [u8])
417    where
418        S: Semiring + Transcribable,
419    {
420        v.write_transcription_bytes_exact(buf);
421        self.absorb_bytes(buf);
422    }
423
424    /// Absorbs a slice of integer into the transcript.
425    fn absorb_int_slice<S>(&mut self, v: &[S], buf: &mut [u8])
426    where
427        S: Semiring + Transcribable,
428    {
429        v.iter().for_each(|x| self.absorb_int(x, buf));
430    }
431}
432
433//
434// Transcribable implementations
435//
436
437macro_rules! impl_transcribable_for_primitives {
438    ($($type:ty),+) => {
439        $(
440            impl GenTranscribable for $type {
441                fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
442                    Self::from_le_bytes(bytes.try_into().expect("Invalid byte slice length"))
443                }
444
445                fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
446                    debug_assert_eq!(buf.len(), Self::NUM_BYTES);
447                    buf.copy_from_slice(&self.to_le_bytes());
448                }
449            }
450
451            impl ConstTranscribable for $type {
452                const NUM_BYTES: usize = std::mem::size_of::<$type>();
453            }
454        )+
455    };
456}
457
458impl_transcribable_for_primitives!(u8, u16, u32, u64, u128);
459impl_transcribable_for_primitives!(i8, i16, i32, i64, i128);
460
461impl GenTranscribable for Boolean {
462    fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
463        (bytes[0] != 0).into()
464    }
465
466    fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
467        buf[0] = self.to_u8();
468    }
469}
470
471impl ConstTranscribable for Boolean {
472    const NUM_BYTES: usize = 1;
473    const NUM_BITS: usize = 1;
474}
475
476impl<const LIMBS: usize> GenTranscribable for Uint<LIMBS> {
477    fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
478        // crypto_bigint::Uint stores limbs in least-to-most significant order.
479        // It matches little-endian order ef limbs encoding, so platform pointer width
480        // does not matter.
481        let (chunked, rem) = bytes.as_chunks::<{ Word::NUM_BYTES }>();
482        assert!(rem.is_empty(), "Invalid byte slice length for Uint");
483        let words = chunked
484            .iter()
485            .map(|chunk| Word::from_le_bytes(*chunk))
486            .collect_array::<LIMBS>()
487            .expect("Invalid length for Uint");
488        Uint::<LIMBS>::from_words(words)
489    }
490
491    #[allow(clippy::arithmetic_side_effects)]
492    fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
493        // crypto_bigint::Uint stores limbs in least-to-most significant order.
494        // It matches little-endian order ef limbs encoding, so platform pointer width
495        // does not matter.
496        assert_eq!(buf.len(), Self::NUM_BYTES, "Buffer size mismatch for Uint");
497        const W_SIZE: usize = size_of::<Word>();
498        for (i, w) in self.as_words().iter().enumerate() {
499            // Performance: reuse buffer and help compiler optimize away materializing
500            // vector
501            buf[(i * W_SIZE)..(i * W_SIZE + W_SIZE)].copy_from_slice(w.to_le_bytes().as_ref());
502        }
503    }
504}
505
506impl<const LIMBS: usize> ConstTranscribable for Uint<LIMBS> {
507    const NUM_BYTES: usize = LIMBS * Word::NUM_BYTES;
508}
509
510impl<const LIMBS: usize> GenTranscribable for Int<LIMBS> {
511    fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
512        *Uint::<LIMBS>::read_transcription_bytes_exact(bytes).as_int()
513    }
514
515    fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
516        self.as_uint().write_transcription_bytes_exact(buf)
517    }
518}
519
520impl<const LIMBS: usize> ConstTranscribable for Int<LIMBS> {
521    const NUM_BYTES: usize = Uint::<LIMBS>::NUM_BYTES;
522}
523
524impl GenTranscribable for BoxedUint {
525    fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
526        // crypto_bigint::BoxedUint stores limbs in least-to-most significant order.
527        // It matches little-endian order ef limbs encoding, so platform pointer width
528        // does not matter.
529        let (chunked, rem) = bytes.as_chunks::<{ Word::NUM_BYTES }>();
530        assert!(rem.is_empty(), "Invalid byte slice length for BoxedUint");
531        let words = chunked
532            .iter()
533            .map(|chunk| Word::from_le_bytes(*chunk))
534            .collect_vec();
535        BoxedUint::from_words(words)
536    }
537
538    #[allow(clippy::arithmetic_side_effects)]
539    fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
540        // crypto_bigint::BoxedUint stores limbs in least-to-most significant order.
541        // It matches little-endian order ef limbs encoding, so platform pointer width
542        // does not matter.
543        assert_eq!(
544            buf.len(),
545            self.bytes_precision(),
546            "Buffer size mismatch for BoxedUint"
547        );
548        const W_SIZE: usize = size_of::<Word>();
549        for (i, w) in self.as_words().iter().enumerate() {
550            // Performance: reuse buffer and help compiler optimize away materializing
551            // vector
552            buf[(i * W_SIZE)..(i * W_SIZE + W_SIZE)].copy_from_slice(w.to_le_bytes().as_ref());
553        }
554    }
555}
556
557impl Transcribable for BoxedUint {
558    /// Up to 255 bytes - so up to 2040 bits - should be plenty.
559    const LENGTH_NUM_BYTES: usize = 1;
560
561    fn read_num_bytes(bytes: &[u8]) -> usize {
562        assert_eq!(bytes.len(), Self::LENGTH_NUM_BYTES);
563        usize::from(bytes[0])
564    }
565
566    fn get_num_bytes(&self) -> usize {
567        usize::from(u8::try_from(self.bytes_precision()).expect("BoxedUint size must fit into u8"))
568    }
569}
570
571// Field elements cross the wire as canonical lifted integers: the field
572// config is bound into the transcript separately, when the field is sampled,
573// and proof readers must deserialize integers and project them through that
574// config, rejecting non-canonical values (`>= modulus`). Accepting raw limbs
575// as Montgomery residues would let malformed proofs inject unreduced
576// residues that break arithmetic preconditions, raw-form equality, and
577// encoding uniqueness.
578//
579// `MontyFieldElement` therefore only supports *writing* here (its raw inner
580// form, used for transcript absorption, which both sides perform on
581// projected canonical elements).
582impl<const LIMBS: usize> GenTranscribable for MontyFieldElement<LIMBS> {
583    fn read_transcription_bytes_exact(_bytes: &[u8]) -> Self {
584        panic!("MontyFieldElement cannot be deserialized without a field config");
585    }
586
587    fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
588        self.0.write_transcription_bytes_exact(buf)
589    }
590}
591
592impl<Mod: ConstMontyParams<LIMBS>, const LIMBS: usize> GenTranscribable
593    for ConstMontyField<Mod, LIMBS>
594{
595    fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
596        let int = Uint::<LIMBS>::read_transcription_bytes_exact(bytes);
597        assert!(
598            int < <Self as ConstBaseField>::MODULUS,
599            "non-canonical field element: lifted integer >= modulus"
600        );
601        Self::new(int)
602    }
603
604    fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
605        self.retrieve().write_transcription_bytes_exact(buf)
606    }
607}
608
609impl<Mod: ConstMontyParams<LIMBS>, const LIMBS: usize> ConstTranscribable
610    for ConstMontyField<Mod, LIMBS>
611{
612    const NUM_BYTES: usize = Uint::<LIMBS>::NUM_BYTES;
613}
614
615impl<T> GenTranscribable for Vec<T>
616where
617    T: ConstTranscribable,
618{
619    fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
620        let chunks = bytes.chunks_exact(T::NUM_BYTES);
621        assert!(
622            chunks.remainder().is_empty(),
623            "Invalid byte slice length for Vec transcription"
624        );
625        chunks.map(T::read_transcription_bytes_exact).collect()
626    }
627
628    fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
629        assert_eq!(
630            buf.len(),
631            mul!(self.len(), T::NUM_BYTES),
632            "Buffer size mismatch for Vec transcription"
633        );
634        for (elem, chunk) in self.iter().zip(buf.chunks_exact_mut(T::NUM_BYTES)) {
635            elem.write_transcription_bytes_exact(chunk);
636        }
637    }
638}
639
640impl<T> Transcribable for Vec<T>
641where
642    T: ConstTranscribable,
643{
644    fn get_num_bytes(&self) -> usize {
645        mul!(self.len(), T::NUM_BYTES)
646    }
647}