diff --git a/Cargo.toml b/Cargo.toml index 99ef75d06..f69893378 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,10 +30,13 @@ cfg-if = "1.0" canonical = {version = "0.7", optional = true} canonical_derive = {version = "0.7", optional = true} rkyv = {version = "0.7", optional = true} +backtrace = {version = "0.3", optional = true} +dusk-cdf = {version = "0.2", optional = true} [dev-dependencies] criterion = "0.3" tempdir = "0.3" +rand = "0.8" [[bench]] name = "plonk" @@ -51,8 +54,7 @@ std = [ "rayon" ] alloc = ["dusk-bls12_381/alloc"] -trace = [] -trace-print = ["trace"] +debug = ["dusk-cdf", "backtrace"] canon = ["dusk-bls12_381/canon", "dusk-jubjub/canon", "canonical", "canonical_derive"] rkyv-impl = ["dusk-bls12_381/rkyv", "dusk-jubjub/rkyv-impl", "rkyv"] diff --git a/README.md b/README.md index 6a961e844..af7eb1fcd 100644 --- a/README.md +++ b/README.md @@ -36,21 +36,16 @@ pub struct TestCircuit { } impl Circuit for TestCircuit { - const CIRCUIT_ID: [u8; 32] = [0xff; 32]; - fn gadget( - &mut self, - composer: &mut TurboComposer, - ) -> Result<(), Error> { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { let a = composer.append_witness(self.a); let b = composer.append_witness(self.b); // Make first constraint a + b = c - let constraint = Constraint::new() - .left(1) - .right(1) - .public(-self.c) - .a(a) - .b(b); + let constraint = + Constraint::new().left(1).right(1).public(-self.c).a(a).b(b); composer.append_gate(constraint); @@ -59,71 +54,38 @@ impl Circuit for TestCircuit { composer.component_range(b, 1 << 5); // Make second constraint a * b = d - let constraint = Constraint::new() - .mult(1) - .public(-self.d) - .a(a) - .b(b); + let constraint = + Constraint::new().mult(1).public(-self.d).a(a).b(b); composer.append_gate(constraint); let e = composer.append_witness(self.e); let scalar_mul_result = composer - .component_mul_generator(e, dusk_jubjub::GENERATOR_EXTENDED); + .component_mul_generator(e, dusk_jubjub::GENERATOR_EXTENDED)?; // Apply the constraint composer.assert_equal_public_point(scalar_mul_result, self.f); + Ok(()) } +} - fn public_inputs(&self) -> Vec { - vec![self.c.into(), self.d.into(), self.f.into()] - } +let label = b"transcript-arguments"; +let pp = PublicParameters::setup(1 << 12, &mut OsRng) + .expect("failed to setup"); - fn padded_gates(&self) -> usize { - 1 << 11 - } -} +let (prover, verifier) = Compiler::compile::(&pp, label) + .expect("failed to compile circuit"); + +// Generate the proof and its public inputs +let (proof, public_inputs) = prover + .prove(&mut OsRng, &TestCircuit::default()) + .expect("failed to prove"); -// Now let's use the Circuit we've just implemented! - -let pp = PublicParameters::setup(1 << 12, &mut OsRng).unwrap(); -// Initialize the circuit -let mut circuit = TestCircuit::default(); -// Compile/preproces the circuit -let (pk, vd) = circuit.compile(&pp).unwrap(); - -// Prover POV -let proof = { - let mut circuit = TestCircuit { - a: BlsScalar::from(20u64), - b: BlsScalar::from(5u64), - c: BlsScalar::from(25u64), - d: BlsScalar::from(100u64), - e: JubJubScalar::from(2u64), - f: JubJubAffine::from( - dusk_jubjub::GENERATOR_EXTENDED * JubJubScalar::from(2u64), - ), - }; - circuit.prove(&pp, &pk, b"Test", &mut OsRng).unwrap() -}; - -// Verifier POV -let public_inputs: Vec = vec![ - BlsScalar::from(25u64).into(), - BlsScalar::from(100u64).into(), - JubJubAffine::from( - dusk_jubjub::GENERATOR_EXTENDED * JubJubScalar::from(2u64), - ) - .into(), -]; -TestCircuit::verify( - &pp, - &vd, - &proof, - &public_inputs, - b"Test", -).unwrap(); +// Verify the generated proof +verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); ``` ### Features @@ -135,12 +97,7 @@ This crate includes a variety of features which will briefly be explained below: - `std`: Enables `std` usage as well as `rayon` parallelization in some proving and verifying ops. It also uses the `std` versions of the elliptic curve deps, which utilizes the `parallel` feature from `dusk-bls12-381`. By default, this is the feature that comes enabled with the crate. -- `trace`: Enables the Circuit debugger tooling. This is essentially the capability of using the - `TurboComposer::check_circuit_satisfied` function. The function will output information about each circuit gate until - one of the gates does not satisfy the equation, or there are no more gates. If there is an unsatisfied gate - equation, the function will panic and return the gate number. -- `trace-print`: Goes a step further than `trace` and prints each `gate` component data, giving a clear overview of all the - values which make up the circuit that we're constructing. +- `debug`: Enables the runtime debugger backend. Will output [CDF](https://crates.io/crates/dusk-cdf) files to the path defined in the `CDF_OUTPUT` environment variable. If used, the binary must be compiled with `debug = true`. For more info, check the [cargo book](https://doc.rust-lang.org/cargo/reference/profiles.html#debug). __The recommended method is to derive the std output, and the std error, and then place them in text file which can be used to efficiently analyse the gates.__ - `canon`: Enables `canonical` serialization for particular data structures, which is very useful in integrating this library within the rest of the Dusk stack - especially for storage purposes. diff --git a/benches/plonk.rs b/benches/plonk.rs index 6fd59c9ad..c0826feb3 100644 --- a/benches/plonk.rs +++ b/benches/plonk.rs @@ -8,127 +8,133 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use dusk_plonk::prelude::*; -use rand_core::OsRng; #[derive(Debug, Clone, Copy)] -struct BenchCircuit { - degree: usize, +struct BenchCircuit { + a: BlsScalar, + b: BlsScalar, + x: BlsScalar, + y: JubJubScalar, + z: JubJubExtended, } -impl From for BenchCircuit -where - T: Into, -{ - fn from(degree: T) -> Self { +impl Default for BenchCircuit { + fn default() -> Self { Self { - degree: 1 << degree.into(), + a: BlsScalar::from(2u64), + b: BlsScalar::from(3u64), + x: BlsScalar::from(6u64), + y: JubJubScalar::from(7u64), + z: dusk_jubjub::GENERATOR_EXTENDED * &JubJubScalar::from(7u64), } } } -impl Circuit for BenchCircuit { - const CIRCUIT_ID: [u8; 32] = [0xff; 32]; - - fn gadget(&mut self, composer: &mut TurboComposer) -> Result<(), Error> { - let mut a = BlsScalar::from(2u64); - let mut b = BlsScalar::from(3u64); - let mut c; - - while composer.gates() < self.padded_gates() { - a += BlsScalar::one(); - b += BlsScalar::one(); - c = a * b + a + b + BlsScalar::one(); - - let x = composer.append_witness(a); - let y = composer.append_witness(b); - let z = composer.append_witness(c); - - let constraint = Constraint::new() - .mult(1) - .left(1) - .right(1) - .output(-BlsScalar::one()) - .constant(1) - .a(x) - .b(y) - .o(z); - - composer.append_gate(constraint); +impl Circuit for BenchCircuit { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { + let w_a = composer.append_witness(self.a); + let w_b = composer.append_witness(self.b); + let w_x = composer.append_witness(self.x); + let w_y = composer.append_witness(self.y); + let w_z = composer.append_point(self.z); + + let mut diff = 0; + let mut prev = composer.constraints(); + + while prev + diff < DEGREE { + let r_w = + composer.gate_mul(Constraint::new().mult(1).a(w_a).b(w_b)); + + composer.append_constant(15); + composer.append_constant_point(self.z); + + composer.assert_equal(w_x, r_w); + composer.assert_equal_point(w_z, w_z); + + composer.gate_add(Constraint::new().left(1).right(1).a(w_a).b(w_b)); + + composer.component_add_point(w_z, w_z); + composer.append_logic_and(w_a, w_b, 254); + composer.append_logic_xor(w_a, w_b, 254); + composer.component_boolean(C::ONE); + composer.component_decomposition::<254>(w_a); + composer.component_mul_generator( + w_y, + dusk_jubjub::GENERATOR_EXTENDED, + )?; + composer.component_mul_point(w_y, w_z); + composer.component_range(w_a, 254); + composer.component_select(C::ONE, w_a, w_b); + composer.component_select_identity(C::ONE, w_z); + composer.component_select_one(C::ONE, w_a); + composer.component_select_point(C::ONE, w_z, w_z); + composer.component_select_zero(C::ONE, w_a); + + diff = composer.constraints() - prev; + prev = composer.constraints(); } Ok(()) } - - fn public_inputs(&self) -> Vec { - vec![] - } - - fn padded_gates(&self) -> usize { - self.degree - } } -fn constraint_system_prove( - circuit: &mut BenchCircuit, +fn run( + c: &mut Criterion, pp: &PublicParameters, - pk: &ProverKey, label: &'static [u8], -) -> Proof { - circuit - .prove(pp, pk, label, &mut OsRng) - .expect("Failed to prove bench circuit!") +) { + let (prover, verifier) = + Compiler::compile::>(&pp, label) + .expect("failed to compile circuit"); + + // sanity run + let (proof, public_inputs) = prover + .prove(&mut rand_core::OsRng, &Default::default()) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + + let power = (DEGREE as f64).log2() as usize; + 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())) + }) + }); + + let description = format!("Verify 2^{} = {} gates", power, DEGREE); + + c.bench_function(description.as_str(), |b| { + b.iter(|| verifier.verify(black_box(&proof), black_box(&public_inputs))) + }); } fn constraint_system_benchmark(c: &mut Criterion) { - let initial_degree = 5; - let final_degree = 17; + const MAX_DEGREE: usize = 17; - let rng = &mut rand_core::OsRng; let label = b"dusk-network"; - let pp = PublicParameters::setup(1 << final_degree, rng) - .expect("Failed to create PP"); - - let data: Vec<(BenchCircuit, ProverKey, VerifierData, Proof)> = - (initial_degree..=final_degree) - .map(|degree| { - let mut circuit = BenchCircuit::from(degree as usize); - let (pk, vd) = - circuit.compile(&pp).expect("Failed to compile circuit!"); - - let proof = - constraint_system_prove(&mut circuit, &pp, &pk, label); - - BenchCircuit::verify(&pp, &vd, &proof, &[], label) - .expect("Failed to verify bench circuit"); - - (circuit, pk, vd, proof) - }) - .collect(); - - data.iter().for_each(|(mut circuit, pk, _, _)| { - let size = circuit.padded_gates(); - let power = (size as f64).log2() as usize; - let description = format!("Prove 2^{} = {} gates", power, size); - - c.bench_function(description.as_str(), |b| { - b.iter(|| { - constraint_system_prove(black_box(&mut circuit), &pp, pk, label) - }) - }); - }); - - data.iter().for_each(|(circuit, _, vd, proof)| { - let size = circuit.padded_gates(); - let power = (size as f64).log2() as usize; - let description = format!("Verify 2^{} = {} gates", power, size); - - c.bench_function(description.as_str(), |b| { - b.iter(|| { - BenchCircuit::verify(&pp, vd, black_box(proof), &[], label) - .expect("Failed to verify bench circuit!"); - }) - }); - }); + let pp = PublicParameters::setup(1 << MAX_DEGREE, &mut rand_core::OsRng) + .expect("failed to generate pp"); + + run::<{ 1 << 5 }>(c, &pp, label); + run::<{ 1 << 6 }>(c, &pp, label); + run::<{ 1 << 7 }>(c, &pp, label); + run::<{ 1 << 8 }>(c, &pp, label); + run::<{ 1 << 9 }>(c, &pp, label); + run::<{ 1 << 10 }>(c, &pp, label); + run::<{ 1 << 11 }>(c, &pp, label); + run::<{ 1 << 12 }>(c, &pp, label); + run::<{ 1 << 13 }>(c, &pp, label); + run::<{ 1 << 14 }>(c, &pp, label); + run::<{ 1 << 15 }>(c, &pp, label); + run::<{ 1 << 16 }>(c, &pp, label); + run::<{ 1 << 17 }>(c, &pp, label); } criterion_group! { diff --git a/src/circuit.rs b/src/circuit.rs deleted file mode 100644 index 9945a6541..000000000 --- a/src/circuit.rs +++ /dev/null @@ -1,387 +0,0 @@ -// 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. - -//! Tools & traits for PLONK circuits - -use crate::commitment_scheme::PublicParameters; -use crate::constraint_system::TurboComposer; -use crate::error::Error; -use crate::proof_system::{Proof, Prover, ProverKey, Verifier, VerifierKey}; -use alloc::vec::Vec; -#[cfg(feature = "canon")] -use canonical_derive::Canon; -use dusk_bls12_381::BlsScalar; -use dusk_bytes::{DeserializableSlice, Serializable, Write}; -use dusk_jubjub::{JubJubAffine, JubJubExtended, JubJubScalar}; -use rand_core::{CryptoRng, RngCore}; - -#[cfg(feature = "rkyv-impl")] -use rkyv::{ - ser::{ScratchSpace, Serializer}, - Archive, Deserialize, Serialize, -}; - -/// Structure that represents a PLONK Circuit Public Input converted into it's -/// &\[[`BlsScalar`]\] repr. -#[derive(Default, Debug, Clone)] -#[cfg_attr(feature = "canon", derive(Canon))] -#[cfg_attr( - feature = "rkyv-impl", - derive(Archive, Deserialize, Serialize), - archive(bound(serialize = "__S: Serializer + ScratchSpace")) -)] -pub struct PublicInputValue( - #[cfg_attr(feature = "rkyv-impl", omit_bounds)] pub(crate) Vec, -); - -impl From for PublicInputValue { - fn from(scalar: BlsScalar) -> Self { - Self(vec![scalar]) - } -} - -impl From for PublicInputValue { - fn from(scalar: JubJubScalar) -> Self { - Self(vec![scalar.into()]) - } -} - -impl From for PublicInputValue { - fn from(point: JubJubAffine) -> Self { - Self(vec![point.get_x(), point.get_y()]) - } -} - -impl From for PublicInputValue { - fn from(point: JubJubExtended) -> Self { - JubJubAffine::from(point).into() - } -} - -/// Collection of structs/objects that the Verifier will use in order to -/// de/serialize data needed for Circuit proof verification. -/// This structure can be seen as a link between the [`Circuit`] public input -/// positions and the [`VerifierKey`] that the Verifier needs to use. -#[derive(Debug, Clone)] -#[cfg_attr( - feature = "rkyv-impl", - derive(Archive, Deserialize, Serialize), - archive(bound(serialize = "__S: Serializer + ScratchSpace")) -)] -pub struct VerifierData { - #[cfg_attr(feature = "rkyv-impl", omit_bounds)] - key: VerifierKey, - #[cfg_attr(feature = "rkyv-impl", omit_bounds)] - public_inputs_indexes: Vec, -} - -impl VerifierData { - /// Creates a new `VerifierData` from a [`VerifierKey`] and the public - /// input positions of the circuit that it represents. - pub const fn new( - key: VerifierKey, - public_inputs_indexes: Vec, - ) -> Self { - Self { - key, - public_inputs_indexes, - } - } - - /// Returns a reference to the contained [`VerifierKey`]. - pub const fn key(&self) -> &VerifierKey { - &self.key - } - - /// Returns a reference to the contained Public Input positions. - pub fn public_inputs_indexes(&self) -> &[usize] { - &self.public_inputs_indexes - } - - /// Deserializes the `VerifierData` into a vector of bytes. - #[allow(unused_must_use)] - pub fn to_var_bytes(&self) -> Vec { - let mut buff = vec![ - 0u8; - VerifierKey::SIZE - + u32::SIZE - + self.public_inputs_indexes.len() * u32::SIZE - ]; - let mut writer = &mut buff[..]; - - writer.write(&self.key.to_bytes()); - writer.write(&(self.public_inputs_indexes.len() as u32).to_bytes()); - self.public_inputs_indexes.iter().copied().for_each(|pos| { - // Omit the result since disk_bytes write can't fail here - // due to the fact that we're writing into a vector basically. - let _ = writer.write(&(pos as u32).to_bytes()); - }); - - buff - } - - /// Serializes `VerifierData` from a slice of bytes. - pub fn from_slice(mut buf: &[u8]) -> Result { - let key = VerifierKey::from_reader(&mut buf)?; - let pos_num = u32::from_reader(&mut buf)? as usize; - - let mut public_inputs_indexes = vec![]; - for _ in 0..pos_num { - public_inputs_indexes.push(u32::from_reader(&mut buf)? as usize); - } - - Ok(Self { - key, - public_inputs_indexes, - }) - } -} - -/// Trait that should be implemented for any circuit function to provide to it -/// the capabilities of automatically being able to generate, and verify proofs -/// as well as compile/preprocess the circuit. -/// # Example -/// -/// ``` -/// use dusk_plonk::prelude::*; -/// use rand_core::OsRng; -/// -/// fn main() -> Result<(), Error> { -/// // Implements a circuit that checks: -/// // 1) a + b = c where C is a PI -/// // 2) a <= 2^6 -/// // 3) b <= 2^5 -/// // 4) a * b = d where D is a PI -/// // 5) JubJub::GENERATOR * e(JubJubScalar) = f where F is a PI -/// #[derive(Debug, Default)] -/// pub struct TestCircuit { -/// a: BlsScalar, -/// b: BlsScalar, -/// c: BlsScalar, -/// d: BlsScalar, -/// e: JubJubScalar, -/// f: JubJubAffine, -/// } -/// -/// impl Circuit for TestCircuit { -/// const CIRCUIT_ID: [u8; 32] = [0xff; 32]; -/// fn gadget( -/// &mut self, -/// composer: &mut TurboComposer, -/// ) -> Result<(), Error> { -/// // Add fixed witness zero -/// let zero = TurboComposer::constant_zero(); -/// let a = composer.append_witness(self.a); -/// let b = composer.append_witness(self.b); -/// -/// // Make first constraint a + b = c -/// let constraint = Constraint::new() -/// .left(1) -/// .right(1) -/// .public(-self.c) -/// .a(a) -/// .b(b); -/// -/// composer.append_gate(constraint); -/// -/// // Check that a and b are in range -/// composer.component_range(a, 1 << 6); -/// composer.component_range(b, 1 << 5); -/// -/// // Make second constraint a * b = d -/// let constraint = Constraint::new() -/// .mult(1) -/// .public(-self.d) -/// .a(a) -/// .b(b); -/// -/// composer.append_gate(constraint); -/// -/// let e = composer.append_witness(self.e); -/// let scalar_mul_result = -/// composer.component_mul_generator( -/// e, dusk_jubjub::GENERATOR_EXTENDED, -/// ); -/// // Apply the constraint -/// composer -/// .assert_equal_public_point(scalar_mul_result, self.f); -/// Ok(()) -/// } -/// -/// fn public_inputs(&self) -> Vec { -/// vec![self.c.into(), self.d.into(), self.f.into()] -/// } -/// -/// fn padded_gates(&self) -> usize { -/// 1 << 11 -/// } -/// } -/// -/// let pp = PublicParameters::setup(1 << 12, &mut OsRng)?; -/// // Initialize the circuit -/// let mut circuit = TestCircuit::default(); -/// // Compile/preprocess the circuit -/// let (pk, vd) = circuit.compile(&pp)?; -/// -/// // Prover POV -/// let proof = { -/// let mut circuit = TestCircuit { -/// a: BlsScalar::from(20u64), -/// b: BlsScalar::from(5u64), -/// c: BlsScalar::from(25u64), -/// d: BlsScalar::from(100u64), -/// e: JubJubScalar::from(2u64), -/// f: JubJubAffine::from( -/// dusk_jubjub::GENERATOR_EXTENDED * JubJubScalar::from(2u64), -/// ), -/// }; -/// -/// circuit.prove(&pp, &pk, b"Test", &mut OsRng) -/// }?; -/// -/// // Verifier POV -/// let public_inputs: Vec = vec![ -/// BlsScalar::from(25u64).into(), -/// BlsScalar::from(100u64).into(), -/// JubJubAffine::from( -/// dusk_jubjub::GENERATOR_EXTENDED * JubJubScalar::from(2u64), -/// ) -/// .into(), -/// ]; -/// -/// TestCircuit::verify( -/// &pp, -/// &vd, -/// &proof, -/// &public_inputs, -/// b"Test", -/// ) -/// } -pub trait Circuit -where - Self: Sized, -{ - /// Circuit identifier associated constant. - const CIRCUIT_ID: [u8; 32]; - - /// Gadget implementation used to fill the composer. - fn gadget(&mut self, composer: &mut TurboComposer) -> Result<(), Error>; - - /// Compiles the circuit by using a function that returns a `Result` - /// with the `ProverKey`, `VerifierKey` and the circuit size. - fn compile( - &mut self, - pub_params: &PublicParameters, - ) -> Result<(ProverKey, VerifierData), Error> { - // Setup PublicParams - let (ck, _) = pub_params.trim(self.padded_gates())?; - - // Generate & save `ProverKey` with some random values. - let mut prover = Prover::new(b"CircuitCompilation"); - - self.gadget(prover.composer_mut())?; - - let public_inputs_indexes = - prover.composer_mut().public_input_indexes(); - - prover.preprocess(&ck)?; - - // Generate & save `VerifierKey` with some random values. - let mut verifier = Verifier::new(b"CircuitCompilation"); - - self.gadget(verifier.composer_mut())?; - - verifier.preprocess(&ck)?; - - Ok(( - prover - .prover_key - .expect("Unexpected error. Missing ProverKey in compilation"), - VerifierData::new( - verifier.verifier_key.expect( - "Unexpected error. Missing VerifierKey in compilation", - ), - public_inputs_indexes, - ), - )) - } - - /// Generates a proof using the provided `CircuitInputs` & `ProverKey` - /// instances. - fn prove( - &mut self, - pub_params: &PublicParameters, - prover_key: &ProverKey, - transcript_init: &'static [u8], - rng: &mut R, - ) -> Result { - let (ck, _) = pub_params.trim(self.padded_gates())?; - - // New Prover instance - let mut prover = Prover::new(transcript_init); - - // Fill witnesses for Prover - self.gadget(prover.composer_mut())?; - - // Add ProverKey to Prover - prover.prover_key = Some(prover_key.clone()); - prover.prove(&ck, rng) - } - - /// Verify the provided proof for the compiled verifier data - fn verify( - pub_params: &PublicParameters, - verifier_data: &VerifierData, - proof: &Proof, - public_inputs: &[PublicInputValue], - transcript_init: &'static [u8], - ) -> Result<(), Error> { - verify( - pub_params, - verifier_data, - proof, - public_inputs, - transcript_init, - ) - } - - /// Return the list of public inputs generated by the gadget - fn public_inputs(&self) -> Vec; - - /// Returns the Circuit size padded to the next power of two. - fn padded_gates(&self) -> usize; -} - -/// Verify the provided proof for the compiled verifier data -pub fn verify( - pub_params: &PublicParameters, - verifier_data: &VerifierData, - proof: &Proof, - public_inputs: &[PublicInputValue], - transcript_init: &'static [u8], -) -> Result<(), Error> { - let gates = verifier_data.key().padded_gates(); - let pi_indexes = verifier_data.public_inputs_indexes(); - - let mut dense_pi = vec![BlsScalar::zero(); gates]; - - public_inputs - .iter() - .map(|pi| pi.0.clone()) - .flatten() - .zip(pi_indexes.iter().cloned()) - .for_each(|(value, pos)| { - dense_pi[pos] = -value; - }); - - let mut verifier = Verifier::new(transcript_init); - - verifier.verifier_key.replace(*verifier_data.key()); - - let opening_key = pub_params.opening_key(); - - verifier.verify(proof, opening_key, &dense_pi, &pi_indexes) -} diff --git a/src/commitment_scheme.rs b/src/commitment_scheme.rs index 92c4d1422..7d6f20a50 100644 --- a/src/commitment_scheme.rs +++ b/src/commitment_scheme.rs @@ -22,7 +22,10 @@ pub(crate) use kzg10::Commitment; pub(crate) use kzg10::AggregateProof; #[cfg(feature = "alloc")] -pub use kzg10::{CommitKey, OpeningKey, PublicParameters}; +pub(crate) use kzg10::{CommitKey, OpeningKey}; + +#[cfg(feature = "alloc")] +pub use kzg10::PublicParameters; #[cfg(all(feature = "alloc", feature = "rkyv-impl"))] pub use kzg10::{ diff --git a/src/commitment_scheme/kzg10/key.rs b/src/commitment_scheme/kzg10/key.rs index fd7d3d853..a995a4b6a 100644 --- a/src/commitment_scheme/kzg10/key.rs +++ b/src/commitment_scheme/kzg10/key.rs @@ -27,7 +27,7 @@ use rkyv::{ /// CommitKey is used to commit to a polynomial which is bounded by the /// max_degree. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "rkyv-impl", derive(Archive, Deserialize, Serialize), diff --git a/src/commitment_scheme/kzg10/srs.rs b/src/commitment_scheme/kzg10/srs.rs index b4f531d4f..32ecbedff 100644 --- a/src/commitment_scheme/kzg10/srs.rs +++ b/src/commitment_scheme/kzg10/srs.rs @@ -40,18 +40,6 @@ pub struct PublicParameters { } impl PublicParameters { - /// Returns an untrimmed [`CommitKey`] reference contained in the - /// `PublicParameters` instance. - pub fn commit_key(&self) -> &CommitKey { - &self.commit_key - } - - /// Returns an [`OpeningKey`] reference contained in the - /// `PublicParameters` instance. - pub fn opening_key(&self) -> &OpeningKey { - &self.opening_key - } - /// The maximum degree is the degree of the constraint system + 6, /// because adding the blinding factors requires some extra elements /// for the SRS: +1 per each wire (we have 4 wires), plus +2 for the @@ -189,7 +177,7 @@ impl PublicParameters { /// /// Returns an error if the truncated degree is larger than the public /// parameters configured degree. - pub fn trim( + pub(crate) fn trim( &self, truncated_degree: usize, ) -> Result<(CommitKey, OpeningKey), Error> { diff --git a/src/composer.rs b/src/composer.rs new file mode 100644 index 000000000..53765210c --- /dev/null +++ b/src/composer.rs @@ -0,0 +1,1052 @@ +// 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. + +//! PLONK turbo composer definitions + +use alloc::vec::Vec; +use core::cmp; +use core::ops::Index; + +use dusk_bls12_381::BlsScalar; +use dusk_bytes::Serializable; +use dusk_jubjub::{JubJubAffine, JubJubExtended, JubJubScalar}; + +use crate::bit_iterator::BitIterator8; +use crate::constraint_system::ecc::WnafRound; +use crate::constraint_system::{ + Constraint, Selector, WiredWitness, Witness, WitnessPoint, +}; +use crate::error::Error; +use crate::runtime::{Runtime, RuntimeEvent}; + +mod builder; +mod circuit; +mod compiler; +mod polynomial; +mod prover; +mod verifier; + +pub use builder::Builder; +pub use circuit::Circuit; +pub use compiler::Compiler; +pub use polynomial::Polynomial; +pub use prover::Prover; +pub use verifier::Verifier; + +/// Circuit builder tool +pub trait Composer: Sized + Index { + /// Zero representation inside the constraint system. + /// + /// A turbo composer expects the first witness to be always present and to + /// be zero. + const ZERO: Witness = Witness::new(0); + + /// `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); + + /// Identity point representation inside the constraint system + const IDENTITY: WitnessPoint = WitnessPoint::new(Self::ZERO, Self::ONE); + + /// Create an empty constraint system. + /// + /// This shouldn't be used directly; instead, use [`Self::initialized`] + #[deprecated( + since = "13.0", + note = "this function is meant for internal use. call `initialized` instead" + )] + fn uninitialized(capacity: usize) -> Self; + + /// Constraints count + fn constraints(&self) -> usize; + + /// Allocate a witness value into the composer and return its index. + #[deprecated( + since = "13.0", + note = "this function is meant for internal use. call `append_witness` instead" + )] + fn append_witness_internal(&mut self, witness: BlsScalar) -> Witness; + + /// Append a new width-4 poly gate/constraint. + #[deprecated( + since = "13.0", + note = "this function is meant for internal use. call `append_custom_gate` instead" + )] + fn append_custom_gate_internal(&mut self, constraint: Constraint); + + /// PLONK runtime controller + fn runtime(&mut self) -> &mut Runtime; + + /// Allocate a witness value into the composer and return its index. + fn append_witness>(&mut self, witness: W) -> Witness { + let witness = witness.into(); + + #[allow(deprecated)] + let witness = self.append_witness_internal(witness); + + let v = self[witness]; + self.runtime() + .event(RuntimeEvent::WitnessAppended { w: witness, v }); + + witness + } + + /// Append a new width-4 poly gate/constraint. + fn append_custom_gate(&mut self, constraint: Constraint) { + self.runtime() + .event(RuntimeEvent::ConstraintAppended { c: constraint }); + + #[allow(deprecated)] + self.append_custom_gate_internal(constraint) + } + + /// Performs a logical AND or XOR op between the inputs provided for the + /// specified number of bits (counting from the least significant bit). + /// + /// Each logic gate adds `(num_bits / 2) + 1` gates to the circuit to + /// perform the whole operation. + /// + /// ## Constraint + /// - is_component_xor = 1 -> Performs XOR between the first `num_bits` for + /// `a` and `b`. + /// - is_component_xor = 0 -> Performs AND between the first `num_bits` for + /// `a` and `b`. + /// + /// # Panics + /// This function will panic if the num_bits specified is not even, ie. + /// `num_bits % 2 != 0`. + fn append_logic_component( + &mut self, + a: Witness, + b: Witness, + num_bits: usize, + is_component_xor: bool, + ) -> Witness { + // the bits are iterated as chunks of two; hence, we require an even + // number + debug_assert_eq!(num_bits & 1, 0); + + let num_bits = cmp::min(num_bits, 256); + let num_quads = num_bits >> 1; + + let bls_four = BlsScalar::from(4u64); + let mut left_acc = BlsScalar::zero(); + let mut right_acc = BlsScalar::zero(); + let mut out_acc = BlsScalar::zero(); + + // skip bits outside of argument `num_bits` + let a_bit_iter = BitIterator8::new(self[a].to_bytes()); + let a_bits: Vec<_> = a_bit_iter.skip(256 - num_bits).collect(); + let b_bit_iter = BitIterator8::new(self[b].to_bytes()); + let b_bits: Vec<_> = b_bit_iter.skip(256 - num_bits).collect(); + + // + // * +-----+-----+-----+-----+ + // * | A | B | C | D | + // * +-----+-----+-----+-----+ + // * | 0 | 0 | w1 | 0 | + // * | a1 | b1 | w2 | d1 | + // * | a2 | b2 | w3 | d2 | + // * | : | : | : | : | + // * | an | bn | 0 | dn | + // * +-----+-----+-----+-----+ + // `an`, `bn` and `dn` are accumulators: `an [& OR ^] bd = dn` + // + // each step will shift last computation two bits to the left and add + // current quad. + // + // `wn` product accumulators will safeguard the quotient polynomial. + + let mut constraint = if is_component_xor { + Constraint::logic_xor(&Constraint::new()) + } else { + Constraint::logic(&Constraint::new()) + }; + + for i in 0..num_quads { + // commit every accumulator + let idx = i * 2; + + let l = (a_bits[idx] as u8) << 1; + let r = a_bits[idx + 1] as u8; + let left_quad = l + r; + let left_quad_bls = BlsScalar::from(left_quad as u64); + + let l = (b_bits[idx] as u8) << 1; + let r = b_bits[idx + 1] as u8; + let right_quad = l + r; + let right_quad_bls = BlsScalar::from(right_quad as u64); + + let out_quad_bls = if is_component_xor { + left_quad ^ right_quad + } else { + left_quad & right_quad + } as u64; + let out_quad_bls = BlsScalar::from(out_quad_bls); + + // `w` argument to safeguard the quotient polynomial + let prod_quad_bls = (left_quad * right_quad) as u64; + let prod_quad_bls = BlsScalar::from(prod_quad_bls); + + // Now that we've computed this round results, we need to apply the + // logic transition constraint that will check that + // a_{i+1} - (a_i << 2) < 4 + // b_{i+1} - (b_i << 2) < 4 + // d_{i+1} - (d_i << 2) < 4 with d_i = a_i [& OR ^] b_i + // Note that multiplying by four is the equivalent of shifting the + // bits two positions to the left. + + left_acc = left_acc * bls_four + left_quad_bls; + right_acc = right_acc * bls_four + right_quad_bls; + out_acc = out_acc * bls_four + out_quad_bls; + + let wit_a = self.append_witness(left_acc); + let wit_b = self.append_witness(right_acc); + let wit_c = self.append_witness(prod_quad_bls); + let wit_d = self.append_witness(out_acc); + + constraint = constraint.o(wit_c); + + self.append_custom_gate(constraint); + + constraint = constraint.a(wit_a).b(wit_b).d(wit_d); + } + + // pad last output with `0` + // | an | bn | 0 | dn | + let a = constraint.witness(WiredWitness::A); + let b = constraint.witness(WiredWitness::B); + let d = constraint.witness(WiredWitness::D); + + let constraint = Constraint::new().a(a).b(b).d(d); + + self.append_custom_gate(constraint); + + d + } + + /// Evaluate `jubjub · Generator` as a [`WitnessPoint`] + /// + /// `generator` will be appended to the circuit description as constant + /// + /// Will error if `jubjub` doesn't fit `Fr` + fn component_mul_generator>( + &mut self, + jubjub: Witness, + generator: P, + ) -> Result { + let generator = generator.into(); + + // the number of bits is truncated to the maximum possible. however, we + // could slice off 3 bits from the top of wnaf since Fr price is + // 252 bits. Alternatively, we could move to base4 and halve the + // number of gates considering that the product of wnaf adjacent + // entries is zero. + let bits: usize = 256; + + // compute 2^iG + let mut wnaf_point_multiples: Vec<_> = { + let mut multiples = vec![JubJubExtended::default(); bits]; + + multiples[0] = generator; + + for i in 1..bits { + multiples[i] = multiples[i - 1].double(); + } + + dusk_jubjub::batch_normalize(&mut multiples).collect() + }; + + wnaf_point_multiples.reverse(); + + // we should error instead of producing invalid proofs - otherwise this + // can easily become an attack vector to either shutdown prover + // services or create malicious statements + let scalar = JubJubScalar::from_bytes(&self[jubjub].to_bytes())?; + + let width = 2; + let wnaf_entries = scalar.compute_windowed_naf(width); + + debug_assert_eq!(wnaf_entries.len(), bits); + + // initialize the accumulators + let mut scalar_acc = vec![BlsScalar::zero()]; + let mut point_acc = vec![JubJubAffine::identity()]; + + // auxillary point to help with checks on the backend + let two = BlsScalar::from(2u64); + let xy_alphas: Vec<_> = wnaf_entries + .iter() + .rev() + .enumerate() + .map(|(i, entry)| { + let (scalar_to_add, point_to_add) = match entry { + 0 => (BlsScalar::zero(), JubJubAffine::identity()), + -1 => (BlsScalar::one().neg(), -wnaf_point_multiples[i]), + 1 => (BlsScalar::one(), wnaf_point_multiples[i]), + _ => return Err(Error::UnsupportedWNAF2k), + }; + + let prev_accumulator = two * scalar_acc[i]; + let scalar = prev_accumulator + scalar_to_add; + scalar_acc.push(scalar); + + let a = JubJubExtended::from(point_acc[i]); + let b = JubJubExtended::from(point_to_add); + let point = a + b; + point_acc.push(point.into()); + + let x_alpha = point_to_add.get_x(); + let y_alpha = point_to_add.get_y(); + + Ok(x_alpha * y_alpha) + }) + .collect::>()?; + + for i in 0..bits { + let acc_x = self.append_witness(point_acc[i].get_x()); + let acc_y = self.append_witness(point_acc[i].get_y()); + let accumulated_bit = self.append_witness(scalar_acc[i]); + + // the point accumulator must start from identity and its scalar + // from zero + if i == 0 { + self.assert_equal_constant(acc_x, BlsScalar::zero(), None); + self.assert_equal_constant(acc_y, BlsScalar::one(), None); + self.assert_equal_constant( + accumulated_bit, + BlsScalar::zero(), + None, + ); + } + + let x_beta = wnaf_point_multiples[i].get_x(); + let y_beta = wnaf_point_multiples[i].get_y(); + + let xy_alpha = self.append_witness(xy_alphas[i]); + let xy_beta = x_beta * y_beta; + + let wnaf_round = WnafRound { + acc_x, + acc_y, + accumulated_bit, + xy_alpha, + x_beta, + y_beta, + xy_beta, + }; + + let constraint = + Constraint::group_add_fixed_base(&Constraint::new()) + .left(wnaf_round.x_beta) + .right(wnaf_round.y_beta) + .constant(wnaf_round.xy_beta) + .a(wnaf_round.acc_x.into()) + .b(wnaf_round.acc_y.into()) + .o(wnaf_round.xy_alpha.into()) + .d(wnaf_round.accumulated_bit.into()); + + self.append_custom_gate(constraint) + } + + // last gate isn't activated for ecc + let acc_x = self.append_witness(point_acc[bits].get_x()); + let acc_y = self.append_witness(point_acc[bits].get_y()); + + // FIXME this implementation presents a plethora of vulnerabilities and + // requires reworking + // + // we are accepting any scalar argument and trusting it to be the + // expected input. it happens to be correct in this + // implementation, but can be exploited by malicious provers who + // might just input anything here + let last_accumulated_bit = self.append_witness(scalar_acc[bits]); + + // FIXME the gate isn't checking anything. maybe remove? + let constraint = + Constraint::new().a(acc_x).b(acc_y).d(last_accumulated_bit); + self.append_gate(constraint); + + // constrain the last element in the accumulator to be equal to the + // input jubjub scalar + self.assert_equal(last_accumulated_bit, jubjub); + + Ok(WitnessPoint::new(acc_x, acc_y)) + } + + /// Initialize the constraint system with dummy gates + fn initialized(capacity: usize) -> Self { + #[allow(deprecated)] + let mut slf = Self::uninitialized(capacity); + + let zero = slf.append_witness(0); + let one = slf.append_witness(1); + + slf.assert_equal_constant(zero, 0, None); + slf.assert_equal_constant(one, 1, None); + + slf.append_dummy_gates(); + slf.append_dummy_gates(); + + slf + } + + /// Append a new width-4 poly gate/constraint. + /// + /// The constraint added will enforce the following: + /// `q_m · a · b + q_l · a + q_r · b + q_o · o + q_4 · d + q_c + PI = 0`. + fn append_gate(&mut self, constraint: Constraint) { + let constraint = Constraint::arithmetic(&constraint); + + self.append_custom_gate(constraint) + } + + /// Evaluate the polynomial and append an output that satisfies the equation + /// + /// Return `None` if the output selector is zero + fn append_evaluated_output(&mut self, s: Constraint) -> Option { + let a = s.witness(WiredWitness::A); + let b = s.witness(WiredWitness::B); + let d = s.witness(WiredWitness::D); + + let a = self[a]; + let b = self[b]; + let d = self[d]; + + let qm = s.coeff(Selector::Multiplication); + let ql = s.coeff(Selector::Left); + let qr = s.coeff(Selector::Right); + let qd = s.coeff(Selector::Fourth); + let qc = s.coeff(Selector::Constant); + let pi = s.coeff(Selector::PublicInput); + + let x = qm * a * b + ql * a + qr * b + qd * d + qc + pi; + + let y = s.coeff(Selector::Output); + + // Invert is an expensive operation; in most cases, `qo` is going to be + // either 1 or -1, so we can optimize these + #[allow(dead_code)] + let o = { + const ONE: BlsScalar = BlsScalar::one(); + const MINUS_ONE: BlsScalar = BlsScalar([ + 0xfffffffd00000003, + 0xfb38ec08fffb13fc, + 0x99ad88181ce5880f, + 0x5bc8f5f97cd877d8, + ]); + + // Can't use a match pattern here since `BlsScalar` doesn't derive + // `PartialEq` + if y == &ONE { + Some(-x) + } else if y == &MINUS_ONE { + Some(x) + } else { + y.invert().map(|y| x * (-y)) + } + }; + + o.map(|o| self.append_witness(o)) + } + + /// Adds blinding factors to the witness polynomials with two dummy + /// arithmetic constraints + fn append_dummy_gates(&mut self) { + let six = self.append_witness(BlsScalar::from(6)); + let one = self.append_witness(BlsScalar::from(1)); + let seven = self.append_witness(BlsScalar::from(7)); + let min_twenty = self.append_witness(-BlsScalar::from(20)); + + // Add a dummy constraint so that we do not have zero polynomials + let constraint = Constraint::new() + .mult(1) + .left(2) + .right(3) + .fourth(1) + .constant(4) + .output(4) + .a(six) + .b(seven) + .d(one) + .o(min_twenty); + + self.append_gate(constraint); + + // Add another dummy constraint so that we do not get the identity + // permutation + let constraint = Constraint::new() + .mult(1) + .left(1) + .right(1) + .constant(127) + .output(1) + .a(min_twenty) + .b(six) + .o(seven); + + self.append_gate(constraint); + } + + /// Constrain a scalar into the circuit description and return an allocated + /// [`Witness`] with its value + fn append_constant>(&mut self, constant: C) -> Witness { + let constant = constant.into(); + let witness = self.append_witness(constant); + + self.assert_equal_constant(witness, constant, None); + + witness + } + + /// Appends a point in affine form as [`WitnessPoint`] + fn append_point>( + &mut self, + affine: P, + ) -> WitnessPoint { + let affine = affine.into(); + + let x = self.append_witness(affine.get_x()); + let y = self.append_witness(affine.get_y()); + + WitnessPoint::new(x, y) + } + + /// Constrain a point into the circuit description and return an allocated + /// [`WitnessPoint`] with its coordinates + fn append_constant_point>( + &mut self, + affine: P, + ) -> WitnessPoint { + let affine = affine.into(); + + let x = self.append_constant(affine.get_x()); + let y = self.append_constant(affine.get_y()); + + WitnessPoint::new(x, y) + } + + /// Appends a point in affine form as [`WitnessPoint`] + /// + /// Creates two public inputs as `(x, y)` + fn append_public_point>( + &mut self, + affine: P, + ) -> WitnessPoint { + let affine = affine.into(); + let point = self.append_point(affine); + + self.assert_equal_constant( + *point.x(), + BlsScalar::zero(), + Some(-affine.get_x()), + ); + + self.assert_equal_constant( + *point.y(), + BlsScalar::zero(), + Some(-affine.get_y()), + ); + + point + } + + /// Allocate a witness value into the composer and return its index. + /// + /// Create a public input with the scalar + fn append_public>(&mut self, public: P) -> Witness { + let public = public.into(); + let witness = self.append_witness(public); + + self.assert_equal_constant(witness, 0, Some(-public)); + + witness + } + + /// Asserts `a == b` by appending a gate + fn assert_equal(&mut self, a: Witness, b: Witness) { + let constraint = + Constraint::new().left(1).right(-BlsScalar::one()).a(a).b(b); + + self.append_gate(constraint); + } + + /// Adds a logical AND gate that performs the bitwise AND between two values + /// for the specified first `num_bits` returning a [`Witness`] + /// holding the result. + /// + /// # Panics + /// + /// If the `num_bits` specified in the fn params is odd. + fn append_logic_and( + &mut self, + a: Witness, + b: Witness, + num_bits: usize, + ) -> Witness { + self.append_logic_component(a, b, num_bits, false) + } + + /// Adds a logical XOR gate that performs the XOR between two values for the + /// specified first `num_bits` returning a [`Witness`] holding the + /// result. + /// + /// # Panics + /// + /// If the `num_bits` specified in the fn params is odd. + fn append_logic_xor( + &mut self, + a: Witness, + b: Witness, + num_bits: usize, + ) -> Witness { + self.append_logic_component(a, b, num_bits, true) + } + + /// Constrain `a` to be equal to `constant + pi`. + /// + /// `constant` will be defined as part of the public circuit description. + fn assert_equal_constant>( + &mut self, + a: Witness, + constant: C, + public: Option, + ) { + let constant = constant.into(); + let constraint = Constraint::new().left(1).constant(-constant).a(a); + let constraint = + public.map(|p| constraint.public(p)).unwrap_or(constraint); + + self.append_gate(constraint); + } + + /// Asserts `a == b` by appending two gates + fn assert_equal_point(&mut self, a: WitnessPoint, b: WitnessPoint) { + self.assert_equal(*a.x(), *b.x()); + self.assert_equal(*b.y(), *b.y()); + } + + /// Asserts `point == public`. + /// + /// Will add `public` affine coordinates `(x,y)` as public inputs + fn assert_equal_public_point>( + &mut self, + point: WitnessPoint, + public: P, + ) { + let public = public.into(); + + self.assert_equal_constant( + *point.x(), + BlsScalar::zero(), + Some(-public.get_x()), + ); + + self.assert_equal_constant( + *point.y(), + BlsScalar::zero(), + Some(-public.get_y()), + ); + } + + /// Adds two curve points by consuming 2 gates. + fn component_add_point( + &mut self, + a: WitnessPoint, + b: WitnessPoint, + ) -> WitnessPoint { + // In order to verify that two points were correctly added + // without going over a degree 4 polynomial, we will need + // x_1, y_1, x_2, y_2 + // x_3, y_3, x_1 * y_2 + + let x_1 = *a.x(); + let y_1 = *a.y(); + let x_2 = *b.x(); + let y_2 = *b.y(); + + let p1 = JubJubAffine::from_raw_unchecked(self[x_1], self[y_1]); + let p2 = JubJubAffine::from_raw_unchecked(self[x_2], self[y_2]); + + let point: JubJubAffine = (JubJubExtended::from(p1) + p2).into(); + + let x_3 = point.get_x(); + let y_3 = point.get_y(); + + let x1_y2 = self[x_1] * self[y_2]; + + let x_1_y_2 = self.append_witness(x1_y2); + let x_3 = self.append_witness(x_3); + let y_3 = self.append_witness(y_3); + + // Add the rest of the prepared points into the composer + let constraint = Constraint::new().a(x_1).b(y_1).o(x_2).d(y_2); + let constraint = Constraint::group_add_variable_base(&constraint); + + self.append_custom_gate(constraint); + + let constraint = Constraint::new().a(x_3).b(y_3).d(x_1_y_2); + + self.append_custom_gate(constraint); + + WitnessPoint::new(x_3, y_3) + } + + /// Adds a boolean constraint (also known as binary constraint) where the + /// gate eq. will enforce that the [`Witness`] received is either `0` or `1` + /// by adding a constraint in the circuit. + /// + /// Note that using this constraint with whatever [`Witness`] that + /// is not representing a value equalling 0 or 1, will always force the + /// equation to fail. + fn component_boolean(&mut self, a: Witness) { + let zero = Self::ZERO; + let constraint = Constraint::new() + .mult(1) + .output(-BlsScalar::one()) + .a(a) + .b(a) + .o(a) + .d(zero); + + self.append_gate(constraint); + } + + /// Decomposes `scalar` into an array truncated to `N` bits (max 256). + /// + /// Asserts the reconstruction of the bits to be equal to `scalar`. + /// + /// Consume `2 · N + 1` gates + fn component_decomposition( + &mut self, + scalar: Witness, + ) -> [Witness; N] { + // Static assertion + assert!(0 < N && N <= 256); + + let mut decomposition = [Self::ZERO; N]; + + let acc = Self::ZERO; + let acc = self[scalar] + .to_bits() + .iter() + .enumerate() + .zip(decomposition.iter_mut()) + .fold(acc, |acc, ((i, w), d)| { + *d = self.append_witness(BlsScalar::from(*w as u64)); + + self.component_boolean(*d); + + let constraint = Constraint::new() + .left(BlsScalar::pow_of_2(i as u64)) + .right(1) + .a(*d) + .b(acc); + + self.gate_add(constraint) + }); + + self.assert_equal(acc, scalar); + + decomposition + } + + /// Conditionally selects identity as [`WitnessPoint`] based on an input + /// bit. + /// + /// bit == 1 => a, + /// bit == 0 => identity, + /// + /// `bit` is expected to be constrained by + /// [`Composer::component_boolean`] + fn component_select_identity( + &mut self, + bit: Witness, + a: WitnessPoint, + ) -> WitnessPoint { + let x = self.component_select_zero(bit, *a.x()); + let y = self.component_select_one(bit, *a.y()); + + WitnessPoint::new(x, y) + } + + /// Evaluate `jubjub · point` as a [`WitnessPoint`] + fn component_mul_point( + &mut self, + jubjub: Witness, + point: WitnessPoint, + ) -> WitnessPoint { + // Turn scalar into bits + let scalar_bits = self.component_decomposition::<252>(jubjub); + + let mut result = Self::IDENTITY; + + for bit in scalar_bits.iter().rev() { + result = self.component_add_point(result, result); + + let point_to_add = self.component_select_identity(*bit, point); + result = self.component_add_point(result, point_to_add); + } + + result + } + + /// Conditionally selects a [`Witness`] based on an input bit. + /// + /// bit == 1 => a, + /// bit == 0 => b, + /// + /// `bit` is expected to be constrained by + /// [`Composer::component_boolean`] + fn component_select( + &mut self, + bit: Witness, + a: Witness, + b: Witness, + ) -> Witness { + // bit * a + let constraint = Constraint::new().mult(1).a(bit).b(a); + let bit_times_a = self.gate_mul(constraint); + + // 1 - bit + let constraint = + Constraint::new().left(-BlsScalar::one()).constant(1).a(bit); + let one_min_bit = self.gate_add(constraint); + + // (1 - bit) * b + let constraint = Constraint::new().mult(1).a(one_min_bit).b(b); + let one_min_bit_b = self.gate_mul(constraint); + + // [ (1 - bit) * b ] + [ bit * a ] + let constraint = Constraint::new() + .left(1) + .right(1) + .a(one_min_bit_b) + .b(bit_times_a); + self.gate_add(constraint) + } + + /// Conditionally selects a [`Witness`] based on an input bit. + /// + /// bit == 1 => value, + /// bit == 0 => 1, + /// + /// `bit` is expected to be constrained by + /// [`Composer::component_boolean`] + fn component_select_one( + &mut self, + bit: Witness, + value: Witness, + ) -> Witness { + let b = self[bit]; + let v = self[value]; + + let f_x = BlsScalar::one() - b + (b * v); + let f_x = self.append_witness(f_x); + + let constraint = Constraint::new() + .mult(1) + .left(-BlsScalar::one()) + .output(-BlsScalar::one()) + .constant(1) + .a(bit) + .b(value) + .o(f_x); + + self.append_gate(constraint); + + f_x + } + + /// Conditionally selects a [`WitnessPoint`] based on an input bit. + /// + /// bit == 1 => a, + /// bit == 0 => b, + /// + /// `bit` is expected to be constrained by + /// [`Composer::component_boolean`] + fn component_select_point( + &mut self, + bit: Witness, + a: WitnessPoint, + b: WitnessPoint, + ) -> WitnessPoint { + let x = self.component_select(bit, *a.x(), *b.x()); + let y = self.component_select(bit, *a.y(), *b.y()); + + WitnessPoint::new(x, y) + } + + /// Conditionally selects a [`Witness`] based on an input bit. + /// + /// bit == 1 => value, + /// bit == 0 => 0, + /// + /// `bit` is expected to be constrained by + /// [`Composer::component_boolean`] + fn component_select_zero( + &mut self, + bit: Witness, + value: Witness, + ) -> Witness { + let constraint = Constraint::new().mult(1).a(bit).b(value); + + self.gate_mul(constraint) + } + + /// Adds a range-constraint gate that checks and constrains a + /// [`Witness`] to be inside of the range \[0,num_bits\]. + /// + /// This function adds `num_bits/4` gates to the circuit description in + /// order to add the range constraint. + /// + ///# Panics + /// This function will panic if the num_bits specified is not even, ie. + /// `num_bits % 2 != 0`. + fn component_range(&mut self, witness: Witness, num_bits: usize) { + // number of bits must be even + debug_assert_eq!(num_bits % 2, 0); + + // convert witness to bit representation and reverse + let bits = self[witness]; + let bit_iter = BitIterator8::new(bits.to_bytes()); + let mut bits: Vec<_> = bit_iter.collect(); + bits.reverse(); + + // considering this is a width-4 program, one gate will contain 4 + // accumulators. each accumulator proves that a single quad is a + // base-4 digit. accumulators are bijective to quads, and these + // are 2-bits each. given that, one gate accumulates 8 bits. + let mut num_gates = num_bits >> 3; + + // given each gate accumulates 8 bits, its count must be padded + if num_bits % 8 != 0 { + num_gates += 1; + } + + // a gate holds 4 quads + let num_quads = num_gates * 4; + + // the wires are left-padded with the difference between the quads count + // and the bits argument + let pad = 1 + (((num_quads << 1) - num_bits) >> 1); + + // last gate is reserved for either the genesis quad or the padding + let used_gates = num_gates + 1; + + let base = Constraint::new(); + let base = Constraint::range(&base); + let mut constraints = vec![base; used_gates]; + + // We collect the set of accumulators to return back to the user + // and keep a running count of the current accumulator + let mut accumulators: Vec = Vec::new(); + let mut accumulator = BlsScalar::zero(); + let four = BlsScalar::from(4); + + for i in pad..=num_quads { + // convert each pair of bits to quads + let bit_index = (num_quads - i) << 1; + let q_0 = bits[bit_index] as u64; + let q_1 = bits[bit_index + 1] as u64; + let quad = q_0 + (2 * q_1); + + accumulator = four * accumulator; + accumulator += BlsScalar::from(quad); + + let accumulator_var = self.append_witness(accumulator); + + accumulators.push(accumulator_var); + + let idx = i / 4; + let witness = match i % 4 { + 0 => WiredWitness::D, + 1 => WiredWitness::O, + 2 => WiredWitness::B, + 3 => WiredWitness::A, + _ => unreachable!(), + }; + + constraints[idx].set_witness(witness, accumulator_var); + } + + // last constraint is zeroed as it is reserved for the genesis quad or + // padding + constraints.last_mut().map(|c| *c = Constraint::new()); + + // the accumulators count is a function to the number of quads. hence, + // this optional gate will not cause different circuits depending on the + // witness because this computation is bound to the constant bits count + // alone. + if let Some(accumulator) = accumulators.last() { + constraints + .last_mut() + .map(|c| c.set_witness(WiredWitness::D, *accumulator)); + } + + constraints + .into_iter() + .for_each(|c| self.append_custom_gate(c)); + + // the accumulators count is a function to the number of quads. hence, + // this optional gate will not cause different circuits depending on the + // witness because this computation is bound to the constant bits count + // alone. + if let Some(accumulator) = accumulators.last() { + self.assert_equal(*accumulator, witness); + } + } + + /// Evaluate and return `o` by appending a new constraint into the circuit. + /// + /// Set `q_o = (-1)` and override the output of the constraint with: + /// `o := q_l · a + q_r · b + q_4 · d + q_c + PI` + fn gate_add(&mut self, s: Constraint) -> Witness { + let s = Constraint::arithmetic(&s).output(-BlsScalar::one()); + + let o = self + .append_evaluated_output(s) + .expect("output selector is -1"); + let s = s.o(o); + + self.append_gate(s); + + o + } + + /// Evaluate and return `o` by appending a new constraint into the circuit. + /// + /// Set `q_o = (-1)` and override the output of the constraint with: + /// `o := q_m · a · b + q_4 · d + q_c + PI` + fn gate_mul(&mut self, s: Constraint) -> Witness { + let s = Constraint::arithmetic(&s).output(-BlsScalar::one()); + + let o = self + .append_evaluated_output(s) + .expect("output selector is -1"); + let s = s.o(o); + + self.append_gate(s); + + o + } + + /// Prove a circuit with a builder initialized with `constraints` capacity. + fn prove(constraints: usize, circuit: &C) -> Result + where + C: Circuit, + { + let mut builder = Self::initialized(constraints); + + circuit.circuit(&mut builder)?; + + builder.runtime().event(RuntimeEvent::ProofFinished); + + Ok(builder) + } +} diff --git a/src/composer/builder.rs b/src/composer/builder.rs new file mode 100644 index 000000000..2fb511668 --- /dev/null +++ b/src/composer/builder.rs @@ -0,0 +1,160 @@ +// 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 alloc::vec::Vec; +use core::ops; + +use dusk_bls12_381::BlsScalar; +use hashbrown::HashMap; + +use crate::constraint_system::{Constraint, Selector, WiredWitness, Witness}; +use crate::permutation::Permutation; +use crate::runtime::Runtime; + +use super::{Composer, Polynomial}; + +/// Construct and prove circuits +#[derive(Debug, Clone)] +pub struct Builder { + /// Constraint system gates + pub(crate) constraints: Vec, + + /// Sparse representation of the public inputs + pub(crate) public_inputs: HashMap, + + /// Witness values + pub(crate) witnesses: Vec, + + /// Permutation argument. + pub(crate) perm: Permutation, + + /// PLONK runtime controller + pub(crate) runtime: Runtime, +} + +impl Builder { + pub(crate) fn public_input_indexes(&self) -> Vec { + let mut public_input_indexes: Vec<_> = + self.public_inputs.keys().copied().collect(); + + public_input_indexes.as_mut_slice().sort(); + + public_input_indexes + } + + pub(crate) fn public_inputs(&self) -> Vec { + self.public_input_indexes() + .iter() + .filter_map(|idx| self.public_inputs.get(idx).copied()) + .collect() + } + + pub(crate) fn dense_public_inputs( + public_input_indexes: &[usize], + public_inputs: &[BlsScalar], + size: usize, + ) -> Vec { + let mut dense_public_inputs = vec![BlsScalar::zero(); size]; + + public_input_indexes + .iter() + .zip(public_inputs.iter()) + .for_each(|(idx, pi)| dense_public_inputs[*idx] = *pi); + + dense_public_inputs + } +} + +impl ops::Index for Builder { + type Output = BlsScalar; + + fn index(&self, w: Witness) -> &Self::Output { + &self.witnesses[w.index()] + } +} + +impl Composer for Builder { + fn uninitialized(capacity: usize) -> Self { + Self { + constraints: Vec::with_capacity(capacity), + public_inputs: HashMap::new(), + witnesses: Vec::with_capacity(capacity), + perm: Permutation::new(), + runtime: Runtime::with_capacity(capacity), + } + } + + fn constraints(&self) -> usize { + self.constraints.len() + } + + fn append_witness_internal(&mut self, witness: BlsScalar) -> Witness { + let n = self.witnesses.len(); + + // Get a new Witness from the permutation + self.perm.new_witness(); + + // Bind the allocated witness + self.witnesses.push(witness); + + Witness::new(n) + } + + fn append_custom_gate_internal(&mut self, constraint: Constraint) { + let n = self.constraints.len(); + + let w_a = constraint.witness(WiredWitness::A); + let w_b = constraint.witness(WiredWitness::B); + let w_o = constraint.witness(WiredWitness::O); + let w_d = constraint.witness(WiredWitness::D); + + let q_m = *constraint.coeff(Selector::Multiplication); + let q_l = *constraint.coeff(Selector::Left); + let q_r = *constraint.coeff(Selector::Right); + let q_o = *constraint.coeff(Selector::Output); + let q_c = *constraint.coeff(Selector::Constant); + let q_d = *constraint.coeff(Selector::Fourth); + + let q_arith = *constraint.coeff(Selector::Arithmetic); + let q_range = *constraint.coeff(Selector::Range); + let q_logic = *constraint.coeff(Selector::Logic); + let q_fixed_group_add = *constraint.coeff(Selector::GroupAddFixedBase); + let q_variable_group_add = + *constraint.coeff(Selector::GroupAddVariableBase); + + let poly = 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_o, + w_d, + }; + + self.constraints.push(poly); + + if constraint.has_public_input() { + let pi = *constraint.coeff(Selector::PublicInput); + + self.public_inputs.insert(n, pi); + } + + self.perm.add_witnesses_to_map(w_a, w_b, w_o, w_d, n); + } + + fn runtime(&mut self) -> &mut Runtime { + &mut self.runtime + } +} diff --git a/src/composer/circuit.rs b/src/composer/circuit.rs new file mode 100644 index 000000000..ffb545bdc --- /dev/null +++ b/src/composer/circuit.rs @@ -0,0 +1,19 @@ +// 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 crate::error::Error; + +use super::Composer; + +/// Circuit implementation that can be proved by a Composer +/// +/// The default implementation will be used to generate the proving arguments. +pub trait Circuit: Default { + /// Circuit definition + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer; +} diff --git a/src/composer/compiler.rs b/src/composer/compiler.rs new file mode 100644 index 000000000..2208ad424 --- /dev/null +++ b/src/composer/compiler.rs @@ -0,0 +1,388 @@ +// 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_bls12_381::BlsScalar; + +use crate::commitment_scheme::{CommitKey, OpeningKey, PublicParameters}; +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}; + +/// Generate the arguments to prove and verify a circuit +pub struct Compiler; + +impl Compiler { + /// Create a new arguments set from a given circuit instance + /// + /// Use the default implementation of the circuit + pub fn compile( + pp: &PublicParameters, + label: &[u8], + ) -> Result<(Prover, Verifier), Error> + where + C: Circuit, + { + Self::compile_with_circuit(pp, label, &Default::default()) + } + + /// Create a new arguments set from a given circuit instance + /// + /// Use the provided circuit instead of the default implementation + pub fn compile_with_circuit( + pp: &PublicParameters, + label: &[u8], + circuit: &C, + ) -> 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); + + circuit.circuit(&mut prover)?; + + let n = (prover.constraints() + 6).next_power_of_two(); + + let (commit, opening) = pp.trim(n)?; + + let (prover, verifier) = + Self::preprocess(label, commit, opening, &prover)?; + + Ok((prover, verifier)) + } + + fn preprocess( + label: &[u8], + commit_key: CommitKey, + opening_key: OpeningKey, + prover: &Builder, + ) -> Result<(Prover, Verifier), Error> + where + C: Circuit, + { + let mut perm = prover.perm.clone(); + + let constraints = prover.constraints(); + let size = constraints.next_power_of_two(); + + let domain = EvaluationDomain::new(size - 1)?; + + // 1. pad circuit to a power of two + // + // we use allocated vectors because the current ifft api only accepts + // slices + let mut q_m = vec![BlsScalar::zero(); size]; + let mut q_l = vec![BlsScalar::zero(); size]; + let mut q_r = vec![BlsScalar::zero(); size]; + let mut q_o = vec![BlsScalar::zero(); size]; + let mut q_c = vec![BlsScalar::zero(); size]; + let mut q_d = vec![BlsScalar::zero(); size]; + let mut q_arith = vec![BlsScalar::zero(); size]; + let mut q_range = vec![BlsScalar::zero(); size]; + let mut q_logic = vec![BlsScalar::zero(); size]; + let mut q_fixed_group_add = vec![BlsScalar::zero(); size]; + let mut q_variable_group_add = vec![BlsScalar::zero(); size]; + + prover.constraints.iter().enumerate().for_each(|(i, c)| { + q_m[i] = c.q_m; + q_l[i] = c.q_l; + q_r[i] = c.q_r; + q_o[i] = c.q_o; + q_c[i] = c.q_c; + q_d[i] = c.q_d; + q_arith[i] = c.q_arith; + q_range[i] = c.q_range; + q_logic[i] = c.q_logic; + q_fixed_group_add[i] = c.q_fixed_group_add; + q_variable_group_add[i] = c.q_variable_group_add; + }); + + let q_m_poly = domain.ifft(&q_m); + let q_l_poly = domain.ifft(&q_l); + let q_r_poly = domain.ifft(&q_r); + let q_o_poly = domain.ifft(&q_o); + let q_c_poly = domain.ifft(&q_c); + let q_d_poly = domain.ifft(&q_d); + let q_arith_poly = domain.ifft(&q_arith); + let q_range_poly = domain.ifft(&q_range); + let q_logic_poly = domain.ifft(&q_logic); + let q_fixed_group_add_poly = domain.ifft(&q_fixed_group_add); + let q_variable_group_add_poly = domain.ifft(&q_variable_group_add); + + let q_m_poly = FftPolynomial::from_coefficients_vec(q_m_poly); + let q_l_poly = FftPolynomial::from_coefficients_vec(q_l_poly); + let q_r_poly = FftPolynomial::from_coefficients_vec(q_r_poly); + let q_o_poly = FftPolynomial::from_coefficients_vec(q_o_poly); + let q_c_poly = FftPolynomial::from_coefficients_vec(q_c_poly); + let q_d_poly = FftPolynomial::from_coefficients_vec(q_d_poly); + let q_arith_poly = FftPolynomial::from_coefficients_vec(q_arith_poly); + let q_range_poly = FftPolynomial::from_coefficients_vec(q_range_poly); + let q_logic_poly = FftPolynomial::from_coefficients_vec(q_logic_poly); + let q_fixed_group_add_poly = + FftPolynomial::from_coefficients_vec(q_fixed_group_add_poly); + let q_variable_group_add_poly = + FftPolynomial::from_coefficients_vec(q_variable_group_add_poly); + + // 2. compute the sigma polynomials + let [s_sigma_1_poly, s_sigma_2_poly, s_sigma_3_poly, s_sigma_4_poly] = + perm.compute_sigma_polynomials(size, &domain); + + let q_m_poly_commit = commit_key.commit(&q_m_poly).unwrap_or_default(); + let q_l_poly_commit = commit_key.commit(&q_l_poly).unwrap_or_default(); + let q_r_poly_commit = commit_key.commit(&q_r_poly).unwrap_or_default(); + let q_o_poly_commit = commit_key.commit(&q_o_poly).unwrap_or_default(); + let q_c_poly_commit = commit_key.commit(&q_c_poly).unwrap_or_default(); + let q_d_poly_commit = commit_key.commit(&q_d_poly).unwrap_or_default(); + let q_arith_poly_commit = + commit_key.commit(&q_arith_poly).unwrap_or_default(); + let q_range_poly_commit = + commit_key.commit(&q_range_poly).unwrap_or_default(); + let q_logic_poly_commit = + commit_key.commit(&q_logic_poly).unwrap_or_default(); + let q_fixed_group_add_poly_commit = commit_key + .commit(&q_fixed_group_add_poly) + .unwrap_or_default(); + let q_variable_group_add_poly_commit = commit_key + .commit(&q_variable_group_add_poly) + .unwrap_or_default(); + + let s_sigma_1_poly_commit = commit_key.commit(&s_sigma_1_poly)?; + let s_sigma_2_poly_commit = commit_key.commit(&s_sigma_2_poly)?; + let s_sigma_3_poly_commit = commit_key.commit(&s_sigma_3_poly)?; + let s_sigma_4_poly_commit = commit_key.commit(&s_sigma_4_poly)?; + + // verifier Key for arithmetic circuits + let arithmetic_verifier_key = widget::arithmetic::VerifierKey { + q_m: q_m_poly_commit, + q_l: q_l_poly_commit, + q_r: q_r_poly_commit, + q_o: q_o_poly_commit, + q_c: q_c_poly_commit, + q_4: q_d_poly_commit, + q_arith: q_arith_poly_commit, + }; + + // verifier Key for range circuits + let range_verifier_key = widget::range::VerifierKey { + q_range: q_range_poly_commit, + }; + + // verifier Key for logic circuits + let logic_verifier_key = widget::logic::VerifierKey { + q_c: q_c_poly_commit, + q_logic: q_logic_poly_commit, + }; + + // verifier Key for ecc circuits + let ecc_verifier_key = + widget::ecc::scalar_mul::fixed_base::VerifierKey { + q_l: q_l_poly_commit, + q_r: q_r_poly_commit, + q_fixed_group_add: q_fixed_group_add_poly_commit, + }; + + // verifier Key for curve addition circuits + let curve_addition_verifier_key = + widget::ecc::curve_addition::VerifierKey { + q_variable_group_add: q_variable_group_add_poly_commit, + }; + + // verifier Key for permutation argument + let permutation_verifier_key = widget::permutation::VerifierKey { + s_sigma_1: s_sigma_1_poly_commit, + s_sigma_2: s_sigma_2_poly_commit, + s_sigma_3: s_sigma_3_poly_commit, + s_sigma_4: s_sigma_4_poly_commit, + }; + + let verifier_key = widget::VerifierKey { + n: constraints, + arithmetic: arithmetic_verifier_key, + logic: logic_verifier_key, + range: range_verifier_key, + fixed_base: ecc_verifier_key, + variable_base: curve_addition_verifier_key, + permutation: permutation_verifier_key, + }; + + let selectors = Polynomials { + q_m: q_m_poly, + q_l: q_l_poly, + q_r: q_r_poly, + q_o: q_o_poly, + q_c: q_c_poly, + q_4: q_d_poly, + q_arith: q_arith_poly, + q_range: q_range_poly, + q_logic: q_logic_poly, + q_fixed_group_add: q_fixed_group_add_poly, + q_variable_group_add: q_variable_group_add_poly, + s_sigma_1: s_sigma_1_poly, + s_sigma_2: s_sigma_2_poly, + s_sigma_3: s_sigma_3_poly, + s_sigma_4: s_sigma_4_poly, + }; + + // The polynomial needs an evaluation domain of 4n. + // Plus, adding the blinding factors translates to + // the polynomial not fitting in 4n, so now we need + // 8n, the next power of 2 + let domain_8n = EvaluationDomain::new(8 * domain.size())?; + + let q_m_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.q_m), + domain_8n, + ); + let q_l_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.q_l), + domain_8n, + ); + let q_r_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.q_r), + domain_8n, + ); + let q_o_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.q_o), + domain_8n, + ); + let q_c_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.q_c), + domain_8n, + ); + let q_4_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.q_4), + domain_8n, + ); + let q_arith_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.q_arith), + domain_8n, + ); + let q_range_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.q_range), + domain_8n, + ); + let q_logic_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.q_logic), + domain_8n, + ); + let q_fixed_group_add_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.q_fixed_group_add), + domain_8n, + ); + let q_variable_group_add_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.q_variable_group_add), + domain_8n, + ); + + let s_sigma_1_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.s_sigma_1), + domain_8n, + ); + let s_sigma_2_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.s_sigma_2), + domain_8n, + ); + let s_sigma_3_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.s_sigma_3), + domain_8n, + ); + let s_sigma_4_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&selectors.s_sigma_4), + domain_8n, + ); + + let linear_eval_8n = Evaluations::from_vec_and_domain( + domain_8n.coset_fft(&[BlsScalar::zero(), BlsScalar::one()]), + domain_8n, + ); + + let arithmetic_prover_key = widget::arithmetic::ProverKey { + q_m: (selectors.q_m, q_m_eval_8n), + q_l: (selectors.q_l.clone(), q_l_eval_8n.clone()), + q_r: (selectors.q_r.clone(), q_r_eval_8n.clone()), + q_o: (selectors.q_o, q_o_eval_8n), + q_c: (selectors.q_c.clone(), q_c_eval_8n.clone()), + q_4: (selectors.q_4, q_4_eval_8n), + q_arith: (selectors.q_arith, q_arith_eval_8n), + }; + + let range_prover_key = widget::range::ProverKey { + q_range: (selectors.q_range, q_range_eval_8n), + }; + + let logic_prover_key = widget::logic::ProverKey { + q_c: (selectors.q_c.clone(), q_c_eval_8n.clone()), + q_logic: (selectors.q_logic, q_logic_eval_8n), + }; + + let ecc_prover_key = widget::ecc::scalar_mul::fixed_base::ProverKey { + q_l: (selectors.q_l, q_l_eval_8n), + q_r: (selectors.q_r, q_r_eval_8n), + q_c: (selectors.q_c, q_c_eval_8n), + q_fixed_group_add: ( + selectors.q_fixed_group_add, + q_fixed_group_add_eval_8n, + ), + }; + + let permutation_prover_key = widget::permutation::ProverKey { + s_sigma_1: (selectors.s_sigma_1, s_sigma_1_eval_8n), + s_sigma_2: (selectors.s_sigma_2, s_sigma_2_eval_8n), + s_sigma_3: (selectors.s_sigma_3, s_sigma_3_eval_8n), + s_sigma_4: (selectors.s_sigma_4, s_sigma_4_eval_8n), + linear_evaluations: linear_eval_8n, + }; + + let curve_addition_prover_key = + widget::ecc::curve_addition::ProverKey { + q_variable_group_add: ( + selectors.q_variable_group_add, + q_variable_group_add_eval_8n, + ), + }; + + let v_h_coset_8n = + domain_8n.compute_vanishing_poly_over_coset(domain.size() as u64); + + let prover_key = ProverKey { + n: domain.size(), + arithmetic: arithmetic_prover_key, + logic: logic_prover_key, + range: range_prover_key, + permutation: permutation_prover_key, + variable_base: curve_addition_prover_key, + fixed_base: ecc_prover_key, + v_h_coset_8n, + }; + + let public_input_indexes = prover.public_input_indexes(); + + let label = label.to_vec(); + + let prover = Prover::new( + label.clone(), + prover_key, + commit_key, + verifier_key.clone(), + size, + constraints, + ); + + let verifier = Verifier::new( + label, + verifier_key, + opening_key, + public_input_indexes, + size, + constraints, + ); + + Ok((prover, verifier)) + } +} diff --git a/src/composer/polynomial.rs b/src/composer/polynomial.rs new file mode 100644 index 000000000..b705eb6ec --- /dev/null +++ b/src/composer/polynomial.rs @@ -0,0 +1,46 @@ +// 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_bls12_381::BlsScalar; + +use crate::constraint_system::Witness; + +/// Represents a polynomial in coefficient form with its associated wire data +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Polynomial { + // Selectors + /// Multiplier selector + pub(crate) q_m: BlsScalar, + /// Left wire selector + pub(crate) q_l: BlsScalar, + /// Right wire selector + pub(crate) q_r: BlsScalar, + /// Output wire selector + pub(crate) q_o: BlsScalar, + /// Constant wire selector + pub(crate) q_c: BlsScalar, + /// Fourth wire selector + pub(crate) q_d: BlsScalar, + /// Arithmetic wire selector + pub(crate) q_arith: BlsScalar, + /// Range selector + pub(crate) q_range: BlsScalar, + /// Logic selector + pub(crate) q_logic: BlsScalar, + /// Fixed base group addition selector + pub(crate) q_fixed_group_add: BlsScalar, + /// Variable base group addition selector + pub(crate) q_variable_group_add: BlsScalar, + + /// Left wire witness. + pub(crate) w_a: Witness, + /// Right wire witness. + pub(crate) w_b: Witness, + /// Fourth wire witness. + pub(crate) w_d: Witness, + /// Output wire witness. + pub(crate) w_o: Witness, +} diff --git a/src/composer/prover.rs b/src/composer/prover.rs new file mode 100644 index 000000000..fbd9cae13 --- /dev/null +++ b/src/composer/prover.rs @@ -0,0 +1,511 @@ +// 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 alloc::vec::Vec; +use core::marker::PhantomData; +use core::ops; + +use dusk_bls12_381::BlsScalar; +use dusk_bytes::{DeserializableSlice, Serializable}; +use merlin::Transcript; +use rand_core::{CryptoRng, RngCore}; + +use crate::commitment_scheme::CommitKey; +use crate::error::Error; +use crate::fft::{EvaluationDomain, Polynomial as FftPolynomial}; +use crate::proof_system::proof::Proof; +use crate::proof_system::{ + linearization_poly, quotient_poly, ProverKey, VerifierKey, +}; +use crate::transcript::TranscriptProtocol; +use crate::util; + +use super::{Builder, Circuit, Composer}; + +/// Turbo Prover with processed keys +#[derive(Clone)] +pub struct Prover +where + C: Circuit, +{ + label: Vec, + pub(crate) prover_key: ProverKey, + pub(crate) commit_key: CommitKey, + pub(crate) verifier_key: VerifierKey, + pub(crate) transcript: Transcript, + pub(crate) size: usize, + pub(crate) constraints: usize, + circuit: PhantomData, +} + +impl ops::Deref for Prover +where + C: Circuit, +{ + type Target = ProverKey; + + fn deref(&self) -> &Self::Target { + &self.prover_key + } +} + +impl Prover +where + C: Circuit, +{ + pub(crate) fn new( + label: Vec, + prover_key: ProverKey, + commit_key: CommitKey, + verifier_key: VerifierKey, + size: usize, + constraints: usize, + ) -> Self { + let transcript = + Transcript::base(label.as_slice(), &verifier_key, constraints); + + Self { + label, + prover_key, + commit_key, + verifier_key, + transcript, + size, + constraints, + circuit: PhantomData, + } + } + + /// adds blinding scalars to a witness vector + /// + /// appends: + /// + /// if hiding degree = 1: (b2*X^(n+1) + b1*X^n - b2*X - b1) + witnesses + /// if hiding degree = 2: (b3*X^(n+2) + b2*X^(n+1) + b1*X^n - b3*X^2 - b2*X + fn blind_poly( + rng: &mut R, + witnesses: &[BlsScalar], + hiding_degree: usize, + domain: &EvaluationDomain, + ) -> FftPolynomial + where + R: RngCore + CryptoRng, + { + let mut w_vec_inverse = domain.ifft(witnesses); + + for i in 0..hiding_degree + 1 { + let blinding_scalar = util::random_scalar(rng); + + w_vec_inverse[i] = w_vec_inverse[i] - blinding_scalar; + w_vec_inverse.push(blinding_scalar); + } + + FftPolynomial::from_coefficients_vec(w_vec_inverse) + } + + fn prepare_serialize( + &self, + ) -> (usize, Vec, Vec, [u8; VerifierKey::SIZE]) { + let prover_key = self.prover_key.to_var_bytes(); + let commit_key = self.commit_key.to_raw_var_bytes(); + let verifier_key = self.verifier_key.to_bytes(); + + let label_len = self.label.len(); + let prover_key_len = prover_key.len(); + let commit_key_len = commit_key.len(); + let verifier_key_len = verifier_key.len(); + + let size = + 48 + label_len + prover_key_len + commit_key_len + verifier_key_len; + + (size, prover_key, commit_key, verifier_key) + } + + /// Serialized size in bytes + pub fn serialized_size(&self) -> usize { + self.prepare_serialize().0 + } + + /// Serialize the prover into bytes + pub fn to_bytes(&self) -> Vec { + let (size, prover_key, commit_key, verifier_key) = + self.prepare_serialize(); + let mut bytes = Vec::with_capacity(size); + + let label_len = self.label.len() as u64; + let prover_key_len = prover_key.len() as u64; + let commit_key_len = commit_key.len() as u64; + let verifier_key_len = verifier_key.len() as u64; + let size = self.size as u64; + let constraints = self.constraints as u64; + + bytes.extend(label_len.to_be_bytes()); + bytes.extend(prover_key_len.to_be_bytes()); + bytes.extend(commit_key_len.to_be_bytes()); + bytes.extend(verifier_key_len.to_be_bytes()); + bytes.extend(size.to_be_bytes()); + bytes.extend(constraints.to_be_bytes()); + + bytes.extend(self.label.as_slice()); + bytes.extend(prover_key); + bytes.extend(commit_key); + bytes.extend(verifier_key); + + bytes + } + + /// Attempt to deserialize the prover from bytes generated via + /// [`Self::to_bytes`] + pub fn try_from_bytes(bytes: B) -> Result + where + B: AsRef<[u8]>, + { + let mut bytes = bytes.as_ref(); + + if bytes.len() < 48 { + return Err(Error::NotEnoughBytes); + } + + let label_len = <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let label_len = u64::from_be_bytes(label_len) as usize; + bytes = &bytes[8..]; + + let prover_key_len = + <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let prover_key_len = u64::from_be_bytes(prover_key_len) as usize; + bytes = &bytes[8..]; + + let commit_key_len = + <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let commit_key_len = u64::from_be_bytes(commit_key_len) as usize; + bytes = &bytes[8..]; + + let verifier_key_len = + <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let verifier_key_len = u64::from_be_bytes(verifier_key_len) as usize; + bytes = &bytes[8..]; + + let size = <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let size = u64::from_be_bytes(size) as usize; + bytes = &bytes[8..]; + + let constraints = + <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let constraints = u64::from_be_bytes(constraints) as usize; + bytes = &bytes[8..]; + + if bytes.len() + < label_len + prover_key_len + commit_key_len + verifier_key_len + { + return Err(Error::NotEnoughBytes); + } + + let label = &bytes[..label_len]; + bytes = &bytes[label_len..]; + + let prover_key = &bytes[..prover_key_len]; + bytes = &bytes[prover_key_len..]; + + let commit_key = &bytes[..commit_key_len]; + bytes = &bytes[commit_key_len..]; + + let verifier_key = &bytes[..verifier_key_len]; + + let label = label.to_vec(); + let prover_key = ProverKey::from_slice(prover_key)?; + + // Safety: checked len + let commit_key = + unsafe { CommitKey::from_slice_unchecked(&commit_key) }; + + let verifier_key = VerifierKey::from_slice(verifier_key)?; + + Ok(Self::new( + label, + prover_key, + commit_key, + verifier_key, + size, + constraints, + )) + } + + /// Prove the circuit + pub fn prove( + &self, + rng: &mut R, + circuit: &C, + ) -> Result<(Proof, Vec), Error> + where + C: Circuit, + R: RngCore + CryptoRng, + { + let prover = Builder::prove(self.constraints, circuit)?; + + let constraints = self.constraints; + let size = self.size; + + let domain = EvaluationDomain::new(constraints)?; + + let mut transcript = self.transcript.clone(); + + let public_inputs = prover.public_inputs(); + let public_input_indexes = prover.public_input_indexes(); + let dense_public_inputs = Builder::dense_public_inputs( + &public_input_indexes, + &public_inputs, + self.size, + ); + + public_inputs + .iter() + .for_each(|pi| transcript.append_scalar(b"pi", pi)); + + // round 1 + // convert wires to padded scalars + let mut a_w_scalar = vec![BlsScalar::zero(); size]; + let mut b_w_scalar = vec![BlsScalar::zero(); size]; + let mut o_w_scalar = vec![BlsScalar::zero(); size]; + let mut d_w_scalar = vec![BlsScalar::zero(); size]; + + prover.constraints.iter().enumerate().for_each(|(i, c)| { + a_w_scalar[i] = prover[c.w_a]; + b_w_scalar[i] = prover[c.w_b]; + o_w_scalar[i] = prover[c.w_o]; + d_w_scalar[i] = prover[c.w_d]; + }); + + let a_w_poly = Self::blind_poly(rng, &a_w_scalar, 1, &domain); + let b_w_poly = Self::blind_poly(rng, &b_w_scalar, 1, &domain); + let o_w_poly = Self::blind_poly(rng, &o_w_scalar, 1, &domain); + let d_w_poly = Self::blind_poly(rng, &d_w_scalar, 1, &domain); + + // commit to wire polynomials + // ([a(x)]_1, [b(x)]_1, [c(x)]_1, [d(x)]_1) + let a_w_poly_commit = self.commit_key.commit(&a_w_poly)?; + let b_w_poly_commit = self.commit_key.commit(&b_w_poly)?; + let o_w_poly_commit = self.commit_key.commit(&o_w_poly)?; + let d_w_poly_commit = self.commit_key.commit(&d_w_poly)?; + + // Add wire polynomial commitments to transcript + transcript.append_commitment(b"a_w", &a_w_poly_commit); + transcript.append_commitment(b"b_w", &b_w_poly_commit); + transcript.append_commitment(b"c_w", &o_w_poly_commit); + transcript.append_commitment(b"d_w", &d_w_poly_commit); + + // round 2 + // permutation challenges + let beta = transcript.challenge_scalar(b"beta"); + transcript.append_scalar(b"beta", &beta); + + let gamma = transcript.challenge_scalar(b"gamma"); + let sigma = [ + &self.prover_key.permutation.s_sigma_1.0, + &self.prover_key.permutation.s_sigma_2.0, + &self.prover_key.permutation.s_sigma_3.0, + &self.prover_key.permutation.s_sigma_4.0, + ]; + let wires = [ + a_w_scalar.as_slice(), + b_w_scalar.as_slice(), + o_w_scalar.as_slice(), + d_w_scalar.as_slice(), + ]; + let permutation = prover + .perm + .compute_permutation_vec(&domain, wires, &beta, &gamma, sigma); + + let z_poly = Self::blind_poly(rng, &permutation, 2, &domain); + let z_poly_commit = self.commit_key.commit(&z_poly)?; + transcript.append_commitment(b"z", &z_poly_commit); + + // round 3 + // compute quotient challenge alpha + let alpha = transcript.challenge_scalar(b"alpha"); + let range_sep_challenge = + transcript.challenge_scalar(b"range separation challenge"); + let logic_sep_challenge = + transcript.challenge_scalar(b"logic separation challenge"); + let fixed_base_sep_challenge = + transcript.challenge_scalar(b"fixed base separation challenge"); + let var_base_sep_challenge = + transcript.challenge_scalar(b"variable base separation challenge"); + + // compute public inputs polynomial + let pi_poly = domain.ifft(&dense_public_inputs); + let pi_poly = FftPolynomial::from_coefficients_vec(pi_poly); + + // compute quotient polynomial + let wires = (&a_w_poly, &b_w_poly, &o_w_poly, &d_w_poly); + let args = &( + alpha, + beta, + gamma, + range_sep_challenge, + logic_sep_challenge, + fixed_base_sep_challenge, + var_base_sep_challenge, + ); + let t_poly = quotient_poly::compute( + &domain, + &self.prover_key, + &z_poly, + wires, + &pi_poly, + args, + )?; + + // split quotient polynomial into 4 degree `n` polynomials + let domain_size = domain.size(); + let t_low_poly = FftPolynomial::from_coefficients_vec( + t_poly[0..domain_size].to_vec(), + ); + let t_mid_poly = FftPolynomial::from_coefficients_vec( + t_poly[domain_size..2 * domain_size].to_vec(), + ); + let t_high_poly = FftPolynomial::from_coefficients_vec( + t_poly[2 * domain_size..3 * domain_size].to_vec(), + ); + let t_4_poly = FftPolynomial::from_coefficients_vec( + t_poly[3 * domain_size..].to_vec(), + ); + + // commit to split quotient polynomial + let t_low_commit = self.commit_key.commit(&t_low_poly)?; + let t_mid_commit = self.commit_key.commit(&t_mid_poly)?; + let t_high_commit = self.commit_key.commit(&t_high_poly)?; + let t_4_commit = self.commit_key.commit(&t_4_poly)?; + + // add quotient polynomial commitments to transcript + transcript.append_commitment(b"t_low", &t_low_commit); + transcript.append_commitment(b"t_mid", &t_mid_commit); + transcript.append_commitment(b"t_high", &t_high_commit); + transcript.append_commitment(b"t_4", &t_4_commit); + + // round 4 + // compute evaluation challenge 'z' + let z_challenge = transcript.challenge_scalar(b"z_challenge"); + + // round 5 + // compute linearization polynomial + let (r_poly, evaluations) = linearization_poly::compute( + &domain, + &self.prover_key, + &( + alpha, + beta, + gamma, + range_sep_challenge, + logic_sep_challenge, + fixed_base_sep_challenge, + var_base_sep_challenge, + z_challenge, + ), + &a_w_poly, + &b_w_poly, + &o_w_poly, + &d_w_poly, + &t_poly, + &z_poly, + ); + + // add evaluations to transcript. + transcript.append_scalar(b"a_eval", &evaluations.proof.a_eval); + transcript.append_scalar(b"b_eval", &evaluations.proof.b_eval); + transcript.append_scalar(b"c_eval", &evaluations.proof.c_eval); + transcript.append_scalar(b"d_eval", &evaluations.proof.d_eval); + transcript + .append_scalar(b"a_next_eval", &evaluations.proof.a_next_eval); + transcript + .append_scalar(b"b_next_eval", &evaluations.proof.b_next_eval); + transcript + .append_scalar(b"d_next_eval", &evaluations.proof.d_next_eval); + transcript.append_scalar( + b"s_sigma_1_eval", + &evaluations.proof.s_sigma_1_eval, + ); + transcript.append_scalar( + b"s_sigma_2_eval", + &evaluations.proof.s_sigma_2_eval, + ); + transcript.append_scalar( + b"s_sigma_3_eval", + &evaluations.proof.s_sigma_3_eval, + ); + transcript + .append_scalar(b"q_arith_eval", &evaluations.proof.q_arith_eval); + transcript.append_scalar(b"q_c_eval", &evaluations.proof.q_c_eval); + transcript.append_scalar(b"q_l_eval", &evaluations.proof.q_l_eval); + transcript.append_scalar(b"q_r_eval", &evaluations.proof.q_r_eval); + transcript.append_scalar(b"perm_eval", &evaluations.proof.perm_eval); + transcript.append_scalar(b"t_eval", &evaluations.t_eval); + transcript.append_scalar(b"r_eval", &evaluations.proof.r_poly_eval); + + // compute Openings using KZG10 + let z_n = z_challenge.pow(&[domain_size as u64, 0, 0, 0]); + let z_two_n = z_challenge.pow(&[2 * domain_size as u64, 0, 0, 0]); + let z_three_n = z_challenge.pow(&[3 * domain_size as u64, 0, 0, 0]); + + let a = &t_low_poly; + let b = &t_mid_poly * &z_n; + let c = &t_high_poly * &z_two_n; + let d = &t_4_poly * &z_three_n; + let abc = &(a + &b) + &c; + + let quot = &abc + &d; + + // compute aggregate witness to polynomials evaluated at the evaluation + // challenge z. The challenge v is selected inside + let aggregate_witness = self.commit_key.compute_aggregate_witness( + &[ + quot, + r_poly, + a_w_poly.clone(), + b_w_poly.clone(), + o_w_poly, + d_w_poly.clone(), + self.prover_key.permutation.s_sigma_1.0.clone(), + self.prover_key.permutation.s_sigma_2.0.clone(), + self.prover_key.permutation.s_sigma_3.0.clone(), + ], + &z_challenge, + &mut transcript, + ); + let w_z_chall_comm = self.commit_key.commit(&aggregate_witness)?; + + // compute aggregate witness to polynomials evaluated at the shifted + // evaluation challenge + let shifted_aggregate_witness = + self.commit_key.compute_aggregate_witness( + &[z_poly, a_w_poly, b_w_poly, d_w_poly], + &(z_challenge * domain.group_gen), + &mut transcript, + ); + let w_z_chall_w_comm = + self.commit_key.commit(&shifted_aggregate_witness)?; + + let proof = Proof { + a_comm: a_w_poly_commit, + b_comm: b_w_poly_commit, + c_comm: o_w_poly_commit, + d_comm: d_w_poly_commit, + + z_comm: z_poly_commit, + + t_low_comm: t_low_commit, + t_mid_comm: t_mid_commit, + t_high_comm: t_high_commit, + t_4_comm: t_4_commit, + + w_z_chall_comm, + w_z_chall_w_comm, + + evaluations: evaluations.proof, + }; + + Ok((proof, public_inputs)) + } +} diff --git a/src/composer/verifier.rs b/src/composer/verifier.rs new file mode 100644 index 000000000..78c44e7c5 --- /dev/null +++ b/src/composer/verifier.rs @@ -0,0 +1,227 @@ +// 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 alloc::vec::Vec; +use core::marker::PhantomData; + +use dusk_bls12_381::BlsScalar; +use dusk_bytes::{DeserializableSlice, Serializable}; +use merlin::Transcript; + +use crate::commitment_scheme::OpeningKey; +use crate::error::Error; +use crate::proof_system::{Proof, VerifierKey}; +use crate::transcript::TranscriptProtocol; + +use super::Builder; + +/// Verify proofs of a given circuit +pub struct Verifier { + label: Vec, + verifier_key: VerifierKey, + opening_key: OpeningKey, + public_input_indexes: Vec, + transcript: Transcript, + size: usize, + constraints: usize, + circuit: PhantomData, +} + +impl Verifier { + pub(crate) fn new( + label: Vec, + verifier_key: VerifierKey, + opening_key: OpeningKey, + public_input_indexes: Vec, + size: usize, + constraints: usize, + ) -> Self { + let transcript = + Transcript::base(label.as_slice(), &verifier_key, constraints); + + Self { + label, + verifier_key, + opening_key, + public_input_indexes, + transcript, + size, + constraints, + circuit: PhantomData, + } + } + + fn prepare_serialize( + &self, + ) -> (usize, [u8; VerifierKey::SIZE], [u8; OpeningKey::SIZE]) { + let verifier_key = self.verifier_key.to_bytes(); + let opening_key = self.opening_key.to_bytes(); + + let label_len = self.label.len(); + let verifier_key_len = verifier_key.len(); + let opening_key_len = opening_key.len(); + let public_input_indexes_len = self.public_input_indexes.len() * 8; + + let size = 48 + + label_len + + verifier_key_len + + opening_key_len + + public_input_indexes_len; + + (size, verifier_key, opening_key) + } + + /// Serialized size in bytes + pub fn serialized_size(&self) -> usize { + self.prepare_serialize().0 + } + + /// Serialize the verifier into bytes + pub fn to_bytes(&self) -> Vec { + let (size, verifier_key, opening_key) = self.prepare_serialize(); + let mut bytes = Vec::with_capacity(size); + + let label_len = self.label.len() as u64; + let verifier_key_len = verifier_key.len() as u64; + let opening_key_len = opening_key.len() as u64; + let public_input_indexes_len = self.public_input_indexes.len() as u64; + let size = self.size as u64; + let constraints = self.constraints as u64; + + bytes.extend(label_len.to_be_bytes()); + bytes.extend(verifier_key_len.to_be_bytes()); + bytes.extend(opening_key_len.to_be_bytes()); + bytes.extend(public_input_indexes_len.to_be_bytes()); + bytes.extend(size.to_be_bytes()); + bytes.extend(constraints.to_be_bytes()); + + bytes.extend(self.label.as_slice()); + bytes.extend(verifier_key); + bytes.extend(opening_key); + + self.public_input_indexes + .iter() + .map(|i| *i as u64) + .map(u64::to_be_bytes) + .for_each(|i| bytes.extend(i)); + + bytes + } + + /// Attempt to deserialize the prover from bytes generated via + /// [`Self::to_bytes`] + pub fn try_from_bytes(bytes: B) -> Result + where + B: AsRef<[u8]>, + { + let mut bytes = bytes.as_ref(); + + if bytes.len() < 48 { + return Err(Error::NotEnoughBytes); + } + + let label_len = <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let label_len = u64::from_be_bytes(label_len) as usize; + bytes = &bytes[8..]; + + let verifier_key_len = + <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let verifier_key_len = u64::from_be_bytes(verifier_key_len) as usize; + bytes = &bytes[8..]; + + let opening_key_len = + <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let opening_key_len = u64::from_be_bytes(opening_key_len) as usize; + bytes = &bytes[8..]; + + let public_input_indexes_len = + <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let public_input_indexes_len = + u64::from_be_bytes(public_input_indexes_len) as usize; + bytes = &bytes[8..]; + + let size = <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let size = u64::from_be_bytes(size) as usize; + bytes = &bytes[8..]; + + let constraints = + <[u8; 8]>::try_from(&bytes[..8]).expect("checked len"); + let constraints = u64::from_be_bytes(constraints) as usize; + bytes = &bytes[8..]; + + if bytes.len() + < label_len + + verifier_key_len + + opening_key_len + + public_input_indexes_len * 8 + { + return Err(Error::NotEnoughBytes); + } + + let label = &bytes[..label_len]; + bytes = &bytes[label_len..]; + + let verifier_key = &bytes[..verifier_key_len]; + bytes = &bytes[verifier_key_len..]; + + let opening_key = &bytes[..opening_key_len]; + bytes = &bytes[opening_key_len..]; + + let public_input_indexes = &bytes[..public_input_indexes_len * 8]; + + let label = label.to_vec(); + let verifier_key = VerifierKey::from_slice(verifier_key)?; + let opening_key = OpeningKey::from_slice(opening_key)?; + let public_input_indexes = public_input_indexes + .chunks_exact(8) + .map(|c| <[u8; 8]>::try_from(c).expect("checked len")) + .map(u64::from_be_bytes) + .map(|n| n as usize) + .collect(); + + Ok(Self::new( + label, + verifier_key, + opening_key, + public_input_indexes, + size, + constraints, + )) + } + + /// Verify a generated proof + pub fn verify( + &self, + proof: &Proof, + public_inputs: &[BlsScalar], + ) -> Result<(), Error> { + if public_inputs.len() != self.public_input_indexes.len() { + return Err(Error::InconsistentPublicInputsLen { + expected: self.public_input_indexes.len(), + provided: public_inputs.len(), + }); + } + + let mut transcript = self.transcript.clone(); + + public_inputs + .iter() + .for_each(|pi| transcript.append_scalar(b"pi", pi)); + + let dense_public_inputs = Builder::dense_public_inputs( + &self.public_input_indexes, + public_inputs, + self.size, + ); + + proof.verify( + &self.verifier_key, + &mut transcript, + &self.opening_key, + &dense_public_inputs, + ) + } +} diff --git a/src/constraint_system.rs b/src/constraint_system.rs index 375619f67..d8114a3d8 100644 --- a/src/constraint_system.rs +++ b/src/constraint_system.rs @@ -9,24 +9,13 @@ //! tools and abstractions, used by the Composer to generate, //! build, preprocess circuits. -pub(crate) mod composer; pub(crate) mod constraint; pub(crate) mod ecc; -pub(crate) mod logic; -pub(crate) mod range; pub(crate) mod witness; pub(crate) use constraint::{Selector, WiredWitness}; pub(crate) use witness::WireData; -mod arithmetic; -mod boolean; - -#[cfg(feature = "std")] -#[cfg(test)] -pub(crate) mod helper; - -pub use composer::TurboComposer; pub use constraint::Constraint; pub use ecc::WitnessPoint; pub use witness::Witness; diff --git a/src/constraint_system/arithmetic.rs b/src/constraint_system/arithmetic.rs deleted file mode 100644 index f23eca7e9..000000000 --- a/src/constraint_system/arithmetic.rs +++ /dev/null @@ -1,228 +0,0 @@ -// 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 crate::constraint_system::{Constraint, TurboComposer, Witness}; -use dusk_bls12_381::BlsScalar; - -impl TurboComposer { - /// Evaluate and return `o` by appending a new constraint into the circuit. - /// - /// Set `q_o = (-1)` and override the output of the constraint with: - /// `o := q_l · a + q_r · b + q_4 · d + q_c + PI` - pub fn gate_add(&mut self, s: Constraint) -> Witness { - let s = Constraint::arithmetic(&s).output(-BlsScalar::one()); - - let o = self.append_output_witness(s); - let s = s.o(o); - - self.append_gate(s); - - o - } - - /// Evaluate and return `o` by appending a new constraint into the circuit. - /// - /// Set `q_o = (-1)` and override the output of the constraint with: - /// `o := q_m · a · b + q_4 · d + q_c + PI` - pub fn gate_mul(&mut self, s: Constraint) -> Witness { - let s = Constraint::arithmetic(&s).output(-BlsScalar::one()); - - let o = self.append_output_witness(s); - let s = s.o(o); - - self.append_gate(s); - - o - } -} - -#[cfg(feature = "std")] -#[cfg(test)] -mod tests { - use crate::constraint_system::{helper, Constraint}; - use dusk_bls12_381::BlsScalar; - - #[test] - fn test_public_inputs() { - helper::gadget_tester( - |composer| { - let one = composer.append_witness(BlsScalar::one()); - - composer.append_dummy_gates(); - - let constraint = - Constraint::new().left(1).right(1).public(1).a(one).b(one); - let should_be_three = composer.gate_add(constraint); - - composer.assert_equal_constant( - should_be_three, - BlsScalar::from(3), - None, - ); - - let constraint = - Constraint::new().left(1).right(1).public(2).a(one).b(one); - let should_be_four = composer.gate_add(constraint); - - composer.assert_equal_constant( - should_be_four, - BlsScalar::from(4), - None, - ); - }, - 200, - ) - .expect("Failed to test circuit public inputs"); - } - - #[test] - fn test_correct_add_mul_gate() { - let res = helper::gadget_tester( - |composer| { - // Verify that (4+5+5) * (6+7+7) = 280 - let four = composer.append_witness(BlsScalar::from(4)); - let five = composer.append_witness(BlsScalar::from(5)); - let six = composer.append_witness(BlsScalar::from(6)); - let seven = composer.append_witness(BlsScalar::from(7)); - - let constraint = Constraint::new() - .left(1) - .right(1) - .fourth(1) - .a(four) - .b(five) - .d(five); - let fourteen = composer.gate_add(constraint); - - let constraint = Constraint::new() - .left(1) - .right(1) - .fourth(1) - .a(six) - .b(seven) - .d(seven); - let twenty = composer.gate_add(constraint); - - // There are quite a few ways to check the equation is correct, - // depending on your circumstance If we already - // have the output wire, we can constrain the output of the - // mul_gate to be equal to it If we do not, we - // can compute it using the `mul` If the output - // is public, we can also constrain the output wire of the mul - // gate to it. This is what this test does - let constraint = - Constraint::new().mult(1).a(fourteen).b(twenty); - let output = composer.gate_mul(constraint); - - composer.assert_equal_constant( - output, - BlsScalar::from(280), - None, - ); - }, - 200, - ); - assert!(res.is_ok()); - } - - #[test] - fn test_correct_add_gate() { - helper::gadget_tester( - |composer| { - let one = composer.append_witness(BlsScalar::one()); - - let constraint = Constraint::new().left(1).constant(2).a(one); - let c = composer.gate_add(constraint); - - composer.assert_equal_constant(c, BlsScalar::from(3), None); - }, - 32, - ) - .expect("Circuit consistency failed"); - } - - #[test] - fn test_correct_big_add_mul_gate() { - let res = helper::gadget_tester( - |composer| { - // Verify that (4+5+5) * (6+7+7) + (8*9) = 352 - let four = composer.append_witness(BlsScalar::from(4)); - let five = composer.append_witness(BlsScalar::from(5)); - let six = composer.append_witness(BlsScalar::from(6)); - let seven = composer.append_witness(BlsScalar::from(7)); - let nine = composer.append_witness(BlsScalar::from(9)); - - let constraint = Constraint::new() - .left(1) - .right(1) - .fourth(1) - .a(four) - .b(five) - .d(five); - let fourteen = composer.gate_add(constraint); - - let constraint = Constraint::new() - .left(1) - .right(1) - .fourth(1) - .a(six) - .b(seven) - .d(seven); - let twenty = composer.gate_add(constraint); - - let constraint = Constraint::new() - .mult(1) - .fourth(8) - .a(fourteen) - .b(twenty) - .d(nine); - let output = composer.gate_mul(constraint); - - composer.assert_equal_constant( - output, - BlsScalar::from(352), - None, - ); - }, - 200, - ); - assert!(res.is_ok()); - } - - #[test] - fn test_incorrect_add_mul_gate() { - let res = helper::gadget_tester( - |composer| { - // Verify that (5+5) * (6+7) != 117 - let five = composer.append_witness(BlsScalar::from(5)); - let six = composer.append_witness(BlsScalar::from(6)); - let seven = composer.append_witness(BlsScalar::from(7)); - - let constraint = - Constraint::new().left(1).right(1).a(five).b(five); - let five_plus_five = composer.gate_add(constraint); - - let constraint = - Constraint::new().left(1).right(1).a(six).b(seven); - let six_plus_seven = composer.gate_add(constraint); - - let constraint = Constraint::new() - .mult(1) - .a(five_plus_five) - .b(six_plus_seven); - let output = composer.gate_mul(constraint); - - composer.assert_equal_constant( - output, - BlsScalar::from(117), - None, - ); - }, - 200, - ); - assert!(res.is_err()); - } -} diff --git a/src/constraint_system/boolean.rs b/src/constraint_system/boolean.rs deleted file mode 100644 index 8726aa630..000000000 --- a/src/constraint_system/boolean.rs +++ /dev/null @@ -1,67 +0,0 @@ -// 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 crate::constraint_system::{Constraint, TurboComposer, Witness}; -use dusk_bls12_381::BlsScalar; - -impl TurboComposer { - /// Adds a boolean constraint (also known as binary constraint) where the - /// gate eq. will enforce that the [`Witness`] received is either `0` or `1` - /// by adding a constraint in the circuit. - /// - /// Note that using this constraint with whatever [`Witness`] that - /// is not representing a value equalling 0 or 1, will always force the - /// equation to fail. - pub fn component_boolean(&mut self, a: Witness) { - let zero = Self::constant_zero(); - let constraint = Constraint::new() - .mult(1) - .output(-BlsScalar::one()) - .a(a) - .b(a) - .o(a) - .d(zero); - - self.append_gate(constraint); - } -} - -#[cfg(feature = "std")] -#[cfg(test)] -mod tests { - use crate::constraint_system::{helper, TurboComposer}; - use dusk_bls12_381::BlsScalar; - - #[test] - fn test_correct_bool_gate() { - let res = helper::gadget_tester( - |composer| { - let zero = TurboComposer::constant_zero(); - let one = composer.append_witness(BlsScalar::one()); - - composer.component_boolean(zero); - composer.component_boolean(one); - }, - 32, - ); - assert!(res.is_ok()) - } - - #[test] - fn test_incorrect_bool_gate() { - let res = helper::gadget_tester( - |composer| { - let zero = composer.append_witness(BlsScalar::from(5)); - let one = composer.append_witness(BlsScalar::one()); - - composer.component_boolean(zero); - composer.component_boolean(one); - }, - 32, - ); - assert!(res.is_err()) - } -} diff --git a/src/constraint_system/composer.rs b/src/constraint_system/composer.rs deleted file mode 100644 index 294ed413b..000000000 --- a/src/constraint_system/composer.rs +++ /dev/null @@ -1,813 +0,0 @@ -// 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. - -//! A `Composer` could be understood as some sort of Trait that is actually -//! defining some kind of Circuit Builder for PLONK. -//! -//! In that sense, here we have the implementation of the [`TurboComposer`] -//! which has been designed in order to provide the maximum amount of -//! performance while having a big scope in utility terms. -//! -//! It allows us not only to build Add and Mul gates but also to build -//! ECC op. gates, Range checks, Logical gates (Bitwise ops) etc. - -// Gate fn's have a large number of attributes but -// it is intended to be like this in order to provide -// maximum performance and minimum circuit sizes. - -use crate::constraint_system::{Constraint, Selector, WiredWitness, Witness}; -use crate::permutation::Permutation; -use alloc::collections::BTreeMap; -use alloc::vec::Vec; -use dusk_bls12_381::BlsScalar; -use hashbrown::HashMap; - -/// The TurboComposer is the circuit-builder tool that the `dusk-plonk` -/// repository provides so that circuit descriptions can be written, stored and -/// transformed into a [`Proof`](crate::proof_system::Proof) at some point. -/// -/// A TurboComposer stores all of the circuit information, this one being -/// all of the witness and circuit descriptors info (values, positions in the -/// circuits, gates and Wires that occupy..), the public inputs, the connection -/// relationships between the witnesses and how they're represented as Wires (so -/// basically the Permutation argument etc..). -/// -/// The TurboComposer also grants us a way to introduce our secret witnesses in -/// into the circuit description as well as the public inputs. We can do this -/// with methods like [`TurboComposer::append_witness`]. -/// -/// The TurboComposer also contains as associated functions all the necessary -/// tools to be able to instrument the circuits that the user needs -/// through the addition of gates. There are functions that may add a single -/// gate to the circuit as for example [`TurboComposer::gate_add`] and others -/// that can add several gates to the circuit description such as -/// [`TurboComposer::component_select`]. -/// -/// Each gate or group of gates adds a specific functionality or operation to -/// the circuit description, and so, that's why we can understand the -/// TurboComposer as a builder. -#[derive(Debug)] -pub struct TurboComposer { - /// Number of arithmetic gates in the circuit - pub(crate) n: usize, - - // Constraint vectors - /// Multiplier selector - pub(crate) q_m: Vec, - /// Left wire selector - pub(crate) q_l: Vec, - /// Right wire selector - pub(crate) q_r: Vec, - /// Output wire selector - pub(crate) q_o: Vec, - /// Constant wire selector - pub(crate) q_c: Vec, - /// Fourth wire selector added for efficiency of implementation - pub(crate) q_4: Vec, - /// Arithmetic wire selector added for efficiency of implementation - pub(crate) q_arith: Vec, - /// Range selector added for efficiency of implementation - pub(crate) q_range: Vec, - /// Logic selector added for efficiency of implementation - pub(crate) q_logic: Vec, - /// Fixed base group addition selector added for efficiency of - /// implementation - pub(crate) q_fixed_group_add: Vec, - /// Variable base group addition selector added for efficiency of - /// implementation - pub(crate) q_variable_group_add: Vec, - - /// Sparse representation of the Public Inputs linking the positions of the - /// non-zero ones to it's actual values. - pub(crate) public_inputs_sparse_store: BTreeMap, - - // Witness vectors - /// Left wire witness vector. - pub(crate) a_w: Vec, - /// Right wire witness vector. - pub(crate) b_w: Vec, - /// Output wire witness vector. - pub(crate) c_w: Vec, - /// Fourth wire witness vector added for efficiency of implementation. - pub(crate) d_w: Vec, - - /// These are the actual witness values. - pub(crate) witnesses: HashMap, - - /// Permutation argument. - pub(crate) perm: Permutation, -} - -impl TurboComposer { - /// Returns a [`Witness`] representation of zero. - /// - /// Every [`TurboComposer`] is initialized with a circuit description - /// containing a representation of zero. This function will return the - /// index of that representation. - pub const fn constant_zero() -> Witness { - Witness::new(0) - } - - /// Return the number of gates in the circuit - pub const fn gates(&self) -> usize { - self.n - } - - /// Evaluate the runtime value of a witness - /// - /// # Safety - /// - /// Witness evaluation inside a gadget isn't expected and could produce an - /// unsound circuit (different circuit representation for the same code). - /// - /// Calling this function performs operations outside the circuit. - pub unsafe fn evaluate_witness(&self, witness: &Witness) -> &BlsScalar { - &self.witnesses[witness] - } - - /// Constructs a dense vector of the Public Inputs from the positions and - /// the sparse vector that contains the values. - pub(crate) fn to_dense_public_inputs(&self) -> Vec { - let mut pi = vec![BlsScalar::zero(); self.n]; - self.public_inputs_sparse_store - .iter() - .for_each(|(pos, value)| { - pi[*pos] = *value; - }); - pi - } - - /// Returns the positions that the Public Inputs occupy in this Composer - /// instance. - // TODO: Find a more performant solution which can return a ref to a Vec or - // Iterator. - pub fn public_input_indexes(&self) -> Vec { - self.public_inputs_sparse_store - .keys() - .copied() - .collect::>() - } -} - -impl Default for TurboComposer { - fn default() -> Self { - Self::new() - } -} - -impl TurboComposer { - /// Generates a new empty `TurboComposer` with all of it's fields set to - /// hold an initial capacity of 0. - /// - /// # Note - /// - /// The usage of this may cause lots of re-allocations since the `Composer` - /// holds `Vec` for every polynomial, and these will need to be re-allocated - /// each time the circuit grows considerably. - pub(crate) fn new() -> Self { - TurboComposer::with_size(0) - } - - /// Constrain a scalar into the circuit description and return an allocated - /// [`Witness`] with its value - pub fn append_constant>(&mut self, value: T) -> Witness { - let scalar = value.into(); - let witness = self.append_witness(scalar); - - self.assert_equal_constant(witness, scalar, None); - - witness - } - - /// Creates a new circuit with an expected circuit size. - /// This will allow for less reallocations when building the circuit - /// since the `Vec`s will already have an appropriate allocation at the - /// beginning of the composing stage. - pub(crate) fn with_size(size: usize) -> Self { - let mut composer = TurboComposer { - n: 0, - - q_m: Vec::with_capacity(size), - q_l: Vec::with_capacity(size), - q_r: Vec::with_capacity(size), - q_o: Vec::with_capacity(size), - q_c: Vec::with_capacity(size), - q_4: Vec::with_capacity(size), - q_arith: Vec::with_capacity(size), - q_range: Vec::with_capacity(size), - q_logic: Vec::with_capacity(size), - q_fixed_group_add: Vec::with_capacity(size), - q_variable_group_add: Vec::with_capacity(size), - public_inputs_sparse_store: BTreeMap::new(), - - a_w: Vec::with_capacity(size), - b_w: Vec::with_capacity(size), - c_w: Vec::with_capacity(size), - d_w: Vec::with_capacity(size), - - witnesses: HashMap::with_capacity(size), - - perm: Permutation::new(), - }; - - // Reserve the first witness to be zero - composer.append_constant(BlsScalar::zero()); - - // Add dummy gates - composer.append_dummy_gates(); - - composer - } - - /// Allocate a witness value into the composer and return its index. - pub fn append_witness>(&mut self, scalar: T) -> Witness { - let scalar = scalar.into(); - - // Get a new Witness from the permutation - let var = self.perm.new_witness(); - - // The composer now links the BlsScalar to the Witness returned from - // the Permutation - self.witnesses.insert(var, scalar); - - var - } - - /// Allocate a witness value into the composer and return its index. - /// - /// Create a public input with the scalar - pub fn append_public_witness>( - &mut self, - scalar: T, - ) -> Witness { - let scalar = scalar.into(); - let witness = self.append_witness(scalar); - - self.assert_equal_constant(witness, 0, Some(-scalar)); - - witness - } - - /// Adds a width-4 poly gate. - /// - /// The final constraint added will enforce the following: - /// `q_m · a · b + q_l · a + q_r · b + q_o · o + q_4 · d + q_c + PI = 0`. - pub fn append_gate(&mut self, s: Constraint) { - let a = s.witness(WiredWitness::A); - let b = s.witness(WiredWitness::B); - let o = s.witness(WiredWitness::O); - let d = s.witness(WiredWitness::D); - - let s = Constraint::arithmetic(&s); - - let q_m = *s.coeff(Selector::Multiplication); - let q_l = *s.coeff(Selector::Left); - let q_r = *s.coeff(Selector::Right); - let q_o = *s.coeff(Selector::Output); - let q_4 = *s.coeff(Selector::Fourth); - let q_c = *s.coeff(Selector::Constant); - let pi = *s.coeff(Selector::PublicInput); - - let q_arith = *s.coeff(Selector::Arithmetic); - let q_range = *s.coeff(Selector::Range); - let q_logic = *s.coeff(Selector::Logic); - let q_fixed_group_add = *s.coeff(Selector::GroupAddFixedBase); - let q_variable_group_add = *s.coeff(Selector::GroupAddVariableBase); - - self.a_w.push(a); - self.b_w.push(b); - self.c_w.push(o); - self.d_w.push(d); - - // Add selector vectors - self.q_m.push(q_m); - self.q_l.push(q_l); - self.q_r.push(q_r); - self.q_o.push(q_o); - self.q_4.push(q_4); - self.q_c.push(q_c); - - self.q_arith.push(q_arith); - self.q_range.push(q_range); - self.q_logic.push(q_logic); - self.q_fixed_group_add.push(q_fixed_group_add); - self.q_variable_group_add.push(q_variable_group_add); - - if s.has_public_input() { - self.public_inputs_sparse_store.insert(self.n, pi); - } - - self.perm.add_witnesses_to_map(a, b, o, d, self.n); - - self.n += 1; - } - - /// Constrain `a` to be equal to `constant + pi`. - /// - /// `constant` will be defined as part of the public circuit description. - pub fn assert_equal_constant>( - &mut self, - a: Witness, - constant: C, - pi: Option, - ) { - let constant = constant.into(); - let constraint = Constraint::new().left(1).constant(-constant).a(a); - - // TODO maybe accept `Constraint` instead of `Option`? - let constraint = match pi { - Some(pi) => constraint.public(pi), - None => constraint, - }; - - self.append_gate(constraint); - } - - /// Asserts `a == b` by appending a gate - pub fn assert_equal(&mut self, a: Witness, b: Witness) { - let constraint = - Constraint::new().left(1).right(-BlsScalar::one()).a(a).b(b); - - self.append_gate(constraint); - } - - /// Conditionally selects a [`Witness`] based on an input bit. - /// - /// bit == 1 => a, - /// bit == 0 => b, - /// - /// `bit` is expected to be constrained by - /// [`TurboComposer::component_boolean`] - pub fn component_select( - &mut self, - bit: Witness, - a: Witness, - b: Witness, - ) -> Witness { - debug_assert!( - self.witnesses[&bit] == BlsScalar::one() - || self.witnesses[&bit] == BlsScalar::zero() - ); - - // bit * a - let constraint = Constraint::new().mult(1).a(bit).b(a); - let bit_times_a = self.gate_mul(constraint); - - // 1 - bit - let constraint = - Constraint::new().left(-BlsScalar::one()).constant(1).a(bit); - let one_min_bit = self.gate_add(constraint); - - // (1 - bit) * b - let constraint = Constraint::new().mult(1).a(one_min_bit).b(b); - let one_min_bit_b = self.gate_mul(constraint); - - // [ (1 - bit) * b ] + [ bit * a ] - let constraint = Constraint::new() - .left(1) - .right(1) - .a(one_min_bit_b) - .b(bit_times_a); - self.gate_add(constraint) - } - - /// Conditionally selects a [`Witness`] based on an input bit. - /// - /// bit == 1 => value, - /// bit == 0 => 0, - /// - /// `bit` is expected to be constrained by - /// [`TurboComposer::component_boolean`] - pub fn component_select_zero( - &mut self, - bit: Witness, - value: Witness, - ) -> Witness { - debug_assert!( - self.witnesses[&bit] == BlsScalar::one() - || self.witnesses[&bit] == BlsScalar::zero() - ); - - let constraint = Constraint::new().mult(1).a(bit).b(value); - - self.gate_mul(constraint) - } - - /// Conditionally selects a [`Witness`] based on an input bit. - /// - /// bit == 1 => value, - /// bit == 0 => 1, - /// - /// `bit` is expected to be constrained by - /// [`TurboComposer::component_boolean`] - pub fn component_select_one( - &mut self, - bit: Witness, - value: Witness, - ) -> Witness { - debug_assert!( - self.witnesses[&bit] == BlsScalar::one() - || self.witnesses[&bit] == BlsScalar::zero() - ); - - let b = self.witnesses[&bit]; - let v = self.witnesses[&value]; - - let f_x = BlsScalar::one() - b + (b * v); - let f_x = self.append_witness(f_x); - - let constraint = Constraint::new() - .mult(1) - .left(-BlsScalar::one()) - .output(-BlsScalar::one()) - .constant(1) - .a(bit) - .b(value) - .o(f_x); - - self.append_gate(constraint); - - f_x - } - - /// Decomposes `scalar` into an array truncated to `N` bits (max 256). - /// - /// Asserts the reconstruction of the bits to be equal to `scalar`. - /// - /// Consume `2 · N + 1` gates - pub fn component_decomposition( - &mut self, - scalar: Witness, - ) -> [Witness; N] { - // Static assertion - assert!(0 < N && N <= 256); - - let mut decomposition = [Self::constant_zero(); N]; - - let acc = Self::constant_zero(); - let acc = self.witnesses[&scalar] - .to_bits() - .iter() - .enumerate() - .zip(decomposition.iter_mut()) - .fold(acc, |acc, ((i, w), d)| { - *d = self.append_witness(BlsScalar::from(*w as u64)); - - self.component_boolean(*d); - - let constraint = Constraint::new() - .left(BlsScalar::pow_of_2(i as u64)) - .right(1) - .a(*d) - .b(acc); - - self.gate_add(constraint) - }); - - self.assert_equal(acc, scalar); - - decomposition - } - - /// This function is used to add a blinding factor to the witness - /// polynomials. It essentially adds two dummy gates to the circuit - /// description which are guaranteed to always satisfy the gate equation. - pub fn append_dummy_gates(&mut self) { - // Add a dummy constraint so that we do not have zero polynomials - self.q_m.push(BlsScalar::from(1)); - self.q_l.push(BlsScalar::from(2)); - self.q_r.push(BlsScalar::from(3)); - self.q_o.push(BlsScalar::from(4)); - self.q_c.push(BlsScalar::from(4)); - self.q_4.push(BlsScalar::one()); - self.q_arith.push(BlsScalar::one()); - self.q_range.push(BlsScalar::zero()); - self.q_logic.push(BlsScalar::zero()); - self.q_fixed_group_add.push(BlsScalar::zero()); - self.q_variable_group_add.push(BlsScalar::zero()); - let var_six = self.append_witness(BlsScalar::from(6)); - let var_one = self.append_witness(BlsScalar::from(1)); - let var_seven = self.append_witness(BlsScalar::from(7)); - let var_min_twenty = self.append_witness(-BlsScalar::from(20)); - self.a_w.push(var_six); - self.b_w.push(var_seven); - self.c_w.push(var_min_twenty); - self.d_w.push(var_one); - self.perm.add_witnesses_to_map( - var_six, - var_seven, - var_min_twenty, - var_one, - self.n, - ); - self.n += 1; - //Add another dummy constraint so that we do not get the identity - // permutation - self.q_m.push(BlsScalar::from(1)); - self.q_l.push(BlsScalar::from(1)); - self.q_r.push(BlsScalar::from(1)); - self.q_o.push(BlsScalar::from(1)); - self.q_c.push(BlsScalar::from(127)); - self.q_4.push(BlsScalar::zero()); - self.q_arith.push(BlsScalar::one()); - self.q_range.push(BlsScalar::zero()); - self.q_logic.push(BlsScalar::zero()); - self.q_fixed_group_add.push(BlsScalar::zero()); - self.q_variable_group_add.push(BlsScalar::zero()); - self.a_w.push(var_min_twenty); - self.b_w.push(var_six); - self.c_w.push(var_seven); - self.d_w.push(Self::constant_zero()); - self.perm.add_witnesses_to_map( - var_min_twenty, - var_six, - var_seven, - Self::constant_zero(), - self.n, - ); - - self.n += 1; - } - - pub(crate) fn append_output_witness(&mut self, s: Constraint) -> Witness { - let a = s.witness(WiredWitness::A); - let b = s.witness(WiredWitness::B); - let d = s.witness(WiredWitness::D); - - let a = self.witnesses[&a]; - let b = self.witnesses[&b]; - let d = self.witnesses[&d]; - - let qm = s.coeff(Selector::Multiplication); - let ql = s.coeff(Selector::Left); - let qr = s.coeff(Selector::Right); - let qd = s.coeff(Selector::Fourth); - let qc = s.coeff(Selector::Constant); - let pi = s.coeff(Selector::PublicInput); - - let x = qm * a * b + ql * a + qr * b + qd * d + qc + pi; - - let y = s.coeff(Selector::Output); - let y: Option = y.invert().into(); - let y = -y.expect("Inconsistent internal usage of `Constraint::evaluate_output`: Output selector should not be zero"); - - let o = x * y; - self.append_witness(o) - } - - /// Utility function that allows to check on the "front-end" - /// side of the PLONK implementation if the identity polynomial - /// is satisfied for each one of the [`TurboComposer`]'s gates. - /// - /// The recommended usage is to derive the std output and the std error to a - /// text file and analyze there the gates. - /// - /// # Panic - /// The function by itself will print each circuit gate info until one of - /// the gates does not satisfy the equation or there are no more gates. If - /// the cause is an unsatisfied gate equation, the function will panic. - #[cfg(feature = "trace")] - #[allow(dead_code)] - pub(crate) fn check_circuit_satisfied(&self) { - let a_w: Vec<&BlsScalar> = self - .a_w - .iter() - .map(|a_w_i| self.witnesses.get(a_w_i).unwrap()) - .collect(); - let b_w: Vec<&BlsScalar> = self - .b_w - .iter() - .map(|b_w_i| self.witnesses.get(b_w_i).unwrap()) - .collect(); - let c_w: Vec<&BlsScalar> = self - .c_w - .iter() - .map(|c_w_i| self.witnesses.get(c_w_i).unwrap()) - .collect(); - let d_w: Vec<&BlsScalar> = self - .d_w - .iter() - .map(|d_w_i| self.witnesses.get(d_w_i).unwrap()) - .collect(); - - // Computes f(f-1)(f-2)(f-3) - let delta = |f: BlsScalar| -> BlsScalar { - let f_1 = f - BlsScalar::one(); - let f_2 = f - BlsScalar::from(2); - let f_3 = f - BlsScalar::from(3); - f * f_1 * f_2 * f_3 - }; - - let pi_vec = self.to_dense_public_inputs(); - let four = BlsScalar::from(4); - - for i in 0..self.n { - let qm = self.q_m[i]; - let ql = self.q_l[i]; - let qr = self.q_r[i]; - let qo = self.q_o[i]; - let qc = self.q_c[i]; - let q4 = self.q_4[i]; - let qarith = self.q_arith[i]; - let qrange = self.q_range[i]; - let qlogic = self.q_logic[i]; - let qfixed = self.q_fixed_group_add[i]; - let qvar = self.q_variable_group_add[i]; - let pi = pi_vec[i]; - - let a = a_w[i]; - let a_next = a_w[(i + 1) % self.n]; - let b = b_w[i]; - let b_next = b_w[(i + 1) % self.n]; - let c = c_w[i]; - let d = d_w[i]; - let d_next = d_w[(i + 1) % self.n]; - - #[cfg(all(feature = "trace-print", feature = "std"))] - std::println!( - "--------------------------------------------\n - #Gate Index = {} - #Constraint Polynomials:\n - - qm -> {:?}\n - - ql -> {:?}\n - - qr -> {:?}\n - - q4 -> {:?}\n - - qo -> {:?}\n - - qc -> {:?}\n - - q_arith -> {:?}\n - - q_range -> {:?}\n - - q_logic -> {:?}\n - - q_fixed_group_add -> {:?}\n - - q_variable_group_add -> {:?}\n - # Witness polynomials:\n - - a_w -> {:?}\n - - b_w -> {:?}\n - - c_w -> {:?}\n - - d_w -> {:?}\n", - i, - qm, - ql, - qr, - q4, - qo, - qc, - qarith, - qrange, - qlogic, - qfixed, - qvar, - a, - b, - c, - d - ); - - let k = qarith - * ((qm * a * b) - + (ql * a) - + (qr * b) - + (qo * c) - + (q4 * d) - + pi - + qc) - + qlogic - * (((delta(a_next - four * a) - delta(b_next - four * b)) - * c) - + delta(a_next - four * a) - + delta(b_next - four * b) - + delta(d_next - four * d) - + match ( - qlogic == BlsScalar::one(), - qlogic == -BlsScalar::one(), - ) { - (true, false) => (a & b) - d, - (false, true) => (a ^ b) - d, - (false, false) => BlsScalar::zero(), - _ => unreachable!(), - }) - + qrange - * (delta(c - four * d) - + delta(b - four * c) - + delta(a - four * b) - + delta(d_next - four * a)); - - assert_eq!(k, BlsScalar::zero(), "Check failed at gate {}", i,); - } - } -} - -#[cfg(feature = "std")] -#[cfg(test)] -mod tests { - use super::*; - use crate::commitment_scheme::PublicParameters; - use crate::constraint_system::helper::*; - use crate::proof_system::{Prover, Verifier}; - use rand_core::OsRng; - - #[test] - /// Tests that a circuit initially has 3 gates - fn test_initial_gates() { - let composer: TurboComposer = TurboComposer::new(); - // Circuit size is n+3 because - // - We have an extra gate which forces the first witness to be zero. - // This is used when the advice wire is not being used. - // - We have two gates which ensure that the permutation polynomial is - // not the identity and - // - Another gate which ensures that the selector polynomials are not - // all zeroes - assert_eq!(3, composer.gates()) - } - - #[allow(unused_variables)] - #[test] - /// Tests that an empty circuit proof passes - fn test_minimal_circuit() { - let res = gadget_tester( - |composer| { - // do nothing except add the dummy gates - composer.append_dummy_gates(); - }, - 200, - ); - assert!(res.is_ok()); - } - - #[test] - fn test_component_select() { - let res = gadget_tester( - |composer| { - let bit_1 = composer.append_witness(BlsScalar::one()); - let bit_0 = TurboComposer::constant_zero(); - - let choice_a = composer.append_witness(BlsScalar::from(10u64)); - let choice_b = composer.append_witness(BlsScalar::from(20u64)); - - let choice = - composer.component_select(bit_1, choice_a, choice_b); - composer.assert_equal(choice, choice_a); - - let choice = - composer.component_select(bit_0, choice_a, choice_b); - composer.assert_equal(choice, choice_b); - }, - 32, - ); - assert!(res.is_ok()); - } - - #[test] - // XXX: Move this to integration tests - fn test_multiple_proofs() { - let public_parameters = - PublicParameters::setup(2 * 30, &mut OsRng).unwrap(); - - // Create a prover struct - let mut prover = Prover::new(b"demo"); - - // Add gadgets - dummy_gadget(10, prover.composer_mut()); - - // Commit Key - let (ck, _) = public_parameters.trim(2 * 20).unwrap(); - - // Preprocess circuit - prover.preprocess(&ck).unwrap(); - - let public_inputs = prover.cs.to_dense_public_inputs(); - - let mut proofs = Vec::new(); - - // Compute multiple proofs - for _ in 0..3 { - proofs.push(prover.prove(&ck, &mut OsRng).unwrap()); - - // Add another witness instance - dummy_gadget(10, prover.composer_mut()); - } - - // Verifier - // - let mut verifier = Verifier::new(b"demo"); - - // Add gadgets - dummy_gadget(10, verifier.composer_mut()); - - // Commit and Verifier Key - let (ck, vk) = public_parameters.trim(2 * 20).unwrap(); - - // Preprocess - verifier.preprocess(&ck).unwrap(); - - let pi_indexes = verifier.composer_mut().public_input_indexes(); - - for proof in proofs { - assert!(verifier - .verify(&proof, &vk, &public_inputs, &pi_indexes) - .is_ok()); - } - } -} diff --git a/src/constraint_system/constraint.rs b/src/constraint_system/constraint.rs index a55e66c94..48659396a 100644 --- a/src/constraint_system/constraint.rs +++ b/src/constraint_system/constraint.rs @@ -4,7 +4,8 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use crate::constraint_system::{TurboComposer, Witness}; +use crate::composer::{Builder, Composer}; +use crate::constraint_system::Witness; use dusk_bls12_381::BlsScalar; /// Selectors used to address a coefficient inside of a [`Constraint`] @@ -96,7 +97,7 @@ impl Constraint { pub const fn new() -> Self { Self { coefficients: [BlsScalar::zero(); 13], - witnesses: [TurboComposer::constant_zero(); 4], + witnesses: [Builder::ZERO; 4], has_public_input: false, } } @@ -123,6 +124,11 @@ impl Constraint { s } + /// Replace the value of an indexed witness + pub(crate) fn set_witness(&mut self, index: WiredWitness, w: Witness) { + self.witnesses[index as usize] = w; + } + /// Return a reference to the specified selector of a circuit constraint. pub(crate) const fn coeff(&self, r: Selector) -> &BlsScalar { &self.coefficients[r as usize] @@ -172,28 +178,28 @@ impl Constraint { /// Set witness `a` wired to `qM` and `qL` pub fn a(mut self, w: Witness) -> Self { - self.witnesses[WiredWitness::A as usize] = w; + self.set_witness(WiredWitness::A, w); self } /// Set witness `b` wired to `qM` and `qR` pub fn b(mut self, w: Witness) -> Self { - self.witnesses[WiredWitness::B as usize] = w; + self.set_witness(WiredWitness::B, w); self } /// Set witness `o` wired to `qO` pub fn o(mut self, w: Witness) -> Self { - self.witnesses[WiredWitness::O as usize] = w; + self.set_witness(WiredWitness::O, w); self } /// Set witness `d` wired to the fourth/advice `q4` coefficient pub fn d(mut self, w: Witness) -> Self { - self.witnesses[WiredWitness::D as usize] = w; + self.set_witness(WiredWitness::D, w); self } @@ -207,15 +213,15 @@ impl Constraint { } #[allow(dead_code)] - // TODO to be used when `TurboComposer` replaces internal selectors with - // this struct + // TODO to be used when `ComposerBackend` replaces internal selectors + // with this struct pub(crate) fn range(s: &Self) -> Self { Self::from_external(s).set(Selector::Range, 1) } #[allow(dead_code)] - // TODO to be used when `TurboComposer` replaces internal selectors with - // this struct + // TODO to be used when `ComposerBackend` replaces internal selectors + // with this struct pub(crate) fn logic(s: &Self) -> Self { Self::from_external(s) .set(Selector::Constant, 1) @@ -223,8 +229,8 @@ impl Constraint { } #[allow(dead_code)] - // TODO to be used when `TurboComposer` replaces internal selectors with - // this struct + // TODO to be used when `ComposerBackend` replaces internal selectors + // with this struct pub(crate) fn logic_xor(s: &Self) -> Self { Self::from_external(s) .set(Selector::Constant, -BlsScalar::one()) @@ -232,15 +238,15 @@ impl Constraint { } #[allow(dead_code)] - // TODO to be used when `TurboComposer` replaces internal selectors with - // this struct + // TODO to be used when `ComposerBackend` replaces internal selectors + // with this struct pub(crate) fn group_add_fixed_base(s: &Self) -> Self { Self::from_external(s).set(Selector::GroupAddFixedBase, 1) } #[allow(dead_code)] - // TODO to be used when `TurboComposer` replaces internal selectors with - // this struct + // TODO to be used when `ComposerBackend` replaces internal selectors + // with this struct pub(crate) fn group_add_variable_base(s: &Self) -> Self { Self::from_external(s).set(Selector::GroupAddVariableBase, 1) } diff --git a/src/constraint_system/ecc.rs b/src/constraint_system/ecc.rs index 9359d0783..28b52490d 100644 --- a/src/constraint_system/ecc.rs +++ b/src/constraint_system/ecc.rs @@ -4,14 +4,8 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -/// Curve addition gate -pub mod curve_addition; -/// Gates related to scalar multiplication -pub mod scalar_mul; - -use crate::constraint_system::{TurboComposer, Witness}; +use crate::constraint_system::Witness; use dusk_bls12_381::BlsScalar; -use dusk_jubjub::JubJubAffine; /// Represents a JubJub point in the circuit #[derive(Debug, Clone, Copy)] @@ -21,6 +15,11 @@ pub struct WitnessPoint { } impl WitnessPoint { + #[allow(dead_code)] + pub(crate) const fn new(x: Witness, y: Witness) -> Self { + Self { x, y } + } + /// Return the X coordinate of the point pub const fn x(&self) -> &Witness { &self.x @@ -32,176 +31,31 @@ impl WitnessPoint { } } -impl TurboComposer { - /// Appends a point in affine form as [`WitnessPoint`] - pub fn append_point>( - &mut self, - affine: P, - ) -> WitnessPoint { - let affine = affine.into(); - - let x = self.append_witness(affine.get_x()); - let y = self.append_witness(affine.get_y()); - - WitnessPoint { x, y } - } - - /// Appends a point in affine form as [`WitnessPoint`] - /// - /// Creates two public inputs as `(x, y)` - pub fn append_public_point>( - &mut self, - affine: P, - ) -> WitnessPoint { - let affine = affine.into(); - let point = self.append_point(affine); - - self.assert_equal_constant( - point.x, - BlsScalar::zero(), - Some(-affine.get_x()), - ); - - self.assert_equal_constant( - point.y, - BlsScalar::zero(), - Some(-affine.get_y()), - ); - - point - } - - /// Constrain a point into the circuit description and return an allocated - /// [`WitnessPoint`] with its coordinates - pub fn append_constant_point>( - &mut self, - affine: P, - ) -> WitnessPoint { - let affine = affine.into(); - - let x = self.append_constant(affine.get_x()); - let y = self.append_constant(affine.get_y()); - - WitnessPoint { x, y } - } - - /// Create an identity [`WitnessPoint`] constrained by the circuit - /// description - pub fn append_constant_identity(&mut self) -> WitnessPoint { - let x = Self::constant_zero(); - let y = self.append_constant(BlsScalar::one()); - - WitnessPoint { x, y } - } - - /// Asserts `point == public`. - /// - /// Will add `public` affine coordinates `(x,y)` as public inputs - pub fn assert_equal_public_point>( - &mut self, - point: WitnessPoint, - public: P, - ) { - let public = public.into(); - - self.assert_equal_constant( - point.x, - BlsScalar::zero(), - Some(-public.get_x()), - ); - - self.assert_equal_constant( - point.y, - BlsScalar::zero(), - Some(-public.get_y()), - ); - } - - /// Asserts `a == b` by appending two gates - pub fn assert_equal_point(&mut self, a: WitnessPoint, b: WitnessPoint) { - self.assert_equal(a.x, b.x); - self.assert_equal(b.y, b.y); - } - - /// Conditionally selects a [`WitnessPoint`] based on an input bit. - /// - /// bit == 1 => a, - /// bit == 0 => b, - /// - /// `bit` is expected to be constrained by - /// [`TurboComposer::component_boolean`] - pub fn component_select_point( - &mut self, - a: WitnessPoint, - b: WitnessPoint, - bit: Witness, - ) -> WitnessPoint { - debug_assert!( - self.witnesses[&bit] == BlsScalar::one() - || self.witnesses[&bit] == BlsScalar::zero() - ); - - let x = self.component_select(bit, *a.x(), *b.x()); - let y = self.component_select(bit, *a.y(), *b.y()); - - WitnessPoint { x, y } - } - - /// Conditionally selects identity as [`WitnessPoint`] based on an input - /// bit. - /// - /// bit == 1 => a, - /// bit == 0 => identity, - /// - /// `bit` is expected to be constrained by - /// [`TurboComposer::component_boolean`] - pub fn component_select_identity( - &mut self, - bit: Witness, - a: WitnessPoint, - ) -> WitnessPoint { - debug_assert!( - self.witnesses[&bit] == BlsScalar::one() - || self.witnesses[&bit] == BlsScalar::zero() - ); - - let x = self.component_select_zero(bit, *a.x()); - let y = self.component_select_one(bit, *a.y()); - - WitnessPoint { x, y } - } -} - -#[cfg(feature = "std")] -#[cfg(test)] -mod tests { - use super::*; - use crate::constraint_system::helper::*; - - #[test] - fn test_component_select_point() { - let res = gadget_tester( - |composer| { - let bit_1 = composer.append_witness(BlsScalar::one()); - let bit_0 = TurboComposer::constant_zero(); - - let point_a = composer.append_constant_identity(); - let point_b = WitnessPoint { - x: composer.append_witness(BlsScalar::from(10u64)), - y: composer.append_witness(BlsScalar::from(20u64)), - }; - - let choice = - composer.component_select_point(point_a, point_b, bit_1); - - composer.assert_equal_point(point_a, choice); - - let choice = - composer.component_select_point(point_a, point_b, bit_0); - composer.assert_equal_point(point_b, choice); - }, - 32, - ); - assert!(res.is_ok()); - } +#[derive(Debug, Clone, Copy)] +/// Contains all of the components needed to verify that a bit scalar +/// multiplication was computed correctly +pub(crate) struct WnafRound> { + /// This is the accumulated x coordinate point that we wish to add (so + /// far.. depends on where you are in the scalar mul) it is linked to + /// the wnaf entry, so must not be revealed + pub acc_x: T, + /// This is the accumulated y coordinate + pub acc_y: T, + + /// This is the wnaf accumulated entry + /// For all intents and purposes, you can think of this as the secret bit + pub accumulated_bit: T, + + /// This is the multiplication of x_\alpha * y_\alpha + /// we need this as a distinct wire, so that the degree of the polynomial + /// does not go over 4 + pub xy_alpha: T, + /// This is the possible x co-ordinate of the wnaf point we are going to + /// add Actual x-co-ordinate = b_i * x_\beta + pub x_beta: BlsScalar, + /// This is the possible y co-ordinate of the wnaf point we are going to + /// add Actual y coordinate = (b_i)^2 [y_\beta -1] + 1 + pub y_beta: BlsScalar, + /// This is the multiplication of x_\beta * y_\beta + pub xy_beta: BlsScalar, } diff --git a/src/constraint_system/ecc/curve_addition.rs b/src/constraint_system/ecc/curve_addition.rs deleted file mode 100644 index 2d5659d2f..000000000 --- a/src/constraint_system/ecc/curve_addition.rs +++ /dev/null @@ -1,10 +0,0 @@ -// 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. - -/// A fixed base curve addition gate -pub(crate) mod fixed_base_gate; -/// A variable base curve addition gate -pub(crate) mod variable_base_gate; diff --git a/src/constraint_system/ecc/curve_addition/fixed_base_gate.rs b/src/constraint_system/ecc/curve_addition/fixed_base_gate.rs deleted file mode 100644 index 21c5f055a..000000000 --- a/src/constraint_system/ecc/curve_addition/fixed_base_gate.rs +++ /dev/null @@ -1,74 +0,0 @@ -// 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 crate::constraint_system::{TurboComposer, Witness}; -use dusk_bls12_381::BlsScalar; - -#[derive(Debug, Clone, Copy)] -/// Contains all of the components needed to verify that a bit scalar -/// multiplication was computed correctly -pub(crate) struct WnafRound> { - /// This is the accumulated x coordinate point that we wish to add (so - /// far.. depends on where you are in the scalar mul) it is linked to - /// the wnaf entry, so must not be revealed - pub acc_x: T, - /// This is the accumulated y coordinate - pub acc_y: T, - - /// This is the wnaf accumulated entry - /// For all intents and purposes, you can think of this as the secret bit - pub accumulated_bit: T, - - /// This is the multiplication of x_\alpha * y_\alpha - /// we need this as a distinct wire, so that the degree of the polynomial - /// does not go over 4 - pub xy_alpha: T, - /// This is the possible x co-ordinate of the wnaf point we are going to - /// add Actual x-co-ordinate = b_i * x_\beta - pub x_beta: BlsScalar, - /// This is the possible y co-ordinate of the wnaf point we are going to - /// add Actual y coordinate = (b_i)^2 [y_\beta -1] + 1 - pub y_beta: BlsScalar, - /// This is the multiplication of x_\beta * y_\beta - pub xy_beta: BlsScalar, -} - -impl TurboComposer { - /// Fixed group addition of a jubjub point - pub(crate) fn fixed_group_add + Copy>( - &mut self, - wnaf_round: WnafRound, - ) { - self.a_w.push(wnaf_round.acc_x.into()); - self.b_w.push(wnaf_round.acc_y.into()); - self.c_w.push(wnaf_round.xy_alpha.into()); - self.d_w.push(wnaf_round.accumulated_bit.into()); - - self.q_l.push(wnaf_round.x_beta); - self.q_r.push(wnaf_round.y_beta); - - self.q_c.push(wnaf_round.xy_beta); - self.q_o.push(BlsScalar::zero()); - self.q_fixed_group_add.push(BlsScalar::one()); - self.q_variable_group_add.push(BlsScalar::zero()); - - self.q_m.push(BlsScalar::zero()); - self.q_4.push(BlsScalar::zero()); - self.q_arith.push(BlsScalar::zero()); - self.q_range.push(BlsScalar::zero()); - self.q_logic.push(BlsScalar::zero()); - - self.perm.add_witnesses_to_map( - wnaf_round.acc_x, - wnaf_round.acc_y, - wnaf_round.xy_alpha, - wnaf_round.accumulated_bit, - self.n, - ); - - self.n += 1; - } -} diff --git a/src/constraint_system/ecc/curve_addition/variable_base_gate.rs b/src/constraint_system/ecc/curve_addition/variable_base_gate.rs deleted file mode 100644 index 674f1d5a9..000000000 --- a/src/constraint_system/ecc/curve_addition/variable_base_gate.rs +++ /dev/null @@ -1,214 +0,0 @@ -// 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 crate::constraint_system::ecc::WitnessPoint; -use crate::constraint_system::TurboComposer; -use dusk_bls12_381::BlsScalar; -use dusk_jubjub::{JubJubAffine, JubJubExtended}; - -impl TurboComposer { - /// Adds two curve points by consuming 2 gates. - pub fn component_add_point( - &mut self, - a: WitnessPoint, - b: WitnessPoint, - ) -> WitnessPoint { - // In order to verify that two points were correctly added - // without going over a degree 4 polynomial, we will need - // x_1, y_1, x_2, y_2 - // x_3, y_3, x_1 * y_2 - - let x_1 = a.x; - let y_1 = a.y; - let x_2 = b.x; - let y_2 = b.y; - - let p1 = JubJubAffine::from_raw_unchecked( - self.witnesses[&x_1], - self.witnesses[&y_1], - ); - - let p2 = JubJubAffine::from_raw_unchecked( - self.witnesses[&x_2], - self.witnesses[&y_2], - ); - - let point: JubJubAffine = (JubJubExtended::from(p1) + p2).into(); - let x_3 = point.get_x(); - let y_3 = point.get_y(); - - let x1_y2 = self.witnesses[&x_1] * self.witnesses[&y_2]; - - // Add the rest of the prepared points into the composer - let x_1_y_2 = self.append_witness(x1_y2); - let x_3 = self.append_witness(x_3); - let y_3 = self.append_witness(y_3); - - // TODO encapsulate this gate addition into a generic `append` method - // The function must be a special case of `append_gate` because of - // `q_arith` and `q_variable_group_add` - - self.a_w.extend(&[x_1, x_3]); - self.b_w.extend(&[y_1, y_3]); - self.c_w.extend(&[x_2, Self::constant_zero()]); - self.d_w.extend(&[y_2, x_1_y_2]); - let zeros = [BlsScalar::zero(), BlsScalar::zero()]; - - self.q_l.extend(&zeros); - self.q_r.extend(&zeros); - self.q_c.extend(&zeros); - self.q_o.extend(&zeros); - self.q_m.extend(&zeros); - self.q_4.extend(&zeros); - self.q_arith.extend(&zeros); - self.q_range.extend(&zeros); - self.q_logic.extend(&zeros); - self.q_fixed_group_add.extend(&zeros); - - self.q_variable_group_add.push(BlsScalar::one()); - self.q_variable_group_add.push(BlsScalar::zero()); - - self.perm.add_witnesses_to_map(x_1, y_1, x_2, y_2, self.n); - self.n += 1; - - self.perm.add_witnesses_to_map( - x_3, - y_3, - Self::constant_zero(), - x_1_y_2, - self.n, - ); - - self.n += 1; - - WitnessPoint { x: x_3, y: y_3 } - } -} - -#[cfg(feature = "std")] -#[cfg(test)] -mod test { - use super::*; - use crate::constraint_system::helper::*; - use crate::constraint_system::Constraint; - use dusk_jubjub::{EDWARDS_D, GENERATOR, GENERATOR_EXTENDED}; - - /// Adds two curve points together using the classical point addition - /// algorithm. This method is slower than WNaf and is just meant to be the - /// source of truth to test the WNaf method. - pub fn classical_point_addition( - composer: &mut TurboComposer, - a: WitnessPoint, - b: WitnessPoint, - ) -> WitnessPoint { - let x1 = a.x; - let y1 = a.y; - - let x2 = b.x; - let y2 = b.y; - - // x1 * y2 - let constraint = Constraint::new().mult(1).a(x1).b(y2); - let x1_y2 = composer.gate_mul(constraint); - - // y1 * x2 - let constraint = Constraint::new().mult(1).a(y1).b(x2); - let y1_x2 = composer.gate_mul(constraint); - - // y1 * y2 - let constraint = Constraint::new().mult(1).a(y1).b(y2); - let y1_y2 = composer.gate_mul(constraint); - - // x1 * x2 - let constraint = Constraint::new().mult(1).a(x1).b(x2); - let x1_x2 = composer.gate_mul(constraint); - - // d x1x2 * y1y2 - let constraint = Constraint::new().mult(EDWARDS_D).a(x1_x2).b(y1_y2); - let d_x1_x2_y1_y2 = composer.gate_mul(constraint); - - // x1y2 + y1x2 - let constraint = Constraint::new().left(1).right(1).a(x1_y2).b(y1_x2); - let x_numerator = composer.gate_add(constraint); - - // y1y2 - a * x1x2 (a=-1) => y1y2 + x1x2 - let constraint = Constraint::new().left(1).right(1).a(y1_y2).b(x1_x2); - let y_numerator = composer.gate_add(constraint); - - // 1 + dx1x2y1y2 - let constraint = Constraint::new().left(1).constant(1).a(d_x1_x2_y1_y2); - let x_denominator = composer.gate_add(constraint); - - // Compute the inverse - let inv_x_denom = unsafe { - composer.evaluate_witness(&x_denominator).invert().unwrap() - }; - let inv_x_denom = composer.append_witness(inv_x_denom); - - // Assert that we actually have the inverse - // inv_x * x = 1 - let constraint = Constraint::new() - .mult(1) - .constant(-BlsScalar::one()) - .a(x_denominator) - .b(inv_x_denom); - composer.append_gate(constraint); - - // 1 - dx1x2y1y2 - let constraint = Constraint::new() - .left(-BlsScalar::one()) - .constant(1) - .a(d_x1_x2_y1_y2); - let y_denominator = composer.gate_add(constraint); - - let inv_y_denom = unsafe { - composer.evaluate_witness(&y_denominator).invert().unwrap() - }; - let inv_y_denom = composer.append_witness(inv_y_denom); - - // Assert that we actually have the inverse - // inv_y * y = 1 - let constraint = Constraint::new() - .mult(1) - .constant(-BlsScalar::one()) - .a(y_denominator) - .b(inv_y_denom); - composer.append_gate(constraint); - - // We can now use the inverses - let constraint = - Constraint::new().mult(1).a(inv_x_denom).b(x_numerator); - let x_3 = composer.gate_mul(constraint); - - let constraint = - Constraint::new().mult(1).a(inv_y_denom).b(y_numerator); - let y_3 = composer.gate_mul(constraint); - - WitnessPoint { x: x_3, y: y_3 } - } - - #[test] - fn test_curve_addition() { - gadget_tester( - |composer| { - let expected = GENERATOR_EXTENDED + GENERATOR_EXTENDED; - - let point_a = composer.append_point(GENERATOR); - let point_b = composer.append_point(GENERATOR); - - let point = composer.component_add_point(point_a, point_b); - - let point2 = - classical_point_addition(composer, point_a, point_b); - - composer.assert_equal_point(point, point2); - composer.assert_equal_public_point(point, expected); - }, - 2000, - ) - .expect("Curve addition failed"); - } -} diff --git a/src/constraint_system/ecc/scalar_mul.rs b/src/constraint_system/ecc/scalar_mul.rs deleted file mode 100644 index 219c3c087..000000000 --- a/src/constraint_system/ecc/scalar_mul.rs +++ /dev/null @@ -1,10 +0,0 @@ -// 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. - -/// Fixed base scalar multiplication gate -pub mod fixed_base; -/// Witness base scalar multiplication gate -pub mod variable_base; diff --git a/src/constraint_system/ecc/scalar_mul/fixed_base.rs b/src/constraint_system/ecc/scalar_mul/fixed_base.rs deleted file mode 100644 index 978ad2ee5..000000000 --- a/src/constraint_system/ecc/scalar_mul/fixed_base.rs +++ /dev/null @@ -1,375 +0,0 @@ -// 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 crate::constraint_system::ecc::curve_addition::fixed_base_gate::WnafRound; -use crate::constraint_system::{ - Constraint, TurboComposer, Witness, WitnessPoint, -}; -use alloc::vec::Vec; -use dusk_bls12_381::BlsScalar; -use dusk_bytes::Serializable; -use dusk_jubjub::{JubJubAffine, JubJubExtended, JubJubScalar}; - -fn compute_wnaf_point_multiples( - generator: JubJubExtended, - num_bits: usize, -) -> Vec { - assert_eq!(generator.is_prime_order().unwrap_u8(), 1); - - let mut multiples = vec![JubJubExtended::default(); num_bits]; - multiples[0] = generator; - for i in 1..num_bits { - multiples[i] = multiples[i - 1].double(); - } - - dusk_jubjub::batch_normalize(&mut multiples).collect() -} - -impl TurboComposer { - /// Evaluate `jubjub · Generator` as a [`WitnessPoint`] - /// - /// `generator` will be appended to the circuit description as constant - pub fn component_mul_generator>( - &mut self, - jubjub: Witness, - generator: P, - ) -> WitnessPoint { - let generator = generator.into(); - - // XXX: we can slice off 3 bits from the top of wnaf, since F_r prime - // has 252 bits. XXX :We can also move to base4 and have half - // the number of gates since wnaf adjacent entries product is - // zero, we will not go over the specified amount - let num_bits = 256; - - // compute 2^iG - let mut point_multiples = - compute_wnaf_point_multiples(generator, num_bits); - - point_multiples.reverse(); - - // Fetch the raw scalar value as bls scalar, then convert to a jubjub - // scalar XXX: Not very Tidy, impl From function in JubJub - // This will panic if the JubJub scalar is not a jubjub scalar indeed - // and was introduced as a BlsScalar. - let raw_jubjub_scalar = - JubJubScalar::from_bytes(&self.witnesses[&jubjub].to_bytes()) - .unwrap(); - - // Convert scalar to wnaf_2(k) - let wnaf_entries = raw_jubjub_scalar.compute_windowed_naf(2); - assert_eq!(wnaf_entries.len(), num_bits); - - // Initialize the accumulators - let mut scalar_acc = vec![BlsScalar::zero()]; - let mut point_acc = vec![JubJubAffine::identity()]; - - // Auxillary point to help with checks on the backend - let mut xy_alphas = Vec::new(); - - // Load values into accumulators based on wnaf entries - for (i, entry) in wnaf_entries.iter().rev().enumerate() { - // Based on the WNAF, we decide what scalar and point to add - let (scalar_to_add, point_to_add) = match entry { - 0 => { (BlsScalar::zero(), JubJubAffine::identity()) } - -1 => { (BlsScalar::one().neg(), -point_multiples[i]) } - 1 => { (BlsScalar::one(), point_multiples[i]) } - _ => unreachable!("Currently WNAF_2(k) is supported. The possible values are 1, -1 and 0. Current entry is {}", entry), - }; - - let prev_accumulator = BlsScalar::from(2u64) * scalar_acc[i]; - scalar_acc.push(prev_accumulator + scalar_to_add); - point_acc.push( - (JubJubExtended::from(point_acc[i]) - + JubJubExtended::from(point_to_add)) - .into(), - ); - - let x_alpha = point_to_add.get_x(); - let y_alpha = point_to_add.get_y(); - - xy_alphas.push(x_alpha * y_alpha); - } - - for i in 0..num_bits { - let acc_x = self.append_witness(point_acc[i].get_x()); - let acc_y = self.append_witness(point_acc[i].get_y()); - - let accumulated_bit = self.append_witness(scalar_acc[i]); - - // We constrain the point accumulator to start from the Identity - // point and the Scalar accumulator to start from zero - if i == 0 { - self.assert_equal_constant(acc_x, BlsScalar::zero(), None); - self.assert_equal_constant(acc_y, BlsScalar::one(), None); - self.assert_equal_constant( - accumulated_bit, - BlsScalar::zero(), - None, - ); - } - - let x_beta = point_multiples[i].get_x(); - let y_beta = point_multiples[i].get_y(); - - let xy_alpha = self.append_witness(xy_alphas[i]); - - let xy_beta = x_beta * y_beta; - - let wnaf_round = WnafRound { - acc_x, - acc_y, - accumulated_bit, - xy_alpha, - x_beta, - y_beta, - xy_beta, - }; - - self.fixed_group_add(wnaf_round); - } - - // Add last gate, but do not activate it for ECC - // It is for use with the previous gate - let acc_x = self.append_witness(point_acc[num_bits].get_x()); - let acc_y = self.append_witness(point_acc[num_bits].get_y()); - let last_accumulated_bit = self.append_witness(scalar_acc[num_bits]); - - // FIXME this gate isn't verifying anything because all the selectors - // are zeroed. Validate what was the intent - let constraint = - Constraint::new().a(acc_x).b(acc_y).d(last_accumulated_bit); - self.append_gate(constraint); - - // Constrain the last element in the accumulator to be equal to the - // input jubjub scalar - self.assert_equal(last_accumulated_bit, jubjub); - - WitnessPoint { x: acc_x, y: acc_y } - } -} - -#[cfg(feature = "std")] -#[cfg(test)] -mod tests { - use super::*; - use crate::constraint_system::helper::*; - use dusk_jubjub::GENERATOR_EXTENDED; - - #[test] - fn test_ecc_constraint() { - let res = gadget_tester( - |composer| { - let scalar = JubJubScalar::from_bytes_wide(&[ - 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, - 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, 103, 6, 169, 175, - 51, 101, 234, 180, 125, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, - ]); - let bls_scalar = BlsScalar::from(scalar); - let secret_scalar = composer.append_witness(bls_scalar); - - let expected_point: JubJubAffine = - (GENERATOR_EXTENDED * scalar).into(); - - let point_scalar = composer - .component_mul_generator(secret_scalar, GENERATOR_EXTENDED); - - composer - .assert_equal_public_point(point_scalar, expected_point); - }, - 600, - ); - assert!(res.is_ok()); - } - - #[test] - fn test_ecc_constraint_zero() { - let res = gadget_tester( - |composer| { - let scalar = JubJubScalar::zero(); - let bls_scalar = BlsScalar::from(scalar); - let secret_scalar = composer.append_witness(bls_scalar); - - let expected_point: JubJubAffine = - (GENERATOR_EXTENDED * scalar).into(); - - let point_scalar = composer - .component_mul_generator(secret_scalar, GENERATOR_EXTENDED); - - composer - .assert_equal_public_point(point_scalar, expected_point); - }, - 600, - ); - assert!(res.is_ok()); - } - - #[test] - fn test_ecc_constraint_should_fail() { - let res = gadget_tester( - |composer| { - let scalar = JubJubScalar::from(100u64); - let bls_scalar = BlsScalar::from(scalar); - let secret_scalar = composer.append_witness(bls_scalar); - // Fails because we are not multiplying by the GENERATOR, it is - // double - - let double_gen = GENERATOR_EXTENDED.double(); - - let expected_point: JubJubAffine = (double_gen * scalar).into(); - - let point_scalar = composer - .component_mul_generator(secret_scalar, GENERATOR_EXTENDED); - - composer - .assert_equal_public_point(point_scalar, expected_point); - }, - 600, - ); - - assert!(res.is_err()); - } - - #[test] - fn test_point_addition() { - let res = gadget_tester( - |composer| { - let point_a = GENERATOR_EXTENDED; - let point_b = point_a.double(); - let expected_point = point_a + point_b; - - let affine_point_a: JubJubAffine = point_a.into(); - let affine_point_b: JubJubAffine = point_b.into(); - let affine_expected_point: JubJubAffine = expected_point.into(); - - let var_point_a_x = - composer.append_witness(affine_point_a.get_x()); - let var_point_a_y = - composer.append_witness(affine_point_a.get_y()); - let point_a = WitnessPoint { - x: var_point_a_x, - y: var_point_a_y, - }; - let var_point_b_x = - composer.append_witness(affine_point_b.get_x()); - let var_point_b_y = - composer.append_witness(affine_point_b.get_y()); - let point_b = WitnessPoint { - x: var_point_b_x, - y: var_point_b_y, - }; - let new_point = composer.component_add_point(point_a, point_b); - - composer.assert_equal_public_point( - new_point, - affine_expected_point, - ); - }, - 600, - ); - - assert!(res.is_ok()); - } - - #[test] - #[allow(non_snake_case)] - fn test_pedersen_hash() { - let res = gadget_tester( - |composer| { - // First component - let scalar_a = JubJubScalar::from(112233u64); - let bls_scalar = BlsScalar::from(scalar_a); - let secret_scalar_a = composer.append_witness(bls_scalar); - let point_a = GENERATOR_EXTENDED; - let c_a: JubJubAffine = (point_a * scalar_a).into(); - - // Second component - let scalar_b = JubJubScalar::from(445566u64); - let bls_scalar = BlsScalar::from(scalar_b); - let secret_scalar_b = composer.append_witness(bls_scalar); - let point_b = point_a.double() + point_a; - let c_b: JubJubAffine = (point_b * scalar_b).into(); - - // Expected pedersen hash - let expected_point: JubJubAffine = - (point_a * scalar_a + point_b * scalar_b).into(); - - // To check this pedersen commitment, we will need to do: - // - Two scalar multiplications - // - One curve addition - // - // Scalar multiplications - let aG = - composer.component_mul_generator(secret_scalar_a, point_a); - let bH = - composer.component_mul_generator(secret_scalar_b, point_b); - - // Depending on the context, one can check if the resulting aG - // and bH are as expected - // - composer.assert_equal_public_point(aG, c_a); - composer.assert_equal_public_point(bH, c_b); - - // Curve addition - let commitment = composer.component_add_point(aG, bH); - - // Add final gates to ensure that the commitment that we - // computed is equal to the public point - composer.assert_equal_public_point(commitment, expected_point); - }, - 1024, - ); - assert!(res.is_ok()); - } - - #[test] - #[allow(non_snake_case)] - fn test_pedersen_balance() { - let res = gadget_tester( - |composer| { - // First component - let scalar_a = JubJubScalar::from(25u64); - let bls_scalar_a = BlsScalar::from(scalar_a); - let secret_scalar_a = composer.append_witness(bls_scalar_a); - // Second component - let scalar_b = JubJubScalar::from(30u64); - let bls_scalar_b = BlsScalar::from(scalar_b); - let secret_scalar_b = composer.append_witness(bls_scalar_b); - // Third component - let scalar_c = JubJubScalar::from(10u64); - let bls_scalar_c = BlsScalar::from(scalar_c); - let secret_scalar_c = composer.append_witness(bls_scalar_c); - // Fourth component - let scalar_d = JubJubScalar::from(45u64); - let bls_scalar_d = BlsScalar::from(scalar_d); - let secret_scalar_d = composer.append_witness(bls_scalar_d); - - let gen = GENERATOR_EXTENDED; - let expected_lhs: JubJubAffine = - (gen * (scalar_a + scalar_b)).into(); - let expected_rhs: JubJubAffine = - (gen * (scalar_c + scalar_d)).into(); - - let P1 = composer.component_mul_generator(secret_scalar_a, gen); - let P2 = composer.component_mul_generator(secret_scalar_b, gen); - let P3 = composer.component_mul_generator(secret_scalar_c, gen); - let P4 = composer.component_mul_generator(secret_scalar_d, gen); - - let commitment_a = composer.component_add_point(P1, P2); - let commitment_b = composer.component_add_point(P3, P4); - - composer.assert_equal_point(commitment_a, commitment_b); - - composer.assert_equal_public_point(commitment_a, expected_lhs); - composer.assert_equal_public_point(commitment_b, expected_rhs); - }, - 2048, - ); - assert!(res.is_ok()); - } -} diff --git a/src/constraint_system/ecc/scalar_mul/variable_base.rs b/src/constraint_system/ecc/scalar_mul/variable_base.rs deleted file mode 100644 index 1f5b1b005..000000000 --- a/src/constraint_system/ecc/scalar_mul/variable_base.rs +++ /dev/null @@ -1,70 +0,0 @@ -// 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 crate::constraint_system::{TurboComposer, Witness, WitnessPoint}; - -impl TurboComposer { - /// Evaluate `jubjub · point` as a [`WitnessPoint`] - pub fn component_mul_point( - &mut self, - jubjub: Witness, - point: WitnessPoint, - ) -> WitnessPoint { - // Turn scalar into bits - let scalar_bits = self.component_decomposition::<252>(jubjub); - - let identity = self.append_constant_identity(); - let mut result = identity; - - for bit in scalar_bits.iter().rev() { - result = self.component_add_point(result, result); - - let point_to_add = self.component_select_identity(*bit, point); - result = self.component_add_point(result, point_to_add); - } - - result - } -} - -#[cfg(feature = "std")] -#[cfg(test)] -mod tests { - use crate::constraint_system::helper::*; - use dusk_bls12_381::BlsScalar; - use dusk_jubjub::GENERATOR; - use dusk_jubjub::{JubJubAffine, JubJubExtended, JubJubScalar}; - - #[test] - fn test_var_base_scalar_mul() { - let res = gadget_tester( - |composer| { - let scalar = JubJubScalar::from_bytes_wide(&[ - 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, - 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, 103, 6, 169, 175, - 51, 101, 234, 180, 125, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, - ]); - let bls_scalar = BlsScalar::from(scalar); - let secret_scalar = composer.append_witness(bls_scalar); - - let expected_point: JubJubAffine = - (JubJubExtended::from(GENERATOR) * scalar).into(); - - let point = composer.append_point(GENERATOR); - - let point_scalar = - composer.component_mul_point(secret_scalar, point); - - composer - .assert_equal_public_point(point_scalar, expected_point); - }, - 4096, - ); - assert!(res.is_ok()); - } -} diff --git a/src/constraint_system/helper.rs b/src/constraint_system/helper.rs deleted file mode 100644 index ef2f82974..000000000 --- a/src/constraint_system/helper.rs +++ /dev/null @@ -1,83 +0,0 @@ -// 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 super::TurboComposer; -use crate::commitment_scheme::PublicParameters; -use crate::constraint_system::Constraint; -use crate::error::Error; -use crate::proof_system::{Prover, Verifier}; -use dusk_bls12_381::BlsScalar; -use rand_core::OsRng; - -/// Adds dummy gates using arithmetic gates -pub(crate) fn dummy_gadget(n: usize, composer: &mut TurboComposer) { - let one = BlsScalar::one(); - let one = composer.append_witness(one); - - for _ in 0..n { - // FIXME dummy gates with zeroed selectors doesn't make sense - let constraint = Constraint::new().left(1).right(1).a(one).b(one); - - composer.gate_add(constraint); - } -} - -/// Takes a generic gadget function with no auxillary input and -/// tests whether it passes an end-to-end test -pub(crate) fn gadget_tester( - gadget: fn(composer: &mut TurboComposer), - n: usize, -) -> Result<(), Error> { - // Common View - let public_parameters = PublicParameters::setup(2 * n, &mut OsRng)?; - // Provers View - let (proof, public_inputs) = { - // Create a prover struct - let mut prover = Prover::new(b"demo"); - - // Additionally key the transcript - prover.key_transcript(b"key", b"additional seed information"); - - // Add gadgets - gadget(&mut prover.composer_mut()); - - // Commit Key - let (ck, _) = public_parameters - .trim(2 * prover.cs.gates().next_power_of_two())?; - - // Preprocess circuit - prover.preprocess(&ck)?; - - // Once the prove method is called, the public inputs are cleared - // So pre-fetch these before calling Prove - let public_inputs = prover.cs.to_dense_public_inputs(); - - // Compute Proof - (prover.prove(&ck, &mut OsRng)?, public_inputs) - }; - // Verifiers view - // - // Create a Verifier object - let mut verifier = Verifier::new(b"demo"); - - // Additionally key the transcript - verifier.key_transcript(b"key", b"additional seed information"); - - // Add gadgets - gadget(&mut verifier.composer_mut()); - - // Compute Commit and Verifier Key - let (ck, vk) = - public_parameters.trim(verifier.cs.gates().next_power_of_two())?; - - // Preprocess circuit - verifier.preprocess(&ck)?; - - let pi_indexes = verifier.composer_mut().public_input_indexes(); - - // Verify proof - verifier.verify(&proof, &vk, &public_inputs, &pi_indexes) -} diff --git a/src/constraint_system/logic.rs b/src/constraint_system/logic.rs deleted file mode 100644 index 0d0882be2..000000000 --- a/src/constraint_system/logic.rs +++ /dev/null @@ -1,507 +0,0 @@ -// 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 crate::bit_iterator::*; -use crate::constraint_system::TurboComposer; -use crate::constraint_system::{WireData, Witness}; -use alloc::vec::Vec; -use dusk_bls12_381::BlsScalar; -use dusk_bytes::Serializable; - -impl TurboComposer { - /// Performs a logical AND or XOR op between the inputs provided for the - /// specified number of bits (counting from the least significant bit). - /// - /// Each logic gate adds `(num_bits / 2) + 1` gates to the circuit to - /// perform the whole operation. - /// - /// ## Constraint - /// - is_component_xor = 1 -> Performs XOR between the first `num_bits` for - /// `a` and `b`. - /// - is_component_xor = 0 -> Performs AND between the first `num_bits` for - /// `a` and `b`. - /// - /// # Panics - /// This function will panic if the num_bits specified is not even, ie. - /// `num_bits % 2 != 0`. - fn logic_gate( - &mut self, - a: Witness, - b: Witness, - num_bits: usize, - is_component_xor: bool, - ) -> Witness { - // We will need to split the input into `num_bits / 2` two-bit chunks. - // This is why we can only accept an even number of `num_bits`. - assert_eq!(num_bits & 1, 0); - // If `num_bits` is greater than 256 (which is the amount of bits in a - // `BlsScalar`), set `num_bits` to 256 - let num_bits = { - match num_bits < 256 { - true => num_bits, - false => 256, - } - }; - - // We will have exactly `num_bits / 2` quads (quaternary digits) - // representing both numbers. - let num_quads = num_bits >> 1; - - // Allocate the bls accumulators for gate construction. - let mut left_acc = BlsScalar::zero(); - let mut right_acc = BlsScalar::zero(); - let mut out_acc = BlsScalar::zero(); - let mut left_quad: u8; - let mut right_quad: u8; - - // Get witnesses in bits in big endian and skip the first (256 - - // num_bits) bits we are not interested in. - let a_bit_iter = BitIterator8::new(self.witnesses[&a].to_bytes()); - let a_bits: Vec<_> = a_bit_iter.skip(256 - num_bits).collect(); - let b_bit_iter = BitIterator8::new(self.witnesses[&b].to_bytes()); - let b_bits: Vec<_> = b_bit_iter.skip(256 - num_bits).collect(); - - assert!(a_bits.len() >= num_bits); - assert!(b_bits.len() >= num_bits); - - // If we take a look to the program memory structure of the ref. impl. - // * +-----+-----+-----+-----+ - // * | A | B | C | D | - // * +-----+-----+-----+-----+ - // * | 0 | 0 | w1 | 0 | - // * | a1 | b1 | w2 | d1 | - // * | a2 | b2 | w3 | d2 | - // * | : | : | : | : | - // * | an | bn | 0 | dn | - // * +-----+-----+-----+-----+ - // The an, bn and dn are accumulators: - // an [& OR ^] bd = dn - // At each step we shift the bits of the last result two positions to - // the left and add the current quad. - // The wn are product accumulators that are needed to prevent the - // degree of the our quotient polynomial from blowing up. - // We need to have d_i, a_i and b_i pointing to one gate ahead of c_i. - // We increase the gate idx and assign d_i, a_i and b_i to `zero`. - // Now we can add the first row as: `| 0 | 0 | -- | 0 |`. - // Note that c_i will be set on the first loop iteration. - self.perm - .add_witness_to_map(Self::constant_zero(), WireData::Left(self.n)); - self.perm - .add_witness_to_map(Self::constant_zero(), WireData::Right(self.n)); - self.perm.add_witness_to_map( - Self::constant_zero(), - WireData::Fourth(self.n), - ); - self.a_w.push(Self::constant_zero()); - self.b_w.push(Self::constant_zero()); - self.d_w.push(Self::constant_zero()); - // Increase the gate index so we can add the following rows in the - // correct order. - self.n += 1; - - // Start generating accumulator rows and adding them to the circuit. - // Note that we will do this process exactly `num_bits / 2` counting - // that the first step above was done correctly to obtain the - // right format the the first row. This means that we will need - // to pad the end of the memory program once we've built it. - // As we can see in the last row structure: `| an | bn | --- | dn |`. - for i in 0..num_quads { - // On each round, we will commit every accumulator step. To do so, - // we first need to get the ith quads of `a` and `b` and then - // compute `out_quad`(logical OP result) and - // `prod_quad`(intermediate prod result). - - // Here we compute each quad by taking the most significant bit - // shifting it one position to the left and adding to it the less - // significant bit to form the quad with a ternary value - // encapsulated in an `u8` in Big Endian form. - left_quad = { - let idx = i * 2; - ((a_bits[idx] as u8) << 1) + (a_bits[idx + 1] as u8) - }; - right_quad = { - let idx = i * 2; - ((b_bits[idx] as u8) << 1) + (b_bits[idx + 1] as u8) - }; - let left_quad_bls = BlsScalar::from(left_quad as u64); - let right_quad_bls = BlsScalar::from(right_quad as u64); - // The `out_quad` is the result of the bitwise ops `&` or `^` - // between the left and right quads. The op is decided - // with a boolean flag set as input of the function. - let out_quad_bls = match is_component_xor { - true => BlsScalar::from((left_quad ^ right_quad) as u64), - false => BlsScalar::from((left_quad & right_quad) as u64), - }; - // We also need to allocate a helper item which is the result - // of the product between the left and right quads. - // This param is identified as `w` in the program memory and - // is needed to prevent the degree of our quotient polynomial from - // blowing up - let prod_quad_bls = - BlsScalar::from((left_quad * right_quad) as u64); - - // Now that we've computed this round results, we need to apply the - // logic transition constraint that will check that - // a_{i+1} - (a_i << 2) < 4 - // b_{i+1} - (b_i << 2) < 4 - // d_{i+1} - (d_i << 2) < 4 with d_i = a_i [& OR ^] b_i - // Note that multiplying by four is the equivalent of shifting the - // bits two positions to the left. - let bls_four = BlsScalar::from(4u64); - let prev_left_acc = left_acc; - let prev_right_acc = right_acc; - let prev_out_acc = out_acc; - left_acc = left_acc * bls_four + left_quad_bls; - right_acc = right_acc * bls_four + right_quad_bls; - out_acc = out_acc * bls_four + out_quad_bls; - // Apply logic transition gates. - // a_{i+1} - (a_i << 2) < 4 - assert!(left_acc - (prev_left_acc * bls_four) < bls_four); - // b_{i+1} - (b_i << 2) < 4 - assert!(right_acc - (prev_right_acc * bls_four) < bls_four); - // d_{i+1} - (d_i << 2) < 4 - assert!(out_acc - (prev_out_acc * bls_four) < bls_four); - - // Get witnesses pointing to the accumulated values. - let wit_a = self.append_witness(left_acc); - let wit_b = self.append_witness(right_acc); - let wit_c = self.append_witness(prod_quad_bls); - let wit_d = self.append_witness(out_acc); - // Add the witnesses to the witness map linking them to it's - // corresponding gate index. - // - // Note that by doing this, we are basically setting the wire_coeffs - // of the wire polynomials, but we still need to link the - // selector_poly coefficients in order to be able to - // have complete gates. - // - // Also note that here we're setting left (`a_i` in above table), - // right (`b_i`) and fourth (`d_i`) witnesses to the - // actual gate, meanwhile we set out (`w_i`) to the - // previous gate. - self.perm.add_witness_to_map(wit_a, WireData::Left(self.n)); - self.perm.add_witness_to_map(wit_b, WireData::Right(self.n)); - self.perm - .add_witness_to_map(wit_d, WireData::Fourth(self.n)); - self.perm - .add_witness_to_map(wit_c, WireData::Output(self.n - 1)); - // Push the witnesses to it's actual wire vector storage - self.a_w.push(wit_a); - self.b_w.push(wit_b); - self.c_w.push(wit_c); - self.d_w.push(wit_d); - // Update the gate index - self.n += 1; - } - - // We have one missing value for the last row of the program memory - // which is `c_w` since the rest of wires are pointing one gate - // ahead. To fix this, we simply pad with a 0 so the last row of - // the program memory will look like this: - // | an | bn | 0 | dn | - self.perm.add_witness_to_map( - Self::constant_zero(), - WireData::Output(self.n - 1), - ); - self.c_w.push(Self::constant_zero()); - - // Now the wire values are set for each gate, indexed and mapped in the - // `witness_map` inside of the `Permutation` struct. - // Now we just need to extend the selector polynomials with the - // appropriate coefficients to form complete logic gates. - for _ in 0..num_quads { - self.q_m.push(BlsScalar::zero()); - self.q_l.push(BlsScalar::zero()); - self.q_r.push(BlsScalar::zero()); - self.q_arith.push(BlsScalar::zero()); - self.q_o.push(BlsScalar::zero()); - self.q_4.push(BlsScalar::zero()); - self.q_range.push(BlsScalar::zero()); - self.q_fixed_group_add.push(BlsScalar::zero()); - self.q_variable_group_add.push(BlsScalar::zero()); - match is_component_xor { - true => { - self.q_c.push(-BlsScalar::one()); - self.q_logic.push(-BlsScalar::one()); - } - false => { - self.q_c.push(BlsScalar::one()); - self.q_logic.push(BlsScalar::one()); - } - }; - } - // For the last gate, `q_c` and `q_logic` we use no-op values (Zero). - self.q_m.push(BlsScalar::zero()); - self.q_l.push(BlsScalar::zero()); - self.q_r.push(BlsScalar::zero()); - self.q_arith.push(BlsScalar::zero()); - self.q_o.push(BlsScalar::zero()); - self.q_4.push(BlsScalar::zero()); - self.q_range.push(BlsScalar::zero()); - self.q_fixed_group_add.push(BlsScalar::zero()); - self.q_variable_group_add.push(BlsScalar::zero()); - - self.q_c.push(BlsScalar::zero()); - self.q_logic.push(BlsScalar::zero()); - - // Now we check that the last gates wire coefficients match the - // original values introduced in the function taking into account the - // bit_num specified on the fn call parameters. - // To make sure we only check the bits that are specified by bit_num, we - // apply a bit_mask to the values. - // Note that we can not construct a `bit_mask` that represents the - // `BlsScalar` made from four `u64::MAX` which would be needed for - // `num_bits = 256`. This is because `BlsScalar` created `from_raw` are - // invalid if their value exceeds the `MODULO`. - match num_bits < 256 { - true => { - let bit_mask = - BlsScalar::from(2u64).pow(&[num_bits as u64, 0, 0, 0]) - - BlsScalar::one(); - assert_eq!( - self.witnesses[&a] & bit_mask, - self.witnesses[&self.a_w[self.n - 1]], - ); - assert_eq!( - self.witnesses[&b] & bit_mask, - self.witnesses[&self.b_w[self.n - 1]] - ); - } - false => { - assert_eq!( - self.witnesses[&a], - self.witnesses[&self.a_w[self.n - 1]], - ); - assert_eq!( - self.witnesses[&b], - self.witnesses[&self.b_w[self.n - 1]] - ); - } - } - // Once the inputs are checked against the accumulated additions, - // we can safely return the resulting witness of the gate computation - // which is stored on the last program memory row and in the column that - // `d_w` is holding. - self.d_w[self.d_w.len() - 1] - } - - /// Adds a logical XOR gate that performs the XOR between two values for the - /// specified first `num_bits` returning a [`Witness`] holding the - /// result. - /// - /// # Panics - /// - /// If the `num_bits` specified in the fn params is odd. - pub fn component_xor( - &mut self, - a: Witness, - b: Witness, - num_bits: usize, - ) -> Witness { - self.logic_gate(a, b, num_bits, true) - } - - /// Adds a logical AND gate that performs the bitwise AND between two values - /// for the specified first `num_bits` returning a [`Witness`] - /// holding the result. - /// - /// # Panics - /// - /// If the `num_bits` specified in the fn params is odd. - pub fn component_and( - &mut self, - a: Witness, - b: Witness, - num_bits: usize, - ) -> Witness { - self.logic_gate(a, b, num_bits, false) - } -} - -#[cfg(feature = "std")] -#[cfg(test)] -mod tests { - use super::super::helper::*; - use dusk_bls12_381::BlsScalar; - #[test] - fn logic_xor() { - let res = gadget_tester( - |composer| { - let a = BlsScalar::from_raw([ - 0xffefffff00000001, - 0x53bda402fffe5bfe, - 0x4982789dacb42eba, - 0xbfe253acde2f251b, - ]); - let b = BlsScalar::from_raw([ - 0x1fe4fa89f2eebc13, - 0x19420effaad6cb43, - 0xfe10a3b5d02ccba5, - 0x2979075741adef02, - ]); - let witness_a = composer.append_witness(a); - let witness_b = composer.append_witness(b); - let bit_num = 32 * 8 - 2; - let xor_res = - composer.component_xor(witness_a, witness_b, bit_num); - // Check that the XOR result is indeed what we are expecting. - let bit_mask = - BlsScalar::from(2).pow(&[bit_num as u64, 0, 0, 0]) - - BlsScalar::one(); - composer.assert_equal_constant( - xor_res, - (a ^ b) & bit_mask, - None, - ); - }, - 900, - ); - assert!(res.is_ok()); - } - - #[test] - fn logic_and() { - let res = gadget_tester( - |composer| { - let a = BlsScalar::from_raw([ - 0xcdbbba32b2059321, - 0xd23d790abc203def, - 0x039290023244ddd2, - 0x221045dddbaaa234, - ]); - let b = BlsScalar::from_raw([ - 0xffeffa89f2eebc13, - 0x19420effaad6cb43, - 0x0000138739efccab, - 0x2979bc292cccde11, - ]); - let witness_a = composer.append_witness(a); - let witness_b = composer.append_witness(b); - let bit_num = 32 * 8; - let and_res = - composer.component_and(witness_a, witness_b, bit_num); - // Check that the XOR result is indeed what we are expecting. - composer.assert_equal_constant(and_res, a & b, None); - }, - 500, - ); - assert!(res.is_ok()); - } - - #[test] - fn test_logic_xor_and_constraint_small() { - // Should pass since the XOR result is correct and the bit-num is even. - let res = gadget_tester( - |composer| { - let a = 500u64; - let b = 357u64; - let bit_num = 10; - let witness_a = composer.append_witness(BlsScalar::from(a)); - let witness_b = composer.append_witness(BlsScalar::from(b)); - let xor_res = - composer.component_xor(witness_a, witness_b, bit_num); - // Check that the XOR result is indeed what we are expecting. - composer.assert_equal_constant( - xor_res, - BlsScalar::from(a ^ b), - None, - ); - }, - 200, - ); - assert!(res.is_ok()); - - // Should pass since the AND result is correct and the bit-num is even. - let res = gadget_tester( - |composer| { - let a = 0x2979bc292cccde11; - let b = 0x5792abc4d3002eda; - // fails if I want to compare 32 * 8 bits but passes like this - let bit_num = 32 * 8 - 2; - let witness_a = composer.append_witness(BlsScalar::from(a)); - let witness_b = composer.append_witness(BlsScalar::from(b)); - let and_res = - composer.component_and(witness_a, witness_b, bit_num); - // Check that the AND result is indeed what we are expecting. - composer.assert_equal_constant( - and_res, - BlsScalar::from(a & b), - None, - ); - }, - 700, - ); - assert!(res.is_ok()); - - // Should not pass since the XOR result is not correct even the bit-num - // is even. - let res = gadget_tester( - |composer| { - let a = 139u64; - let b = 33u64; - let bit_num = 10; - let witness_a = composer.append_witness(BlsScalar::from(a)); - let witness_b = composer.append_witness(BlsScalar::from(b)); - let and_res = - composer.component_and(witness_a, witness_b, bit_num); - // AND result should not pass - composer.assert_equal_constant( - and_res, - BlsScalar::from(a ^ b), - None, - ); - }, - 200, - ); - assert!(res.is_err()); - - // Should pass even if the bit-num is less than the number bit-size - let res = gadget_tester( - |composer| { - let a = 256u64; - let b = 0xd23d790abc203def; - let bit_num = 60; - let witness_a = composer.append_witness(BlsScalar::from(a)); - let witness_b = composer.append_witness(BlsScalar::from(b)); - let xor_res = - composer.component_xor(witness_a, witness_b, bit_num); - // Check that the XOR result is indeed what we are expecting. - let bit_mask = - BlsScalar::from(2).pow(&[bit_num as u64, 0, 0, 0]) - - BlsScalar::one(); - composer.assert_equal_constant( - xor_res, - BlsScalar::from(a ^ b) & BlsScalar::from(bit_mask), - None, - ); - }, - 200, - ); - assert!(res.is_ok()); - } - - #[test] - #[should_panic] - fn test_logical_gate_odd_bit_num() { - // Should fail since the bit-num is odd. - let _ = gadget_tester( - |composer| { - let witness_a = - composer.append_witness(BlsScalar::from(500u64)); - let witness_b = - composer.append_witness(BlsScalar::from(499u64)); - let xor_res = composer.component_xor(witness_a, witness_b, 9); - // Check that the XOR result is indeed what we are expecting. - composer.assert_equal_constant( - xor_res, - BlsScalar::from(7u64), - None, - ); - }, - 200, - ); - } -} diff --git a/src/constraint_system/range.rs b/src/constraint_system/range.rs deleted file mode 100644 index 5f2622552..000000000 --- a/src/constraint_system/range.rs +++ /dev/null @@ -1,253 +0,0 @@ -// 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 crate::bit_iterator::*; -use crate::constraint_system::TurboComposer; -use crate::constraint_system::{WireData, Witness}; -use alloc::vec::Vec; -use dusk_bls12_381::BlsScalar; -use dusk_bytes::Serializable; - -impl TurboComposer { - /// Adds a range-constraint gate that checks and constrains a - /// [`Witness`] to be inside of the range \[0,num_bits\]. - /// - /// This function adds `num_bits/4` gates to the circuit description in - /// order to add the range constraint. - /// - ///# Panics - /// This function will panic if the num_bits specified is not even, ie. - /// `num_bits % 2 != 0`. - pub fn component_range(&mut self, witness: Witness, num_bits: usize) { - // Adds `Witness` into the appropriate witness position - // based on the accumulator number a_i - let add_wire = - |composer: &mut TurboComposer, i: usize, witness: Witness| { - // Since four quads can fit into one gate, the gate index does - // not change for every four wires - let gate_index = composer.gates() + (i / 4); - - let wire_data = match i % 4 { - 0 => { - composer.d_w.push(witness); - WireData::Fourth(gate_index) - } - 1 => { - composer.c_w.push(witness); - WireData::Output(gate_index) - } - 2 => { - composer.b_w.push(witness); - WireData::Right(gate_index) - } - 3 => { - composer.a_w.push(witness); - WireData::Left(gate_index) - } - _ => unreachable!(), - }; - - composer.perm.add_witness_to_map(witness, wire_data); - }; - - // Note: A quad is a quaternary digit - // - // Number of bits should be even, this means that user must pad the - // number of bits external. - assert_eq!(num_bits % 2, 0); - - // Convert witness to bit representation and reverse - let bits = self.witnesses[&witness]; - let bit_iter = BitIterator8::new(bits.to_bytes()); - let mut bits: Vec<_> = bit_iter.collect(); - bits.reverse(); - - // For a width-4 program, one gate will contain 4 accumulators - // Each accumulator proves that a single quad is a base-4 digit. - // Since there is 1-1 mapping between accumulators and quads - // and quads contain 2 bits, one gate accumulates 8 bits. - // We can therefore work out the number of gates needed; - let mut num_gates = num_bits >> 3; - - // The number of bits may be divisible by 2 but not by 8. - // Example: If we wanted to prove a number was within the range [0,2^10 - // -1 ] We would need 10 bits. When dividing by 10 by 8, we will - // get 1 as the number of gates, when in fact we need 2 gates In - // general, we will need to add an extra gate, if the number of bits is - // not divisible by 8 - if num_bits % 8 != 0 { - num_gates += 1; - } - - // Since each gate holds 4 quads, the number of quads that will be - // needed to prove that the witness is within a specific range can be - // computed from the number of gates - let num_quads = num_gates * 4; - - // There are now two things to note in terms of padding: - // 1. (a_{i+1}, a_i) proves that {q_i+1} is a quaternary digit. - // In order to prove that the first digit is a quad, we need to add a - // zero accumulator (genesis quad) 2. We need the last gate to - // contain 1 quad, so the range gate equation is not used on the last - // gate. This is needed because the range gate equation looks at - // the fourth for the next gate, which is not guaranteed to pass. - // We therefore prepend quads until we have 1 quad in the last gate. - // This will at most add one extra gate. - // - // There are two cases to consider: - // Case 1: If the number of bits used is divisible by 8, then it is also - // divisible by 4. This means that we can find out how many - // gates are needed by dividing the number of bits by 8 However, - // since we will always need a genesis quad, it will mean that we will - // need an another gate to hold the extra quad Example: Take 32 - // bits. We compute the number of gates to be 32/8 = 4 full gates, we - // then add 1 because we need the genesis accumulator - // In this case, we only pad by one quad, which is the genesis quad. - // Moreover, the genesis quad is the quad that has added the extra gate. - // - // Case 2: When the number of bits is not divisible by 8 - // Since the number is not divisible by 4, as in case 1, when we add the - // genesis quad, we will have more than 1 quad on the last row - // In this case, the genesis quad, did not add an extra gate. What will - // add the extra gate, is the padding. We must apply padding in - // order to ensure the last row has only one quad in on the fourth wire - // In this case, it is the padding which will add an extra number of - // gates Example: 34 bits requires 17 quads. We add one for the - // zeroed out accumulator. To make 18 quads. We can fit all of these - // quads in 5 gates. 18 % 4 = 2 so on the last row, we will have - // two quads, which is bad. We must pad the beginning in order - // to get one quad on the last row We can work out how much we - // need to pad by the following equation (18+X) % 4 = 1 - // X is 3 , so we pad 3 extra zeroes - // We now have 21 quads in the system now and 21 / 4 = 5 remainder 1, so - // we will need 5 full gates and extra gate with 1 quad. - let pad = 1 + (((num_quads << 1) - num_bits) >> 1); - - // The last observation; we will always use 1 more gate than the number - // of gates calculated Either due to the genesis quad, or the - // padding used to ensure we have 1 quad on the last gate - let used_gates = num_gates + 1; - - // We collect the set of accumulators to return back to the user - // and keep a running count of the current accumulator - let mut accumulators: Vec = Vec::new(); - let mut accumulator = BlsScalar::zero(); - let four = BlsScalar::from(4); - - // First we pad our gates by the necessary amount - for i in 0..pad { - add_wire(self, i, Self::constant_zero()); - } - - for i in pad..=num_quads { - // Convert each pair of bits to quads - let bit_index = (num_quads - i) << 1; - let q_0 = bits[bit_index] as u64; - let q_1 = bits[bit_index + 1] as u64; - let quad = q_0 + (2 * q_1); - - // Compute the next accumulator term - accumulator = four * accumulator; - accumulator += BlsScalar::from(quad); - - let accumulator_var = self.append_witness(accumulator); - accumulators.push(accumulator_var); - - add_wire(self, i, accumulator_var); - } - - // Set the selector polynomials for all of the gates we used - let zeros = vec![BlsScalar::zero(); used_gates]; - let ones = vec![BlsScalar::one(); used_gates]; - - self.q_m.extend(zeros.iter()); - self.q_l.extend(zeros.iter()); - self.q_r.extend(zeros.iter()); - self.q_o.extend(zeros.iter()); - self.q_c.extend(zeros.iter()); - self.q_arith.extend(zeros.iter()); - self.q_4.extend(zeros.iter()); - self.q_fixed_group_add.extend(zeros.iter()); - self.q_variable_group_add.extend(zeros.iter()); - self.q_range.extend(ones.iter()); - self.q_logic.extend(zeros.iter()); - self.n += used_gates; - - // As mentioned above, we must switch off the range constraint for the - // last gate Remember; it will contain one quad in the fourth - // wire, which will be used in the gate before it - // Furthermore, we set the left, right and output wires to zero - *self.q_range.last_mut().unwrap() = BlsScalar::zero(); - self.a_w.push(Self::constant_zero()); - self.b_w.push(Self::constant_zero()); - self.c_w.push(Self::constant_zero()); - - // Lastly, we must link the last accumulator value to the initial - // witness This last constraint will pass as long as - // - The witness is within the number of bits initially specified - let last_accumulator = accumulators.len() - 1; - self.assert_equal(accumulators[last_accumulator], witness); - accumulators[last_accumulator] = witness; - } -} - -#[cfg(feature = "std")] -#[cfg(test)] -mod tests { - use super::super::helper::*; - use dusk_bls12_381::BlsScalar; - - #[test] - fn test_range_constraint() { - // Should fail as the number is not 32 bits - let res = gadget_tester( - |composer| { - let witness = composer.append_witness(BlsScalar::from( - (u32::max_value() as u64) + 1, - )); - composer.component_range(witness, 32); - }, - 200, - ); - assert!(res.is_err()); - - // Should fail as number is greater than 32 bits - let res = gadget_tester( - |composer| { - let witness = - composer.append_witness(BlsScalar::from(u64::max_value())); - composer.component_range(witness, 32); - }, - 200, - ); - assert!(res.is_err()); - - // Should pass as the number is within 34 bits - let res = gadget_tester( - |composer| { - let witness = - composer.append_witness(BlsScalar::from(2u64.pow(34) - 1)); - composer.component_range(witness, 34); - }, - 200, - ); - assert!(res.is_ok()); - } - - #[test] - #[should_panic] - fn test_odd_bit_range() { - // Should fail as the number we we need a even number of bits - let _ok = gadget_tester( - |composer| { - let witness = composer - .append_witness(BlsScalar::from(u32::max_value() as u64)); - composer.component_range(witness, 33); - }, - 200, - ); - } -} diff --git a/src/constraint_system/witness.rs b/src/constraint_system/witness.rs index feeb873bd..fcf718911 100644 --- a/src/constraint_system/witness.rs +++ b/src/constraint_system/witness.rs @@ -23,7 +23,7 @@ pub(crate) enum WireData { Fourth(usize), } -/// Witness data indexed in a [`TurboComposer`](super::TurboComposer) instance +/// Allocated witness in the constraint system. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct Witness { index: usize, diff --git a/src/debugger.rs b/src/debugger.rs new file mode 100644 index 000000000..06ea54fd5 --- /dev/null +++ b/src/debugger.rs @@ -0,0 +1,225 @@ +// 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. + +//! Debugger module + +use std::env; +use std::path::PathBuf; + +use dusk_bls12_381::BlsScalar; +use dusk_bytes::Serializable; +use dusk_cdf::{ + Config, Constraint as CdfConstraint, Encoder, IndexedWitness, Polynomial, + Source, Witness as CdfWitness, +}; + +use crate::constraint_system::{Constraint, Selector, WiredWitness, Witness}; +use crate::runtime::RuntimeEvent; + +/// PLONK debugger +#[derive(Debug, Clone)] +pub(crate) struct Debugger { + witnesses: Vec<(Source, Witness, BlsScalar)>, + constraints: Vec<(Source, Constraint)>, +} + +impl Debugger { + /// Resolver the caller function + fn resolve_caller() -> Source { + let mut source = None; + + backtrace::trace(|frame| { + // Resolve this instruction pointer to a symbol name + backtrace::resolve_frame(frame, |symbol| { + if symbol + .name() + .map(|n| format!("{}", n)) + .filter(|s| !s.starts_with("backtrace::")) + .filter(|s| !s.starts_with("dusk_plonk::")) + .filter(|s| !s.starts_with("core::")) + .filter(|s| !s.starts_with("std::")) + .is_some() + { + if let Some(path) = symbol.filename() { + let line = symbol.lineno().unwrap_or_default() as u64; + let col = symbol.colno().unwrap_or_default() as u64; + let path = path.canonicalize().unwrap_or_default(); + let path = format!("{}", path.display()).into(); + + source.replace(Source::new(line, col, path)); + } + } + }); + + source.is_none() + }); + + source.unwrap_or_default() + } + + fn write_output(&self) { + let path = match env::var("CDF_OUTPUT") { + Ok(path) => PathBuf::from(path), + Err(env::VarError::NotPresent) => return (), + Err(env::VarError::NotUnicode(_)) => { + eprintln!("the provided `CDF_OUTPUT` isn't valid unicode"); + return (); + } + }; + + let witnesses = self.witnesses.iter().map(|(source, w, value)| { + let id = w.index(); + let value = value.to_bytes().into(); + let source = source.clone(); + + CdfWitness::new(id, value, source) + }); + + let constraints = + self.constraints + .iter() + .enumerate() + .map(|(id, (source, c))| { + let source = source.clone(); + + let qm = c.coeff(Selector::Multiplication); + let ql = c.coeff(Selector::Left); + let qr = c.coeff(Selector::Right); + let qd = c.coeff(Selector::Fourth); + let qc = c.coeff(Selector::Constant); + let qo = c.coeff(Selector::Output); + let pi = c.coeff(Selector::PublicInput); + let qarith = c.coeff(Selector::Arithmetic); + let qrange = c.coeff(Selector::Range); + let qlogic = c.coeff(Selector::Logic); + let qfixed_add = c.coeff(Selector::GroupAddFixedBase); + let qvariable_add = c.coeff(Selector::GroupAddVariableBase); + + let wai = c.witness(WiredWitness::A).index(); + let wbi = c.witness(WiredWitness::B).index(); + let wdi = c.witness(WiredWitness::D).index(); + let woi = c.witness(WiredWitness::O).index(); + + let wav = self + .witnesses + .get(wai) + .map(|(_, _, v)| *v) + .unwrap_or_default(); + + let wbv = self + .witnesses + .get(wbi) + .map(|(_, _, v)| *v) + .unwrap_or_default(); + + let wdv = self + .witnesses + .get(wdi) + .map(|(_, _, v)| *v) + .unwrap_or_default(); + + let wov = self + .witnesses + .get(woi) + .map(|(_, _, v)| *v) + .unwrap_or_default(); + + // TODO check arith, range, logic & ecc wires + let eval = qm * wav * wbv + + ql * wav + + qr * wbv + + qd * wdv + + qo * wov + + qc + + pi; + + let re = eval == BlsScalar::zero(); + + let qm = qm.to_bytes().into(); + let ql = ql.to_bytes().into(); + let qr = qr.to_bytes().into(); + let qd = qd.to_bytes().into(); + let qc = qc.to_bytes().into(); + let qo = qo.to_bytes().into(); + let pi = pi.to_bytes().into(); + let qarith = qarith.to_bytes().into(); + let qlogic = qlogic.to_bytes().into(); + let qvariable_add = qvariable_add.to_bytes().into(); + + // TODO add these to CDF + let _ = (qrange, qfixed_add); + + // TODO IndexedWitness is to be deprecated in favor of a + // simplified index + + let wav = wav.to_bytes().into(); + let wbv = wbv.to_bytes().into(); + let wdv = wdv.to_bytes().into(); + let wov = wov.to_bytes().into(); + + let wa = IndexedWitness::new(wai, None, wav); + let wb = IndexedWitness::new(wbi, None, wbv); + let wd = IndexedWitness::new(wdi, None, wdv); + let wo = IndexedWitness::new(woi, None, wov); + + let poly = Polynomial::new( + qm, + ql, + qr, + qd, + qc, + qo, + pi, + qarith, + qlogic, + qvariable_add, + wa, + wb, + wd, + wo, + re, + ); + + CdfConstraint::new(id, poly, source) + }); + + let config = Config::new(); + + if let Err(e) = + Encoder::init_file(config, witnesses, constraints, &path) + .and_then(|mut c| c.write_all()) + { + eprintln!( + "failed to output CDF file to '{}': {}", + path.display(), + e + ); + } + } + + pub(crate) fn with_capacity(capacity: usize) -> Self { + Self { + witnesses: Vec::with_capacity(capacity), + constraints: Vec::with_capacity(capacity), + } + } + + pub(crate) fn event(&mut self, event: RuntimeEvent) { + match event { + RuntimeEvent::WitnessAppended { w, v } => { + self.witnesses.push((Self::resolve_caller(), w, v)); + } + + RuntimeEvent::ConstraintAppended { c } => { + self.constraints.push((Self::resolve_caller(), c)); + } + + RuntimeEvent::ProofFinished => { + self.write_output(); + } + } + } +} diff --git a/src/error.rs b/src/error.rs index 75256711e..83a631a4a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,7 +9,7 @@ use dusk_bytes::Error as DuskBytesError; /// Defines all possible errors that can be encountered in PLONK. -#[derive(core::fmt::Debug)] +#[derive(Debug, Clone, Copy)] pub enum Error { // FFT errors /// This error occurs when an error triggers on any of the fft module @@ -72,6 +72,20 @@ pub enum Error { /// This error occurs when a malformed BLS scalar is decoded from a byte /// array. BlsScalarMalformed, + /// WNAF2k should be in `[-1, 0, 1]` + UnsupportedWNAF2k, + /// The provided public inputs doesn't match the circuit definition + PublicInputNotFound { + /// Expected public input wasn't found + index: usize, + }, + /// The provided public inputs length doesn't match the processed verifier + InconsistentPublicInputsLen { + /// Expected value + expected: usize, + /// Provided value + provided: usize, + }, } #[cfg(feature = "std")] @@ -127,6 +141,16 @@ impl std::fmt::Display for Error { Self::PointMalformed => write!(f, "BLS point bytes malformed"), Self::BlsScalarMalformed => write!(f, "BLS scalar bytes malformed"), Self::BytesError(err) => write!(f, "{:?}", err), + Self::UnsupportedWNAF2k => write!( + f, + "WNAF2k cannot hold values not contained in `[-1..1]`" + ), + Self::PublicInputNotFound { + index + } => write!(f, "The public input of index {} is defined in the circuit description, but wasn't declared in the prove instance", index), + Self::InconsistentPublicInputsLen { + expected, provided, + } => write!(f, "The provided public inputs set of length {} doesn't match the processed verifier: {}", provided, expected), } } } diff --git a/src/fft/polynomial.rs b/src/fft/polynomial.rs index 2c856597c..c98d7afaa 100644 --- a/src/fft/polynomial.rs +++ b/src/fft/polynomial.rs @@ -60,11 +60,6 @@ impl Polynomial { || self.coeffs.iter().all(|coeff| coeff == &BlsScalar::zero()) } - /// Constructs a new polynomial from a list of coefficients. - pub(crate) fn from_coefficients_slice(coeffs: &[BlsScalar]) -> Self { - Self::from_coefficients_vec(coeffs.to_vec()) - } - /// Constructs a new polynomial from a list of coefficients. /// /// # Panics diff --git a/src/lib.rs b/src/lib.rs index cb4c4c572..043f58071 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,13 +65,17 @@ if #[cfg(feature = "alloc")] { mod bit_iterator; mod permutation; mod util; + mod transcript; - pub mod circuit; pub mod constraint_system; + pub mod composer; + pub mod runtime; }); mod fft; -mod transcript; + +#[cfg(feature = "debug")] +pub(crate) mod debugger; pub mod commitment_scheme; pub mod error; diff --git a/src/permutation.rs b/src/permutation.rs index b2a31e835..d6b5f78e8 100644 --- a/src/permutation.rs +++ b/src/permutation.rs @@ -17,7 +17,7 @@ pub(crate) mod constants; /// Permutation provides the necessary state information and functions /// to create the permutation polynomial. In the literature, Z(X) is the /// "accumulator", this is what this codebase calls the permutation polynomial. -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct Permutation { // Maps a witness to the wires that it is associated to. pub(crate) witness_map: HashMap>, @@ -301,7 +301,7 @@ impl Permutation { #[cfg(test)] mod test { use super::*; - use crate::constraint_system::{Constraint, TurboComposer}; + //use crate::constraint_system::Constraint; use crate::fft::Polynomial; use dusk_bls12_381::BlsScalar; use rand_core::OsRng; @@ -652,115 +652,6 @@ mod test { ) } - #[test] - fn test_multizip_permutation_poly() { - let mut cs = TurboComposer::with_size(4); - - let x1 = cs.append_witness(BlsScalar::from_raw([4, 0, 0, 0])); - let x2 = cs.append_witness(BlsScalar::from_raw([12, 0, 0, 0])); - let x3 = cs.append_witness(BlsScalar::from_raw([8, 0, 0, 0])); - let x4 = cs.append_witness(BlsScalar::from_raw([3, 0, 0, 0])); - - let one = BlsScalar::one(); - let two = BlsScalar::from_raw([2, 0, 0, 0]); - - // x1 * x4 = x2 - let constraint = - Constraint::new().mult(1).output(-one).a(x1).b(x4).o(x2); - cs.append_gate(constraint); - - // x1 + x3 = x2 - let constraint = Constraint::new() - .left(1) - .right(1) - .output(-one) - .a(x1) - .b(x3) - .o(x2); - cs.append_gate(constraint); - - // x1 + x2 = 2*x3 - let constraint = Constraint::new() - .left(1) - .right(1) - .output(-two) - .a(x1) - .b(x2) - .o(x3); - cs.append_gate(constraint); - - // x3 * x4 = 2*x2 - let constraint = - Constraint::new().mult(1).output(-two).a(x3).b(x4).o(x2); - cs.append_gate(constraint); - - let domain = EvaluationDomain::new(cs.gates()).unwrap(); - let pad = vec![BlsScalar::zero(); domain.size() - cs.a_w.len()]; - let mut a_w_scalar: Vec = - cs.a_w.iter().map(|v| cs.witnesses[v]).collect(); - let mut b_w_scalar: Vec = - cs.b_w.iter().map(|v| cs.witnesses[v]).collect(); - let mut c_w_scalar: Vec = - cs.c_w.iter().map(|v| cs.witnesses[v]).collect(); - let mut d_w_scalar: Vec = - cs.d_w.iter().map(|v| cs.witnesses[v]).collect(); - - a_w_scalar.extend(&pad); - b_w_scalar.extend(&pad); - c_w_scalar.extend(&pad); - d_w_scalar.extend(&pad); - - let sigmas: Vec> = cs - .perm - .compute_sigma_permutations(7) - .iter() - .map(|wd| cs.perm.compute_permutation_lagrange(wd, &domain)) - .collect(); - - let beta = BlsScalar::random(&mut OsRng); - let gamma = BlsScalar::random(&mut OsRng); - - let sigma_polys: Vec = sigmas - .iter() - .map(|v| Polynomial::from_coefficients_vec(domain.ifft(v))) - .collect(); - - let mz = Polynomial::from_coefficients_vec(domain.ifft( - &cs.perm.compute_permutation_vec( - &domain, - [&a_w_scalar, &b_w_scalar, &c_w_scalar, &d_w_scalar], - &beta, - &gamma, - [ - &sigma_polys[0], - &sigma_polys[1], - &sigma_polys[2], - &sigma_polys[3], - ], - ), - )); - - let old_z = Polynomial::from_coefficients_vec(domain.ifft( - &compute_fast_permutation_poly( - &domain, - &a_w_scalar, - &b_w_scalar, - &c_w_scalar, - &d_w_scalar, - &beta, - &gamma, - ( - &sigma_polys[0], - &sigma_polys[1], - &sigma_polys[2], - &sigma_polys[3], - ), - ), - )); - - assert_eq!(mz, old_z); - } - #[test] fn test_permutation_format() { let mut perm: Permutation = Permutation::new(); diff --git a/src/prelude.rs b/src/prelude.rs index 5d0a91ee8..7a788ff81 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,13 +11,12 @@ #[cfg(feature = "alloc")] pub use crate::{ - circuit::{self, Circuit, PublicInputValue, VerifierData}, - commitment_scheme::{CommitKey, OpeningKey, PublicParameters}, - constraint_system::{Constraint, TurboComposer, Witness, WitnessPoint}, - proof_system::{Prover, ProverKey, Verifier}, + commitment_scheme::PublicParameters, + composer::{Builder, Circuit, Compiler, Composer, Prover, Verifier}, + constraint_system::{Constraint, Witness, WitnessPoint}, }; pub use crate::error::Error; -pub use crate::proof_system::{Proof, VerifierKey}; +pub use crate::proof_system::Proof; pub use dusk_bls12_381::BlsScalar; pub use dusk_jubjub::{JubJubAffine, JubJubExtended, JubJubScalar}; diff --git a/src/proof_system.rs b/src/proof_system.rs index b0e2a52fa..480a485b2 100644 --- a/src/proof_system.rs +++ b/src/proof_system.rs @@ -12,15 +12,11 @@ pub(crate) mod widget; cfg_if::cfg_if!( if #[cfg(feature = "alloc")] { - mod preprocess; - pub(crate) mod quotient_poly; - pub(crate) mod prover; - pub(crate) mod verifier; + pub(crate) mod preprocess; - pub use prover::Prover; - pub use verifier::Verifier; - pub use widget::alloc::ProverKey; + pub(crate) use widget::alloc::ProverKey; + pub(crate) use widget::VerifierKey; cfg_if::cfg_if!( if #[cfg(feature = "rkyv-impl")] { @@ -31,7 +27,6 @@ cfg_if::cfg_if!( ); pub use proof::Proof; -pub use widget::VerifierKey; cfg_if::cfg_if!( if #[cfg(feature = "rkyv-impl")] { diff --git a/src/proof_system/preprocess.rs b/src/proof_system/preprocess.rs index db7899d1d..91560f806 100644 --- a/src/proof_system/preprocess.rs +++ b/src/proof_system/preprocess.rs @@ -6,409 +6,32 @@ //! Methods to preprocess the constraint system for use in a proof -use crate::commitment_scheme::CommitKey; -use crate::constraint_system::TurboComposer; - -use crate::error::Error; -use crate::fft::{EvaluationDomain, Evaluations, Polynomial}; -use crate::proof_system::{widget, ProverKey}; -use dusk_bls12_381::BlsScalar; -use merlin::Transcript; +use crate::fft::Polynomial; /// Struct that contains all selector and permutation [`Polynomials`]s pub(crate) struct Polynomials { // selector polynomials defining arithmetic circuits - q_m: Polynomial, - q_l: Polynomial, - q_r: Polynomial, - q_o: Polynomial, - q_c: Polynomial, + pub(crate) q_m: Polynomial, + pub(crate) q_l: Polynomial, + pub(crate) q_r: Polynomial, + pub(crate) q_o: Polynomial, + pub(crate) q_c: Polynomial, // additional selector for 3-input gates added for efficiency of // implementation - q_4: Polynomial, + pub(crate) q_4: Polynomial, // additional selectors for different kinds of circuits added for // efficiency of implementation - q_arith: Polynomial, // arithmetic circuits - q_range: Polynomial, // range proofs - q_logic: Polynomial, // boolean operations - q_fixed_group_add: Polynomial, // ecc circuits - q_variable_group_add: Polynomial, // ecc circuits + pub(crate) q_arith: Polynomial, // arithmetic circuits + pub(crate) q_range: Polynomial, // range proofs + pub(crate) q_logic: Polynomial, // boolean operations + pub(crate) q_fixed_group_add: Polynomial, // ecc circuits + pub(crate) q_variable_group_add: Polynomial, // ecc circuits // copy permutation polynomials - s_sigma_1: Polynomial, - s_sigma_2: Polynomial, - s_sigma_3: Polynomial, - s_sigma_4: Polynomial, // for q_4 -} - -impl TurboComposer { - /// Pads the circuit to the next power of two - /// - /// # Note: - /// - /// `diff` is the difference between circuit size and next power of two - fn pad(&mut self, diff: usize) { - // Add a zero variable to circuit - let zero_scalar = BlsScalar::zero(); - let zero_var = Self::constant_zero(); - - let zeroes_scalar = vec![zero_scalar; diff]; - let zeroes_var = vec![zero_var; diff]; - - self.q_m.extend(zeroes_scalar.iter()); - self.q_l.extend(zeroes_scalar.iter()); - self.q_r.extend(zeroes_scalar.iter()); - self.q_o.extend(zeroes_scalar.iter()); - self.q_c.extend(zeroes_scalar.iter()); - self.q_4.extend(zeroes_scalar.iter()); - self.q_arith.extend(zeroes_scalar.iter()); - self.q_range.extend(zeroes_scalar.iter()); - self.q_logic.extend(zeroes_scalar.iter()); - self.q_fixed_group_add.extend(zeroes_scalar.iter()); - self.q_variable_group_add.extend(zeroes_scalar.iter()); - - self.a_w.extend(zeroes_var.iter()); - self.b_w.extend(zeroes_var.iter()); - self.c_w.extend(zeroes_var.iter()); - self.d_w.extend(zeroes_var.iter()); - - self.n += diff; - } - - /// Checks that all of the wires of the composer have the same length. - fn check_poly_same_len(&self) -> Result<(), Error> { - let k = self.q_m.len(); - - if self.q_o.len() == k - && self.q_l.len() == k - && self.q_r.len() == k - && self.q_c.len() == k - && self.q_4.len() == k - && self.q_arith.len() == k - && self.q_range.len() == k - && self.q_logic.len() == k - && self.q_fixed_group_add.len() == k - && self.q_variable_group_add.len() == k - && self.a_w.len() == k - && self.b_w.len() == k - && self.c_w.len() == k - && self.d_w.len() == k - { - Ok(()) - } else { - Err(Error::MismatchedPolyLen) - } - } - - /// These are the parts of preprocessing that the prover must compute - /// Although the prover does not need the verification key, he must compute - /// the commitments in order to seed the transcript, allowing both the - /// prover and verifier to have the same view - pub(crate) fn preprocess_prover( - &mut self, - commit_key: &CommitKey, - transcript: &mut Transcript, - ) -> Result { - let (_, selectors, domain) = - self.preprocess_shared(commit_key, transcript)?; - - // The polynomial needs an evaluation domain of 4n. - // Plus, adding the blinding factors translates to - // the polynomial not fitting in 4n, so now we need - // 8n, the next power of 2 - let domain_8n = EvaluationDomain::new(8 * domain.size())?; - let q_m_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.q_m), - domain_8n, - ); - let q_l_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.q_l), - domain_8n, - ); - let q_r_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.q_r), - domain_8n, - ); - let q_o_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.q_o), - domain_8n, - ); - let q_c_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.q_c), - domain_8n, - ); - let q_4_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.q_4), - domain_8n, - ); - let q_arith_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.q_arith), - domain_8n, - ); - let q_range_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.q_range), - domain_8n, - ); - let q_logic_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.q_logic), - domain_8n, - ); - let q_fixed_group_add_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.q_fixed_group_add), - domain_8n, - ); - let q_variable_group_add_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.q_variable_group_add), - domain_8n, - ); - - let s_sigma_1_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.s_sigma_1), - domain_8n, - ); - let s_sigma_2_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.s_sigma_2), - domain_8n, - ); - let s_sigma_3_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.s_sigma_3), - domain_8n, - ); - let s_sigma_4_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&selectors.s_sigma_4), - domain_8n, - ); - - // XXX: Remove this and compute it on the fly - let linear_eval_8n = Evaluations::from_vec_and_domain( - domain_8n.coset_fft(&[BlsScalar::zero(), BlsScalar::one()]), - domain_8n, - ); - - // Prover Key for arithmetic circuits - let arithmetic_prover_key = widget::arithmetic::ProverKey { - q_m: (selectors.q_m, q_m_eval_8n), - q_l: (selectors.q_l.clone(), q_l_eval_8n.clone()), - q_r: (selectors.q_r.clone(), q_r_eval_8n.clone()), - q_o: (selectors.q_o, q_o_eval_8n), - q_c: (selectors.q_c.clone(), q_c_eval_8n.clone()), - q_4: (selectors.q_4, q_4_eval_8n), - q_arith: (selectors.q_arith, q_arith_eval_8n), - }; - - // Prover Key for range circuits - let range_prover_key = widget::range::ProverKey { - q_range: (selectors.q_range, q_range_eval_8n), - }; - - // Prover Key for logic circuits - let logic_prover_key = widget::logic::ProverKey { - q_c: (selectors.q_c.clone(), q_c_eval_8n.clone()), - q_logic: (selectors.q_logic, q_logic_eval_8n), - }; - - // Prover Key for ecc circuits - let ecc_prover_key = widget::ecc::scalar_mul::fixed_base::ProverKey { - q_l: (selectors.q_l, q_l_eval_8n), - q_r: (selectors.q_r, q_r_eval_8n), - q_c: (selectors.q_c, q_c_eval_8n), - q_fixed_group_add: ( - selectors.q_fixed_group_add, - q_fixed_group_add_eval_8n, - ), - }; - - // Prover Key for permutation argument - let permutation_prover_key = widget::permutation::ProverKey { - s_sigma_1: (selectors.s_sigma_1, s_sigma_1_eval_8n), - s_sigma_2: (selectors.s_sigma_2, s_sigma_2_eval_8n), - s_sigma_3: (selectors.s_sigma_3, s_sigma_3_eval_8n), - s_sigma_4: (selectors.s_sigma_4, s_sigma_4_eval_8n), - linear_evaluations: linear_eval_8n, - }; - - // Prover Key for curve addition - let curve_addition_prover_key = - widget::ecc::curve_addition::ProverKey { - q_variable_group_add: ( - selectors.q_variable_group_add, - q_variable_group_add_eval_8n, - ), - }; - - let prover_key = ProverKey { - n: domain.size(), - arithmetic: arithmetic_prover_key, - logic: logic_prover_key, - range: range_prover_key, - permutation: permutation_prover_key, - variable_base: curve_addition_prover_key, - fixed_base: ecc_prover_key, - // Compute 8n evaluations for X^n -1 - v_h_coset_8n: domain_8n - .compute_vanishing_poly_over_coset(domain.size() as u64), - }; - - Ok(prover_key) - } - - /// The verifier only requires the commitments in order to verify a - /// [`Proof`](super::Proof) We can therefore speed up preprocessing for the - /// verifier by skipping the FFTs needed to compute the 8n evaluations. - pub(crate) fn preprocess_verifier( - &mut self, - commit_key: &CommitKey, - transcript: &mut Transcript, - ) -> Result { - let (verifier_key, _, _) = - self.preprocess_shared(commit_key, transcript)?; - Ok(verifier_key) - } - - /// Both the [`Prover`](super::Prover) and [`Verifier`](super::Verifier) - /// must perform IFFTs on the selector polynomials and permutation - /// polynomials in order to commit to them and have the same transcript - /// view. - fn preprocess_shared( - &mut self, - commit_key: &CommitKey, - transcript: &mut Transcript, - ) -> Result<(widget::VerifierKey, Polynomials, EvaluationDomain), Error> - { - let domain = EvaluationDomain::new(self.n)?; - - // Check that the length of the wires is consistent. - self.check_poly_same_len()?; - - // 1. Pad circuit to a power of two - self.pad(domain.size as usize - self.n); - - let q_m_poly = - Polynomial::from_coefficients_slice(&domain.ifft(&self.q_m)); - let q_l_poly = - Polynomial::from_coefficients_slice(&domain.ifft(&self.q_l)); - let q_r_poly = - Polynomial::from_coefficients_slice(&domain.ifft(&self.q_r)); - let q_o_poly = - Polynomial::from_coefficients_slice(&domain.ifft(&self.q_o)); - let q_c_poly = - Polynomial::from_coefficients_slice(&domain.ifft(&self.q_c)); - let q_4_poly = - Polynomial::from_coefficients_slice(&domain.ifft(&self.q_4)); - let q_arith_poly = - Polynomial::from_coefficients_slice(&domain.ifft(&self.q_arith)); - let q_range_poly = - Polynomial::from_coefficients_slice(&domain.ifft(&self.q_range)); - let q_logic_poly = - Polynomial::from_coefficients_slice(&domain.ifft(&self.q_logic)); - let q_fixed_group_add_poly = Polynomial::from_coefficients_slice( - &domain.ifft(&self.q_fixed_group_add), - ); - let q_variable_group_add_poly = Polynomial::from_coefficients_slice( - &domain.ifft(&self.q_variable_group_add), - ); - - // 2. Compute the sigma polynomials - let [s_sigma_1_poly, s_sigma_2_poly, s_sigma_3_poly, s_sigma_4_poly] = - self.perm.compute_sigma_polynomials(self.n, &domain); - - let q_m_poly_commit = commit_key.commit(&q_m_poly).unwrap_or_default(); - let q_l_poly_commit = commit_key.commit(&q_l_poly).unwrap_or_default(); - let q_r_poly_commit = commit_key.commit(&q_r_poly).unwrap_or_default(); - let q_o_poly_commit = commit_key.commit(&q_o_poly).unwrap_or_default(); - let q_c_poly_commit = commit_key.commit(&q_c_poly).unwrap_or_default(); - let q_4_poly_commit = commit_key.commit(&q_4_poly).unwrap_or_default(); - let q_arith_poly_commit = - commit_key.commit(&q_arith_poly).unwrap_or_default(); - let q_range_poly_commit = - commit_key.commit(&q_range_poly).unwrap_or_default(); - let q_logic_poly_commit = - commit_key.commit(&q_logic_poly).unwrap_or_default(); - let q_fixed_group_add_poly_commit = commit_key - .commit(&q_fixed_group_add_poly) - .unwrap_or_default(); - let q_variable_group_add_poly_commit = commit_key - .commit(&q_variable_group_add_poly) - .unwrap_or_default(); - - let s_sigma_1_poly_commit = commit_key.commit(&s_sigma_1_poly)?; - let s_sigma_2_poly_commit = commit_key.commit(&s_sigma_2_poly)?; - let s_sigma_3_poly_commit = commit_key.commit(&s_sigma_3_poly)?; - let s_sigma_4_poly_commit = commit_key.commit(&s_sigma_4_poly)?; - - // Verifier Key for arithmetic circuits - let arithmetic_verifier_key = widget::arithmetic::VerifierKey { - q_m: q_m_poly_commit, - q_l: q_l_poly_commit, - q_r: q_r_poly_commit, - q_o: q_o_poly_commit, - q_c: q_c_poly_commit, - q_4: q_4_poly_commit, - q_arith: q_arith_poly_commit, - }; - // Verifier Key for range circuits - let range_verifier_key = widget::range::VerifierKey { - q_range: q_range_poly_commit, - }; - // Verifier Key for logic circuits - let logic_verifier_key = widget::logic::VerifierKey { - q_c: q_c_poly_commit, - q_logic: q_logic_poly_commit, - }; - // Verifier Key for ecc circuits - let ecc_verifier_key = - widget::ecc::scalar_mul::fixed_base::VerifierKey { - q_l: q_l_poly_commit, - q_r: q_r_poly_commit, - q_fixed_group_add: q_fixed_group_add_poly_commit, - }; - // Verifier Key for curve addition circuits - let curve_addition_verifier_key = - widget::ecc::curve_addition::VerifierKey { - q_variable_group_add: q_variable_group_add_poly_commit, - }; - - // Verifier Key for permutation argument - let permutation_verifier_key = widget::permutation::VerifierKey { - s_sigma_1: s_sigma_1_poly_commit, - s_sigma_2: s_sigma_2_poly_commit, - s_sigma_3: s_sigma_3_poly_commit, - s_sigma_4: s_sigma_4_poly_commit, - }; - - let verifier_key = widget::VerifierKey { - n: self.gates(), - arithmetic: arithmetic_verifier_key, - logic: logic_verifier_key, - range: range_verifier_key, - fixed_base: ecc_verifier_key, - variable_base: curve_addition_verifier_key, - permutation: permutation_verifier_key, - }; - - let selectors = Polynomials { - q_m: q_m_poly, - q_l: q_l_poly, - q_r: q_r_poly, - q_o: q_o_poly, - q_c: q_c_poly, - q_4: q_4_poly, - q_arith: q_arith_poly, - q_range: q_range_poly, - q_logic: q_logic_poly, - q_fixed_group_add: q_fixed_group_add_poly, - q_variable_group_add: q_variable_group_add_poly, - s_sigma_1: s_sigma_1_poly, - s_sigma_2: s_sigma_2_poly, - s_sigma_3: s_sigma_3_poly, - s_sigma_4: s_sigma_4_poly, - }; - - // Add the circuit description to the transcript - verifier_key.seed_transcript(transcript); - - Ok((verifier_key, selectors, domain)) - } + pub(crate) s_sigma_1: Polynomial, + pub(crate) s_sigma_2: Polynomial, + pub(crate) s_sigma_3: Polynomial, + pub(crate) s_sigma_4: Polynomial, // for q_4 } diff --git a/src/proof_system/proof.rs b/src/proof_system/proof.rs index 023caf17c..2dc207b2f 100644 --- a/src/proof_system/proof.rs +++ b/src/proof_system/proof.rs @@ -6,9 +6,6 @@ //! A Proof stores the commitments to all of the elements that //! are needed to univocally identify a prove of some statement. -//! -//! This module contains the implementation of the `TurboComposer`s -//! `Proof` structure and it's methods. use super::linearization_poly::ProofEvaluations; use crate::commitment_scheme::Commitment; @@ -26,10 +23,10 @@ use rkyv::{ /// /// It's main goal is to allow the `Verifier` to /// formally verify that the secret witnesses used to generate the [`Proof`] -/// satisfy a circuit that both [`Prover`](super::Prover) and -/// [`Verifier`](super::Verifier) have in common succintly and without any -/// capabilities of adquiring any kind of knowledge about the witness used to -/// construct the Proof. +/// satisfy a circuit that both [`Builder`](crate::prelude::Builder) and +/// [`Verifier`](crate::prelude::Verifier) have in common succintly +/// and without any capabilities of adquiring any kind of knowledge about the +/// witness used to construct the Proof. #[derive(Debug, Eq, PartialEq, Clone, Default)] #[cfg_attr( feature = "rkyv-impl", diff --git a/src/proof_system/prover.rs b/src/proof_system/prover.rs deleted file mode 100644 index 686ef9840..000000000 --- a/src/proof_system/prover.rs +++ /dev/null @@ -1,468 +0,0 @@ -// 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 crate::{ - commitment_scheme::CommitKey, - constraint_system::{TurboComposer, Witness}, - error::Error, - fft::{EvaluationDomain, Polynomial}, - proof_system::{ - linearization_poly, proof::Proof, quotient_poly, ProverKey, - }, - transcript::TranscriptProtocol, - util, -}; -use alloc::vec::Vec; -use dusk_bls12_381::BlsScalar; -use merlin::Transcript; -use rand_core::{CryptoRng, RngCore}; - -/// Abstraction structure designed to construct a circuit and generate -/// [`Proof`]s for it. -#[allow(missing_debug_implementations)] -pub struct Prover { - /// ProverKey which is used to create proofs about a specific PLONK circuit - pub prover_key: Option, - - pub(crate) cs: TurboComposer, - /// Store the messages exchanged during the preprocessing stage - /// This is copied each time, we make a proof - pub preprocessed_transcript: Transcript, -} - -impl Prover { - /// Mutable borrow of [`TurboComposer`]. - pub fn composer_mut(&mut self) -> &mut TurboComposer { - &mut self.cs - } - - /// Preprocesses the underlying constraint system. - pub fn preprocess(&mut self, commit_key: &CommitKey) -> Result<(), Error> { - if self.prover_key.is_some() { - return Err(Error::CircuitAlreadyPreprocessed); - } - let pk = self - .cs - .preprocess_prover(commit_key, &mut self.preprocessed_transcript)?; - self.prover_key = Some(pk); - Ok(()) - } -} - -impl Default for Prover { - fn default() -> Prover { - Prover::new(b"plonk") - } -} - -impl Prover { - /// Creates a new `Prover` instance. - pub fn new(label: &'static [u8]) -> Prover { - Prover { - prover_key: None, - cs: TurboComposer::new(), - preprocessed_transcript: Transcript::new(label), - } - } - - /// Creates a new `Prover` object with some expected size. - pub fn with_size(label: &'static [u8], size: usize) -> Prover { - Prover { - prover_key: None, - cs: TurboComposer::with_size(size), - preprocessed_transcript: Transcript::new(label), - } - } - - /// Returns the number of gates in the circuit thet the `Prover` actually - /// stores inside. - pub const fn gates(&self) -> usize { - self.cs.gates() - } - - /// Split `t(X)` poly into 4 degree `n` polynomials. - pub(crate) fn split_tx_poly( - &self, - n: usize, - t_x: &Polynomial, - ) -> (Polynomial, Polynomial, Polynomial, Polynomial) { - ( - Polynomial::from_coefficients_vec(t_x[0..n].to_vec()), - Polynomial::from_coefficients_vec(t_x[n..2 * n].to_vec()), - Polynomial::from_coefficients_vec(t_x[2 * n..3 * n].to_vec()), - Polynomial::from_coefficients_vec(t_x[3 * n..].to_vec()), - ) - } - - /// Computes the quotient Opening [`Polynomial`]. - fn compute_quotient_opening_poly( - n: usize, - t_low_poly: &Polynomial, - t_mid_poly: &Polynomial, - t_high_poly: &Polynomial, - t_4_poly: &Polynomial, - z_challenge: &BlsScalar, - ) -> Polynomial { - // Compute z^n , z^2n , z^3n - let z_n = z_challenge.pow(&[n as u64, 0, 0, 0]); - let z_two_n = z_challenge.pow(&[2 * n as u64, 0, 0, 0]); - let z_three_n = z_challenge.pow(&[3 * n as u64, 0, 0, 0]); - - let a = t_low_poly; - let b = t_mid_poly * &z_n; - let c = t_high_poly * &z_two_n; - let d = t_4_poly * &z_three_n; - let abc = &(a + &b) + &c; - &abc + &d - } - - /// Convert witnesses to their actual witness values. - pub(crate) fn to_scalars(&self, vars: &[Witness]) -> Vec { - vars.iter().map(|var| self.cs.witnesses[var]).collect() - } - - /// Resets the witnesses in the prover object. - /// This function is used when the user wants to make multiple proofs with - /// the same circuit. - pub fn clear_witness(&mut self) { - self.cs = TurboComposer::new(); - } - - /// Clears all data in the `Prover` instance. - /// This function is used when the user wants to use the same `Prover` to - /// make a [`Proof`] regarding a different circuit. - pub fn clear(&mut self) { - self.clear_witness(); - self.prover_key = None; - self.preprocessed_transcript = Transcript::new(b"plonk"); - } - - /// Keys the [`Transcript`] with additional seed information - /// Wrapper around [`Transcript::append_message`]. - pub fn key_transcript(&mut self, label: &'static [u8], message: &[u8]) { - self.preprocessed_transcript.append_message(label, message); - } - - /// Adds the blinding scalars to a given vector. Always the same elements - /// of 'w_vec' are modified at the beginning of it, and appended at the end: - /// if hiding degree = 1: (b2*X^(n+1) + b1*X^n - b2*X - b1) + w_vec - /// if hiding degree = 2: (b3*X^(n+2) + b2*X^(n+1) + b1*X^n - b3*X^2 - b2*X - /// - b1) + w_vec - pub(crate) fn blind_poly( - w_vec: &Vec, - hiding_degree: usize, - domain: &EvaluationDomain, - rng: &mut R, - ) -> Polynomial { - let mut w_vec_inverse = domain.ifft(w_vec); - - for i in 0..hiding_degree + 1 { - // we declare and randomly select a blinding scalar - let blinding_scalar = util::random_scalar(rng); - // modify the first elements of the vector - w_vec_inverse[i] = w_vec_inverse[i] - blinding_scalar; - // append last elements at the end of the vector - w_vec_inverse.push(blinding_scalar); - } - - Polynomial::from_coefficients_vec(w_vec_inverse) - } - - /// Creates a [`Proof]` that demonstrates that a circuit is satisfied. - /// - /// # Note - /// - /// If you intend to construct multiple [`Proof`]s with different witnesses, - /// after calling this method, the user should then call - /// [`Prover::clear_witness`]. - /// This is automatically done when [`Prover::prove`] is called. - pub fn prove_with_preprocessed( - &self, - commit_key: &CommitKey, - prover_key: &ProverKey, - rng: &mut R, - ) -> Result { - let domain = EvaluationDomain::new(self.cs.gates())?; - - // Since the caller is passing a pre-processed circuit we assume that - // the Transcript has been seeded with the preprocessed commitments - let mut transcript = self.preprocessed_transcript.clone(); - - // PIs have to be part of the transcript - for pi_index in self.cs.public_input_indexes().iter() { - transcript.append_scalar( - b"pi", - &self.cs.to_dense_public_inputs()[*pi_index], - ); - } - - //** ROUND 1 ********************************************************** - // Convert wires to BlsScalars padding them to the correct domain size. - // Note that `d_w` is added for the additional selector for 3-input - // gates `q_4`. - let pad = vec![BlsScalar::zero(); domain.size() - self.cs.a_w.len()]; - let a_w_scalar = &[&self.to_scalars(&self.cs.a_w)[..], &pad].concat(); - let b_w_scalar = &[&self.to_scalars(&self.cs.b_w)[..], &pad].concat(); - let c_w_scalar = &[&self.to_scalars(&self.cs.c_w)[..], &pad].concat(); - let d_w_scalar = &[&self.to_scalars(&self.cs.d_w)[..], &pad].concat(); - - // Wires are now in evaluation form, convert them to coefficients so - // that we may commit to them - let a_w_poly = Prover::blind_poly(&a_w_scalar, 1, &domain, rng); - let b_w_poly = Prover::blind_poly(&b_w_scalar, 1, &domain, rng); - let c_w_poly = Prover::blind_poly(&c_w_scalar, 1, &domain, rng); - let d_w_poly = Prover::blind_poly(&d_w_scalar, 1, &domain, rng); - - // Commit to wire polynomials - // ([a(x)]_1, [b(x)]_1, [c(x)]_1, [d(x)]_1) - let a_w_poly_commit = commit_key.commit(&a_w_poly)?; - let b_w_poly_commit = commit_key.commit(&b_w_poly)?; - let c_w_poly_commit = commit_key.commit(&c_w_poly)?; - let d_w_poly_commit = commit_key.commit(&d_w_poly)?; - - // Add wire polynomial commitments to transcript - transcript.append_commitment(b"a_w", &a_w_poly_commit); - transcript.append_commitment(b"b_w", &b_w_poly_commit); - transcript.append_commitment(b"c_w", &c_w_poly_commit); - transcript.append_commitment(b"d_w", &d_w_poly_commit); - - //** ROUND 2 ********************************************************** - // Permutation challenges - let beta = transcript.challenge_scalar(b"beta"); - transcript.append_scalar(b"beta", &beta); - let gamma = transcript.challenge_scalar(b"gamma"); - - let z_poly = Prover::blind_poly( - &self.cs.perm.compute_permutation_vec( - &domain, - [a_w_scalar, b_w_scalar, c_w_scalar, d_w_scalar], - &beta, - &gamma, - [ - &prover_key.permutation.s_sigma_1.0, - &prover_key.permutation.s_sigma_2.0, - &prover_key.permutation.s_sigma_3.0, - &prover_key.permutation.s_sigma_4.0, - ], - ), - 2, - &domain, - rng, - ); - - // Commit to permutation polynomial - let z_poly_commit = commit_key.commit(&z_poly)?; - - // Add commitment to permutation polynomial to transcript - transcript.append_commitment(b"z", &z_poly_commit); - - //** ROUND 3 ********************************************************** - // Compute quotient challenge 'alpha' - let alpha = transcript.challenge_scalar(b"alpha"); - let range_sep_challenge = - transcript.challenge_scalar(b"range separation challenge"); - let logic_sep_challenge = - transcript.challenge_scalar(b"logic separation challenge"); - let fixed_base_sep_challenge = - transcript.challenge_scalar(b"fixed base separation challenge"); - let var_base_sep_challenge = - transcript.challenge_scalar(b"variable base separation challenge"); - - // Compute public inputs polynomial - let pi_poly = Polynomial::from_coefficients_vec( - domain.ifft(&self.cs.to_dense_public_inputs()), - ); - - // Compute quotient polynomial - let t_poly = quotient_poly::compute( - &domain, - prover_key, - &z_poly, - (&a_w_poly, &b_w_poly, &c_w_poly, &d_w_poly), - &pi_poly, - &( - alpha, - beta, - gamma, - range_sep_challenge, - logic_sep_challenge, - fixed_base_sep_challenge, - var_base_sep_challenge, - ), - )?; - - // Split quotient polynomial into 4 degree `n` polynomials - let (t_low_poly, t_mid_poly, t_high_poly, t_4_poly) = - self.split_tx_poly(domain.size(), &t_poly); - - // Commit to split quotient polynomial - let t_low_commit = commit_key.commit(&t_low_poly)?; - let t_mid_commit = commit_key.commit(&t_mid_poly)?; - let t_high_commit = commit_key.commit(&t_high_poly)?; - let t_4_commit = commit_key.commit(&t_4_poly)?; - - // Add quotient polynomial commitments to transcript - transcript.append_commitment(b"t_low", &t_low_commit); - transcript.append_commitment(b"t_mid", &t_mid_commit); - transcript.append_commitment(b"t_high", &t_high_commit); - transcript.append_commitment(b"t_4", &t_4_commit); - - //** ROUND 4 ********************************************************** - // Compute evaluation challenge 'z' - let z_challenge = transcript.challenge_scalar(b"z_challenge"); - // the evaluations are computed altogether in next round - - //** ROUND 5 ********************************************************** - // Compute linearization polynomial - let (r_poly, evaluations) = linearization_poly::compute( - &domain, - prover_key, - &( - alpha, - beta, - gamma, - range_sep_challenge, - logic_sep_challenge, - fixed_base_sep_challenge, - var_base_sep_challenge, - z_challenge, - ), - &a_w_poly, - &b_w_poly, - &c_w_poly, - &d_w_poly, - &t_poly, - &z_poly, - ); - - // Add evaluations to transcript. - // Part of these are from round 5 in the paper. - // Note that even tough some of the evaluations are not added to the - // transcript, they are still sent as part of the `Proof` in the return - // value. - transcript.append_scalar(b"a_eval", &evaluations.proof.a_eval); - transcript.append_scalar(b"b_eval", &evaluations.proof.b_eval); - transcript.append_scalar(b"c_eval", &evaluations.proof.c_eval); - transcript.append_scalar(b"d_eval", &evaluations.proof.d_eval); - transcript - .append_scalar(b"a_next_eval", &evaluations.proof.a_next_eval); - transcript - .append_scalar(b"b_next_eval", &evaluations.proof.b_next_eval); - transcript - .append_scalar(b"d_next_eval", &evaluations.proof.d_next_eval); - transcript.append_scalar( - b"s_sigma_1_eval", - &evaluations.proof.s_sigma_1_eval, - ); - transcript.append_scalar( - b"s_sigma_2_eval", - &evaluations.proof.s_sigma_2_eval, - ); - transcript.append_scalar( - b"s_sigma_3_eval", - &evaluations.proof.s_sigma_3_eval, - ); - transcript - .append_scalar(b"q_arith_eval", &evaluations.proof.q_arith_eval); - transcript.append_scalar(b"q_c_eval", &evaluations.proof.q_c_eval); - transcript.append_scalar(b"q_l_eval", &evaluations.proof.q_l_eval); - transcript.append_scalar(b"q_r_eval", &evaluations.proof.q_r_eval); - transcript.append_scalar(b"perm_eval", &evaluations.proof.perm_eval); - transcript.append_scalar(b"t_eval", &evaluations.t_eval); - transcript.append_scalar(b"r_eval", &evaluations.proof.r_poly_eval); - - // Compute Openings using KZG10 - // We merge the quotient polynomial using the challenge z so the SRS - // is linear in the circuit size `n` - let quot = Self::compute_quotient_opening_poly( - domain.size(), - &t_low_poly, - &t_mid_poly, - &t_high_poly, - &t_4_poly, - &z_challenge, - ); - - // Compute aggregate witness to polynomials evaluated at the evaluation - // challenge z. The challenge v is selected inside - let aggregate_witness = commit_key.compute_aggregate_witness( - &[ - quot, - r_poly, - a_w_poly.clone(), - b_w_poly.clone(), - c_w_poly, - d_w_poly.clone(), - prover_key.permutation.s_sigma_1.0.clone(), - prover_key.permutation.s_sigma_2.0.clone(), - prover_key.permutation.s_sigma_3.0.clone(), - ], - &z_challenge, - &mut transcript, - ); - let w_z_chall_comm = commit_key.commit(&aggregate_witness)?; - - // Compute aggregate witness to polynomials evaluated at the shifted - // evaluation challenge - let shifted_aggregate_witness = commit_key.compute_aggregate_witness( - &[z_poly, a_w_poly, b_w_poly, d_w_poly], - &(z_challenge * domain.group_gen), - &mut transcript, - ); - let w_z_chall_w_comm = commit_key.commit(&shifted_aggregate_witness)?; - - // Create Proof - Ok(Proof { - a_comm: a_w_poly_commit, - b_comm: b_w_poly_commit, - c_comm: c_w_poly_commit, - d_comm: d_w_poly_commit, - - z_comm: z_poly_commit, - - t_low_comm: t_low_commit, - t_mid_comm: t_mid_commit, - t_high_comm: t_high_commit, - t_4_comm: t_4_commit, - - w_z_chall_comm, - w_z_chall_w_comm, - - evaluations: evaluations.proof, - }) - } - - /// Proves a circuit is satisfied, then clears the witness variables - /// If the circuit is not pre-processed, then the preprocessed circuit will - /// also be computed. - pub fn prove( - &mut self, - commit_key: &CommitKey, - rng: &mut R, - ) -> Result { - let prover_key: &ProverKey; - - if self.prover_key.is_none() { - // Preprocess circuit - let prover_key = self.cs.preprocess_prover( - commit_key, - &mut self.preprocessed_transcript, - )?; - // Store preprocessed circuit and transcript in the Prover - self.prover_key = Some(prover_key); - } - - prover_key = self.prover_key.as_ref().unwrap(); - - let proof = - self.prove_with_preprocessed(commit_key, prover_key, rng)?; - - // Clear witness and reset composer variables - self.clear_witness(); - - Ok(proof) - } -} diff --git a/src/proof_system/quotient_poly.rs b/src/proof_system/quotient_poly.rs index 062c55502..4df949426 100644 --- a/src/proof_system/quotient_poly.rs +++ b/src/proof_system/quotient_poly.rs @@ -98,9 +98,9 @@ pub(crate) fn compute( }) .collect(); - Ok(Polynomial::from_coefficients_vec( - domain_8n.coset_ifft("ient), - )) + let coset = domain_8n.coset_ifft("ient); + + Ok(Polynomial::from_coefficients_vec(coset)) } // Ensures that the circuit is satisfied diff --git a/src/proof_system/verifier.rs b/src/proof_system/verifier.rs deleted file mode 100644 index 838016509..000000000 --- a/src/proof_system/verifier.rs +++ /dev/null @@ -1,109 +0,0 @@ -// 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 crate::commitment_scheme::{CommitKey, OpeningKey}; -use crate::constraint_system::TurboComposer; -use crate::error::Error; -use crate::proof_system::widget::VerifierKey; -use crate::proof_system::Proof; -use crate::transcript::TranscriptProtocol; -use dusk_bls12_381::BlsScalar; -use merlin::Transcript; - -/// Abstraction structure designed verify [`Proof`]s. -#[allow(missing_debug_implementations)] -pub struct Verifier { - /// VerificationKey which is used to verify a specific PLONK circuit - pub verifier_key: Option, - - pub(crate) cs: TurboComposer, - /// Store the messages exchanged during the preprocessing stage - /// This is copied each time, we make a proof, so that we can use the same - /// verifier to Verify multiple proofs from the same circuit. If this - /// is not copied, then the verification procedure will modify - /// the transcript, making it unusable for future proofs. - pub preprocessed_transcript: Transcript, -} - -impl Default for Verifier { - fn default() -> Verifier { - Verifier::new(b"plonk") - } -} - -impl Verifier { - /// Creates a new `Verifier` instance. - pub fn new(label: &'static [u8]) -> Verifier { - Verifier { - verifier_key: None, - cs: TurboComposer::new(), - preprocessed_transcript: Transcript::new(label), - } - } - - /// Creates a new `Verifier` instance with some expected size. - pub fn with_size(label: &'static [u8], size: usize) -> Verifier { - Verifier { - verifier_key: None, - cs: TurboComposer::with_size(size), - preprocessed_transcript: Transcript::new(label), - } - } - - /// Returns the number of gates in the circuit. - pub const fn gates(&self) -> usize { - self.cs.gates() - } - - /// Mutable borrow of the [`TurboComposer`]. - pub fn composer_mut(&mut self) -> &mut TurboComposer { - &mut self.cs - } - - /// Preprocess a circuit to obtain a [`VerifierKey`] and a circuit - /// descriptor so that the `Verifier` instance can verify [`Proof`]s - /// for this circuit descriptor instance. - pub fn preprocess(&mut self, commit_key: &CommitKey) -> Result<(), Error> { - let vk = self.cs.preprocess_verifier( - commit_key, - &mut self.preprocessed_transcript, - )?; - - self.verifier_key = Some(vk); - Ok(()) - } - - /// Keys the [`Transcript`] with additional seed information - /// Wrapper around [`Transcript::append_message`]. - pub fn key_transcript(&mut self, label: &'static [u8], message: &[u8]) { - self.preprocessed_transcript.append_message(label, message); - } - - /// Verifies a [`Proof`]. - pub fn verify( - &self, - proof: &Proof, - opening_key: &OpeningKey, - public_inputs: &[BlsScalar], - pi_indexes: &[usize], - ) -> Result<(), Error> { - let mut cloned_transcript = self.preprocessed_transcript.clone(); - - // PIs have to be part of the transcript - for pi_index in pi_indexes.iter() { - cloned_transcript.append_scalar(b"pi", &public_inputs[*pi_index]); - } - - let verifier_key = self.verifier_key.as_ref().unwrap(); - - proof.verify( - verifier_key, - &mut cloned_transcript, - opening_key, - public_inputs, - ) - } -} diff --git a/src/proof_system/widget.rs b/src/proof_system/widget.rs index 92b4fefea..51bcb9076 100644 --- a/src/proof_system/widget.rs +++ b/src/proof_system/widget.rs @@ -107,11 +107,6 @@ impl Serializable<{ 20 * Commitment::SIZE + u64::SIZE }> for VerifierKey { } impl VerifierKey { - /// Returns the Circuit size padded to the next power of two. - pub const fn padded_gates(&self) -> usize { - self.n.next_power_of_two() - } - /// Constructs a [`VerifierKey`] from the widget VerifierKey's that are /// constructed based on the selector polynomial commitments and the /// sigma polynomial commitments. diff --git a/src/runtime.rs b/src/runtime.rs new file mode 100644 index 000000000..a23aca1a7 --- /dev/null +++ b/src/runtime.rs @@ -0,0 +1,59 @@ +// 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. + +//! PLONK runtime controller + +use dusk_bls12_381::BlsScalar; + +use crate::constraint_system::{Constraint, Witness}; + +#[cfg(feature = "debug")] +use crate::debugger::Debugger; + +/// Runtime events +#[derive(Debug, Clone, Copy)] +pub enum RuntimeEvent { + /// A witness was appended to the constraint system + WitnessAppended { + /// Appended witness + w: Witness, + /// Witness value + v: BlsScalar, + }, + + /// A constraint was appended + ConstraintAppended { + /// Appended constraint + c: Constraint, + }, + + /// The proof construction was finished + ProofFinished, +} + +/// Runtime structure with debugger +#[derive(Debug, Clone)] +pub struct Runtime { + #[cfg(feature = "debug")] + debugger: Debugger, +} + +impl Runtime { + /// Create a new PLONK runtime with the provided capacity + #[allow(unused_variables)] + pub fn with_capacity(capacity: usize) -> Self { + Self { + #[cfg(feature = "debug")] + debugger: Debugger::with_capacity(capacity), + } + } + + #[allow(unused_variables)] + pub(crate) fn event(&mut self, event: RuntimeEvent) { + #[cfg(feature = "debug")] + self.debugger.event(event); + } +} diff --git a/src/transcript.rs b/src/transcript.rs index 37a73a3d7..741f9b8ba 100644 --- a/src/transcript.rs +++ b/src/transcript.rs @@ -6,11 +6,16 @@ //! This is an extension over the [Merlin Transcript](Transcript) //! which adds a few extra functionalities. -use crate::commitment_scheme::Commitment; + +use core::mem; + use dusk_bls12_381::BlsScalar; use dusk_bytes::Serializable; use merlin::Transcript; +use crate::commitment_scheme::Commitment; +use crate::proof_system::VerifierKey; + /// Transcript adds an abstraction over the Merlin transcript /// For convenience pub(crate) trait TranscriptProtocol { @@ -25,6 +30,13 @@ pub(crate) trait TranscriptProtocol { /// Append domain separator for the circuit size. fn circuit_domain_sep(&mut self, n: u64); + + /// Create a new instance of the base transcript of the protocol + fn base( + label: &[u8], + verifier_key: &VerifierKey, + constraints: usize, + ) -> Self; } impl TranscriptProtocol for Transcript { @@ -47,4 +59,28 @@ impl TranscriptProtocol for Transcript { self.append_message(b"dom-sep", b"circuit_size"); self.append_u64(b"n", n); } + + fn base( + label: &[u8], + verifier_key: &VerifierKey, + constraints: usize, + ) -> Self { + // Transcript can't be serialized/deserialized. One alternative is to + // fork merlin and implement these functionalities, so we can use custom + // transcripts for provers and verifiers. However, we don't have a use + // case for this feature in Dusk. + + // Safety: static lifetime is a pointless requirement from merlin that + // doesn't add any security but instead restricts a lot the + // serialization and deserialization of transcripts + let label = unsafe { mem::transmute(label) }; + + let mut transcript = Transcript::new(label); + + transcript.circuit_domain_sep(constraints as u64); + + verifier_key.seed_transcript(&mut transcript); + + transcript + } } diff --git a/tests/boolean.rs b/tests/boolean.rs new file mode 100644 index 000000000..a38013104 --- /dev/null +++ b/tests/boolean.rs @@ -0,0 +1,426 @@ +// 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_plonk::prelude::*; +use rand::rngs::StdRng; +use rand::SeedableRng; + +#[test] +fn boolean_works() { + let rng = &mut StdRng::seed_from_u64(8349u64); + + let n = 1 << 4; + let label = b"demo"; + let pp = PublicParameters::setup(n, rng).expect("failed to create pp"); + + pub struct DummyCircuit { + a: BlsScalar, + } + + impl DummyCircuit { + pub fn new(a: BlsScalar) -> Self { + Self { a } + } + } + + impl Default for DummyCircuit { + fn default() -> Self { + Self::new(1u64.into()) + } + } + + impl Circuit for DummyCircuit { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { + let w_a = composer.append_witness(self.a); + + composer.component_boolean(w_a); + + Ok(()) + } + } + + let (prover, verifier) = Compiler::compile::(&pp, label) + .expect("failed to compile circuit"); + + // default works + { + let a = BlsScalar::one(); + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(a)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + + let a = BlsScalar::zero(); + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(a)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // negative works + { + let a = BlsScalar::from(2u64); + + prover + .prove(rng, &DummyCircuit::new(a)) + .expect_err("invalid circuit"); + } +} + +#[test] +fn select_works() { + let rng = &mut StdRng::seed_from_u64(8349u64); + + let n = 1 << 6; + let label = b"demo"; + let pp = PublicParameters::setup(n, rng).expect("failed to create pp"); + + #[derive(Clone)] + pub struct DummyCircuit { + bit: BlsScalar, + a: BlsScalar, + b: BlsScalar, + res: BlsScalar, + + zero_bit: BlsScalar, + zero_a: BlsScalar, + zero_res: BlsScalar, + + one_bit: BlsScalar, + one_a: BlsScalar, + one_res: BlsScalar, + + point_bit: BlsScalar, + point_a: JubJubExtended, + point_b: JubJubExtended, + point_res: JubJubExtended, + + identity_bit: BlsScalar, + identity_a: JubJubExtended, + identity_res: JubJubExtended, + } + + impl DummyCircuit { + pub fn new( + bit: BlsScalar, + a: BlsScalar, + b: BlsScalar, + zero_bit: BlsScalar, + zero_a: BlsScalar, + one_bit: BlsScalar, + one_a: BlsScalar, + point_bit: BlsScalar, + point_a: JubJubExtended, + point_b: JubJubExtended, + identity_bit: BlsScalar, + identity_a: JubJubExtended, + ) -> Self { + let res = if bit == BlsScalar::one() { a } else { b }; + + let zero_res = if zero_bit == BlsScalar::one() { + zero_a + } else { + BlsScalar::zero() + }; + + let one_res = if one_bit == BlsScalar::one() { + one_a + } else { + BlsScalar::one() + }; + + let point_res = if one_bit == BlsScalar::one() { + point_a + } else { + point_b + }; + + let identity_res = if identity_bit == BlsScalar::one() { + identity_a + } else { + JubJubExtended::identity() + }; + + Self { + bit, + a, + b, + res, + zero_bit, + zero_a, + zero_res, + one_bit, + one_a, + one_res, + point_bit, + point_a, + point_b, + point_res, + identity_bit, + identity_a, + identity_res, + } + } + } + + impl Default for DummyCircuit { + fn default() -> Self { + let bit = BlsScalar::one(); + let a = BlsScalar::from(3u64); + let b = BlsScalar::from(5u64); + let zero_bit = BlsScalar::zero(); + let zero_a = BlsScalar::from(7u64); + let one_bit = BlsScalar::one(); + let one_a = BlsScalar::from(11u64); + let point_bit = BlsScalar::zero(); + let point_a = + dusk_jubjub::GENERATOR_EXTENDED * &JubJubScalar::from(13u64); + let point_b = + dusk_jubjub::GENERATOR_EXTENDED * &JubJubScalar::from(17u64); + let identity_bit = BlsScalar::one(); + let identity_a = + dusk_jubjub::GENERATOR_EXTENDED * &JubJubScalar::from(19u64); + + Self::new( + bit, + a, + b, + zero_bit, + zero_a, + one_bit, + one_a, + point_bit, + point_a, + point_b, + identity_bit, + identity_a, + ) + } + } + + impl Circuit for DummyCircuit { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { + let w_bit = composer.append_witness(self.bit); + let w_a = composer.append_witness(self.a); + let w_b = composer.append_witness(self.b); + let w_res = composer.append_witness(self.res); + let w_zero_bit = composer.append_witness(self.zero_bit); + let w_zero_a = composer.append_witness(self.zero_a); + let w_zero_res = composer.append_witness(self.zero_res); + let w_one_bit = composer.append_witness(self.one_bit); + let w_one_a = composer.append_witness(self.one_a); + let w_one_res = composer.append_witness(self.one_res); + let w_point_bit = composer.append_witness(self.point_bit); + let w_point_a = composer.append_point(self.point_a); + let w_point_b = composer.append_point(self.point_b); + let w_point_res = composer.append_point(self.point_res); + let w_identity_bit = composer.append_witness(self.identity_bit); + let w_identity_a = composer.append_point(self.identity_a); + let w_identity_res = composer.append_point(self.identity_res); + + let w_x = composer.component_select(w_bit, w_a, w_b); + composer.assert_equal(w_x, w_res); + + let w_zero_x = composer.component_select_zero(w_zero_bit, w_zero_a); + composer.assert_equal(w_zero_x, w_zero_res); + + let w_one_x = composer.component_select_one(w_one_bit, w_one_a); + composer.assert_equal(w_one_x, w_one_res); + + let w_point_x = composer.component_select_point( + w_point_bit, + w_point_a, + w_point_b, + ); + composer.assert_equal_point(w_point_x, w_point_res); + + let w_identity_x = composer + .component_select_identity(w_identity_bit, w_identity_a); + composer.assert_equal_point(w_identity_x, w_identity_res); + + Ok(()) + } + } + + let (prover, verifier) = Compiler::compile::(&pp, label) + .expect("failed to compile circuit"); + + // default works + { + let bit = BlsScalar::one(); + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + let zero_bit = bit; + let zero_a = BlsScalar::random(rng); + let one_bit = bit; + let one_a = BlsScalar::random(rng); + let point_bit = bit; + let point_a = JubJubScalar::random(rng); + let point_a = dusk_jubjub::GENERATOR_EXTENDED * &point_a; + let point_b = JubJubScalar::random(rng); + let point_b = dusk_jubjub::GENERATOR_EXTENDED * &point_b; + let identity_bit = bit; + let identity_a = JubJubScalar::random(rng); + let identity_a = dusk_jubjub::GENERATOR_EXTENDED * &identity_a; + + let circuit = DummyCircuit::new( + bit, + a, + b, + zero_bit, + zero_a, + one_bit, + one_a, + point_bit, + point_a, + point_b, + identity_bit, + identity_a, + ); + + let (proof, public_inputs) = + prover.prove(rng, &circuit).expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + + let bit = BlsScalar::zero(); + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + let zero_bit = bit; + let zero_a = BlsScalar::random(rng); + let one_bit = bit; + let one_a = BlsScalar::random(rng); + let point_bit = bit; + let point_a = JubJubScalar::random(rng); + let point_a = dusk_jubjub::GENERATOR_EXTENDED * &point_a; + let point_b = JubJubScalar::random(rng); + let point_b = dusk_jubjub::GENERATOR_EXTENDED * &point_b; + let identity_bit = bit; + let identity_a = JubJubScalar::random(rng); + let identity_a = dusk_jubjub::GENERATOR_EXTENDED * &identity_a; + + let circuit = DummyCircuit::new( + bit, + a, + b, + zero_bit, + zero_a, + one_bit, + one_a, + point_bit, + point_a, + point_b, + identity_bit, + identity_a, + ); + + let (proof, public_inputs) = + prover.prove(rng, &circuit).expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + let bit = BlsScalar::one(); + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + let zero_bit = bit; + let zero_a = BlsScalar::random(rng); + let one_bit = bit; + let one_a = BlsScalar::random(rng); + let point_bit = bit; + let point_a = JubJubScalar::random(rng); + let point_a = dusk_jubjub::GENERATOR_EXTENDED * &point_a; + let point_b = JubJubScalar::random(rng); + let point_b = dusk_jubjub::GENERATOR_EXTENDED * &point_b; + let identity_bit = bit; + let identity_a = JubJubScalar::random(rng); + let identity_a = dusk_jubjub::GENERATOR_EXTENDED * &identity_a; + + let base = DummyCircuit::new( + bit, + a, + b, + zero_bit, + zero_a, + one_bit, + one_a, + point_bit, + point_a, + point_b, + identity_bit, + identity_a, + ); + + // negative select works + { + let mut circuit = base.clone(); + + circuit.res = -circuit.res; + + prover.prove(rng, &circuit).expect_err("invalid proof"); + } + + // negative select zero works + { + let mut circuit = base.clone(); + + circuit.zero_res = -circuit.zero_res; + + prover.prove(rng, &circuit).expect_err("invalid proof"); + } + + // negative select one works + { + let mut circuit = base.clone(); + + circuit.one_res = -circuit.one_res; + + prover.prove(rng, &circuit).expect_err("invalid proof"); + } + + // negative select point works + { + let mut circuit = base.clone(); + + let x = dusk_jubjub::GENERATOR_EXTENDED * &JubJubScalar::one(); + + circuit.point_res = circuit.point_res + x; + + prover.prove(rng, &circuit).expect_err("invalid proof"); + } + + // negative select identity works + { + let mut circuit = base.clone(); + + let x = dusk_jubjub::GENERATOR_EXTENDED * &JubJubScalar::one(); + + circuit.identity_res = circuit.identity_res + x; + + prover.prove(rng, &circuit).expect_err("invalid proof"); + } +} diff --git a/tests/circuit.rs b/tests/circuit.rs deleted file mode 100644 index c6f1e219e..000000000 --- a/tests/circuit.rs +++ /dev/null @@ -1,144 +0,0 @@ -// 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_plonk::prelude::*; -use rand_core::OsRng; -use std::fs; - -type Result = std::result::Result>; - -// Implements a circuit that checks: -// 1) a + b = c where C is a PI -// 2) a <= 2^6 -// 3) b <= 2^5 -// 4) a * b = d where D is a PI -// 5) JubJub::GENERATOR * e(JubJubScalar) = f where F is a PI -#[derive(Debug, Default)] -pub struct TestCircuit { - a: BlsScalar, - b: BlsScalar, - c: BlsScalar, - d: BlsScalar, - e: JubJubScalar, - f: JubJubAffine, -} - -impl Circuit for TestCircuit { - const CIRCUIT_ID: [u8; 32] = [0xff; 32]; - fn gadget( - &mut self, - composer: &mut TurboComposer, - ) -> std::result::Result<(), Error> { - let a = composer.append_witness(self.a); - let b = composer.append_witness(self.b); - - // Make first constraint a + b = c - let constraint = - Constraint::new().left(1).right(1).public(-self.c).a(a).b(b); - composer.append_gate(constraint); - - // Check that a and b are in range - composer.component_range(a, 1 << 6); - composer.component_range(b, 1 << 5); - - // Make second constraint a * b = d - let constraint = Constraint::new().mult(1).public(-self.d).a(a).b(b); - composer.append_gate(constraint); - - let e = composer.append_witness(self.e); - let scalar_mul_result = composer - .component_mul_generator(e, dusk_jubjub::GENERATOR_EXTENDED); - - // Apply the constraint - composer.assert_equal_public_point(scalar_mul_result, self.f); - - Ok(()) - } - - fn public_inputs(&self) -> Vec { - vec![self.c.into(), self.d.into(), self.f.into()] - } - - fn padded_gates(&self) -> usize { - 1 << 11 - } -} - -#[test] -fn test_full() -> Result<()> { - use tempdir::TempDir; - - let tmp = TempDir::new("plonk-keys-test-full")?.into_path(); - let pp_path = tmp.join("pp_testcirc"); - let pk_path = tmp.join("pk_testcirc"); - let vd_path = tmp.join("vd_testcirc"); - - // Generate CRS - let pp_p = PublicParameters::setup(1 << 12, &mut OsRng)?; - fs::write(&pp_path, &pp_p.to_raw_var_bytes())?; - - // Read PublicParameters - let pp = fs::read(pp_path)?; - let pp = unsafe { PublicParameters::from_slice_unchecked(&pp) }; - - // Initialize the circuit - let mut circuit = TestCircuit::default(); - - // Compile/preprocess the circuit - let (pk_p, vd_p) = circuit.compile(&pp)?; - - // Write the keys - fs::write(&pk_path, &pk_p.to_var_bytes())?; - - // Read ProverKey - let pk = fs::read(pk_path)?; - let pk = ProverKey::from_slice(&pk)?; - - assert_eq!(pk, pk_p); - - // Store the VerifierData just for the verifier side: - // (You could also store public_inputs_indexes and VerifierKey separately). - fs::write(&vd_path, &vd_p.to_var_bytes())?; - let vd = fs::read(vd_path)?; - let vd = VerifierData::from_slice(&vd)?; - - assert_eq!(vd_p.key(), vd.key()); - assert_eq!(vd_p.public_inputs_indexes(), vd.public_inputs_indexes()); - - // Prover POV - let proof = { - let mut circuit = TestCircuit { - a: BlsScalar::from(20u64), - b: BlsScalar::from(5u64), - c: BlsScalar::from(25u64), - d: BlsScalar::from(100u64), - e: JubJubScalar::from(2u64), - f: JubJubAffine::from( - dusk_jubjub::GENERATOR_EXTENDED * JubJubScalar::from(2u64), - ), - }; - - circuit.prove(&pp, &pk, b"Test", &mut OsRng) - }?; - - // Verifier POV - let public_inputs: Vec = vec![ - BlsScalar::from(25u64).into(), - BlsScalar::from(100u64).into(), - JubJubAffine::from( - dusk_jubjub::GENERATOR_EXTENDED * JubJubScalar::from(2u64), - ) - .into(), - ]; - - Ok(TestCircuit::verify( - &pp, - &vd, - &proof, - &public_inputs, - b"Test", - )?) -} diff --git a/tests/composer.rs b/tests/composer.rs new file mode 100644 index 000000000..ef27edec6 --- /dev/null +++ b/tests/composer.rs @@ -0,0 +1,113 @@ +// 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_plonk::prelude::*; +use rand::rngs::StdRng; +use rand::SeedableRng; + +#[test] +fn circuit_with_all_gates() { + let rng = &mut StdRng::seed_from_u64(8349u64); + + let n = 1 << 12; + let label = b"demo"; + let pp = PublicParameters::setup(n, rng).expect("failed to create pp"); + + pub struct DummyCircuit { + a: BlsScalar, + b: BlsScalar, + x: BlsScalar, + y: JubJubScalar, + z: JubJubExtended, + } + + impl Default for DummyCircuit { + fn default() -> Self { + Self { + a: BlsScalar::from(2u64), + b: BlsScalar::from(3u64), + x: BlsScalar::from(6u64), + y: JubJubScalar::from(7u64), + z: dusk_jubjub::GENERATOR_EXTENDED * &JubJubScalar::from(7u64), + } + } + } + + impl Circuit for DummyCircuit { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { + let w_a = composer.append_witness(self.a); + let w_b = composer.append_witness(self.b); + let w_x = composer.append_witness(self.x); + let w_y = composer.append_witness(self.y); + let w_z = composer.append_point(self.z); + + let s = Constraint::new().mult(1).a(w_a).b(w_b); + + let r_w = composer.gate_mul(s); + + composer.append_constant(15); + composer.append_constant_point(self.z); + composer.append_public_point(self.z); + composer.append_public(self.y); + + composer.assert_equal(w_x, r_w); + composer.assert_equal_constant(w_x, 0, Some(-self.x)); + composer.assert_equal_point(w_z, w_z); + composer.assert_equal_public_point(w_z, self.z); + + composer.gate_add(Constraint::new().left(1).right(1).a(w_a).b(w_b)); + + composer.component_add_point(w_z, w_z); + composer.append_logic_and(w_a, w_b, 254); + composer.component_boolean(Builder::ONE); + composer.component_decomposition::<254>(w_a); + composer.component_mul_generator( + w_y, + dusk_jubjub::GENERATOR_EXTENDED, + )?; + composer.component_mul_point(w_y, w_z); + composer.component_range(w_a, 254); + composer.component_select(Builder::ONE, w_a, w_b); + composer.component_select_identity(Builder::ONE, w_z); + composer.component_select_one(Builder::ONE, w_a); + composer.component_select_point(Builder::ONE, w_z, w_z); + composer.component_select_zero(Builder::ONE, w_a); + composer.append_logic_xor(w_a, w_b, 254); + + Ok(()) + } + } + + let (prover, verifier) = Compiler::compile::(&pp, label) + .expect("failed to compile circuit"); + + let len = prover.serialized_size(); + let prover = prover.to_bytes(); + + assert_eq!(prover.len(), len); + + let prover: Prover = + Prover::try_from_bytes(&prover).expect("failed to deserialize prover"); + + let len = verifier.serialized_size(); + let verifier = verifier.to_bytes(); + + assert_eq!(verifier.len(), len); + + let verifier: Verifier = Verifier::try_from_bytes(&verifier) + .expect("failed to deserialize verifier"); + + let (proof, public_inputs) = prover + .prove(rng, &Default::default()) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); +} diff --git a/tests/decomposition.rs b/tests/decomposition.rs new file mode 100644 index 000000000..25d03e15b --- /dev/null +++ b/tests/decomposition.rs @@ -0,0 +1,91 @@ +// 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_plonk::prelude::*; +use rand::rngs::StdRng; +use rand::SeedableRng; + +#[test] +fn decomposition_works() { + let rng = &mut StdRng::seed_from_u64(8349u64); + + let n = 1 << 10; + let label = b"demo"; + let pp = PublicParameters::setup(n, rng).expect("failed to create pp"); + + pub struct DummyCircuit { + a: BlsScalar, + bits: [BlsScalar; N], + } + + impl DummyCircuit { + pub fn new(a: BlsScalar) -> Self { + let mut bits = [BlsScalar::zero(); N]; + + bits.iter_mut() + .zip(a.to_bits().iter()) + .for_each(|(b, v)| *b = BlsScalar::from(*v as u64)); + + Self { a, bits } + } + } + + impl Default for DummyCircuit { + fn default() -> Self { + Self::new(BlsScalar::from(23u64)) + } + } + + impl Circuit for DummyCircuit { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { + let w_a = composer.append_witness(self.a); + let mut w_bits: [Witness; N] = [C::ZERO; N]; + + w_bits + .iter_mut() + .zip(self.bits.iter()) + .for_each(|(w, b)| *w = composer.append_witness(*b)); + + let w_x: [Witness; N] = composer.component_decomposition(w_a); + + w_bits.iter().zip(w_x.iter()).for_each(|(w, b)| { + composer.assert_equal(*w, *b); + }); + + Ok(()) + } + } + + let (prover, verifier) = Compiler::compile::>(&pp, label) + .expect("failed to compile circuit"); + + // default works + { + let a = BlsScalar::random(rng); + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::<256>::new(a)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // negative works + { + let a = BlsScalar::random(rng); + + let mut circuit = DummyCircuit::<256>::new(a); + + circuit.bits[10] = circuit.bits[10] ^ BlsScalar::one(); + + prover.prove(rng, &circuit).expect_err("invalid proof"); + } +} diff --git a/tests/ecc.rs b/tests/ecc.rs new file mode 100644 index 000000000..4ad773963 --- /dev/null +++ b/tests/ecc.rs @@ -0,0 +1,306 @@ +// 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_plonk::prelude::*; +use rand::rngs::StdRng; +use rand::SeedableRng; + +#[test] +fn mul_generator_works() { + let rng = &mut StdRng::seed_from_u64(8349u64); + + let n = 1 << 9; + let label = b"demo"; + let pp = PublicParameters::setup(n, rng).expect("failed to create pp"); + + pub struct DummyCircuit { + a: JubJubScalar, + b: JubJubExtended, + } + + impl DummyCircuit { + pub fn new(a: JubJubScalar) -> Self { + Self { + a, + b: dusk_jubjub::GENERATOR_EXTENDED * &a, + } + } + } + + impl Default for DummyCircuit { + fn default() -> Self { + Self::new(JubJubScalar::from(7u64)) + } + } + + impl Circuit for DummyCircuit { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { + let w_a = composer.append_witness(self.a); + let w_b = composer.append_point(self.b); + + let w_x = composer.component_mul_generator( + w_a, + dusk_jubjub::GENERATOR_EXTENDED, + )?; + + composer.assert_equal_point(w_b, w_x); + + Ok(()) + } + } + + let (prover, verifier) = Compiler::compile::(&pp, label) + .expect("failed to compile circuit"); + + // default works + { + let a = JubJubScalar::random(rng); + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(a)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // negative check + { + let a = JubJubScalar::from(7u64); + let b = dusk_jubjub::GENERATOR_EXTENDED * &a; + + let x = JubJubScalar::from(8u64); + let y = dusk_jubjub::GENERATOR_EXTENDED * &x; + + assert_ne!(b, y); + + prover + .prove(rng, &DummyCircuit { a, b: y }) + .expect_err("invalid ecc proof isn't feasible"); + } + + // invalid jubjub won't panic + { + let a = -BlsScalar::one(); + let a = JubJubScalar::from_raw(a.0); + + let x = JubJubScalar::from(8u64); + let y = dusk_jubjub::GENERATOR_EXTENDED * &x; + + prover + .prove(rng, &DummyCircuit { a, b: y }) + .expect_err("invalid ecc proof isn't feasible"); + } +} + +#[test] +fn add_point_works() { + let rng = &mut StdRng::seed_from_u64(8349u64); + + let n = 1 << 4; + let label = b"demo"; + let pp = PublicParameters::setup(n, rng).expect("failed to create pp"); + + pub struct DummyCircuit { + a: JubJubExtended, + b: JubJubExtended, + c: JubJubExtended, + } + + impl DummyCircuit { + pub fn new(a: &JubJubScalar, b: &JubJubScalar) -> Self { + let a = dusk_jubjub::GENERATOR_EXTENDED * a; + let b = dusk_jubjub::GENERATOR_EXTENDED * b; + let c = a + b; + + Self { a, b, c } + } + } + + impl Default for DummyCircuit { + fn default() -> Self { + Self::new(&JubJubScalar::from(7u64), &JubJubScalar::from(8u64)) + } + } + + impl Circuit for DummyCircuit { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { + let w_a = composer.append_point(self.a); + let w_b = composer.append_point(self.b); + let w_c = composer.append_point(self.c); + + let w_x = composer.component_add_point(w_a, w_b); + + composer.assert_equal_point(w_c, w_x); + + Ok(()) + } + } + + let (prover, verifier) = Compiler::compile::(&pp, label) + .expect("failed to compile circuit"); + + // default works + { + let a = JubJubScalar::random(rng); + let b = JubJubScalar::random(rng); + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(&a, &b)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // identity works + { + let a = JubJubScalar::random(rng); + let a = dusk_jubjub::GENERATOR_EXTENDED * &a; + + let (proof, public_inputs) = prover + .prove( + rng, + &DummyCircuit { + a, + b: JubJubExtended::identity(), + c: a, + }, + ) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // zero works + { + let (proof, public_inputs) = prover + .prove( + rng, + &DummyCircuit { + a: JubJubExtended::identity(), + b: JubJubExtended::identity(), + c: JubJubExtended::identity(), + }, + ) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // negative check + { + let a = JubJubScalar::from(7u64); + let a = dusk_jubjub::GENERATOR_EXTENDED * &a; + + let b = JubJubScalar::from(8u64); + let b = dusk_jubjub::GENERATOR_EXTENDED * &b; + + let c = JubJubScalar::from(9u64); + let c = dusk_jubjub::GENERATOR_EXTENDED * &c; + + assert_ne!(c, a + b); + + prover + .prove(rng, &DummyCircuit { a, b, c }) + .expect_err("invalid ecc proof isn't feasible"); + } +} + +#[test] +fn mul_point_works() { + let rng = &mut StdRng::seed_from_u64(8349u64); + + let n = 1 << 11; + let label = b"demo"; + let pp = PublicParameters::setup(n, rng).expect("failed to create pp"); + + pub struct DummyCircuit { + a: JubJubScalar, + b: JubJubExtended, + c: JubJubExtended, + } + + impl DummyCircuit { + pub fn new(a: JubJubScalar, b: JubJubExtended) -> Self { + let c = b * &a; + + Self { a, b, c } + } + } + + impl Default for DummyCircuit { + fn default() -> Self { + let b = JubJubScalar::from(8u64); + let b = dusk_jubjub::GENERATOR_EXTENDED * &b; + + Self::new(JubJubScalar::from(7u64), b) + } + } + + impl Circuit for DummyCircuit { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { + let w_a = composer.append_witness(self.a); + let w_b = composer.append_point(self.b); + let w_c = composer.append_point(self.c); + + let w_x = composer.component_mul_point(w_a, w_b); + + composer.assert_equal_point(w_c, w_x); + + Ok(()) + } + } + + let (prover, verifier) = Compiler::compile::(&pp, label) + .expect("failed to compile circuit"); + + // default works + { + let a = JubJubScalar::random(rng); + let b = JubJubScalar::random(rng); + let b = dusk_jubjub::GENERATOR_EXTENDED * &b; + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(a, b)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // negative works + { + let a = JubJubScalar::random(rng); + let b = JubJubScalar::random(rng); + let b = dusk_jubjub::GENERATOR_EXTENDED * &b; + let c = b * &a; + + let x = JubJubScalar::random(rng); + let x = dusk_jubjub::GENERATOR_EXTENDED * &x; + + assert_ne!(c, x); + + prover + .prove(rng, &DummyCircuit { a, b, c: x }) + .expect_err("circuit is not satisfied"); + } +} diff --git a/tests/logic.rs b/tests/logic.rs new file mode 100644 index 000000000..a7aa1282f --- /dev/null +++ b/tests/logic.rs @@ -0,0 +1,317 @@ +// 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_plonk::prelude::*; +use rand::rngs::StdRng; +use rand::SeedableRng; + +#[test] +fn logic_and_works() { + let rng = &mut StdRng::seed_from_u64(8349u64); + + let n = 1 << 8; + let label = b"demo"; + let pp = PublicParameters::setup(n, rng).expect("failed to create pp"); + + pub struct DummyCircuit { + a: BlsScalar, + b: BlsScalar, + c: BlsScalar, + bits: usize, + } + + impl DummyCircuit { + pub fn new(a: BlsScalar, b: BlsScalar, bits: usize) -> Self { + let x = BlsScalar::pow_of_2(bits as u64) - BlsScalar::one(); + + let a = a & x; + let b = b & x; + let c = a & b & x; + + Self { a, b, c, bits } + } + } + + impl Default for DummyCircuit { + fn default() -> Self { + Self::new(7u64.into(), 8u64.into(), 256) + } + } + + impl Circuit for DummyCircuit { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { + let w_a = composer.append_witness(self.a); + let w_b = composer.append_witness(self.b); + let w_c = composer.append_witness(self.c); + + let w_x = composer.append_logic_and(w_a, w_b, self.bits); + + composer.assert_equal(w_c, w_x); + + Ok(()) + } + } + + let (prover, verifier) = Compiler::compile::(&pp, label) + .expect("failed to compile circuit"); + + // default works + { + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(a, b, 256)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // negative works + { + let bits = 256; + + let x = BlsScalar::pow_of_2(bits as u64) - BlsScalar::one(); + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let a = a & x; + let b = b & x; + let c = a & b & x; + + let m = BlsScalar::random(rng) & x; + let n = a & m & x; + + assert_ne!(c, n); + + prover + .prove(rng, &DummyCircuit { a, b, c: n, bits }) + .expect_err("the provided proof isn't valid"); + } + + // small bits works + { + let bits = 30; + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let circuit = DummyCircuit::new(a, b, bits); + + let (prover, verifier) = + Compiler::compile_with_circuit(&pp, label, &circuit) + .expect("failed to compile circuit"); + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(a, b, bits)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // zero bits works + { + let bits = 0; + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let circuit = DummyCircuit::new(a, b, bits); + + let (prover, verifier) = + Compiler::compile_with_circuit(&pp, label, &circuit) + .expect("failed to compile circuit"); + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(a, b, bits)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // odd bits will compile + { + let bits = 55; + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let circuit = DummyCircuit::new(a, b, bits); + + Compiler::compile_with_circuit(&pp, label, &circuit) + .expect("failed to compile circuit"); + } +} + +#[test] +fn logic_xor_works() { + let rng = &mut StdRng::seed_from_u64(8349u64); + + let n = 1 << 8; + let label = b"demo"; + let pp = PublicParameters::setup(n, rng).expect("failed to create pp"); + + pub struct DummyCircuit { + a: BlsScalar, + b: BlsScalar, + c: BlsScalar, + bits: usize, + } + + impl DummyCircuit { + pub fn new(a: BlsScalar, b: BlsScalar, bits: usize) -> Self { + let x = BlsScalar::pow_of_2(bits as u64) - BlsScalar::one(); + + let a = a & x; + let b = b & x; + let c = (a ^ b) & x; + + Self { a, b, c, bits } + } + } + + impl Default for DummyCircuit { + fn default() -> Self { + Self::new(7u64.into(), 8u64.into(), 256) + } + } + + impl Circuit for DummyCircuit { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { + let w_a = composer.append_witness(self.a); + let w_b = composer.append_witness(self.b); + let w_c = composer.append_witness(self.c); + + let w_x = composer.append_logic_xor(w_a, w_b, self.bits); + + composer.assert_equal(w_c, w_x); + + Ok(()) + } + } + + let (prover, verifier) = Compiler::compile::(&pp, label) + .expect("failed to compile circuit"); + + // default works + { + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(a, b, 256)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // negative works + { + let bits = 256; + + let x = BlsScalar::pow_of_2(bits as u64) - BlsScalar::one(); + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let a = a & x; + let b = b & x; + let c = (a ^ b) & x; + + let m = BlsScalar::random(rng) & x; + let n = (a ^ m) & x; + + assert_ne!(c, n); + + prover + .prove(rng, &DummyCircuit { a, b, c: n, bits }) + .expect_err("the provided proof isn't valid"); + } + + // small bits works + { + let bits = 30; + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let circuit = DummyCircuit::new(a, b, bits); + + let (prover, verifier) = + Compiler::compile_with_circuit(&pp, label, &circuit) + .expect("failed to compile circuit"); + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(a, b, bits)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // zero bits works + { + let bits = 0; + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let circuit = DummyCircuit::new(a, b, bits); + + let (prover, verifier) = + Compiler::compile_with_circuit(&pp, label, &circuit) + .expect("failed to compile circuit"); + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(a, b, bits)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // odd bits will compile + { + let bits = 55; + + let a = BlsScalar::random(rng); + let b = BlsScalar::random(rng); + + let circuit = DummyCircuit::new(a, b, bits); + + Compiler::compile_with_circuit(&pp, label, &circuit) + .expect("failed to compile circuit"); + } +} diff --git a/tests/range.rs b/tests/range.rs new file mode 100644 index 000000000..573df5459 --- /dev/null +++ b/tests/range.rs @@ -0,0 +1,87 @@ +// 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_plonk::prelude::*; +use rand::rngs::StdRng; +use rand::SeedableRng; + +#[test] +fn range_works() { + let rng = &mut StdRng::seed_from_u64(8349u64); + + let n = 1 << 5; + let label = b"demo"; + let pp = PublicParameters::setup(n, rng).expect("failed to create pp"); + + const DEFAULT_BITS: usize = 76; + + pub struct DummyCircuit { + a: BlsScalar, + bits: usize, + } + + impl DummyCircuit { + pub fn new(a: BlsScalar, bits: usize) -> Self { + Self { a, bits } + } + } + + impl Default for DummyCircuit { + fn default() -> Self { + Self::new(7u64.into(), DEFAULT_BITS) + } + } + + impl Circuit for DummyCircuit { + fn circuit(&self, composer: &mut C) -> Result<(), Error> + where + C: Composer, + { + let w_a = composer.append_witness(self.a); + + composer.component_range(w_a, self.bits); + + Ok(()) + } + } + + let (prover, verifier) = Compiler::compile::(&pp, label) + .expect("failed to compile circuit"); + + // default works + { + let a = BlsScalar::from(u64::MAX); + + let (proof, public_inputs) = prover + .prove(rng, &DummyCircuit::new(a, DEFAULT_BITS)) + .expect("failed to prove"); + + verifier + .verify(&proof, &public_inputs) + .expect("failed to verify proof"); + } + + // negative works + { + let a = -BlsScalar::pow_of_2(DEFAULT_BITS as u64 + 1); + + prover + .prove(rng, &DummyCircuit::new(a, DEFAULT_BITS)) + .expect_err("bits aren't in range"); + } + + // odd bits won't panic + { + let a = BlsScalar::one(); + + Compiler::compile_with_circuit::( + &pp, + label, + &DummyCircuit::new(a, DEFAULT_BITS + 1), + ) + .expect("failed to compile circuit"); + } +}