1mod errors {
10 use ark_std::string::String;
11 use displaydoc::Display;
12
13 #[derive(Display, Debug)]
15 pub enum TranscriptError {
16 InvalidTranscript(String),
18 SerializationError(ark_serialize::SerializationError),
20 }
21
22 impl From<ark_serialize::SerializationError> for TranscriptError {
23 fn from(e: ark_serialize::SerializationError) -> Self {
24 Self::SerializationError(e)
25 }
26 }
27}
28
29pub use errors::TranscriptError;
30
31use ark_ff::PrimeField;
32use ark_serialize::CanonicalSerialize;
33use ark_std::{marker::PhantomData, string::ToString};
34use jf_utils::to_bytes;
35use merlin::Transcript;
36
37#[derive(Clone)]
47pub struct IOPTranscript<F: PrimeField> {
48 transcript: Transcript,
49 is_empty: bool,
50 #[doc(hidden)]
51 phantom: PhantomData<F>,
52}
53
54impl<F: PrimeField> IOPTranscript<F> {
56 pub fn new(label: &'static [u8]) -> Self {
58 Self {
59 transcript: Transcript::new(label),
60 is_empty: true,
61 phantom: PhantomData,
62 }
63 }
64
65 pub fn append_message(
67 &mut self,
68 label: &'static [u8],
69 msg: &[u8],
70 ) -> Result<(), TranscriptError> {
71 self.transcript.append_message(label, msg);
72 self.is_empty = false;
73 Ok(())
74 }
75
76 pub fn append_serializable_element<S: CanonicalSerialize>(
78 &mut self,
79 label: &'static [u8],
80 group_elem: &S,
81 ) -> Result<(), TranscriptError> {
82 self.append_message(label, &to_bytes!(group_elem)?)
83 }
84
85 pub fn get_and_append_challenge(&mut self, label: &'static [u8]) -> Result<F, TranscriptError> {
91 if self.is_empty {
93 return Err(TranscriptError::InvalidTranscript(
94 "transcript is empty".to_string(),
95 ));
96 }
97
98 let mut buf = [0u8; 64];
99 self.transcript.challenge_bytes(label, &mut buf);
100 let challenge = F::from_le_bytes_mod_order(&buf);
101 self.append_serializable_element(label, &challenge)?;
102 Ok(challenge)
103 }
104
105 pub fn get_and_append_byte_challenge(
111 &mut self,
112 label: &'static [u8],
113 dest: &mut [u8],
114 ) -> Result<(), TranscriptError> {
115 if self.is_empty {
117 return Err(TranscriptError::InvalidTranscript(
118 "transcript is empty".to_string(),
119 ));
120 }
121
122 self.transcript.challenge_bytes(label, dest);
123 self.append_message(label, dest)?;
124 Ok(())
125 }
126}