halo2/halo2_backend/src/plonk.rs

406 lines
14 KiB
Rust

use blake2b_simd::Params as Blake2bParams;
use group::ff::{Field, FromUniformBytes, PrimeField};
use crate::arithmetic::CurveAffine;
use crate::helpers::{
polynomial_slice_byte_length, read_polynomial_vec, write_polynomial_slice, SerdeCurveAffine,
SerdePrimeField,
};
use crate::plonk::circuit::{ConstraintSystemBack, PinnedConstraintSystem};
use crate::poly::{
Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, PinnedEvaluationDomain,
Polynomial,
};
use crate::transcript::{ChallengeScalar, EncodedChallenge, Transcript};
pub(crate) use evaluation::Evaluator;
use halo2_common::SerdeFormat;
use std::io;
mod circuit;
mod error;
mod evaluation;
pub mod keygen;
mod lookup;
mod permutation;
pub mod prover;
mod shuffle;
mod vanishing;
pub mod verifier;
pub use error::*;
/// This is a verifying key which allows for the verification of proofs for a
/// particular circuit.
#[derive(Clone, Debug)]
pub struct VerifyingKey<C: CurveAffine> {
/// Evaluation domain
domain: EvaluationDomain<C::Scalar>,
/// Commitments to fixed columns
fixed_commitments: Vec<C>,
/// Permutation verifying key
permutation: permutation::VerifyingKey<C>,
/// Constraint system
cs: ConstraintSystemBack<C::Scalar>,
/// Cached maximum degree of `cs` (which doesn't change after construction).
cs_degree: usize,
/// The representative of this `VerifyingKey` in transcripts.
transcript_repr: C::Scalar,
/// Legacy field that indicates wether the circuit was compiled with compressed selectors or
/// not using the legacy API.
pub compress_selectors: Option<bool>,
}
// Current version of the VK
const VERSION: u8 = 0x04;
impl<C: SerdeCurveAffine> VerifyingKey<C>
where
C::Scalar: SerdePrimeField + FromUniformBytes<64>,
{
/// Writes a verifying key to a buffer.
///
/// Writes a curve element according to `format`:
/// - `Processed`: Writes a compressed curve element with coordinates in standard form.
/// Writes a field element in standard form, with endianness specified by the
/// `PrimeField` implementation.
/// - Otherwise: Writes an uncompressed curve element with coordinates in Montgomery form
/// Writes a field element into raw bytes in its internal Montgomery representation,
/// WITHOUT performing the expensive Montgomery reduction.
pub fn write<W: io::Write>(&self, writer: &mut W, format: SerdeFormat) -> io::Result<()> {
// Version byte that will be checked on read.
writer.write_all(&[VERSION])?;
let k = &self.domain.k();
assert!(*k <= C::Scalar::S);
// k value fits in 1 byte
writer.write_all(&[*k as u8])?;
writer.write_all(&(self.fixed_commitments.len() as u32).to_le_bytes())?;
for commitment in &self.fixed_commitments {
commitment.write(writer, format)?;
}
self.permutation.write(writer, format)?;
Ok(())
}
/// Reads a verification key from a buffer.
///
/// Reads a curve element from the buffer and parses it according to the `format`:
/// - `Processed`: Reads a compressed curve element and decompresses it.
/// Reads a field element in standard form, with endianness specified by the
/// `PrimeField` implementation, and checks that the element is less than the modulus.
/// - `RawBytes`: Reads an uncompressed curve element with coordinates in Montgomery form.
/// Checks that field elements are less than modulus, and then checks that the point is on the curve.
/// - `RawBytesUnchecked`: Reads an uncompressed curve element with coordinates in Montgomery form;
/// does not perform any checks
pub fn read<R: io::Read>(
reader: &mut R,
format: SerdeFormat,
cs: ConstraintSystemBack<C::Scalar>,
) -> io::Result<Self> {
let mut version_byte = [0u8; 1];
reader.read_exact(&mut version_byte)?;
if VERSION != version_byte[0] {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"unexpected version byte",
));
}
let mut k = [0u8; 1];
reader.read_exact(&mut k)?;
let k = u8::from_le_bytes(k);
if k as u32 > C::Scalar::S {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"circuit size value (k): {} exceeds maxium: {}",
k,
C::Scalar::S
),
));
}
let domain = keygen::create_domain::<C>(&cs, k as u32);
let mut num_fixed_columns = [0u8; 4];
reader.read_exact(&mut num_fixed_columns)?;
let num_fixed_columns = u32::from_le_bytes(num_fixed_columns);
let fixed_commitments: Vec<_> = (0..num_fixed_columns)
.map(|_| C::read(reader, format))
.collect::<Result<_, _>>()?;
let permutation = permutation::VerifyingKey::read(reader, &cs.permutation, format)?;
Ok(Self::from_parts(domain, fixed_commitments, permutation, cs))
}
/// Writes a verifying key to a vector of bytes using [`Self::write`].
pub fn to_bytes(&self, format: SerdeFormat) -> Vec<u8> {
let mut bytes = Vec::<u8>::with_capacity(self.bytes_length(format));
Self::write(self, &mut bytes, format).expect("Writing to vector should not fail");
bytes
}
/// Reads a verification key from a slice of bytes using [`Self::read`].
pub fn from_bytes(
mut bytes: &[u8],
format: SerdeFormat,
cs: ConstraintSystemBack<C::Scalar>,
) -> io::Result<Self> {
Self::read(&mut bytes, format, cs)
}
}
impl<C: CurveAffine> VerifyingKey<C> {
fn bytes_length(&self, format: SerdeFormat) -> usize
where
C: SerdeCurveAffine,
{
10 + (self.fixed_commitments.len() * C::byte_length(format))
+ self.permutation.bytes_length(format)
}
fn from_parts(
domain: EvaluationDomain<C::Scalar>,
fixed_commitments: Vec<C>,
permutation: permutation::VerifyingKey<C>,
cs: ConstraintSystemBack<C::Scalar>,
) -> Self
where
C::ScalarExt: FromUniformBytes<64>,
{
// Compute cached values.
let cs_degree = cs.degree();
let mut vk = Self {
domain,
fixed_commitments,
permutation,
cs,
cs_degree,
// Temporary, this is not pinned.
transcript_repr: C::Scalar::ZERO,
compress_selectors: None,
};
let mut hasher = Blake2bParams::new()
.hash_length(64)
.personal(b"Halo2-Verify-Key")
.to_state();
let s = format!("{:?}", vk.pinned());
hasher.update(&(s.len() as u64).to_le_bytes());
hasher.update(s.as_bytes());
// Hash in final Blake2bState
vk.transcript_repr = C::Scalar::from_uniform_bytes(hasher.finalize().as_array());
vk
}
/// Hashes a verification key into a transcript.
pub fn hash_into<E: EncodedChallenge<C>, T: Transcript<C, E>>(
&self,
transcript: &mut T,
) -> io::Result<()> {
transcript.common_scalar(self.transcript_repr)?;
Ok(())
}
/// Obtains a pinned representation of this verification key that contains
/// the minimal information necessary to reconstruct the verification key.
pub fn pinned(&self) -> PinnedVerificationKey<'_, C> {
PinnedVerificationKey {
base_modulus: C::Base::MODULUS,
scalar_modulus: C::Scalar::MODULUS,
domain: self.domain.pinned(),
fixed_commitments: &self.fixed_commitments,
permutation: &self.permutation,
cs: self.cs.pinned(),
}
}
/// Returns commitments of fixed polynomials
pub fn fixed_commitments(&self) -> &Vec<C> {
&self.fixed_commitments
}
/// Returns `VerifyingKey` of permutation
pub fn permutation(&self) -> &permutation::VerifyingKey<C> {
&self.permutation
}
/// Returns `ConstraintSystem`
pub fn cs(&self) -> &ConstraintSystemBack<C::Scalar> {
&self.cs
}
/// Returns representative of this `VerifyingKey` in transcripts
pub fn transcript_repr(&self) -> C::Scalar {
self.transcript_repr
}
}
/// Minimal representation of a verification key that can be used to identify
/// its active contents.
#[allow(dead_code)]
#[derive(Debug)]
pub struct PinnedVerificationKey<'a, C: CurveAffine> {
base_modulus: &'static str,
scalar_modulus: &'static str,
domain: PinnedEvaluationDomain<'a, C::Scalar>,
cs: PinnedConstraintSystem<'a, C::Scalar>,
fixed_commitments: &'a Vec<C>,
permutation: &'a permutation::VerifyingKey<C>,
}
/// This is a proving key which allows for the creation of proofs for a
/// particular circuit.
#[derive(Clone, Debug)]
pub struct ProvingKey<C: CurveAffine> {
vk: VerifyingKey<C>,
l0: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
l_last: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
l_active_row: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
fixed_values: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
fixed_polys: Vec<Polynomial<C::Scalar, Coeff>>,
fixed_cosets: Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
permutation: permutation::ProvingKey<C>,
ev: Evaluator<C>,
}
impl<C: CurveAffine> ProvingKey<C>
where
C::Scalar: FromUniformBytes<64>,
{
/// Get the underlying [`VerifyingKey`].
pub fn get_vk(&self) -> &VerifyingKey<C> {
&self.vk
}
/// Gets the total number of bytes in the serialization of `self`
fn bytes_length(&self, format: SerdeFormat) -> usize
where
C: SerdeCurveAffine,
{
let scalar_len = C::Scalar::default().to_repr().as_ref().len();
self.vk.bytes_length(format)
+ 12
+ scalar_len * (self.l0.len() + self.l_last.len() + self.l_active_row.len())
+ polynomial_slice_byte_length(&self.fixed_values)
+ polynomial_slice_byte_length(&self.fixed_polys)
+ polynomial_slice_byte_length(&self.fixed_cosets)
+ self.permutation.bytes_length()
}
}
impl<C: SerdeCurveAffine> ProvingKey<C>
where
C::Scalar: SerdePrimeField + FromUniformBytes<64>,
{
/// Writes a proving key to a buffer.
///
/// Writes a curve element according to `format`:
/// - `Processed`: Writes a compressed curve element with coordinates in standard form.
/// Writes a field element in standard form, with endianness specified by the
/// `PrimeField` implementation.
/// - Otherwise: Writes an uncompressed curve element with coordinates in Montgomery form
/// Writes a field element into raw bytes in its internal Montgomery representation,
/// WITHOUT performing the expensive Montgomery reduction.
/// Does so by first writing the verifying key and then serializing the rest of the data (in the form of field polynomials)
pub fn write<W: io::Write>(&self, writer: &mut W, format: SerdeFormat) -> io::Result<()> {
self.vk.write(writer, format)?;
self.l0.write(writer, format)?;
self.l_last.write(writer, format)?;
self.l_active_row.write(writer, format)?;
write_polynomial_slice(&self.fixed_values, writer, format)?;
write_polynomial_slice(&self.fixed_polys, writer, format)?;
write_polynomial_slice(&self.fixed_cosets, writer, format)?;
self.permutation.write(writer, format)?;
Ok(())
}
/// Reads a proving key from a buffer.
/// Does so by reading verification key first, and then deserializing the rest of the file into the remaining proving key data.
///
/// Reads a curve element from the buffer and parses it according to the `format`:
/// - `Processed`: Reads a compressed curve element and decompresses it.
/// Reads a field element in standard form, with endianness specified by the
/// `PrimeField` implementation, and checks that the element is less than the modulus.
/// - `RawBytes`: Reads an uncompressed curve element with coordinates in Montgomery form.
/// Checks that field elements are less than modulus, and then checks that the point is on the curve.
/// - `RawBytesUnchecked`: Reads an uncompressed curve element with coordinates in Montgomery form;
/// does not perform any checks
pub fn read<R: io::Read>(
reader: &mut R,
format: SerdeFormat,
cs: ConstraintSystemBack<C::Scalar>,
) -> io::Result<Self> {
let vk = VerifyingKey::<C>::read::<R>(reader, format, cs)?;
let l0 = Polynomial::read(reader, format)?;
let l_last = Polynomial::read(reader, format)?;
let l_active_row = Polynomial::read(reader, format)?;
let fixed_values = read_polynomial_vec(reader, format)?;
let fixed_polys = read_polynomial_vec(reader, format)?;
let fixed_cosets = read_polynomial_vec(reader, format)?;
let permutation = permutation::ProvingKey::read(reader, format)?;
let ev = Evaluator::new(vk.cs());
Ok(Self {
vk,
l0,
l_last,
l_active_row,
fixed_values,
fixed_polys,
fixed_cosets,
permutation,
ev,
})
}
/// Writes a proving key to a vector of bytes using [`Self::write`].
pub fn to_bytes(&self, format: SerdeFormat) -> Vec<u8> {
let mut bytes = Vec::<u8>::with_capacity(self.bytes_length(format));
Self::write(self, &mut bytes, format).expect("Writing to vector should not fail");
bytes
}
/// Reads a proving key from a slice of bytes using [`Self::read`].
pub fn from_bytes(
mut bytes: &[u8],
format: SerdeFormat,
cs: ConstraintSystemBack<C::Scalar>,
) -> io::Result<Self> {
Self::read(&mut bytes, format, cs)
}
}
impl<C: CurveAffine> VerifyingKey<C> {
/// Get the underlying [`EvaluationDomain`].
pub fn get_domain(&self) -> &EvaluationDomain<C::Scalar> {
&self.domain
}
}
#[derive(Clone, Copy, Debug)]
pub struct Theta;
pub type ChallengeTheta<F> = ChallengeScalar<F, Theta>;
#[derive(Clone, Copy, Debug)]
pub struct Beta;
pub type ChallengeBeta<F> = ChallengeScalar<F, Beta>;
#[derive(Clone, Copy, Debug)]
pub struct Gamma;
pub type ChallengeGamma<F> = ChallengeScalar<F, Gamma>;
#[derive(Clone, Copy, Debug)]
pub struct Y;
pub type ChallengeY<F> = ChallengeScalar<F, Y>;
#[derive(Clone, Copy, Debug)]
pub struct X;
pub type ChallengeX<F> = ChallengeScalar<F, X>;