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 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 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 fn get_and_append_challenge(&mut self, label: &'static [u8]) -> Result<F, PolyIOPErrors> {
87 // we need to reject when transcript is empty
88 if self.is_empty {
89 return Err(PolyIOPErrors::InvalidTranscript(
90 "transcript is empty".to_string(),
91 ));
92 }
93
94 let mut buf = [0u8; 64];
95 self.transcript.challenge_bytes(label, &mut buf);
96 let challenge = F::from_le_bytes_mod_order(&buf);
97 self.transcript
98 .append_message(label, &to_bytes!(&challenge)?);
99 Ok(challenge)
100 }
101
102 // Generate a list of challenges from the current transcript
103 // and append them to the transcript.
104 //
105 // The output field element are statistical uniform as long
106 // as the field has a size less than 2^384.
107 pub(crate) fn get_and_append_challenge_vectors(
108 &mut self,
109 label: &'static [u8],
110 len: usize,
111 ) -> Result<Vec<F>, PolyIOPErrors> {
112 // we need to reject when transcript is empty
113 if self.is_empty {
114 return Err(PolyIOPErrors::InvalidTranscript(
115 "transcript is empty".to_string(),
116 ));
117 }
118
119 let mut res = vec![];
120 for _ in 0..len {
121 res.push(self.get_and_append_challenge(label)?)
122 }
123 Ok(res)
124 }
125}