Skip to main content

zinc_transcript/
traits.rs

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