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