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