hyperplonk/transcript/src/lib.rs
transcript/src/lib.rsRaw
1//! Module for PolyIOP transcript.
2//! TODO(ZZ): move this module to HyperPlonk where the transcript will also be
3//! useful.
4//! TODO(ZZ): decide which APIs need to be public.
5
6mod errors;
7pub use errors::TranscriptErrors;
8
9use ark_ff::PrimeField;
10use ark_serialize::CanonicalSerialize;
11use merlin::Transcript;
12use std::marker::PhantomData;
13
14/// An IOP transcript consists of a Merlin transcript and a flag `is_empty` to
15/// indicate that if the transcript is empty.
16///
17/// It is associated with a prime field `F` for which challenges are generated
18/// over.
19///
20/// The `is_empty` flag is useful in the case where a protocol is initiated by
21/// the verifier, in which case the prover should start its phase by receiving a
22/// `non-empty` transcript.
23#[derive(Clone)]
24pub struct IOPTranscript<F: PrimeField> {
25 transcript: Transcript,
26 is_empty: bool,
27 #[doc(hidden)]
28 phantom: PhantomData<F>,
29}
30
31// TODO: Make this into a Trait
32impl<F: PrimeField> IOPTranscript<F> {
33 /// Create a new IOP transcript.
34 pub fn new(label: &'static [u8]) -> Self {
35 Self {
36 transcript: Transcript::new(label),
37 is_empty: true,
38 phantom: PhantomData::default(),
39 }
40 }
41
42 // Append the message to the transcript.
43 pub fn append_message(
44 &mut self,
45 label: &'static [u8],
46 msg: &[u8],
47 ) -> Result<(), TranscriptErrors> {
48 self.transcript.append_message(label, msg);
49 self.is_empty = false;
50 Ok(())
51 }
52
53 // Append the message to the transcript.
54 pub fn append_field_element(
55 &mut self,
56 label: &'static [u8],
57 field_elem: &F,
58 ) -> Result<(), TranscriptErrors> {
59 self.append_message(label, &to_bytes!(field_elem)?)
60 }
61
62 // Append the message to the transcript.
63 pub fn append_serializable_element<S: CanonicalSerialize>(
64 &mut self,
65 label: &'static [u8],
66 group_elem: &S,
67 ) -> Result<(), TranscriptErrors> {
68 self.append_message(label, &to_bytes!(group_elem)?)
69 }
70
71 // Generate the challenge from the current transcript
72 // and append it to the transcript.
73 //
74 // The output field element is statistical uniform as long
75 // as the field has a size less than 2^384.
76 pub fn get_and_append_challenge(
77 &mut self,
78 label: &'static [u8],
79 ) -> Result<F, TranscriptErrors> {
80 // we need to reject when transcript is empty
81 if self.is_empty {
82 return Err(TranscriptErrors::InvalidTranscript(
83 "transcript is empty".to_string(),
84 ));
85 }
86
87 let mut buf = [0u8; 64];
88 self.transcript.challenge_bytes(label, &mut buf);
89 let challenge = F::from_le_bytes_mod_order(&buf);
90 self.transcript
91 .append_message(label, &to_bytes!(&challenge)?);
92 Ok(challenge)
93 }
94
95 // Generate a list of challenges from the current transcript
96 // and append them to the transcript.
97 //
98 // The output field element are statistical uniform as long
99 // as the field has a size less than 2^384.
100 pub fn get_and_append_challenge_vectors(
101 &mut self,
102 label: &'static [u8],
103 len: usize,
104 ) -> Result<Vec<F>, TranscriptErrors> {
105 // we need to reject when transcript is empty
106 if self.is_empty {
107 return Err(TranscriptErrors::InvalidTranscript(
108 "transcript is empty".to_string(),
109 ));
110 }
111
112 let mut res = vec![];
113 for _ in 0..len {
114 res.push(self.get_and_append_challenge(label)?)
115 }
116 Ok(res)
117 }
118}
119
120/// Takes as input a struct, and converts them to a series of bytes. All traits
121/// that implement `CanonicalSerialize` can be automatically converted to bytes
122/// in this manner.
123#[macro_export]
124macro_rules! to_bytes {
125 ($x:expr) => {{
126 let mut buf = ark_std::vec![];
127 ark_serialize::CanonicalSerialize::serialize($x, &mut buf).map(|_| buf)
128 }};
129}