From 38aaa09077deffd7eb73c9560e066c3fe3039b2e Mon Sep 17 00:00:00 2001 From: moana Date: Thu, 16 Mar 2023 17:27:12 +0100 Subject: [PATCH] Add and refactor tests for decomposition component Also add a simple example as to how the scalar will be decomposed. Resolves: #738 and #704 --- CHANGELOG.md | 2 + src/composer.rs | 19 +++-- tests/decomposition.rs | 180 ++++++++++++++++++++++++++++------------- 3 files changed, 136 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c77b736b..7724be5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add and restructure tests for range component [#735] - Add and restructure tests for boolean and select components [#731] - Add tests for `gate_add` and `gate_mul` [#736] +- Add and restructure tests for `component_decomposition` [#738] ### Removed @@ -468,6 +469,7 @@ is necessary since `rkyv/validation` was required as a bound. - Proof system module. +[#738]: https://github.com/dusk-network/plonk/issues/738 [#746]: https://github.com/dusk-network/plonk/issues/746 [#736]: https://github.com/dusk-network/plonk/issues/736 [#735]: https://github.com/dusk-network/plonk/issues/735 diff --git a/src/composer.rs b/src/composer.rs index 9798b15c..60a7751b 100644 --- a/src/composer.rs +++ b/src/composer.rs @@ -725,11 +725,16 @@ pub trait Composer: Sized + Index { self.append_gate(constraint); } - /// Decomposes `scalar` into an array truncated to `N` bits (max 256). + /// Decomposes `scalar` into an array truncated to `N` bits (max 256) in + /// little endian. + /// The `scalar` for 4, for example, would be deconstructed into the array + /// `[0, 0, 1]` for `N = 3` and `[0, 0, 1, 0, 0]` for `N = 5`. /// - /// Asserts the reconstruction of the bits to be equal to `scalar`. + /// Asserts the reconstruction of the bits to be equal to `scalar`. So with + /// the above example, the deconstruction of 4 for `N < 3` would result in + /// an unsatisfied circuit. /// - /// Consume `2 · N + 1` gates + /// Consumes `2 · N + 1` gates fn component_decomposition( &mut self, scalar: Witness, @@ -745,15 +750,15 @@ pub trait Composer: Sized + Index { .iter() .enumerate() .zip(decomposition.iter_mut()) - .fold(acc, |acc, ((i, w), d)| { - *d = self.append_witness(BlsScalar::from(*w as u64)); + .fold(acc, |acc, ((i, bit), w_bit)| { + *w_bit = self.append_witness(BlsScalar::from(*bit as u64)); - self.component_boolean(*d); + self.component_boolean(*w_bit); let constraint = Constraint::new() .left(BlsScalar::pow_of_2(i as u64)) .right(1) - .a(*d) + .a(*w_bit) .b(acc); self.gate_add(constraint) diff --git a/tests/decomposition.rs b/tests/decomposition.rs index 25d03e15..27289127 100644 --- a/tests/decomposition.rs +++ b/tests/decomposition.rs @@ -8,84 +8,148 @@ 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"); +mod common; +use common::{check_satisfied_circuit, check_unsatisfied_circuit, setup}; - pub struct DummyCircuit { +#[test] +fn component_decomposition() { + pub struct TestCircuit { a: BlsScalar, - bits: [BlsScalar; N], + decomp_expected: [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 TestCircuit { + pub fn new(a: BlsScalar, decomp_expected: [BlsScalar; N]) -> Self { + Self { a, decomp_expected } } } - impl Default for DummyCircuit { + impl Default for TestCircuit { fn default() -> Self { - Self::new(BlsScalar::from(23u64)) + Self::new(BlsScalar::zero(), [BlsScalar::zero(); N]) } } - impl Circuit for DummyCircuit { + impl Circuit for TestCircuit { 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 decomp_circuit: [Witness; N] = + composer.component_decomposition(w_a); - 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); - }); + decomp_circuit.iter().zip(self.decomp_expected).for_each( + |(bit_circuit, bit_expected)| { + let w_bit_expected = composer.append_witness(bit_expected); + composer.assert_equal(*bit_circuit, w_bit_expected); + }, + ); 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"); - } + let label = b"component_decomposition"; + let rng = &mut StdRng::seed_from_u64(0x1ea); + let capacity = 1 << 10; + let pi = vec![]; + + // Test N = 1 + // + // Compile new circuit descriptions for the prover and verifier + const N1: usize = 1; + let circuit = TestCircuit::::default(); + let (prover, verifier) = setup(capacity, rng, label, &circuit); + + // Test default works: + let msg = "Default circuit verification should pass"; + check_satisfied_circuit(&prover, &verifier, &pi, &circuit, rng, &msg); + + // Test bls one + let msg = "Verification of satisfied circuit should pass"; + let a = BlsScalar::one(); + let mut decomp_expected = [BlsScalar::zero(); N1]; + decomp_expected[0] = BlsScalar::one(); + let circuit = TestCircuit::new(a, decomp_expected); + check_satisfied_circuit(&prover, &verifier, &pi, &circuit, rng, &msg); + + // Test bls two fails + let msg = "Proof creation of unsatisfied circuit should fail"; + let a = BlsScalar::from(2); + let decomp_expected = [BlsScalar::zero(); N1]; + let circuit = TestCircuit::new(a, decomp_expected); + check_unsatisfied_circuit(&prover, &circuit, rng, &msg); + + // Test N = 64 + // + // Compile new circuit descriptions for the prover and verifier + const N64: usize = 64; + let circuit = TestCircuit::::default(); + let (prover, verifier) = setup(capacity, rng, label, &circuit); + + // Test default works: + let msg = "Default circuit verification should pass"; + check_satisfied_circuit(&prover, &verifier, &pi, &circuit, rng, &msg); + + // Test bls two + let msg = "Verification of satisfied circuit should pass"; + let a = BlsScalar::from(2); + let mut decomp_expected = [BlsScalar::zero(); N64]; + decomp_expected[1] = BlsScalar::one(); + let circuit = TestCircuit::new(a, decomp_expected); + check_satisfied_circuit(&prover, &verifier, &pi, &circuit, rng, &msg); + + // Test bls forty two + let msg = "Verification of satisfied circuit should pass"; + let a = BlsScalar::from(42); + let mut decomp_expected = [BlsScalar::zero(); N64]; + decomp_expected[5] = BlsScalar::one(); + decomp_expected[3] = BlsScalar::one(); + decomp_expected[1] = BlsScalar::one(); + let circuit = TestCircuit::new(a, decomp_expected); + check_satisfied_circuit(&prover, &verifier, &pi, &circuit, rng, &msg); + + // Test u64::MAX + let msg = "Verification of satisfied circuit should pass"; + let a = BlsScalar::from(u64::MAX); + let decomp_expected = [BlsScalar::one(); N64]; + let circuit = TestCircuit::new(a, decomp_expected); + check_satisfied_circuit(&prover, &verifier, &pi, &circuit, rng, &msg); + + // Test 2 * u64::MAX + 1 fails + let msg = "Proof creation of unsatisfied circuit should fail"; + let a = BlsScalar::from(u64::MAX) * BlsScalar::from(2) + BlsScalar::one(); + let decomp_expected = [BlsScalar::one(); N64]; + let circuit = TestCircuit::new(a, decomp_expected); + check_unsatisfied_circuit(&prover, &circuit, rng, &msg); + + // Test N = 64 + // + // Compile new circuit descriptions for the prover and verifier + const N256: usize = 256; + let circuit = TestCircuit::::default(); + let (prover, verifier) = setup(capacity, rng, label, &circuit); + + // Test random works: + let msg = "Verification of satisfied circuit should pass"; + let a = BlsScalar::random(rng); + let mut decomp_expected = [BlsScalar::zero(); N256]; + a.to_bits().iter().enumerate().for_each(|(i, bit)| { + decomp_expected[i] = BlsScalar::from(*bit as u64); + }); + let circuit = TestCircuit::new(a, decomp_expected); + check_satisfied_circuit(&prover, &verifier, &pi, &circuit, rng, &msg); + + // Test flipping one bit fails + let msg = "Proof creation of unsatisfied circuit should fail"; + let a = BlsScalar::random(rng); + let mut decomp_expected = [BlsScalar::zero(); N256]; + a.to_bits().iter().enumerate().for_each(|(i, bit)| { + decomp_expected[i] = BlsScalar::from(*bit as u64); + }); + decomp_expected[123] *= -BlsScalar::one(); + decomp_expected[123] += BlsScalar::one(); + let circuit = TestCircuit::new(a, decomp_expected); + check_unsatisfied_circuit(&prover, &circuit, rng, &msg); }