From a107f2f60e81fd3f8064578091d4af4a0e2e233c Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Mon, 15 Aug 2022 18:34:20 +0200 Subject: [PATCH] Refactor Composer into trait A composer is a set of functionalities that can be extended into different implementators, such as debuggers or even PLONKup. This commit introduces the composer as trait, and targets to reduce the complexity of the implementation of multiple components of the composer. With this simplification, we achieved significant performance boost for public inputs evaluation. We also removed some of the exported types that are leaked internals. The user shouldn't be aware of public inputs indexes, kzg commitments, keys or any permutation argument. Instead, he should work with proofs, public inputs, public parameters, labels, circuits, provers and verifiers. The proof generation was largely simplified, reducing its arguments to a random number generator for the proof blinders, and a circuit instance for its witnesses. The proof verification was simplified to take only the proof and its public inputs. The public inputs are not encoded into the proof because they are often used from external sources, such as blockchain payload. A debugger was introduced and it will output CDF files if the feature flag is on, and the `CDF_OUTPUT` environment variable is set. These CDF files are expected to be read from the TCDB debugger - a CLI application inspired in gdb, for zk-SNARKS Plonk circuits. Finally, a type-safe constraint was introduced, binding the prover and verifier with their concrete circuit implementation. This allowed great simplification of the verification process since the user don't need to manage verification keys anymore. --- Cargo.toml | 6 +- README.md | 95 +- benches/plonk.rs | 204 ++-- src/circuit.rs | 387 ------ src/commitment_scheme.rs | 5 +- src/commitment_scheme/kzg10/key.rs | 2 +- src/commitment_scheme/kzg10/srs.rs | 14 +- src/composer.rs | 1052 +++++++++++++++++ src/composer/builder.rs | 160 +++ src/composer/circuit.rs | 19 + src/composer/compiler.rs | 388 ++++++ src/composer/polynomial.rs | 46 + src/composer/prover.rs | 511 ++++++++ src/composer/verifier.rs | 227 ++++ src/constraint_system.rs | 11 - src/constraint_system/arithmetic.rs | 228 ---- src/constraint_system/boolean.rs | 67 -- src/constraint_system/composer.rs | 813 ------------- src/constraint_system/constraint.rs | 38 +- src/constraint_system/ecc.rs | 212 +--- src/constraint_system/ecc/curve_addition.rs | 10 - .../ecc/curve_addition/fixed_base_gate.rs | 74 -- .../ecc/curve_addition/variable_base_gate.rs | 214 ---- src/constraint_system/ecc/scalar_mul.rs | 10 - .../ecc/scalar_mul/fixed_base.rs | 375 ------ .../ecc/scalar_mul/variable_base.rs | 70 -- src/constraint_system/helper.rs | 83 -- src/constraint_system/logic.rs | 507 -------- src/constraint_system/range.rs | 253 ---- src/constraint_system/witness.rs | 2 +- src/debugger.rs | 225 ++++ src/error.rs | 26 +- src/fft/polynomial.rs | 5 - src/lib.rs | 8 +- src/permutation.rs | 113 +- src/prelude.rs | 9 +- src/proof_system.rs | 11 +- src/proof_system/preprocess.rs | 409 +------ src/proof_system/proof.rs | 11 +- src/proof_system/prover.rs | 468 -------- src/proof_system/quotient_poly.rs | 6 +- src/proof_system/verifier.rs | 109 -- src/proof_system/widget.rs | 5 - src/runtime.rs | 59 + src/transcript.rs | 38 +- tests/boolean.rs | 426 +++++++ tests/circuit.rs | 144 --- tests/composer.rs | 113 ++ tests/decomposition.rs | 91 ++ tests/ecc.rs | 306 +++++ tests/logic.rs | 317 +++++ tests/range.rs | 87 ++ 52 files changed, 4324 insertions(+), 4745 deletions(-) delete mode 100644 src/circuit.rs create mode 100644 src/composer.rs create mode 100644 src/composer/builder.rs create mode 100644 src/composer/circuit.rs create mode 100644 src/composer/compiler.rs create mode 100644 src/composer/polynomial.rs create mode 100644 src/composer/prover.rs create mode 100644 src/composer/verifier.rs delete mode 100644 src/constraint_system/arithmetic.rs delete mode 100644 src/constraint_system/boolean.rs delete mode 100644 src/constraint_system/composer.rs delete mode 100644 src/constraint_system/ecc/curve_addition.rs delete mode 100644 src/constraint_system/ecc/curve_addition/fixed_base_gate.rs delete mode 100644 src/constraint_system/ecc/curve_addition/variable_base_gate.rs delete mode 100644 src/constraint_system/ecc/scalar_mul.rs delete mode 100644 src/constraint_system/ecc/scalar_mul/fixed_base.rs delete mode 100644 src/constraint_system/ecc/scalar_mul/variable_base.rs delete mode 100644 src/constraint_system/helper.rs delete mode 100644 src/constraint_system/logic.rs delete mode 100644 src/constraint_system/range.rs create mode 100644 src/debugger.rs delete mode 100644 src/proof_system/prover.rs delete mode 100644 src/proof_system/verifier.rs create mode 100644 src/runtime.rs create mode 100644 tests/boolean.rs delete mode 100644 tests/circuit.rs create mode 100644 tests/composer.rs create mode 100644 tests/decomposition.rs create mode 100644 tests/ecc.rs create mode 100644 tests/logic.rs create mode 100644 tests/range.rs 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"); + } +}