Merge pull request #149 from zcash/transcript-blake2b

Replace DummyHash with BLAKE2b
This commit is contained in:
ebfull 2021-02-01 10:00:27 -07:00 committed by GitHub
commit fb82730d85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 65 additions and 97 deletions

View file

@ -3,10 +3,10 @@ extern crate criterion;
extern crate halo2; extern crate halo2;
use halo2::arithmetic::FieldExt; use halo2::arithmetic::FieldExt;
use halo2::pasta::{EqAffine, Fp, Fq}; use halo2::pasta::{EqAffine, Fp};
use halo2::plonk::*; use halo2::plonk::*;
use halo2::poly::{commitment::Params, Rotation}; use halo2::poly::{commitment::Params, Rotation};
use halo2::transcript::{DummyHashRead, DummyHashWrite}; use halo2::transcript::{Blake2bRead, Blake2bWrite};
use std::marker::PhantomData; use std::marker::PhantomData;
@ -242,7 +242,7 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) {
}; };
// Create a proof // Create a proof
let mut transcript = DummyHashWrite::init(vec![], Fq::one()); let mut transcript = Blake2bWrite::init(vec![]);
create_proof(&params, &pk, &[circuit], &[], &mut transcript) create_proof(&params, &pk, &[circuit], &[], &mut transcript)
.expect("proof generation should not fail") .expect("proof generation should not fail")
}); });
@ -254,7 +254,7 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) {
}; };
// Create a proof // Create a proof
let mut transcript = DummyHashWrite::init(vec![], Fq::one()); let mut transcript = Blake2bWrite::init(vec![]);
create_proof(&params, &pk, &[circuit], &[], &mut transcript) create_proof(&params, &pk, &[circuit], &[], &mut transcript)
.expect("proof generation should not fail"); .expect("proof generation should not fail");
let proof = transcript.finalize(); let proof = transcript.finalize();
@ -262,7 +262,7 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) {
c.bench_function(&verifier_name, |b| { c.bench_function(&verifier_name, |b| {
b.iter(|| { b.iter(|| {
let msm = params.empty_msm(); let msm = params.empty_msm();
let mut transcript = DummyHashRead::init(&proof[..], Fq::one()); let mut transcript = Blake2bRead::init(&proof[..]);
let guard = verify_proof(&params, pk.get_vk(), msm, &[], &mut transcript).unwrap(); let guard = verify_proof(&params, pk.get_vk(), msm, &[], &mut transcript).unwrap();
let msm = guard.clone().use_challenges(); let msm = guard.clone().use_challenges();
assert!(msm.eval()); assert!(msm.eval());

View file

@ -1,13 +1,13 @@
use halo2::{ use halo2::{
arithmetic::{Curve, FieldExt}, arithmetic::{Curve, FieldExt},
model::ModelRecorder, model::ModelRecorder,
pasta::{EqAffine, Fp, Fq}, pasta::{EqAffine, Fp},
plonk::*, plonk::*,
poly::{ poly::{
commitment::{Blind, Params}, commitment::{Blind, Params},
Rotation, Rotation,
}, },
transcript::{DummyHashRead, DummyHashWrite}, transcript::{Blake2bRead, Blake2bWrite},
}; };
use std::marker::PhantomData; use std::marker::PhantomData;
@ -279,7 +279,7 @@ fn main() {
}; };
// Create a proof // Create a proof
let mut transcript = DummyHashWrite::init(vec![], Fq::one()); let mut transcript = Blake2bWrite::init(vec![]);
create_proof(&params, &pk, &[circuit], &[&[pubinputs]], &mut transcript) create_proof(&params, &pk, &[circuit], &[&[pubinputs]], &mut transcript)
.expect("proof generation should not fail"); .expect("proof generation should not fail");
let proof: Vec<u8> = transcript.finalize(); let proof: Vec<u8> = transcript.finalize();
@ -289,7 +289,7 @@ fn main() {
let pubinput_slice = &[pubinput]; let pubinput_slice = &[pubinput];
let msm = params.empty_msm(); let msm = params.empty_msm();
let mut transcript = DummyHashRead::init(&proof[..], Fq::one()); let mut transcript = Blake2bRead::init(&proof[..]);
let guard = verify_proof( let guard = verify_proof(
&params, &params,
pk.get_vk(), pk.get_vk(),

View file

@ -147,12 +147,12 @@ type ChallengeX<F> = ChallengeScalar<F, X>;
fn test_proving() { fn test_proving() {
use crate::arithmetic::{Curve, FieldExt}; use crate::arithmetic::{Curve, FieldExt};
use crate::dev::MockProver; use crate::dev::MockProver;
use crate::pasta::{EqAffine, Fp, Fq}; use crate::pasta::{EqAffine, Fp};
use crate::poly::{ use crate::poly::{
commitment::{Blind, Params}, commitment::{Blind, Params},
Rotation, Rotation,
}; };
use crate::transcript::{DummyHashRead, DummyHashWrite}; use crate::transcript::{Blake2bRead, Blake2bWrite};
use circuit::{Advice, Column, Fixed}; use circuit::{Advice, Column, Fixed};
use std::marker::PhantomData; use std::marker::PhantomData;
const K: u32 = 5; const K: u32 = 5;
@ -504,7 +504,7 @@ fn test_proving() {
assert_eq!(prover.verify(), Ok(())); assert_eq!(prover.verify(), Ok(()));
for _ in 0..100 { for _ in 0..100 {
let mut transcript = DummyHashWrite::init(vec![], Fq::one()); let mut transcript = Blake2bWrite::init(vec![]);
// Create a proof // Create a proof
create_proof( create_proof(
&params, &params,
@ -519,7 +519,7 @@ fn test_proving() {
let pubinput_slice = &[pubinput]; let pubinput_slice = &[pubinput];
let pubinput_slice_copy = &[pubinput]; let pubinput_slice_copy = &[pubinput];
let msm = params.empty_msm(); let msm = params.empty_msm();
let mut transcript = DummyHashRead::init(&proof[..], Fq::one()); let mut transcript = Blake2bRead::init(&proof[..]);
let guard = verify_proof( let guard = verify_proof(
&params, &params,
pk.get_vk(), pk.get_vk(),
@ -539,7 +539,7 @@ fn test_proving() {
} }
let msm = guard.clone().use_challenges(); let msm = guard.clone().use_challenges();
assert!(msm.clone().eval()); assert!(msm.clone().eval());
let mut transcript = DummyHashRead::init(&proof[..], Fq::one()); let mut transcript = Blake2bRead::init(&proof[..]);
let mut vk_buffer = vec![]; let mut vk_buffer = vec![];
pk.get_vk().write(&mut vk_buffer).unwrap(); pk.get_vk().write(&mut vk_buffer).unwrap();
let vk = VerifyingKey::<EqAffine>::read::<_, MyCircuit<Fp>>(&mut &vk_buffer[..], &params) let vk = VerifyingKey::<EqAffine>::read::<_, MyCircuit<Fp>>(&mut &vk_buffer[..], &params)

View file

@ -336,7 +336,7 @@ fn test_opening_proof() {
use crate::arithmetic::{eval_polynomial, FieldExt}; use crate::arithmetic::{eval_polynomial, FieldExt};
use crate::pasta::{EpAffine, Fq}; use crate::pasta::{EpAffine, Fq};
use crate::transcript::{ use crate::transcript::{
ChallengeScalar, DummyHashRead, DummyHashWrite, Transcript, TranscriptRead, TranscriptWrite, Blake2bRead, Blake2bWrite, ChallengeScalar, Transcript, TranscriptRead, TranscriptWrite,
}; };
let params = Params::<EpAffine>::new(K); let params = Params::<EpAffine>::new(K);
@ -356,7 +356,7 @@ fn test_opening_proof() {
let p = params.commit(&px, blind).to_affine(); let p = params.commit(&px, blind).to_affine();
let mut transcript = DummyHashWrite::<Vec<u8>, EpAffine>::init(vec![], Field::zero()); let mut transcript = Blake2bWrite::<Vec<u8>, EpAffine>::init(vec![]);
transcript.write_point(p).unwrap(); transcript.write_point(p).unwrap();
let x = ChallengeScalar::<_, ()>::get(&mut transcript); let x = ChallengeScalar::<_, ()>::get(&mut transcript);
// Evaluate the polynomial // Evaluate the polynomial
@ -370,7 +370,7 @@ fn test_opening_proof() {
}; };
// Verify the opening proof // Verify the opening proof
let mut transcript = DummyHashRead::<&[u8], EpAffine>::init(&proof[..], Field::zero()); let mut transcript = Blake2bRead::<&[u8], EpAffine>::init(&proof[..]);
let p_prime = transcript.read_point().unwrap(); let p_prime = transcript.read_point().unwrap();
assert_eq!(p, p_prime); assert_eq!(p, p_prime);
let x_prime = ChallengeScalar::<_, ()>::get(&mut transcript); let x_prime = ChallengeScalar::<_, ()>::get(&mut transcript);

View file

@ -242,7 +242,7 @@ fn test_roundtrip() {
let bvx = eval_polynomial(&bx, x); let bvx = eval_polynomial(&bx, x);
let cvy = eval_polynomial(&cx, y); let cvy = eval_polynomial(&cx, y);
let mut transcript = crate::transcript::DummyHashWrite::init(vec![], Field::one()); let mut transcript = crate::transcript::Blake2bWrite::init(vec![]);
create_proof( create_proof(
&params, &params,
&mut transcript, &mut transcript,
@ -268,7 +268,7 @@ fn test_roundtrip() {
{ {
let mut proof = &proof[..]; let mut proof = &proof[..];
let mut transcript = crate::transcript::DummyHashRead::init(&mut proof, Field::one()); let mut transcript = crate::transcript::Blake2bRead::init(&mut proof);
let msm = params.empty_msm(); let msm = params.empty_msm();
let guard = verify_proof( let guard = verify_proof(
@ -301,7 +301,7 @@ fn test_roundtrip() {
{ {
let mut proof = &proof[..]; let mut proof = &proof[..];
let mut transcript = crate::transcript::DummyHashRead::init(&mut proof, Field::one()); let mut transcript = crate::transcript::Blake2bRead::init(&mut proof);
let msm = params.empty_msm(); let msm = params.empty_msm();
let guard = verify_proof( let guard = verify_proof(

View file

@ -1,7 +1,9 @@
//! This module contains utilities and traits for dealing with Fiat-Shamir //! This module contains utilities and traits for dealing with Fiat-Shamir
//! transcripts. //! transcripts.
use blake2b_simd::{Params as Blake2bParams, State as Blake2bState};
use ff::Field; use ff::Field;
use std::convert::TryInto;
use std::ops::Deref; use std::ops::Deref;
use crate::arithmetic::{CurveAffine, FieldExt}; use crate::arithmetic::{CurveAffine, FieldExt};
@ -39,30 +41,29 @@ pub trait TranscriptWrite<C: CurveAffine>: Transcript<C> {
fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()>; fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()>;
} }
/// This is just a simple (and completely broken) transcript reader /// We will replace BLAKE2b with an algebraic hash function in a later version.
/// implementation, standing in for some algebraic hash function that we'll
/// switch to later.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DummyHashRead<R: Read, C: CurveAffine> { pub struct Blake2bRead<R: Read, C: CurveAffine> {
base_state: C::Base, state: Blake2bState,
scalar_state: C::Scalar,
read_scalar: bool,
reader: R, reader: R,
_marker: PhantomData<C>,
} }
impl<R: Read, C: CurveAffine> DummyHashRead<R, C> { impl<R: Read, C: CurveAffine> Blake2bRead<R, C> {
/// Initialize a transcript given an input buffer and a key. /// Initialize a transcript given an input buffer and a key.
pub fn init(reader: R, key: C::Base) -> Self { pub fn init(reader: R) -> Self {
DummyHashRead { Blake2bRead {
base_state: key + &C::Base::from_u64(1013), state: Blake2bParams::new()
scalar_state: C::Scalar::from_u64(1013), .hash_length(64)
read_scalar: false, .personal(C::BLAKE2B_PERSONALIZATION)
.to_state(),
reader, reader,
_marker: PhantomData,
} }
} }
} }
impl<R: Read, C: CurveAffine> TranscriptRead<C> for DummyHashRead<R, C> { impl<R: Read, C: CurveAffine> TranscriptRead<C> for Blake2bRead<R, C> {
fn read_point(&mut self) -> io::Result<C> { fn read_point(&mut self) -> io::Result<C> {
let mut compressed = [0u8; 32]; let mut compressed = [0u8; 32];
self.reader.read_exact(&mut compressed[..])?; self.reader.read_exact(&mut compressed[..])?;
@ -77,21 +78,19 @@ impl<R: Read, C: CurveAffine> TranscriptRead<C> for DummyHashRead<R, C> {
fn read_scalar(&mut self) -> io::Result<C::Scalar> { fn read_scalar(&mut self) -> io::Result<C::Scalar> {
let mut data = [0u8; 32]; let mut data = [0u8; 32];
self.reader.read_exact(&mut data)?; self.reader.read_exact(&mut data)?;
let scalar = Option::from(C::Scalar::from_bytes(&data)).ok_or_else(|| { let scalar: C::Scalar = Option::from(C::Scalar::from_bytes(&data)).ok_or_else(|| {
io::Error::new( io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
"invalid field element encoding in proof", "invalid field element encoding in proof",
) )
})?; })?;
self.scalar_state += &(scalar * &C::Scalar::ZETA); self.state.update(&scalar.to_bytes());
self.scalar_state = self.scalar_state.square();
self.read_scalar = true;
Ok(scalar) Ok(scalar)
} }
} }
impl<R: Read, C: CurveAffine> Transcript<C> for DummyHashRead<R, C> { impl<R: Read, C: CurveAffine> Transcript<C> for Blake2bRead<R, C> {
fn common_point(&mut self, point: C) -> io::Result<()> { fn common_point(&mut self, point: C) -> io::Result<()> {
let (x, y) = Option::from(point.get_xy()).ok_or_else(|| { let (x, y) = Option::from(point.get_xy()).ok_or_else(|| {
io::Error::new( io::Error::new(
@ -99,53 +98,38 @@ impl<R: Read, C: CurveAffine> Transcript<C> for DummyHashRead<R, C> {
"cannot write points at infinity to the transcript", "cannot write points at infinity to the transcript",
) )
})?; })?;
self.base_state += &(x * &C::Base::ZETA); self.state.update(&x.to_bytes());
self.base_state = self.base_state.square(); self.state.update(&y.to_bytes());
self.base_state += &(y * &C::Base::ZETA);
self.base_state = self.base_state.square();
Ok(()) Ok(())
} }
fn squeeze_challenge(&mut self) -> C::Base { fn squeeze_challenge(&mut self) -> C::Base {
if self.read_scalar { let hasher = self.state.clone();
let x = C::Base::from_bytes(&self.scalar_state.to_bytes()).unwrap(); let result: [u8; 64] = hasher.finalize().as_bytes().try_into().unwrap();
self.base_state += &(x * &C::Base::ZETA); self.state.update(&result[..]);
self.base_state = self.base_state.square(); C::Base::from_bytes_wide(&result)
self.scalar_state = self.scalar_state.square();
self.read_scalar = false;
}
let tmp = self.base_state;
for _ in 0..5 {
self.base_state *= &(C::Base::ZETA + &C::Base::ZETA);
self.base_state += &C::Base::ZETA;
self.base_state = self.base_state.square();
}
tmp
} }
} }
/// This is just a simple (and completely broken) transcript writer /// We will replace BLAKE2b with an algebraic hash function in a later version.
/// implementation, standing in for some algebraic hash function that we'll
/// switch to later.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DummyHashWrite<W: Write, C: CurveAffine> { pub struct Blake2bWrite<W: Write, C: CurveAffine> {
base_state: C::Base, state: Blake2bState,
scalar_state: C::Scalar,
written_scalar: bool,
writer: W, writer: W,
_marker: PhantomData<C>,
} }
impl<W: Write, C: CurveAffine> DummyHashWrite<W, C> { impl<W: Write, C: CurveAffine> Blake2bWrite<W, C> {
/// Initialize a transcript given an output buffer and a key. /// Initialize a transcript given an output buffer and a key.
pub fn init(writer: W, key: C::Base) -> Self { pub fn init(writer: W) -> Self {
DummyHashWrite { Blake2bWrite {
base_state: key + &C::Base::from_u64(1013), state: Blake2bParams::new()
scalar_state: C::Scalar::from_u64(1013), .hash_length(64)
written_scalar: false, .personal(C::BLAKE2B_PERSONALIZATION)
.to_state(),
writer, writer,
_marker: PhantomData,
} }
} }
@ -156,22 +140,20 @@ impl<W: Write, C: CurveAffine> DummyHashWrite<W, C> {
} }
} }
impl<W: Write, C: CurveAffine> TranscriptWrite<C> for DummyHashWrite<W, C> { impl<W: Write, C: CurveAffine> TranscriptWrite<C> for Blake2bWrite<W, C> {
fn write_point(&mut self, point: C) -> io::Result<()> { fn write_point(&mut self, point: C) -> io::Result<()> {
self.common_point(point)?; self.common_point(point)?;
let compressed = point.to_bytes(); let compressed = point.to_bytes();
self.writer.write_all(&compressed[..]) self.writer.write_all(&compressed[..])
} }
fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> { fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {
self.scalar_state += &(scalar * &C::Scalar::ZETA); self.state.update(&scalar.to_bytes());
self.scalar_state = self.scalar_state.square();
self.written_scalar = true;
let data = scalar.to_bytes(); let data = scalar.to_bytes();
self.writer.write_all(&data[..]) self.writer.write_all(&data[..])
} }
} }
impl<W: Write, C: CurveAffine> Transcript<C> for DummyHashWrite<W, C> { impl<W: Write, C: CurveAffine> Transcript<C> for Blake2bWrite<W, C> {
fn common_point(&mut self, point: C) -> io::Result<()> { fn common_point(&mut self, point: C) -> io::Result<()> {
let (x, y) = Option::from(point.get_xy()).ok_or_else(|| { let (x, y) = Option::from(point.get_xy()).ok_or_else(|| {
io::Error::new( io::Error::new(
@ -179,31 +161,17 @@ impl<W: Write, C: CurveAffine> Transcript<C> for DummyHashWrite<W, C> {
"cannot write points at infinity to the transcript", "cannot write points at infinity to the transcript",
) )
})?; })?;
self.base_state += &(x * &C::Base::ZETA); self.state.update(&x.to_bytes());
self.base_state = self.base_state.square(); self.state.update(&y.to_bytes());
self.base_state += &(y * &C::Base::ZETA);
self.base_state = self.base_state.square();
Ok(()) Ok(())
} }
fn squeeze_challenge(&mut self) -> C::Base { fn squeeze_challenge(&mut self) -> C::Base {
if self.written_scalar { let hasher = self.state.clone();
let x = C::Base::from_bytes(&self.scalar_state.to_bytes()).unwrap(); let result: [u8; 64] = hasher.finalize().as_bytes().try_into().unwrap();
self.base_state += &(x * &C::Base::ZETA); self.state.update(&result[..]);
self.base_state = self.base_state.square(); C::Base::from_bytes_wide(&result)
self.scalar_state = self.scalar_state.square();
self.written_scalar = false;
}
let tmp = self.base_state;
for _ in 0..5 {
self.base_state *= &(C::Base::ZETA + &C::Base::ZETA);
self.base_state += &C::Base::ZETA;
self.base_state = self.base_state.square();
}
tmp
} }
} }