halo2/src/poly/commitment.rs

354 lines
10 KiB
Rust
Raw Normal View History

2020-09-07 09:22:25 -07:00
//! This module contains an implementation of the polynomial commitment scheme
//! described in the [Halo][halo] paper.
//!
//! [halo]: https://eprint.iacr.org/2019/1021
use blake2b_simd::{Params as Blake2bParams, State as Blake2bState};
2020-09-12 11:55:48 -07:00
use super::{Coeff, LagrangeCoeff, Polynomial};
use crate::arithmetic::{best_fft, best_multiexp, parallelize, Curve, CurveAffine, FieldExt};
use ff::{Field, PrimeField};
use std::convert::TryInto;
use std::ops::{Add, AddAssign, Mul, MulAssign};
2020-09-07 09:22:25 -07:00
2020-10-13 07:16:20 -07:00
mod msm;
2020-09-07 09:22:25 -07:00
mod prover;
mod verifier;
2020-10-13 07:16:20 -07:00
pub use msm::MSM;
pub use prover::create_proof;
pub use verifier::{verify_proof, Accumulator, Guard};
2020-09-07 09:22:25 -07:00
/// These are the public parameters for the polynomial commitment scheme.
#[derive(Debug)]
pub struct Params<C: CurveAffine> {
pub(crate) k: u32,
pub(crate) n: u64,
pub(crate) g: Vec<C>,
pub(crate) g_lagrange: Vec<C>,
pub(crate) h: C,
pub(crate) u: C,
2020-10-13 07:16:20 -07:00
}
2020-09-07 09:22:25 -07:00
impl<C: CurveAffine> Params<C> {
/// Initializes parameters for the curve, given a random oracle to draw
/// points from.
pub fn new(k: u32) -> Self {
2020-09-07 09:22:25 -07:00
// This is usually a limitation on the curve, but we also want 32-bit
// architectures to be supported.
assert!(k < 32);
// No goofy hardware please.
assert!(core::mem::size_of::<usize>() >= 4);
let n: u64 = 1 << k;
let try_and_increment = |hasher: &Blake2bState| {
let mut trial = 0u64;
loop {
let mut hasher = hasher.clone();
hasher.update(&(trial.to_le_bytes())[..]);
let p = C::from_bytes(&hasher.finalize().as_bytes().try_into().unwrap());
if bool::from(p.is_some()) {
break p.unwrap();
}
trial += 1;
}
};
2020-09-07 09:22:25 -07:00
let g = {
2020-09-07 09:22:25 -07:00
let mut g = Vec::with_capacity(n as usize);
g.resize(n as usize, C::zero());
parallelize(&mut g, move |g, start| {
let mut hasher = Blake2bParams::new()
.hash_length(32)
.personal(C::BLAKE2B_PERSONALIZATION)
.to_state();
hasher.update(b"G vector");
for (i, g) in g.iter_mut().enumerate() {
let i = (i + start) as u64;
let mut hasher = hasher.clone();
hasher.update(&(i.to_le_bytes())[..]);
*g = try_and_increment(&hasher);
2020-09-07 09:22:25 -07:00
}
});
g
};
// Let's evaluate all of the Lagrange basis polynomials
// using an inverse FFT.
let mut alpha_inv = C::Scalar::ROOT_OF_UNITY_INV;
for _ in k..C::Scalar::S {
alpha_inv = alpha_inv.square();
}
let mut g_lagrange_projective = g.iter().map(|g| g.to_projective()).collect::<Vec<_>>();
best_fft(&mut g_lagrange_projective, alpha_inv, k);
let minv = C::Scalar::TWO_INV.pow_vartime(&[k as u64, 0, 0, 0]);
parallelize(&mut g_lagrange_projective, |g, _| {
for g in g.iter_mut() {
*g *= minv;
}
});
let g_lagrange = {
let mut g_lagrange = vec![C::zero(); n as usize];
parallelize(&mut g_lagrange, |g_lagrange, starts| {
C::Projective::batch_to_affine(
&g_lagrange_projective[starts..(starts + g_lagrange.len())],
g_lagrange,
);
});
drop(g_lagrange_projective);
g_lagrange
};
let h = {
let mut hasher = Blake2bParams::new()
.hash_length(32)
.personal(C::BLAKE2B_PERSONALIZATION)
.to_state();
hasher.update(b"H");
try_and_increment(&hasher)
};
let u = {
let mut hasher = Blake2bParams::new()
.hash_length(32)
.personal(C::BLAKE2B_PERSONALIZATION)
.to_state();
hasher.update(b"U");
try_and_increment(&hasher)
2020-09-07 09:22:25 -07:00
};
Params {
k,
n,
g,
g_lagrange,
h,
u,
2020-09-07 09:22:25 -07:00
}
}
/// This computes a commitment to a polynomial described by the provided
/// slice of coefficients. The commitment will be blinded by the blinding
/// factor `r`.
pub fn commit(
&self,
poly: &Polynomial<C::Scalar, Coeff>,
r: Blind<C::Scalar>,
) -> C::Projective {
2020-12-22 04:27:36 -08:00
metrics::increment_counter!("multiexp", "size" => format!("{}", poly.len() + 1), "fn" => "commit");
2020-09-07 09:22:25 -07:00
let mut tmp_scalars = Vec::with_capacity(poly.len() + 1);
let mut tmp_bases = Vec::with_capacity(poly.len() + 1);
tmp_scalars.extend(poly.iter());
tmp_scalars.push(r.0);
tmp_bases.extend(self.g.iter());
tmp_bases.push(self.h);
best_multiexp::<C>(&tmp_scalars, &tmp_bases)
}
/// This commits to a polynomial using its evaluations over the $2^k$ size
/// evaluation domain. The commitment will be blinded by the blinding factor
/// `r`.
pub fn commit_lagrange(
&self,
poly: &Polynomial<C::Scalar, LagrangeCoeff>,
r: Blind<C::Scalar>,
) -> C::Projective {
2020-12-22 04:27:36 -08:00
metrics::increment_counter!("multiexp", "size" => format!("{}", poly.len() + 1), "fn" => "commit_lagrange");
2020-09-07 09:22:25 -07:00
let mut tmp_scalars = Vec::with_capacity(poly.len() + 1);
let mut tmp_bases = Vec::with_capacity(poly.len() + 1);
tmp_scalars.extend(poly.iter());
tmp_scalars.push(r.0);
tmp_bases.extend(self.g_lagrange.iter());
tmp_bases.push(self.h);
best_multiexp::<C>(&tmp_scalars, &tmp_bases)
}
2020-09-12 11:55:48 -07:00
/// Generates an empty multiscalar multiplication struct using the
/// appropriate params.
pub fn empty_msm(&self) -> MSM<C> {
2020-10-13 07:16:20 -07:00
MSM::new(self)
2020-09-12 11:55:48 -07:00
}
/// Getter for g generators
pub fn get_g(&self) -> Vec<C> {
self.g.clone()
}
2020-09-07 09:22:25 -07:00
}
/// Wrapper type around a blinding factor.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct Blind<F>(pub F);
impl<F: FieldExt> Default for Blind<F> {
2020-09-07 09:22:25 -07:00
fn default() -> Self {
Blind(F::one())
}
}
impl<F: FieldExt> Add for Blind<F> {
2020-09-07 09:22:25 -07:00
type Output = Self;
fn add(self, rhs: Blind<F>) -> Self {
Blind(self.0 + rhs.0)
}
}
impl<F: FieldExt> Mul for Blind<F> {
2020-09-07 09:22:25 -07:00
type Output = Self;
fn mul(self, rhs: Blind<F>) -> Self {
Blind(self.0 * rhs.0)
}
}
impl<F: FieldExt> AddAssign for Blind<F> {
2020-09-07 09:22:25 -07:00
fn add_assign(&mut self, rhs: Blind<F>) {
self.0 += rhs.0;
}
}
impl<F: FieldExt> MulAssign for Blind<F> {
2020-09-07 09:22:25 -07:00
fn mul_assign(&mut self, rhs: Blind<F>) {
self.0 *= rhs.0;
}
}
impl<F: FieldExt> AddAssign<F> for Blind<F> {
2020-09-07 09:22:25 -07:00
fn add_assign(&mut self, rhs: F) {
self.0 += rhs;
}
}
impl<F: FieldExt> MulAssign<F> for Blind<F> {
2020-09-07 09:22:25 -07:00
fn mul_assign(&mut self, rhs: F) {
self.0 *= rhs;
}
}
#[test]
fn test_commit_lagrange_epaffine() {
2020-09-07 09:22:25 -07:00
const K: u32 = 6;
use crate::pasta::{EpAffine, Fq};
let params = Params::<EpAffine>::new(K);
2020-09-07 09:22:25 -07:00
let domain = super::EvaluationDomain::new(1, K);
let mut a = domain.empty_lagrange();
for (i, a) in a.iter_mut().enumerate() {
*a = Fq::from(i as u64);
}
let b = domain.lagrange_to_coeff(a.clone());
let alpha = Blind(Fq::rand());
2020-09-07 09:22:25 -07:00
assert_eq!(params.commit(&b, alpha), params.commit_lagrange(&a, alpha));
}
#[test]
fn test_commit_lagrange_eqaffine() {
const K: u32 = 6;
use crate::pasta::{EqAffine, Fp};
let params = Params::<EqAffine>::new(K);
let domain = super::EvaluationDomain::new(1, K);
let mut a = domain.empty_lagrange();
for (i, a) in a.iter_mut().enumerate() {
*a = Fp::from(i as u64);
}
let b = domain.lagrange_to_coeff(a.clone());
let alpha = Blind(Fp::rand());
assert_eq!(params.commit(&b, alpha), params.commit_lagrange(&a, alpha));
}
2020-09-07 09:22:25 -07:00
#[test]
fn test_opening_proof() {
const K: u32 = 6;
use ff::Field;
2020-09-07 09:22:25 -07:00
use super::{
commitment::{Blind, Params},
EvaluationDomain,
};
use crate::arithmetic::{eval_polynomial, Curve, FieldExt};
use crate::pasta::{EpAffine, Fq};
use crate::transcript::{
ChallengeScalar, DummyHashRead, DummyHashWrite, Transcript, TranscriptRead, TranscriptWrite,
};
2020-09-07 09:22:25 -07:00
let params = Params::<EpAffine>::new(K);
2020-09-07 09:22:25 -07:00
let domain = EvaluationDomain::new(1, K);
let mut px = domain.empty_coeff();
for (i, a) in px.iter_mut().enumerate() {
*a = Fq::from(i as u64);
}
let blind = Blind(Fq::rand());
2020-09-07 09:22:25 -07:00
let p = params.commit(&px, blind).to_affine();
let mut transcript = DummyHashWrite::<Vec<u8>, EpAffine>::init(vec![], Field::zero());
transcript.write_point(p).unwrap();
let x = ChallengeScalar::<_, ()>::get(&mut transcript);
2020-09-07 09:22:25 -07:00
// Evaluate the polynomial
let v = eval_polynomial(&px, *x);
transcript.write_scalar(v).unwrap();
2020-09-07 09:22:25 -07:00
let (proof, ch_prover) = {
create_proof(&params, &mut transcript, &px, blind, *x).unwrap();
let ch_prover = transcript.squeeze_challenge();
(transcript.finalize(), ch_prover)
};
// Verify the opening proof
let mut transcript = DummyHashRead::<&[u8], EpAffine>::init(&proof[..], Field::zero());
let p_prime = transcript.read_point().unwrap();
assert_eq!(p, p_prime);
let x_prime = ChallengeScalar::<_, ()>::get(&mut transcript);
assert_eq!(*x, *x_prime);
let v_prime = transcript.read_scalar().unwrap();
assert_eq!(v, v_prime);
let mut commitment_msm = params.empty_msm();
commitment_msm.append_term(Field::one(), p);
let guard = verify_proof(&params, commitment_msm, &mut transcript, *x, v).unwrap();
let ch_verifier = transcript.squeeze_challenge();
assert_eq!(ch_prover, ch_verifier);
// Test guard behavior prior to checking another proof
{
// Test use_challenges()
let msm_challenges = guard.clone().use_challenges();
assert!(msm_challenges.eval());
// Test use_g()
let g = guard.compute_g();
let (msm_g, _accumulator) = guard.clone().use_g(g);
assert!(msm_g.eval());
2020-09-07 09:22:25 -07:00
}
}