hyperplonk/poly-iop/src/transcript.rs
poly-iop/src/transcript.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
6use ark_ff::PrimeField;
7use merlin::Transcript;
8use std::marker::PhantomData;
9
10use crate::{errors::PolyIOPErrors, structs::IOPProverMessage, to_bytes, virtual_poly::VPAuxInfo};
11
12/// An IOP transcript consists of a Merlin transcript and a flag `is_empty` to
13/// indicate that if the transcript is empty.
14///
15/// It is associated with a prime field `F` for which challenges are generated
16/// over.
17///
18/// The `is_empty` flag is useful in the case where a protocol is initiated by
19/// the verifier, in which case the prover should start its phase by receiving a
20/// `non-empty` transcript.
21#[derive(Clone)]
22pub struct IOPTranscript<F: PrimeField> {
23 transcript: Transcript,
24 is_empty: bool,
25 #[doc(hidden)]
26 phantom: PhantomData<F>,
27}
28
29impl<F: PrimeField> IOPTranscript<F> {
30 /// Create a new IOP transcript.
31 pub(crate) fn new(label: &'static [u8]) -> Self {
32 Self {
33 transcript: Transcript::new(label),
34 is_empty: true,
35 phantom: PhantomData::default(),
36 }
37 }
38
39 // Append the message to the transcript.
40 pub fn append_message(
41 &mut self,
42 label: &'static [u8],
43 msg: &[u8],
44 ) -> Result<(), PolyIOPErrors> {
45 self.transcript.append_message(label, msg);
46 self.is_empty = false;
47 Ok(())
48 }
49
50 // Append the aux information for a virtual polynomial.
51 pub(crate) fn append_aux_info(&mut self, aux_info: &VPAuxInfo<F>) -> Result<(), PolyIOPErrors> {
52 let message = format!(
53 "max_mul {} num_var {}",
54 aux_info.max_degree, aux_info.num_variables
55 );
56 self.append_message(b"aux info", message.as_bytes())?;
57
58 Ok(())
59 }
60
61 // Append the message to the transcript.
62 pub(crate) fn append_field_element(
63 &mut self,
64 label: &'static [u8],
65 field_elem: &F,
66 ) -> Result<(), PolyIOPErrors> {
67 self.append_message(label, &to_bytes!(field_elem)?)
68 }
69
70 // Append a prover message to the transcript.
71 pub(crate) fn append_prover_message(
72 &mut self,
73 prover_message: &IOPProverMessage<F>,
74 ) -> Result<(), PolyIOPErrors> {
75 for e in prover_message.evaluations.iter() {
76 self.append_field_element(b"prover_message", e)?;
77 }
78 Ok(())
79 }
80
81 // Generate the challenge from the current transcript
82 // and append it to the transcript.
83 //
84 // The output field element is statistical uniform as long
85 // as the field has a size less than 2^384.
86 pub(crate) fn get_and_append_challenge(
87 &mut self,
88 label: &'static [u8],
89 ) -> Result<F, PolyIOPErrors> {
90 // we need to reject when transcript is empty
91 if self.is_empty {
92 return Err(PolyIOPErrors::InvalidTranscript(
93 "transcript is empty".to_string(),
94 ));
95 }
96
97 let mut buf = [0u8; 64];
98 self.transcript.challenge_bytes(label, &mut buf);
99 let challenge = F::from_le_bytes_mod_order(&buf);
100 self.transcript
101 .append_message(label, &to_bytes!(&challenge)?);
102 Ok(challenge)
103 }
104
105 // Generate a list of challenges from the current transcript
106 // and append them to the transcript.
107 //
108 // The output field element are statistical uniform as long
109 // as the field has a size less than 2^384.
110 pub(crate) fn get_and_append_challenge_vectors(
111 &mut self,
112 label: &'static [u8],
113 len: usize,
114 ) -> Result<Vec<F>, PolyIOPErrors> {
115 // we need to reject when transcript is empty
116 if self.is_empty {
117 return Err(PolyIOPErrors::InvalidTranscript(
118 "transcript is empty".to_string(),
119 ));
120 }
121
122 let mut res = vec![];
123 for _ in 0..len {
124 res.push(self.get_and_append_challenge(label)?)
125 }
126 Ok(res)
127 }
128}