From c4fda0957bbc0a3f78fd5c89873c3918b505ae0c Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Sun, 2 Apr 2023 16:24:41 +0200 Subject: [PATCH] feat: add `compress` to compiler This commit introduces `Compiler::compress` and `Compiler::decompress`, two functions that will create a compressed representation of a circuit that can be used to generate prover and verifier keys. The compression strategy takes advantage of the fact that circuit representations are sparse; meaning, most of the scalars are zeroes. We also have a higher incidence of `1` and `-1`. This will result in expressive gains in terms of storage. For instance, a `2^16` circuit can be compressed into roughly 250Kb. --- CHANGELOG.md | 1 + Cargo.toml | 7 +- benches/plonk.rs | 8 +- src/composer.rs | 4 +- src/composer/compiler.rs | 76 ++++- src/composer/compiler/compress.rs | 375 ++++++++++++++++++++++++ src/composer/compiler/compress/hades.rs | 62 ++++ src/composer/prover.rs | 20 +- src/composer/verifier.rs | 7 +- src/constraint_system/constraint.rs | 27 +- src/constraint_system/witness.rs | 6 + src/error.rs | 3 + tests/common/mod.rs | 12 +- tests/composer.rs | 22 +- 14 files changed, 569 insertions(+), 61 deletions(-) create mode 100644 src/composer/compiler/compress.rs create mode 100644 src/composer/compiler/compress/hades.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7724be5f..786f27c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add and restructure tests for boolean and select components [#731] - Add tests for `gate_add` and `gate_mul` [#736] - Add and restructure tests for `component_decomposition` [#738] +- Add `Compile::compress` and `Compile::decompress` [#752] ### Removed diff --git a/Cargo.toml b/Cargo.toml index b85ff0e5..9c835fdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,10 @@ dusk-bls12_381 = {version = "0.11", default-features = false, features = ["group dusk-jubjub = {version = "0.12", default-features = false} itertools = {version = "0.9", default-features = false} hashbrown = {version = "0.9", default-features=false, features = ["ahash"]} +msgpacker = {version = "0.4", default-features=false, features = ["alloc", "derive"], optional=true} +miniz_oxide = {version = "0.7", default-features=false, features = ["with-alloc"], optional = true} rayon = {version = "1.3", optional = true} +sha2 = {version = "0.10", default-features = false, optional = true} cfg-if = "1.0" # Dusk related deps for WASMI serde canonical = {version = "0.7", optional = true} @@ -52,10 +55,12 @@ std = [ "dusk-jubjub/default", "itertools/default", "hashbrown/default", + "msgpacker/std", + "miniz_oxide/std", "alloc", "rayon" ] -alloc = ["dusk-bls12_381/alloc"] +alloc = ["dusk-bls12_381/alloc", "msgpacker", "miniz_oxide", "sha2"] debug = ["dusk-cdf", "backtrace"] canon = ["dusk-bls12_381/canon", "dusk-jubjub/canon", "canonical", "canonical_derive"] rkyv-impl = ["dusk-bls12_381/rkyv-impl", "dusk-jubjub/rkyv-impl", "rkyv", "bytecheck"] diff --git a/benches/plonk.rs b/benches/plonk.rs index c0826feb..6affff9a 100644 --- a/benches/plonk.rs +++ b/benches/plonk.rs @@ -90,9 +90,11 @@ fn run( Compiler::compile::>(&pp, label) .expect("failed to compile circuit"); + let circuit: BenchCircuit = BenchCircuit::default(); + // sanity run let (proof, public_inputs) = prover - .prove(&mut rand_core::OsRng, &Default::default()) + .prove(&mut rand_core::OsRng, &circuit) .expect("failed to prove"); verifier @@ -103,9 +105,7 @@ fn run( let description = format!("Prove 2^{} = {} gates", power, DEGREE); c.bench_function(description.as_str(), |b| { - b.iter(|| { - black_box(prover.prove(&mut rand_core::OsRng, &Default::default())) - }) + b.iter(|| black_box(prover.prove(&mut rand_core::OsRng, &circuit))) }); let description = format!("Verify 2^{} = {} gates", power, DEGREE); diff --git a/src/composer.rs b/src/composer.rs index 60a7751b..461cf894 100644 --- a/src/composer.rs +++ b/src/composer.rs @@ -42,13 +42,13 @@ pub trait Composer: Sized + Index { /// /// A turbo composer expects the first witness to be always present and to /// be zero. - const ZERO: Witness = Witness::new(0); + const ZERO: Witness = Witness::ZERO; /// `One` representation inside the constraint system. /// /// A turbo composer expects the 2nd witness to be always present and to /// be one. - const ONE: Witness = Witness::new(1); + const ONE: Witness = Witness::ONE; /// Identity point representation inside the constraint system const IDENTITY: WitnessPoint = WitnessPoint::new(Self::ZERO, Self::ONE); diff --git a/src/composer/compiler.rs b/src/composer/compiler.rs index 2208ad42..4d02593c 100644 --- a/src/composer/compiler.rs +++ b/src/composer/compiler.rs @@ -4,15 +4,22 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + use dusk_bls12_381::BlsScalar; use crate::commitment_scheme::{CommitKey, OpeningKey, PublicParameters}; +use crate::constraint_system::{Constraint, Selector, Witness}; use crate::error::Error; use crate::fft::{EvaluationDomain, Evaluations, Polynomial as FftPolynomial}; use crate::proof_system::preprocess::Polynomials; use crate::proof_system::{widget, ProverKey}; -use super::{Builder, Circuit, Composer, Prover, Verifier}; +use super::{Builder, Circuit, Composer, Polynomial, Prover, Verifier}; + +#[cfg(feature = "alloc")] +mod compress; /// Generate the arguments to prove and verify a circuit pub struct Compiler; @@ -24,11 +31,16 @@ impl Compiler { pub fn compile( pp: &PublicParameters, label: &[u8], - ) -> Result<(Prover, Verifier), Error> + ) -> Result<(Prover, Verifier), Error> where C: Circuit, { - Self::compile_with_circuit(pp, label, &Default::default()) + let max_size = Self::max_size(pp); + let mut builder = Builder::initialized(max_size); + + C::default().circuit(&mut builder)?; + + Self::compile_with_builder(pp, label, &builder) } /// Create a new arguments set from a given circuit instance @@ -38,34 +50,70 @@ impl Compiler { pp: &PublicParameters, label: &[u8], circuit: &C, - ) -> Result<(Prover, Verifier), Error> + ) -> Result<(Prover, Verifier), Error> where C: Circuit, { - let max_size = (pp.commit_key.powers_of_g.len() - 1) >> 1; - let mut prover = Builder::initialized(max_size); + let max_size = Self::max_size(pp); + let mut builder = Builder::initialized(max_size); - circuit.circuit(&mut prover)?; + circuit.circuit(&mut builder)?; - let n = (prover.constraints() + 6).next_power_of_two(); + Self::compile_with_builder(pp, label, &builder) + } + + /// Return a bytes representation of a compressed circuit, capable of + /// generating its prover and verifier instances. + #[cfg(feature = "alloc")] + pub fn compress(pp: &PublicParameters) -> Result, Error> + where + C: Circuit, + { + compress::CompressedCircuit::from_circuit::( + pp, + compress::Version::V2, + ) + } + + /// Generates a [Prover] and [Verifier] from a buffer created by + /// [Self::compress]. + pub fn decompress( + pp: &PublicParameters, + label: &[u8], + compressed: &[u8], + ) -> Result<(Prover, Verifier), Error> { + compress::CompressedCircuit::from_bytes(pp, label, compressed) + } + + /// Returns the maximum constraints length for the parameters. + fn max_size(pp: &PublicParameters) -> usize { + (pp.commit_key.powers_of_g.len() - 1) >> 1 + } + + /// Create a new arguments set from a given circuit instance + /// + /// Use the default implementation of the circuit + fn compile_with_builder( + pp: &PublicParameters, + label: &[u8], + builder: &Builder, + ) -> Result<(Prover, Verifier), Error> { + let n = (builder.constraints() + 6).next_power_of_two(); let (commit, opening) = pp.trim(n)?; let (prover, verifier) = - Self::preprocess(label, commit, opening, &prover)?; + Self::preprocess(label, commit, opening, &builder)?; Ok((prover, verifier)) } - fn preprocess( + fn preprocess( label: &[u8], commit_key: CommitKey, opening_key: OpeningKey, prover: &Builder, - ) -> Result<(Prover, Verifier), Error> - where - C: Circuit, - { + ) -> Result<(Prover, Verifier), Error> { let mut perm = prover.perm.clone(); let constraints = prover.constraints(); diff --git a/src/composer/compiler/compress.rs b/src/composer/compiler/compress.rs new file mode 100644 index 00000000..f9c9cf06 --- /dev/null +++ b/src/composer/compiler/compress.rs @@ -0,0 +1,375 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +use dusk_bytes::Serializable; +use hashbrown::HashMap; +use msgpacker::{MsgPacker, Packable, Unpackable}; + +use alloc::vec::Vec; + +use super::{ + BlsScalar, Builder, Circuit, Compiler, Composer, Constraint, Error, + Polynomial, Prover, PublicParameters, Selector, Verifier, Witness, +}; + +mod hades; + +#[derive( + Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, MsgPacker, +)] +pub struct CompressedConstraint { + pub polynomial: usize, + pub w_a: usize, + pub w_b: usize, + pub w_d: usize, + pub w_o: usize, +} + +#[derive( + Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, MsgPacker, +)] +pub struct CompressedPolynomial { + pub q_m: usize, + pub q_l: usize, + pub q_r: usize, + pub q_o: usize, + pub q_c: usize, + pub q_d: usize, + pub q_arith: usize, + pub q_range: usize, + pub q_logic: usize, + pub q_fixed_group_add: usize, + pub q_variable_group_add: usize, +} + +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, MsgPacker, +)] +pub enum Version { + V1, + V2, +} + +impl Version { + pub fn into_scalars(self) -> HashMap { + match self { + Version::V1 => { + [BlsScalar::zero(), BlsScalar::one(), -BlsScalar::one()] + .into_iter() + .enumerate() + .map(|(i, s)| (s, i)) + .collect() + } + Version::V2 => { + let mut scalars = Self::V1.into_scalars(); + // assert we don't override a previously inserted constant + for s in hades::constants() { + let len = scalars.len(); + scalars.entry(s).or_insert(len); + } + for r in hades::mds() { + for s in r { + let len = scalars.len(); + scalars.entry(s).or_insert(len); + } + } + scalars + } + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, MsgPacker)] +pub struct CompressedCircuit { + version: Version, + public_inputs: Vec, + witnesses: usize, + scalars: Vec<[u8; BlsScalar::SIZE]>, + polynomials: Vec, + circuit: Vec, +} + +impl CompressedCircuit { + pub fn from_circuit( + pp: &PublicParameters, + version: Version, + ) -> Result, Error> + where + C: Circuit, + { + let max_size = Compiler::max_size(pp); + let mut builder = Builder::initialized(max_size); + C::default().circuit(&mut builder)?; + Ok(Self::from_builder(version, builder)) + } + + pub fn from_builder(version: Version, builder: Builder) -> Vec { + let mut public_inputs: Vec<_> = + builder.public_inputs.keys().copied().collect(); + public_inputs.sort(); + + let witnesses = builder.witnesses.len(); + let polynomials = builder.constraints; + + let circuit = polynomials.into_iter(); + let mut scalars = version.into_scalars(); + let base_scalars_len = scalars.len(); + let mut polynomials = HashMap::new(); + let circuit = circuit + .map( + |Polynomial { + q_m, + q_l, + q_r, + q_o, + q_c, + q_d, + q_arith, + q_range, + q_logic, + q_fixed_group_add, + q_variable_group_add, + w_a, + w_b, + w_d, + w_o, + }| { + let len = scalars.len(); + let q_m = *scalars.entry(q_m).or_insert(len); + let len = scalars.len(); + let q_l = *scalars.entry(q_l).or_insert(len); + let len = scalars.len(); + let q_r = *scalars.entry(q_r).or_insert(len); + let len = scalars.len(); + let q_o = *scalars.entry(q_o).or_insert(len); + let len = scalars.len(); + let q_c = *scalars.entry(q_c).or_insert(len); + let len = scalars.len(); + let q_d = *scalars.entry(q_d).or_insert(len); + let len = scalars.len(); + let q_arith = *scalars.entry(q_arith).or_insert(len); + let len = scalars.len(); + let q_range = *scalars.entry(q_range).or_insert(len); + let len = scalars.len(); + let q_logic = *scalars.entry(q_logic).or_insert(len); + let len = scalars.len(); + let q_fixed_group_add = + *scalars.entry(q_fixed_group_add).or_insert(len); + let len = scalars.len(); + let q_variable_group_add = + *scalars.entry(q_variable_group_add).or_insert(len); + let polynomial = CompressedPolynomial { + q_m, + q_l, + q_r, + q_o, + q_c, + q_d, + q_arith, + q_range, + q_logic, + q_fixed_group_add, + q_variable_group_add, + }; + + let len = polynomials.len(); + let polynomial = + *polynomials.entry(polynomial).or_insert(len); + + CompressedConstraint { + polynomial, + w_a: w_a.index(), + w_b: w_b.index(), + w_d: w_d.index(), + w_o: w_o.index(), + } + }, + ) + .collect(); + + let scalars_map = scalars; + let mut scalars = vec![[0u8; BlsScalar::SIZE]; scalars_map.len()]; + scalars_map + .into_iter() + .for_each(|(s, i)| scalars[i] = s.to_bytes()); + + // clear the scalars that can be determiniscally reconstructed from the + // version + let scalars = scalars.split_off(base_scalars_len); + + let polynomials_map = polynomials; + let mut polynomials = + vec![CompressedPolynomial::default(); polynomials_map.len()]; + polynomials_map + .into_iter() + .for_each(|(p, i)| polynomials[i] = p); + + let compressed = Self { + version, + public_inputs, + witnesses, + scalars, + polynomials, + circuit, + }; + let mut buf = Vec::with_capacity( + 1 + compressed.scalars.len() * BlsScalar::SIZE + + compressed.polynomials.len() * 88 + + compressed.circuit.len() * 40, + ); + compressed.pack(&mut buf); + miniz_oxide::deflate::compress_to_vec(&buf, 10) + } + + pub fn from_bytes( + pp: &PublicParameters, + label: &[u8], + compressed: &[u8], + ) -> Result<(Prover, Verifier), Error> { + let compressed = miniz_oxide::inflate::decompress_to_vec(compressed) + .map_err(|_| Error::InvalidCompressedCircuit)?; + let ( + _, + Self { + version, + public_inputs, + witnesses, + scalars, + polynomials, + circuit, + }, + ) = Self::unpack(&compressed) + .map_err(|_| Error::InvalidCompressedCircuit)?; + + let version_scalars_map = version.into_scalars(); + let mut version_scalars = + vec![BlsScalar::zero(); version_scalars_map.len()]; + version_scalars_map + .into_iter() + .for_each(|(s, i)| version_scalars[i] = s); + for s in scalars { + version_scalars.push(BlsScalar::from_bytes(&s)?); + } + let scalars = version_scalars; + + #[allow(deprecated)] + // we use `uninitialized` because the decompressor will also contain the + // dummy constraints, if they were part of the prover when encoding. + let mut builder = Builder::uninitialized(circuit.len()); + + let mut pi = 0; + (0..witnesses).for_each(|_| { + builder.append_witness(BlsScalar::zero()); + }); + + for ( + i, + CompressedConstraint { + polynomial, + w_a, + w_b, + w_d, + w_o, + }, + ) in circuit.into_iter().enumerate() + { + let CompressedPolynomial { + q_m, + q_l, + q_r, + q_o, + q_c, + q_d, + q_arith, + q_range, + q_logic, + q_fixed_group_add, + q_variable_group_add, + } = polynomials + .get(polynomial) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + + let q_m = scalars + .get(q_m) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + let q_l = scalars + .get(q_l) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + let q_r = scalars + .get(q_r) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + let q_o = scalars + .get(q_o) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + let q_c = scalars + .get(q_c) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + let q_d = scalars + .get(q_d) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + let q_arith = scalars + .get(q_arith) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + let q_range = scalars + .get(q_range) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + let q_logic = scalars + .get(q_logic) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + let q_fixed_group_add = scalars + .get(q_fixed_group_add) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + let q_variable_group_add = scalars + .get(q_variable_group_add) + .copied() + .ok_or(Error::InvalidCompressedCircuit)?; + + let w_a = Witness::new(w_a); + let w_b = Witness::new(w_b); + let w_d = Witness::new(w_d); + let w_o = Witness::new(w_o); + + let mut constraint = Constraint::default() + .set(Selector::Multiplication, q_m) + .set(Selector::Left, q_l) + .set(Selector::Right, q_r) + .set(Selector::Output, q_o) + .set(Selector::Constant, q_c) + .set(Selector::Fourth, q_d) + .set(Selector::Arithmetic, q_arith) + .set(Selector::Range, q_range) + .set(Selector::Logic, q_logic) + .set(Selector::GroupAddFixedBase, q_fixed_group_add) + .set(Selector::GroupAddVariableBase, q_variable_group_add) + .a(w_a) + .b(w_b) + .d(w_d) + .o(w_o); + + if let Some(idx) = public_inputs.get(pi) { + if idx == &i { + pi += 1; + constraint = constraint.public(BlsScalar::zero()); + } + } + + builder.append_custom_gate(constraint); + } + + Compiler::compile_with_builder(pp, label, &builder) + } +} diff --git a/src/composer/compiler/compress/hades.rs b/src/composer/compiler/compress/hades.rs new file mode 100644 index 00000000..8b1000cd --- /dev/null +++ b/src/composer/compiler/compress/hades.rs @@ -0,0 +1,62 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +use sha2::{Digest, Sha512}; + +use super::BlsScalar; + +const CONSTANTS: usize = 960; + +// Extracted from +// https://github.com/dusk-network/Hades252/blob/a4d55e06ee9ff7f549043582e8d194eb0a01bf24/assets/HOWTO.md + +pub fn constants() -> [BlsScalar; CONSTANTS] { + let mut cnst = [BlsScalar::zero(); CONSTANTS]; + let mut p = BlsScalar::one(); + let mut bytes = b"poseidon-for-plonk".to_vec(); + + cnst.iter_mut().for_each(|c| { + bytes = Sha512::digest(bytes.as_slice()).to_vec(); + + let mut v = [0x00u8; 64]; + v.copy_from_slice(&bytes[0..64]); + + *c = BlsScalar::from_bytes_wide(&v) + p; + p = *c; + }); + + cnst +} + +const WIDTH: usize = 5; + +pub fn mds() -> [[BlsScalar; WIDTH]; WIDTH] { + let mut matrix = [[BlsScalar::zero(); WIDTH]; WIDTH]; + let mut xs = [BlsScalar::zero(); WIDTH]; + let mut ys = [BlsScalar::zero(); WIDTH]; + + // Generate x and y values deterministically for the cauchy matrix + // where x[i] != y[i] to allow the values to be inverted + // and there are no duplicates in the x vector or y vector, so that the + // determinant is always non-zero [a b] + // [c d] + // det(M) = (ad - bc) ; if a == b and c == d => det(M) =0 + // For an MDS matrix, every possible mxm submatrix, must have det(M) != 0 + (0..WIDTH).for_each(|i| { + xs[i] = BlsScalar::from(i as u64); + ys[i] = BlsScalar::from((i + WIDTH) as u64); + }); + + let mut m = 0; + (0..WIDTH).for_each(|i| { + (0..WIDTH).for_each(|j| { + matrix[m][j] = (xs[i] + ys[j]).invert().unwrap(); + }); + m += 1; + }); + + matrix +} diff --git a/src/composer/prover.rs b/src/composer/prover.rs index fbd9cae1..ddec11a1 100644 --- a/src/composer/prover.rs +++ b/src/composer/prover.rs @@ -5,7 +5,6 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use alloc::vec::Vec; -use core::marker::PhantomData; use core::ops; use dusk_bls12_381::BlsScalar; @@ -27,10 +26,7 @@ use super::{Builder, Circuit, Composer}; /// Turbo Prover with processed keys #[derive(Clone)] -pub struct Prover -where - C: Circuit, -{ +pub struct Prover { label: Vec, pub(crate) prover_key: ProverKey, pub(crate) commit_key: CommitKey, @@ -38,13 +34,9 @@ where pub(crate) transcript: Transcript, pub(crate) size: usize, pub(crate) constraints: usize, - circuit: PhantomData, } -impl ops::Deref for Prover -where - C: Circuit, -{ +impl ops::Deref for Prover { type Target = ProverKey; fn deref(&self) -> &Self::Target { @@ -52,10 +44,7 @@ where } } -impl Prover -where - C: Circuit, -{ +impl Prover { pub(crate) fn new( label: Vec, prover_key: ProverKey, @@ -75,7 +64,6 @@ where transcript, size, constraints, - circuit: PhantomData, } } @@ -234,7 +222,7 @@ where } /// Prove the circuit - pub fn prove( + pub fn prove( &self, rng: &mut R, circuit: &C, diff --git a/src/composer/verifier.rs b/src/composer/verifier.rs index 78c44e7c..ec81a83b 100644 --- a/src/composer/verifier.rs +++ b/src/composer/verifier.rs @@ -5,7 +5,6 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use alloc::vec::Vec; -use core::marker::PhantomData; use dusk_bls12_381::BlsScalar; use dusk_bytes::{DeserializableSlice, Serializable}; @@ -19,7 +18,7 @@ use crate::transcript::TranscriptProtocol; use super::Builder; /// Verify proofs of a given circuit -pub struct Verifier { +pub struct Verifier { label: Vec, verifier_key: VerifierKey, opening_key: OpeningKey, @@ -27,10 +26,9 @@ pub struct Verifier { transcript: Transcript, size: usize, constraints: usize, - circuit: PhantomData, } -impl Verifier { +impl Verifier { pub(crate) fn new( label: Vec, verifier_key: VerifierKey, @@ -50,7 +48,6 @@ impl Verifier { transcript, size, constraints, - circuit: PhantomData, } } diff --git a/src/constraint_system/constraint.rs b/src/constraint_system/constraint.rs index c7015815..bf846663 100644 --- a/src/constraint_system/constraint.rs +++ b/src/constraint_system/constraint.rs @@ -55,8 +55,8 @@ pub(crate) enum WiredWitness { /// evaluation #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Constraint { - coefficients: [BlsScalar; 12], - witnesses: [Witness; 4], + coefficients: [BlsScalar; Self::COEFFICIENTS], + witnesses: [Witness; Self::WITNESSES], // TODO Workaround solution to keep the sparse public input indexes in the // composer @@ -93,21 +93,21 @@ impl AsRef<[BlsScalar]> for Constraint { } impl Constraint { + /// Internal coefficients count. + pub const COEFFICIENTS: usize = 12; + + /// Internal witnesses count. + pub const WITNESSES: usize = 4; + /// Initiate the composition of a new selector description of a circuit. pub const fn new() -> Self { Self { - coefficients: [BlsScalar::zero(); 12], - witnesses: [Builder::ZERO; 4], + coefficients: [BlsScalar::zero(); Self::COEFFICIENTS], + witnesses: [Builder::ZERO; Self::WITNESSES], has_public_input: false, } } - fn set>(mut self, r: Selector, s: T) -> Self { - self.coefficients[r as usize] = s.into(); - - self - } - fn from_external(constraint: &Self) -> Self { const EXTERNAL: usize = Selector::Arithmetic as usize; @@ -124,6 +124,13 @@ impl Constraint { s } + /// Replace the value of a polynomial selector + pub(crate) fn set>(mut self, r: Selector, s: T) -> Self { + self.coefficients[r as usize] = s.into(); + + self + } + /// Replace the value of an indexed witness pub(crate) fn set_witness(&mut self, index: WiredWitness, w: Witness) { self.witnesses[index as usize] = w; diff --git a/src/constraint_system/witness.rs b/src/constraint_system/witness.rs index fcf71891..61949fe0 100644 --- a/src/constraint_system/witness.rs +++ b/src/constraint_system/witness.rs @@ -30,6 +30,12 @@ pub struct Witness { } impl Witness { + /// A `0` witness representation. + pub const ZERO: Witness = Witness::new(0); + + /// A `1` witness representation. + pub const ONE: Witness = Witness::new(1); + /// Generate a new [`Witness`] pub(crate) const fn new(index: usize) -> Self { Self { index } diff --git a/src/error.rs b/src/error.rs index 83a631a4..bec2832f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -86,6 +86,8 @@ pub enum Error { /// Provided value provided: usize, }, + /// The provided compressed circuit bytes representation is invalid. + InvalidCompressedCircuit, } #[cfg(feature = "std")] @@ -151,6 +153,7 @@ impl std::fmt::Display for Error { Self::InconsistentPublicInputsLen { expected, provided, } => write!(f, "The provided public inputs set of length {} doesn't match the processed verifier: {}", provided, expected), + Self::InvalidCompressedCircuit => write!(f, "invalid compressed circuit"), } } } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 0f99f85d..beaa7302 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -14,7 +14,7 @@ pub(crate) fn setup( rng: &mut R, label: &[u8], circuit: &C, -) -> (Prover, Verifier) +) -> (Prover, Verifier) where C: Circuit, R: RngCore + CryptoRng, @@ -27,8 +27,8 @@ where // Check that proof creation and verification of a satisfied circuit passes // and that the public inputs are as expected pub(crate) fn check_satisfied_circuit( - prover: &Prover, - verifier: &Verifier, + prover: &Prover, + verifier: &Verifier, pi_expected: &Vec, circuit: &C, rng: &mut R, @@ -53,8 +53,8 @@ pub(crate) fn check_satisfied_circuit( // tests. #[allow(dead_code)] pub(crate) fn check_satisfied_circuit_fails( - prover: &Prover, - verifier: &Verifier, + prover: &Prover, + verifier: &Verifier, pi_expected: &Vec, circuit: &C, rng: &mut R, @@ -76,7 +76,7 @@ pub(crate) fn check_satisfied_circuit_fails( // This is also the case when the constants appended to the circuit does not // match the ones from the circuit description pub(crate) fn check_unsatisfied_circuit( - prover: &Prover, + prover: &Prover, circuit: &C, rng: &mut R, msg: &str, diff --git a/tests/composer.rs b/tests/composer.rs index 25a68da3..fc37d84b 100644 --- a/tests/composer.rs +++ b/tests/composer.rs @@ -87,12 +87,28 @@ fn circuit_with_all_gates() { let (prover, verifier) = Compiler::compile::(&pp, label) .expect("failed to compile circuit"); + let compressed = Compiler::compress::(&pp) + .expect("failed to compress circuit"); + + let (decompressed_prover, decompressed_verifier) = + Compiler::decompress(&pp, label, &compressed).unwrap(); + + let decoded_prover_bytes = decompressed_prover.to_bytes(); let len = prover.serialized_size(); let prover = prover.to_bytes(); assert_eq!(prover.len(), len); + assert_eq!(decoded_prover_bytes, prover); + + let (proof, public_inputs) = decompressed_prover + .prove(rng, &DummyCircuit::default()) + .expect("failed to prove"); + + decompressed_verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); - let prover: Prover = + let prover = Prover::try_from_bytes(&prover).expect("failed to deserialize prover"); let len = verifier.serialized_size(); @@ -100,11 +116,11 @@ fn circuit_with_all_gates() { assert_eq!(verifier.len(), len); - let verifier: Verifier = Verifier::try_from_bytes(&verifier) + let verifier = Verifier::try_from_bytes(&verifier) .expect("failed to deserialize verifier"); let (proof, public_inputs) = prover - .prove(rng, &Default::default()) + .prove(rng, &DummyCircuit::default()) .expect("failed to prove"); verifier