Skip to content

Commit

Permalink
Add and refactor tests for decomposition component
Browse files Browse the repository at this point in the history
Also add a simple example as to how the scalar will be decomposed.

Resolves: #738 and #704
  • Loading branch information
moCello committed Mar 22, 2023
1 parent 8575e27 commit 38aaa09
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 65 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -468,6 +469,7 @@ is necessary since `rkyv/validation` was required as a bound.
- Proof system module.

<!-- ISSUES -->
[#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
Expand Down
19 changes: 12 additions & 7 deletions src/composer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,11 +725,16 @@ pub trait Composer: Sized + Index<Witness, Output = BlsScalar> {
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<const N: usize>(
&mut self,
scalar: Witness,
Expand All @@ -745,15 +750,15 @@ pub trait Composer: Sized + Index<Witness, Output = BlsScalar> {
.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)
Expand Down
180 changes: 122 additions & 58 deletions tests/decomposition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<const N: usize> {
#[test]
fn component_decomposition() {
pub struct TestCircuit<const N: usize> {
a: BlsScalar,
bits: [BlsScalar; N],
decomp_expected: [BlsScalar; N],
}

impl<const N: usize> DummyCircuit<N> {
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<const N: usize> TestCircuit<N> {
pub fn new(a: BlsScalar, decomp_expected: [BlsScalar; N]) -> Self {
Self { a, decomp_expected }
}
}

impl<const N: usize> Default for DummyCircuit<N> {
impl<const N: usize> Default for TestCircuit<N> {
fn default() -> Self {
Self::new(BlsScalar::from(23u64))
Self::new(BlsScalar::zero(), [BlsScalar::zero(); N])
}
}

impl<const N: usize> Circuit for DummyCircuit<N> {
impl<const N: usize> Circuit for TestCircuit<N> {
fn circuit<C>(&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::<DummyCircuit<256>>(&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::<N1>::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::<N64>::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::<N256>::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);
}

0 comments on commit 38aaa09

Please sign in to comment.