jf_plonk/transcript/
rescue.rs

1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the Jellyfish library.
3
4// You should have received a copy of the MIT License
5// along with the Jellyfish library. If not, see <https://mit-license.org/>.
6
7//! This module is a defines rescue transcript.
8use super::PlonkTranscript;
9use crate::{
10    errors::PlonkError,
11    proof_system::structs::{PlookupEvaluations, ProofEvaluations, VerifyingKey},
12};
13use ark_ec::{
14    pairing::Pairing,
15    short_weierstrass::{Affine, SWCurveConfig as SWParam},
16};
17use ark_std::vec::Vec;
18use jf_crhf::CRHF;
19use jf_pcs::prelude::Commitment;
20use jf_relation::gadgets::ecc::{SWToTEConParam, TEPoint};
21use jf_rescue::{crhf::VariableLengthRescueCRHF, RescueParameter, STATE_SIZE};
22use jf_utils::{bytes_to_field_elements, field_switching, fq_to_fr_with_mask};
23
24/// Transcript with rescue hash function.
25///
26/// It is currently implemented simply as
27/// - an append only vector of field elements
28/// - a state that is initialized with 0
29///
30/// We keep appending new elements to the transcript vector,
31/// and when a challenge is to be generated,
32/// we reset the state with the fresh challenge.
33///
34/// 1. state: \[F: STATE_SIZE\] = hash(state|transcript)
35/// 2. challenge = state\[0\]
36/// 3. transcript = vec!\[\]
37pub struct RescueTranscript<F>
38where
39    F: RescueParameter,
40{
41    transcript: Vec<F>,
42    state: [F; STATE_SIZE],
43}
44
45impl<F> PlonkTranscript<F> for RescueTranscript<F>
46where
47    F: RescueParameter + SWToTEConParam,
48{
49    /// Create a new plonk transcript. `_label` is omitted for efficiency.
50    fn new(_label: &'static [u8]) -> Self {
51        RescueTranscript {
52            transcript: Vec::new(),
53            state: [F::zero(); STATE_SIZE],
54        }
55    }
56
57    fn append_vk_and_pub_input<E, P>(
58        &mut self,
59        vk: &VerifyingKey<E>,
60        pub_input: &[E::ScalarField],
61    ) -> Result<(), PlonkError>
62    where
63        E: Pairing<BaseField = F, G1Affine = Affine<P>>,
64        P: SWParam<BaseField = F>,
65    {
66        // to enable a more efficient verifier circuit, we remove
67        // the following messages (c.f. merlin transcript)
68        //  - field_size_in_bits
69        //  - domain size
70        //  - number of inputs
71        //  - wire subsets separators
72
73        // selector commitments
74        for com in vk.selector_comms.iter() {
75            // convert the SW form commitments into TE form
76            let te_point: TEPoint<F> = com.0.into();
77            self.transcript.push(te_point.get_x());
78            self.transcript.push(te_point.get_y());
79        }
80        // sigma commitments
81        for com in vk.sigma_comms.iter() {
82            // convert the SW form commitments into TE form
83            let te_point: TEPoint<F> = com.0.into();
84            self.transcript.push(te_point.get_x());
85            self.transcript.push(te_point.get_y());
86        }
87        // public input
88        for e in pub_input {
89            self.transcript.push(field_switching(e))
90        }
91
92        Ok(())
93    }
94
95    /// Append the message to the transcript. `_label` is omitted for
96    /// efficiency.
97    fn append_message(&mut self, _label: &'static [u8], msg: &[u8]) -> Result<(), PlonkError> {
98        // We remove the labels for better efficiency
99
100        let mut f = bytes_to_field_elements(msg);
101        self.transcript.append(&mut f);
102        Ok(())
103    }
104
105    /// Append a single commitment to the transcript. `_label` is omitted for
106    /// efficiency.
107    fn append_commitment<E, P>(
108        &mut self,
109        _label: &'static [u8],
110        comm: &Commitment<E>,
111    ) -> Result<(), PlonkError>
112    where
113        E: Pairing<BaseField = F, G1Affine = Affine<P>>,
114        P: SWParam<BaseField = F>,
115    {
116        // convert the SW form commitments into TE form
117        let te_point: TEPoint<F> = comm.0.into();
118        // push the x and y coordinate of comm (in twisted
119        // edwards form) to the transcript
120
121        self.transcript.push(te_point.get_x());
122        self.transcript.push(te_point.get_y());
123        Ok(())
124    }
125
126    fn append_field_elem<E>(
127        &mut self,
128        _label: &'static [u8],
129        challenge: &E::ScalarField,
130    ) -> Result<(), PlonkError>
131    where
132        E: Pairing<BaseField = F>,
133    {
134        self.transcript.push(field_switching(challenge));
135        Ok(())
136    }
137
138    fn append_proof_evaluations<E: Pairing>(
139        &mut self,
140        evals: &ProofEvaluations<E::ScalarField>,
141    ) -> Result<(), PlonkError> {
142        for e in &evals.wires_evals {
143            self.transcript.push(field_switching(e))
144        }
145        for e in &evals.wire_sigma_evals {
146            self.transcript.push(field_switching(e))
147        }
148        self.transcript.push(field_switching(&evals.perm_next_eval));
149        Ok(())
150    }
151
152    fn append_plookup_evaluations<E: Pairing>(
153        &mut self,
154        evals: &PlookupEvaluations<E::ScalarField>,
155    ) -> Result<(), PlonkError> {
156        for eval in evals.evals_vec().iter() {
157            self.transcript.push(field_switching(eval));
158        }
159        for next_eval in evals.next_evals_vec().iter() {
160            self.transcript.push(field_switching(next_eval));
161        }
162        Ok(())
163    }
164
165    /// Generate the challenge for the current transcript,
166    /// `_label` is omitted for efficiency.
167    fn get_challenge<E>(&mut self, _label: &'static [u8]) -> Result<E::ScalarField, PlonkError>
168    where
169        E: Pairing<BaseField = F>,
170    {
171        // 1. state: [F: STATE_SIZE] = hash(state|transcript)
172        // 2. challenge = state[0] in Fr
173        // 3. transcript = Vec::new()
174
175        let input = [self.state.as_ref(), self.transcript.as_ref()].concat();
176        let tmp: [F; STATE_SIZE] = VariableLengthRescueCRHF::evaluate(&input)?;
177        let challenge = fq_to_fr_with_mask::<F, E::ScalarField>(&tmp[0]);
178        self.state.copy_from_slice(&tmp);
179        self.transcript = Vec::new();
180
181        Ok(challenge)
182    }
183}