Make Transcript generic over curve points

Co-authored-by: Jack Grigg <jack@electriccoin.co>
This commit is contained in:
therealyingtong 2020-10-15 07:35:06 +08:00 committed by Jack Grigg
parent 1e8769b738
commit 43337dea1b
9 changed files with 161 additions and 131 deletions

View File

@ -9,7 +9,6 @@ use crate::arithmetic::CurveAffine;
use crate::poly::{
multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial,
};
use crate::transcript::Hasher;
mod circuit;
mod keygen;
@ -78,6 +77,8 @@ pub enum Error {
BoundsFailure,
/// Opening error
OpeningError,
/// Transcript error
TranscriptError,
}
impl<C: CurveAffine> ProvingKey<C> {
@ -94,21 +95,6 @@ impl<C: CurveAffine> VerifyingKey<C> {
}
}
/// Hash a point into transcript
pub fn hash_point<C: CurveAffine, H: Hasher<C::Base>>(
transcript: &mut H,
point: &C,
) -> Result<(), Error> {
let tmp = point.get_xy();
if bool::from(tmp.is_none()) {
return Err(Error::SynthesisError);
};
let tmp = tmp.unwrap();
transcript.absorb(tmp.0);
transcript.absorb(tmp.1);
Ok(())
}
#[test]
fn test_proving() {
use crate::arithmetic::{Curve, Field};

View File

@ -1,6 +1,6 @@
use super::{
circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed},
hash_point, Error, Proof, ProvingKey,
Error, Proof, ProvingKey,
};
use crate::arithmetic::{
eval_polynomial, get_challenge_scalar, parallelize, BatchInvert, Challenge, Curve, CurveAffine,
@ -11,7 +11,7 @@ use crate::poly::{
multiopen::{self, ProverQuery},
LagrangeCoeff, Polynomial, Rotation,
};
use crate::transcript::Hasher;
use crate::transcript::{Hasher, Transcript};
impl<C: CurveAffine> Proof<C> {
/// This creates a proof for the provided `circuit` when given the public
@ -92,7 +92,7 @@ impl<C: CurveAffine> Proof<C> {
let witness = witness;
// Create a transcript for obtaining Fiat-Shamir challenges.
let mut transcript = HBase::init(C::Base::one());
let mut transcript = Transcript::<C, HBase, HScalar>::new();
// Compute commitments to aux column polynomials
let aux_commitments_projective: Vec<_> = aux
@ -105,7 +105,7 @@ impl<C: CurveAffine> Proof<C> {
drop(aux_commitments_projective);
for commitment in &aux_commitments {
hash_point(&mut transcript, commitment)?;
transcript.absorb_point(commitment).ok();
}
let aux_polys: Vec<_> = aux
@ -143,7 +143,7 @@ impl<C: CurveAffine> Proof<C> {
drop(advice_commitments_projective);
for commitment in &advice_commitments {
hash_point(&mut transcript, commitment)?;
transcript.absorb_point(commitment).ok();
}
let advice_polys: Vec<_> = witness
@ -277,7 +277,7 @@ impl<C: CurveAffine> Proof<C> {
// Hash each permutation product commitment
for c in &permutation_product_commitments {
hash_point(&mut transcript, c)?;
transcript.absorb_point(c).ok();
}
// Obtain challenge for keeping all separate gates linearly independent
@ -385,7 +385,7 @@ impl<C: CurveAffine> Proof<C> {
// Hash each h(X) piece
for c in h_commitments.iter() {
hash_point(&mut transcript, c)?;
transcript.absorb_point(c).ok();
}
let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
@ -444,10 +444,6 @@ impl<C: CurveAffine> Proof<C> {
.map(|poly| eval_polynomial(poly, x_3))
.collect();
// We set up a second transcript on the scalar field to hash in openings of
// our polynomial commitments.
let mut transcript_scalar = HScalar::init(C::Scalar::one());
// Hash each advice evaluation
for eval in advice_evals
.iter()
@ -458,13 +454,9 @@ impl<C: CurveAffine> Proof<C> {
.chain(permutation_product_inv_evals.iter())
.chain(permutation_evals.iter().flat_map(|evals| evals.iter()))
{
transcript_scalar.absorb(*eval);
transcript.absorb_scalar(*eval);
}
let transcript_scalar_point =
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
transcript.absorb(transcript_scalar_point);
let mut instances: Vec<ProverQuery<C>> = Vec::new();
for (query_index, &(column, at)) in pk.vk.cs.advice_queries.iter().enumerate() {
@ -558,9 +550,8 @@ impl<C: CurveAffine> Proof<C> {
}
}
let multiopening =
multiopen::Proof::create(params, &mut transcript, &mut transcript_scalar, instances)
.map_err(|_| Error::OpeningError)?;
let multiopening = multiopen::Proof::create(params, &mut transcript, instances)
.map_err(|_| Error::OpeningError)?;
Ok(Proof {
advice_commitments,

View File

@ -1,11 +1,11 @@
use super::{hash_point, Error, Proof, VerifyingKey};
use super::{Error, Proof, VerifyingKey};
use crate::arithmetic::{get_challenge_scalar, Challenge, CurveAffine, Field};
use crate::poly::{
commitment::{Guard, Params, MSM},
multiopen::VerifierQuery,
Rotation,
};
use crate::transcript::Hasher;
use crate::transcript::{Hasher, Transcript};
impl<'a, C: CurveAffine> Proof<C> {
/// Returns a boolean indicating whether or not the proof is valid
@ -27,16 +27,16 @@ impl<'a, C: CurveAffine> Proof<C> {
}
// Create a transcript for obtaining Fiat-Shamir challenges.
let mut transcript = HBase::init(C::Base::one());
let mut transcript = Transcript::<C, HBase, HScalar>::new();
// Hash the aux (external) commitments into the transcript
for commitment in aux_commitments {
hash_point(&mut transcript, commitment)?;
transcript.absorb_point(commitment).ok();
}
// Hash the prover's advice commitments into the transcript
for commitment in &self.advice_commitments {
hash_point(&mut transcript, commitment)?;
transcript.absorb_point(commitment).ok();
}
// Sample x_0 challenge
@ -47,7 +47,7 @@ impl<'a, C: CurveAffine> Proof<C> {
// Hash each permutation product commitment
for c in &self.permutation_product_commitments {
hash_point(&mut transcript, c)?;
transcript.absorb_point(c).ok();
}
// Sample x_2 challenge, which keeps the gates linearly independent.
@ -55,7 +55,7 @@ impl<'a, C: CurveAffine> Proof<C> {
// Obtain a commitment to h(X) in the form of multiple pieces of degree n - 1
for c in &self.h_commitments {
hash_point(&mut transcript, c)?;
transcript.absorb_point(c).ok();
}
// Sample x_3 challenge, which is used to ensure the circuit is
@ -66,10 +66,6 @@ impl<'a, C: CurveAffine> Proof<C> {
// commitments open to the correct values.
self.check_hx(params, vk, x_0, x_1, x_2, x_3)?;
// Hash together all the openings provided by the prover into a new
// transcript on the scalar field.
let mut transcript_scalar = HScalar::init(C::Scalar::one());
for eval in self
.advice_evals
.iter()
@ -80,13 +76,9 @@ impl<'a, C: CurveAffine> Proof<C> {
.chain(self.permutation_product_inv_evals.iter())
.chain(self.permutation_evals.iter().flat_map(|evals| evals.iter()))
{
transcript_scalar.absorb(*eval);
transcript.absorb_scalar(*eval);
}
let transcript_scalar_point =
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
transcript.absorb(transcript_scalar_point);
let mut queries: Vec<VerifierQuery<'a, C>> = Vec::new();
for (query_index, &(column, at)) in vk.cs.advice_queries.iter().enumerate() {
@ -180,13 +172,7 @@ impl<'a, C: CurveAffine> Proof<C> {
// We are now convinced the circuit is satisfied so long as the
// polynomial commitments open to the correct values.
self.multiopening
.verify(
params,
&mut transcript,
&mut transcript_scalar,
queries,
msm,
)
.verify(params, &mut transcript, queries, msm)
.map_err(|_| Error::OpeningError)
}

View File

@ -248,8 +248,10 @@ fn test_opening_proof() {
commitment::{Blind, Params},
EvaluationDomain,
};
use crate::arithmetic::{eval_polynomial, get_challenge_scalar, Challenge, Curve, Field};
use crate::transcript::{DummyHash, Hasher};
use crate::arithmetic::{
eval_polynomial, get_challenge_scalar, Challenge, Curve, CurveAffine, Field,
};
use crate::transcript::{DummyHash, Hasher, Transcript};
use crate::tweedle::{EpAffine, Fp, Fq};
let params = Params::<EpAffine>::new::<DummyHash<Fp>>(K);
@ -265,17 +267,18 @@ fn test_opening_proof() {
let p = params.commit(&px, blind).to_affine();
let mut transcript = DummyHash::init(Field::one());
let mut hasher = DummyHash::init(Field::one());
let (p_x, p_y) = p.get_xy().unwrap();
transcript.absorb(p_x);
transcript.absorb(p_y);
let x_packed = transcript.squeeze().get_lower_128();
hasher.absorb(p_x);
hasher.absorb(p_y);
let x_packed = hasher.squeeze().get_lower_128();
let x: Fq = get_challenge_scalar(Challenge(x_packed));
// Evaluate the polynomial
let v = eval_polynomial(&px, x);
transcript.absorb(Fp::from_bytes(&v.to_bytes()).unwrap()); // unlikely to fail since p ~ q
hasher.absorb(Fp::from_bytes(&v.to_bytes()).unwrap()); // unlikely to fail since p ~ q
let scalar_hasher = DummyHash::init(Fq::one());
let mut transcript = Transcript::init_with_hashers(&hasher, &scalar_hasher);
loop {
let transcript_dup = transcript.clone();
@ -283,7 +286,7 @@ fn test_opening_proof() {
let opening_proof = Proof::create(&params, &mut transcript, &px, blind, x);
if opening_proof.is_err() {
transcript = transcript_dup;
transcript.absorb(Field::one());
transcript.absorb_base(Field::one());
} else {
let opening_proof = opening_proof.unwrap();
// Verify the opening proof

View File

@ -4,7 +4,7 @@ use crate::arithmetic::{
best_multiexp, compute_inner_product, get_challenge_scalar, parallelize, small_multiexp,
Challenge, Curve, CurveAffine, Field,
};
use crate::transcript::Hasher;
use crate::transcript::{Hasher, Transcript};
impl<C: CurveAffine> Proof<C> {
/// Create a polynomial commitment opening proof for the polynomial defined
@ -20,13 +20,17 @@ impl<C: CurveAffine> Proof<C> {
/// opening v, and the point x. It's probably also nice for the transcript
/// to have seen the elliptic curve description and the SRS, if you want to
/// be rigorous.
pub fn create<H: Hasher<C::Base>>(
pub fn create<HBase, HScalar>(
params: &Params<C>,
transcript: &mut H,
transcript: &mut Transcript<C, HBase, HScalar>,
px: &Polynomial<C::Scalar, Coeff>,
blind: Blind<C::Scalar>,
x: C::Scalar,
) -> Result<Self, Error> {
) -> Result<Self, Error>
where
HBase: Hasher<C::Base>,
HScalar: Hasher<C::Scalar>,
{
let mut blind = blind.0;
// We're limited to polynomials of degree n - 1.
@ -90,15 +94,10 @@ impl<C: CurveAffine> Proof<C> {
// until the challenge is a square.
let mut transcript = transcript.clone();
// Feed L and R into the cloned transcript.
// We expect these to not be points at infinity due to the randomness.
let (l_x, l_y) = l.get_xy().unwrap();
let (r_x, r_y) = r.get_xy().unwrap();
// Feed L and R into the cloned transcript...
transcript.absorb(l_x);
transcript.absorb(l_y);
transcript.absorb(r_x);
transcript.absorb(r_y);
transcript.absorb_point(&l).ok();
transcript.absorb_point(&r).ok();
// ... and get the squared challenge.
let challenge_sq_packed = transcript.squeeze().get_lower_128();
@ -122,12 +121,8 @@ impl<C: CurveAffine> Proof<C> {
let challenge_sq = challenge.square();
// Feed L and R into the real transcript
let (l_x, l_y) = l.get_xy().unwrap();
let (r_x, r_y) = r.get_xy().unwrap();
transcript.absorb(l_x);
transcript.absorb(l_y);
transcript.absorb(r_x);
transcript.absorb(r_y);
transcript.absorb_point(&l).ok();
transcript.absorb_point(&r).ok();
// And obtain the challenge, even though we already have it, since
// squeezing affects the transcript.
@ -172,11 +167,8 @@ impl<C: CurveAffine> Proof<C> {
let delta = best_multiexp(&[d, d * &b, s], &[g, u, params.h]).to_affine();
let (delta_x, delta_y) = delta.get_xy().unwrap();
// Feed delta into the transcript
transcript.absorb(delta_x);
transcript.absorb(delta_y);
transcript.absorb_point(&delta).ok();
// Obtain the challenge c.
let c_packed = transcript.squeeze().get_lower_128();

View File

@ -1,6 +1,6 @@
use super::super::Error;
use super::{Params, Proof, MSM};
use crate::transcript::Hasher;
use crate::transcript::{Hasher, Transcript};
use crate::arithmetic::{
best_multiexp, get_challenge_scalar, Challenge, Curve, CurveAffine, Field,
@ -65,15 +65,19 @@ impl<C: CurveAffine> Proof<C> {
/// Checks to see if an [`Proof`] is valid given the current `transcript`,
/// and a point `x` that the polynomial commitment `p` opens purportedly to
/// the value `v`.
pub fn verify<'a, H: Hasher<C::Base>>(
pub fn verify<'a, HBase, HScalar>(
&self,
params: &'a Params<C>,
mut msm: MSM<'a, C>,
transcript: &mut H,
transcript: &mut Transcript<C, HBase, HScalar>,
x: C::Scalar,
mut commitment_msm: MSM<'a, C>,
v: C::Scalar,
) -> Result<Guard<'a, C>, Error> {
) -> Result<Guard<'a, C>, Error>
where
HBase: Hasher<C::Base>,
HScalar: Hasher<C::Scalar>,
{
// Check for well-formedness
if self.rounds.len() != params.k as usize {
return Err(Error::OpeningError);
@ -105,17 +109,13 @@ impl<C: CurveAffine> Proof<C> {
for round in &self.rounds {
// Feed L and R into the transcript.
let l = round.0.get_xy();
let r = round.1.get_xy();
if bool::from(l.is_none() | r.is_none()) {
let l = round.0;
let r = round.1;
if bool::from(l.get_xy().is_none() | r.get_xy().is_none()) {
return Err(Error::OpeningError);
}
let l = l.unwrap();
let r = r.unwrap();
transcript.absorb(l.0);
transcript.absorb(l.1);
transcript.absorb(r.0);
transcript.absorb(r.1);
transcript.absorb_point(&l).ok();
transcript.absorb_point(&r).ok();
let challenge_sq_packed = transcript.squeeze().get_lower_128();
let challenge_sq: C::Scalar = get_challenge_scalar(Challenge(challenge_sq_packed));
@ -148,15 +148,13 @@ impl<C: CurveAffine> Proof<C> {
challenges_sq_packed.push(Challenge(challenge_sq_packed));
}
let delta = self.delta.get_xy();
if bool::from(delta.is_none()) {
let delta = self.delta;
if bool::from(delta.get_xy().is_none()) {
return Err(Error::OpeningError);
}
let delta = delta.unwrap();
// Feed delta into the transcript
transcript.absorb(delta.0);
transcript.absorb(delta.1);
transcript.absorb_point(&delta).ok();
// Get the challenge `c`
let c_packed = transcript.squeeze().get_lower_128();

View File

@ -8,8 +8,7 @@ use crate::arithmetic::{
eval_polynomial, get_challenge_scalar, kate_division, lagrange_interpolate, Challenge, Curve,
CurveAffine, Field,
};
use crate::plonk::hash_point;
use crate::transcript::Hasher;
use crate::transcript::{Hasher, Transcript};
use std::marker::PhantomData;
#[derive(Debug, Clone)]
@ -24,8 +23,7 @@ impl<C: CurveAffine> Proof<C> {
/// Create a multi-opening proof
pub fn create<'a, I, HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>(
params: &Params<C>,
transcript: &mut HBase,
transcript_scalar: &mut HScalar,
transcript: &mut Transcript<C, HBase, HScalar>,
queries: I,
) -> Result<Self, Error>
where
@ -110,8 +108,9 @@ impl<C: CurveAffine> Proof<C> {
let (opening, q_evals) = loop {
let mut transcript = transcript.clone();
let mut transcript_scalar = transcript_scalar.clone();
hash_point(&mut transcript, &f_commitment).unwrap();
transcript
.absorb_point(&f_commitment)
.map_err(|_| Error::SamplingError)?;
let x_6: C::Scalar =
get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
@ -122,13 +121,9 @@ impl<C: CurveAffine> Proof<C> {
.collect();
for eval in q_evals.iter() {
transcript_scalar.absorb(*eval);
transcript.absorb_scalar(*eval);
}
let transcript_scalar_point =
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
transcript.absorb(transcript_scalar_point);
let x_7: C::Scalar =
get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));

View File

@ -6,8 +6,7 @@ use super::{construct_intermediate_sets, Proof, Query, VerifierQuery};
use crate::arithmetic::{
eval_polynomial, get_challenge_scalar, lagrange_interpolate, Challenge, CurveAffine, Field,
};
use crate::plonk::hash_point;
use crate::transcript::Hasher;
use crate::transcript::{Hasher, Transcript};
#[derive(Debug, Clone)]
struct CommitmentData<C: CurveAffine> {
@ -21,8 +20,7 @@ impl<C: CurveAffine> Proof<C> {
pub fn verify<'a, I, HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>(
&self,
params: &'a Params<C>,
transcript: &mut HBase,
transcript_scalar: &mut HScalar,
transcript: &mut Transcript<C, HBase, HScalar>,
queries: I,
mut msm: MSM<'a, C>,
) -> Result<Guard<'a, C>, Error>
@ -75,20 +73,18 @@ impl<C: CurveAffine> Proof<C> {
}
// Obtain the commitment to the multi-point quotient polynomial f(X).
hash_point(transcript, &self.f_commitment).unwrap();
transcript
.absorb_point(&self.f_commitment)
.map_err(|_| Error::SamplingError)?;
// Sample a challenge x_6 for checking that f(X) was committed to
// correctly.
let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
for eval in self.q_evals.iter() {
transcript_scalar.absorb(*eval);
transcript.absorb_scalar(*eval);
}
let transcript_scalar_point =
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
transcript.absorb(transcript_scalar_point);
// We can compute the expected msm_eval at x_6 using the q_evals provided
// by the prover and from x_5
let msm_eval = point_sets

View File

@ -1,7 +1,8 @@
//! This module contains utilities and traits for dealing with Fiat-Shamir
//! transcripts.
use crate::arithmetic::Field;
use crate::arithmetic::{CurveAffine, Field};
use std::marker::PhantomData;
/// This is a generic interface for a sponge function that can be used for
/// Fiat-Shamir transformations.
@ -23,9 +24,9 @@ pub struct DummyHash<F: Field> {
}
impl<F: Field> Hasher<F> for DummyHash<F> {
fn init(value: F) -> Self {
fn init(key: F) -> Self {
DummyHash {
power: F::ZETA + F::one() + value,
power: F::ZETA + F::one() + key,
state: F::ZETA,
}
}
@ -43,3 +44,85 @@ impl<F: Field> Hasher<F> for DummyHash<F> {
tmp
}
}
/// A transcript that can absorb points from both the base field and scalar
/// field of a curve
#[derive(Debug, Clone)]
pub struct Transcript<C: CurveAffine, HBase, HScalar>
where
HBase: Hasher<C::Base>,
HScalar: Hasher<C::Scalar>,
{
// Hasher over the base field
base_hasher: HBase,
// Hasher over the scalar field
scalar_hasher: HScalar,
// Indicates if scalar(s) has been hashed but not squeezed
scalar_needs_squeezing: bool,
// PhantomData
_marker: PhantomData<C>,
}
impl<C: CurveAffine, HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>
Transcript<C, HBase, HScalar>
{
/// Initialise a new transcript with Field::one() as keys
/// in both the base_hasher and scalar_hasher
pub fn new() -> Self {
let base_hasher = HBase::init(C::Base::one());
let scalar_hasher = HScalar::init(C::Scalar::one());
Transcript {
base_hasher,
scalar_hasher,
scalar_needs_squeezing: false,
_marker: PhantomData,
}
}
/// Initialise a new transcript with some given base_hasher and
/// scalar_hasher
pub fn init_with_hashers(base_hasher: &HBase, scalar_hasher: &HScalar) -> Self {
Transcript {
base_hasher: base_hasher.clone(),
scalar_hasher: scalar_hasher.clone(),
scalar_needs_squeezing: false,
_marker: PhantomData,
}
}
/// Absorb a curve point into the transcript by absorbing
/// its x and y coordinates
pub fn absorb_point(&mut self, point: &C) -> Result<(), ()> {
let tmp = point.get_xy();
if bool::from(tmp.is_none()) {
return Err(());
};
let tmp = tmp.unwrap();
self.base_hasher.absorb(tmp.0);
self.base_hasher.absorb(tmp.1);
Ok(())
}
/// Absorb a base into the base_hasher
pub fn absorb_base(&mut self, base: C::Base) {
self.base_hasher.absorb(base);
}
/// Absorb a scalar into the scalar_hasher
pub fn absorb_scalar(&mut self, scalar: C::Scalar) {
self.scalar_hasher.absorb(scalar);
self.scalar_needs_squeezing = true;
}
/// Squeeze the transcript to obtain a C::Base value.
pub fn squeeze(&mut self) -> C::Base {
if self.scalar_needs_squeezing {
let transcript_scalar_point =
C::Base::from_bytes(&(self.scalar_hasher.squeeze()).to_bytes()).unwrap();
self.base_hasher.absorb(transcript_scalar_point);
self.scalar_needs_squeezing = false;
}
self.base_hasher.squeeze()
}
}