diff --git a/.github/workflows/dusk_ci.yml b/.github/workflows/dusk_ci.yml index 9e1cace..226c858 100644 --- a/.github/workflows/dusk_ci.yml +++ b/.github/workflows/dusk_ci.yml @@ -18,14 +18,28 @@ jobs: name: Dusk Analyzer uses: dusk-network/.github/.github/workflows/dusk-analysis.yml@main - test_std: - name: Tests std + build_benches: + name: Build Benchmarks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 + - run: cargo bench --features=cipher,zk --no-run + + check_merkle: + name: Check merkle compiles without zk + uses: dusk-network/.github/.github/workflows/run-tests.yml@main + with: + test_flags: --features=merkle --no-run + + check_cipher: + name: Check cipher compiles without zk uses: dusk-network/.github/.github/workflows/run-tests.yml@main with: - test_flags: --features=merkle,rkyv-impl,size_32 + test_flags: --features=cipher --no-run - test_no_std: - name: Tests no_std + test_all: + name: Tests all uses: dusk-network/.github/.github/workflows/run-tests.yml@main with: - test_flags: --no-default-features + test_flags: --features=zk,cipher,merkle,rkyv-impl,size_32 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7062af7..71fbe15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Restructure crate features [#184] + ## [0.33.0] - 2024-01-03 ### Changed @@ -417,12 +421,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#215]: https://github.com/dusk-network/poseidon252/issues/215 [#212]: https://github.com/dusk-network/poseidon252/issues/212 -[#198]: https://github.com/dusk-network/poseidon252/issues/198 [#206]: https://github.com/dusk-network/poseidon252/issues/206 [#203]: https://github.com/dusk-network/poseidon252/issues/203 [#200]: https://github.com/dusk-network/poseidon252/issues/200 +[#198]: https://github.com/dusk-network/poseidon252/issues/198 [#197]: https://github.com/dusk-network/Poseidon252/issues/197 [#189]: https://github.com/dusk-network/poseidon252/issues/189 +[#184]: https://github.com/dusk-network/poseidon252/issues/184 [#181]: https://github.com/dusk-network/poseidon252/issues/181 [#180]: https://github.com/dusk-network/poseidon252/issues/180 [#175]: https://github.com/dusk-network/poseidon252/issues/175 diff --git a/Cargo.toml b/Cargo.toml index fabc60c..d30c27a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ dusk-bls12_381 = { version = "0.13", default-features = false } dusk-jubjub = { version = "0.14", default-features = false } dusk-bytes = "0.1" dusk-hades = "0.24" -dusk-plonk = { version = "0.19", default-features = false, features = ["alloc"] } +dusk-plonk = { version = "0.19", default-features = false, features = ["alloc"], optional = true } rkyv = { version = "0.7", optional = true, default-features = false } bytecheck = { version = "0.6", optional = true, default-features = false } @@ -24,16 +24,12 @@ rand = { version = "0.8", default-features = false, features = ["getrandom", "st ff = { version = "0.13", default-features = false } [features] -default = [ - "dusk-plonk/std", - "dusk-jubjub/default", - "dusk-bls12_381/default", - "alloc", -] -alloc = [ +zk = [ + "dusk-plonk", "dusk-hades/plonk" ] merkle = [] +cipher = [] size_16 = ["rkyv/size_16"] size_32 = ["rkyv/size_32"] size_64 = ["rkyv/size_64"] @@ -66,11 +62,14 @@ codegen-units = 1 [[bench]] name = "sponge" harness = false +required-features = ["zk"] [[bench]] name = "cipher_encrypt" harness = false +required-features = ["cipher", "zk"] [[bench]] name = "cipher_decrypt" harness = false +required-features = ["cipher", "zk"] diff --git a/benches/cipher_decrypt.rs b/benches/cipher_decrypt.rs index 9a31879..33a4619 100644 --- a/benches/cipher_decrypt.rs +++ b/benches/cipher_decrypt.rs @@ -28,8 +28,10 @@ pub struct CipherDecrypt { impl CipherDecrypt { pub fn random(rng: &mut StdRng) -> Self { - let shared = - GENERATOR.to_niels().mul(&JubJubScalar::random(rng)).into(); + let shared = GENERATOR + .to_niels() + .mul(&JubJubScalar::random(&mut *rng)) + .into(); let nonce = BlsScalar::random(&mut *rng); let message = [BlsScalar::random(&mut *rng), BlsScalar::random(&mut *rng)]; @@ -44,14 +46,11 @@ impl CipherDecrypt { } impl Circuit for CipherDecrypt { - fn circuit(&self, composer: &mut C) -> Result<(), Error> - where - C: Composer, - { + fn circuit(&self, composer: &mut Composer) -> Result<(), Error> { let shared = composer.append_point(self.shared); let nonce = composer.append_witness(self.nonce); - let mut cipher_circuit = [C::ZERO; CIPHER_SIZE]; + let mut cipher_circuit = [Composer::ZERO; CIPHER_SIZE]; self.cipher .cipher() .iter() diff --git a/benches/cipher_encrypt.rs b/benches/cipher_encrypt.rs index 108e21e..1213664 100644 --- a/benches/cipher_encrypt.rs +++ b/benches/cipher_encrypt.rs @@ -27,8 +27,10 @@ pub struct CipherEncrypt { impl CipherEncrypt { pub fn random(rng: &mut StdRng) -> Self { - let shared = - GENERATOR.to_niels().mul(&JubJubScalar::random(rng)).into(); + let shared = GENERATOR + .to_niels() + .mul(&JubJubScalar::random(&mut *rng)) + .into(); let nonce = BlsScalar::random(&mut *rng); let message = [BlsScalar::random(&mut *rng), BlsScalar::random(&mut *rng)]; @@ -42,14 +44,11 @@ impl CipherEncrypt { } impl Circuit for CipherEncrypt { - fn circuit(&self, composer: &mut C) -> Result<(), Error> - where - C: Composer, - { + fn circuit(&self, composer: &mut Composer) -> Result<(), Error> { let shared = composer.append_point(self.shared); let nonce = composer.append_witness(self.nonce); - let mut message_circuit = [C::ZERO; MESSAGE_CAPACITY]; + let mut message_circuit = [Composer::ZERO; MESSAGE_CAPACITY]; self.message .iter() .zip(message_circuit.iter_mut()) diff --git a/benches/sponge.rs b/benches/sponge.rs index af6dbde..1fd9d4b 100644 --- a/benches/sponge.rs +++ b/benches/sponge.rs @@ -25,11 +25,8 @@ impl SpongeCircuit { } impl Circuit for SpongeCircuit { - fn circuit(&self, composer: &mut C) -> Result<(), Error> - where - C: Composer, - { - let mut w_message = [C::ZERO; WIDTH]; + fn circuit(&self, composer: &mut Composer) -> Result<(), Error> { + let mut w_message = [Composer::ZERO; WIDTH]; w_message .iter_mut() .zip(self.message) diff --git a/src/cipher.rs b/src/cipher.rs index e7f25e5..964efcd 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -96,6 +96,9 @@ use bytecheck::CheckBytes; #[cfg(feature = "rkyv-impl")] use rkyv::{Archive, Deserialize, Serialize}; +#[cfg(feature = "zk")] +pub use zk::{decrypt, encrypt}; + const MESSAGE_CAPACITY: usize = 2; const CIPHER_SIZE: usize = MESSAGE_CAPACITY + 1; const CIPHER_BYTES_SIZE: usize = CIPHER_SIZE * BlsScalar::SIZE; @@ -249,8 +252,113 @@ impl PoseidonCipher { } } -#[cfg(feature = "alloc")] -mod zk; +#[cfg(feature = "zk")] +mod zk { + use super::PoseidonCipher; + use dusk_hades::GadgetStrategy; + + use dusk_plonk::prelude::*; + + impl PoseidonCipher { + /// Returns the initial state of the encryption within a composer + /// circuit + pub fn initial_state_circuit( + composer: &mut Composer, + ks0: Witness, + ks1: Witness, + nonce: Witness, + ) -> [Witness; dusk_hades::WIDTH] { + let domain = BlsScalar::from_raw([0x100000000u64, 0, 0, 0]); + let domain = composer.append_constant(domain); + + let length = BlsScalar::from_raw([ + PoseidonCipher::capacity() as u64, + 0, + 0, + 0, + ]); + let length = composer.append_constant(length); + + [domain, length, ks0, ks1, nonce] + } + } + + /// Given a shared secret calculated using any key protocol compatible with + /// bls and jubjub, perform the encryption of the message. + /// + /// The returned set of variables is the cipher text + pub fn encrypt( + composer: &mut Composer, + shared_secret: &WitnessPoint, + nonce: Witness, + message: &[Witness], + ) -> [Witness; PoseidonCipher::cipher_size()] { + let ks0 = *shared_secret.x(); + let ks1 = *shared_secret.y(); -#[cfg(feature = "alloc")] -pub use zk::{decrypt, encrypt}; + let mut cipher = [Composer::ZERO; PoseidonCipher::cipher_size()]; + + let mut state = + PoseidonCipher::initial_state_circuit(composer, ks0, ks1, nonce); + + GadgetStrategy::gadget(composer, &mut state); + + (0..PoseidonCipher::capacity()).for_each(|i| { + let x = if i < message.len() { + message[i] + } else { + Composer::ZERO + }; + + let constraint = + Constraint::new().left(1).a(state[i + 1]).right(1).b(x); + + state[i + 1] = composer.gate_add(constraint); + + cipher[i] = state[i + 1]; + }); + + GadgetStrategy::gadget(composer, &mut state); + cipher[PoseidonCipher::capacity()] = state[1]; + + cipher + } + + /// Given a shared secret calculated using any key protocol compatible with + /// bls and jubjub, perform the decryption of the cipher. + /// + /// The returned set of variables is the original message + pub fn decrypt( + composer: &mut Composer, + shared_secret: &WitnessPoint, + nonce: Witness, + cipher: &[Witness], + ) -> [Witness; PoseidonCipher::capacity()] { + let ks0 = *shared_secret.x(); + let ks1 = *shared_secret.y(); + + let mut message = [Composer::ZERO; PoseidonCipher::capacity()]; + let mut state = + PoseidonCipher::initial_state_circuit(composer, ks0, ks1, nonce); + + GadgetStrategy::gadget(composer, &mut state); + + (0..PoseidonCipher::capacity()).for_each(|i| { + let constraint = Constraint::new() + .left(1) + .a(cipher[i]) + .right(-BlsScalar::one()) + .b(state[i + 1]); + + message[i] = composer.gate_add(constraint); + + state[i + 1] = cipher[i]; + }); + + GadgetStrategy::gadget(composer, &mut state); + + composer.assert_equal(cipher[PoseidonCipher::capacity()], state[1]); + + message + } +} diff --git a/src/cipher/zk.rs b/src/cipher/zk.rs deleted file mode 100644 index ae4998a..0000000 --- a/src/cipher/zk.rs +++ /dev/null @@ -1,108 +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::cipher::PoseidonCipher; -use dusk_hades::GadgetStrategy; - -use dusk_plonk::prelude::*; - -impl PoseidonCipher { - /// Returns the initial state of the encryption within a composer circuit - pub fn initial_state_circuit( - composer: &mut Composer, - ks0: Witness, - ks1: Witness, - nonce: Witness, - ) -> [Witness; dusk_hades::WIDTH] { - let domain = BlsScalar::from_raw([0x100000000u64, 0, 0, 0]); - let domain = composer.append_constant(domain); - - let length = - BlsScalar::from_raw([PoseidonCipher::capacity() as u64, 0, 0, 0]); - let length = composer.append_constant(length); - - [domain, length, ks0, ks1, nonce] - } -} - -/// Given a shared secret calculated using any key protocol compatible with bls -/// and jubjub, perform the encryption of the message. -/// -/// The returned set of variables is the cipher text -pub fn encrypt( - composer: &mut Composer, - shared_secret: &WitnessPoint, - nonce: Witness, - message: &[Witness], -) -> [Witness; PoseidonCipher::cipher_size()] { - let ks0 = *shared_secret.x(); - let ks1 = *shared_secret.y(); - - let mut cipher = [Composer::ZERO; PoseidonCipher::cipher_size()]; - - let mut state = - PoseidonCipher::initial_state_circuit(composer, ks0, ks1, nonce); - - GadgetStrategy::gadget(composer, &mut state); - - (0..PoseidonCipher::capacity()).for_each(|i| { - let x = if i < message.len() { - message[i] - } else { - Composer::ZERO - }; - - let constraint = - Constraint::new().left(1).a(state[i + 1]).right(1).b(x); - - state[i + 1] = composer.gate_add(constraint); - - cipher[i] = state[i + 1]; - }); - - GadgetStrategy::gadget(composer, &mut state); - cipher[PoseidonCipher::capacity()] = state[1]; - - cipher -} - -/// Given a shared secret calculated using any key protocol compatible with bls -/// and jubjub, perform the decryption of the cipher. -/// -/// The returned set of variables is the original message -pub fn decrypt( - composer: &mut Composer, - shared_secret: &WitnessPoint, - nonce: Witness, - cipher: &[Witness], -) -> [Witness; PoseidonCipher::capacity()] { - let ks0 = *shared_secret.x(); - let ks1 = *shared_secret.y(); - - let mut message = [Composer::ZERO; PoseidonCipher::capacity()]; - let mut state = - PoseidonCipher::initial_state_circuit(composer, ks0, ks1, nonce); - - GadgetStrategy::gadget(composer, &mut state); - - (0..PoseidonCipher::capacity()).for_each(|i| { - let constraint = Constraint::new() - .left(1) - .a(cipher[i]) - .right(-BlsScalar::one()) - .b(state[i + 1]); - - message[i] = composer.gate_add(constraint); - - state[i + 1] = cipher[i]; - }); - - GadgetStrategy::gadget(composer, &mut state); - - composer.assert_equal(cipher[PoseidonCipher::capacity()], state[1]); - - message -} diff --git a/src/lib.rs b/src/lib.rs index 0077fdf..7b58e9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,12 +5,13 @@ // Copyright (c) DUSK NETWORK. All rights reserved. #![no_std] -#![cfg_attr(feature = "alloc", warn(missing_docs), doc = include_str!("../README.md"))] +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] -#[cfg(feature = "alloc")] extern crate alloc; /// Encryption and decryption implementation over a Poseidon cipher +#[cfg(feature = "cipher")] pub mod cipher; /// Module containing a fixed-length Poseidon hash implementation with one @@ -19,3 +20,8 @@ pub mod perm_uses; /// Implementation for the Poseidon Sponge hash function pub mod sponge; + +/* +/// Implementation of the Poseidon permutation based on the Hades strategy +mod permutation; +*/ diff --git a/src/sponge.rs b/src/sponge.rs index 34fd42c..69a26ed 100644 --- a/src/sponge.rs +++ b/src/sponge.rs @@ -6,13 +6,14 @@ #[cfg(feature = "merkle")] pub mod merkle; + pub mod truncated; -#[cfg(feature = "alloc")] -use dusk_hades::GadgetStrategy; +use dusk_bls12_381::BlsScalar; use dusk_hades::{ScalarStrategy, Strategy, WIDTH}; -use dusk_plonk::prelude::*; +#[cfg(feature = "zk")] +pub use zk::gadget; /// The `hash` function takes an arbitrary number of Scalars and returns the /// hash in the form of one scalar by using the `Hades` permutation of a state @@ -75,55 +76,63 @@ pub fn hash(messages: &[BlsScalar]) -> BlsScalar { state[1] } -/// Mirror the implementation of [`hash`] inside of a PLONK circuit. -/// -/// The circuit will be defined by the length of `messages`. This means that -/// the circuit description will be different for different messages sizes. -/// -/// The expected usage is the length of the message to be known publicly as the -/// circuit definition. Hence, the padding value `1` will be appended as a -/// constant in the circuit description. -/// -/// The returned value is the hashed witness data computed as a variable. -/// -/// [`hash`]: crate::sponge::hash -#[cfg(feature = "alloc")] -pub fn gadget(composer: &mut Composer, messages: &[Witness]) -> Witness { - let mut state = [Composer::ZERO; WIDTH]; - - let l = messages.len(); - let m = l / (WIDTH - 1); - let n = m * (WIDTH - 1); - let last_iteration = if l == n { m - 1 } else { l / (WIDTH - 1) }; +#[cfg(feature = "zk")] +mod zk { + use super::WIDTH; + + use dusk_hades::GadgetStrategy; + use dusk_plonk::prelude::{Composer, Constraint, Witness}; + + /// Mirror the implementation of [`hash`] inside of a PLONK circuit. + /// + /// The circuit will be defined by the length of `messages`. This means that + /// the circuit description will be different for different messages sizes. + /// + /// The expected usage is the length of the message to be known publicly as + /// the circuit definition. Hence, the padding value `1` will be + /// appended as a constant in the circuit description. + /// + /// The returned value is the hashed witness data computed as a variable. + /// + /// [`hash`]: crate::sponge::hash + pub fn gadget(composer: &mut Composer, messages: &[Witness]) -> Witness { + let mut state = [Composer::ZERO; WIDTH]; + + let l = messages.len(); + let m = l / (WIDTH - 1); + let n = m * (WIDTH - 1); + let last_iteration = if l == n { m - 1 } else { l / (WIDTH - 1) }; + + messages + .chunks(WIDTH - 1) + .enumerate() + .for_each(|(i, chunk)| { + state[1..].iter_mut().zip(chunk.iter()).for_each(|(s, c)| { + let constraint = + Constraint::new().left(1).a(*s).right(1).b(*c); + + *s = composer.gate_add(constraint); + }); + + if i == last_iteration && chunk.len() < WIDTH - 1 { + let constraint = Constraint::new() + .left(1) + .a(state[chunk.len() + 1]) + .constant(1); + + state[chunk.len() + 1] = composer.gate_add(constraint); + } else if i == last_iteration { + GadgetStrategy::gadget(composer, &mut state); + + let constraint = + Constraint::new().left(1).a(state[1]).constant(1); + + state[1] = composer.gate_add(constraint); + } - messages - .chunks(WIDTH - 1) - .enumerate() - .for_each(|(i, chunk)| { - state[1..].iter_mut().zip(chunk.iter()).for_each(|(s, c)| { - let constraint = Constraint::new().left(1).a(*s).right(1).b(*c); - - *s = composer.gate_add(constraint); - }); - - if i == last_iteration && chunk.len() < WIDTH - 1 { - let constraint = Constraint::new() - .left(1) - .a(state[chunk.len() + 1]) - .constant(1); - - state[chunk.len() + 1] = composer.gate_add(constraint); - } else if i == last_iteration { GadgetStrategy::gadget(composer, &mut state); + }); - let constraint = - Constraint::new().left(1).a(state[1]).constant(1); - - state[1] = composer.gate_add(constraint); - } - - GadgetStrategy::gadget(composer, &mut state); - }); - - state[1] + state[1] + } } diff --git a/src/sponge/merkle.rs b/src/sponge/merkle.rs index 938daa2..59ab48b 100644 --- a/src/sponge/merkle.rs +++ b/src/sponge/merkle.rs @@ -7,11 +7,11 @@ //! Implement the sponge framework specialized for merkle trees where the input //! length is constant and the output is always exactly one scalar. -#[cfg(feature = "alloc")] -use dusk_hades::GadgetStrategy; +use dusk_bls12_381::BlsScalar; use dusk_hades::{ScalarStrategy, Strategy, WIDTH}; -use dusk_plonk::prelude::*; +#[cfg(feature = "zk")] +pub use zk::gadget; // Computes the tag from the domain-separator and arity. Output length is // set to 1. @@ -64,30 +64,37 @@ pub fn hash(messages: &[BlsScalar; A]) -> BlsScalar { state[1] } -/// Mirror the implementation of merkle [`hash`] inside of a PLONK circuit. -/// -/// The tag is dependent of the arity `A` as described in [`hash`] and -/// appended to the circuit as a constant. This means that a pre-computed -/// circuit over one arity can never be verified with a circuit of another -/// arity. -/// -/// The returned value is the witness of the hash of the levels. -#[cfg(feature = "alloc")] -pub fn gadget( - composer: &mut Composer, - messages: &[Witness; A], -) -> Witness { - // initialize the state with the capacity - let mut state = [Composer::ZERO; WIDTH]; - state[0] = composer.append_witness(BlsScalar::from(tag::())); +#[cfg(feature = "zk")] +mod zk { + use super::{tag, WIDTH}; - messages.chunks(WIDTH - 1).for_each(|chunk| { - state[1..].iter_mut().zip(chunk.iter()).for_each(|(s, c)| { - let constraint = Constraint::new().left(1).a(*s).right(1).b(*c); - *s = composer.gate_add(constraint); + use dusk_hades::GadgetStrategy; + use dusk_plonk::prelude::*; + + /// Mirror the implementation of merkle [`hash`] inside of a PLONK circuit. + /// + /// The tag is dependent of the arity `A` as described in [`hash`] and + /// appended to the circuit as a constant. This means that a pre-computed + /// circuit over one arity can never be verified with a circuit of another + /// arity. + /// + /// The returned value is the witness of the hash of the levels. + pub fn gadget( + composer: &mut Composer, + messages: &[Witness; A], + ) -> Witness { + // initialize the state with the capacity + let mut state = [Composer::ZERO; WIDTH]; + state[0] = composer.append_witness(BlsScalar::from(tag::())); + + messages.chunks(WIDTH - 1).for_each(|chunk| { + state[1..].iter_mut().zip(chunk.iter()).for_each(|(s, c)| { + let constraint = Constraint::new().left(1).a(*s).right(1).b(*c); + *s = composer.gate_add(constraint); + }); + GadgetStrategy::gadget(composer, &mut state); }); - GadgetStrategy::gadget(composer, &mut state); - }); - state[1] + state[1] + } } diff --git a/src/sponge/truncated.rs b/src/sponge/truncated.rs index debd739..9fc28cb 100644 --- a/src/sponge/truncated.rs +++ b/src/sponge/truncated.rs @@ -8,7 +8,9 @@ use crate::sponge; use dusk_bls12_381::BlsScalar; +use dusk_jubjub::JubJubScalar; +#[cfg(feature = "zk")] use dusk_plonk::prelude::*; /// The constant represents the bitmask used to truncate the hashing results of @@ -56,7 +58,7 @@ pub fn hash(messages: &[BlsScalar]) -> JubJubScalar { /// truncated to fit inside of a [`JubJubScalar`]. /// /// [`hash`]: crate::sponge::hash -#[cfg(feature = "alloc")] +#[cfg(feature = "zk")] pub fn gadget(composer: &mut Composer, message: &[Witness]) -> Witness { let h = sponge::gadget(composer, message); diff --git a/tests/cipher.rs b/tests/cipher.rs index 3141594..d62ecc7 100644 --- a/tests/cipher.rs +++ b/tests/cipher.rs @@ -4,22 +4,16 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -#![cfg(feature = "alloc")] +#![cfg(feature = "cipher")] use core::ops::Mul; use dusk_bls12_381::BlsScalar; use dusk_bytes::Serializable; -use dusk_jubjub::{ - dhke, JubJubAffine, JubJubExtended, JubJubScalar, GENERATOR, - GENERATOR_EXTENDED, -}; -use dusk_poseidon::cipher::{self, PoseidonCipher}; +use dusk_jubjub::{JubJubAffine, JubJubScalar, GENERATOR}; +use dusk_poseidon::cipher::PoseidonCipher; use ff::Field; -use rand::rngs::{OsRng, StdRng}; -use rand::{RngCore, SeedableRng}; - -use dusk_plonk::prelude::Error as PlonkError; -use dusk_plonk::prelude::*; +use rand::rngs::OsRng; +use rand::RngCore; fn gen() -> ( [BlsScalar; PoseidonCipher::capacity()], @@ -125,130 +119,142 @@ fn bytes() { assert_eq!(message, decrypt); } -#[derive(Debug)] -pub struct TestCipherCircuit<'a> { - secret: JubJubScalar, - public: JubJubExtended, - nonce: BlsScalar, - message: &'a [BlsScalar], - cipher: &'a [BlsScalar], -} +#[cfg(feature = "zk")] +mod zk { + use super::*; + + use dusk_jubjub::{dhke, JubJubExtended, GENERATOR_EXTENDED}; + use dusk_plonk::prelude::Error as PlonkError; + use dusk_plonk::prelude::*; + use dusk_poseidon::cipher; + use rand::rngs::StdRng; + use rand::SeedableRng; -impl<'a> TestCipherCircuit<'a> { - pub const fn new( + #[derive(Debug)] + pub struct TestCipherCircuit<'a> { secret: JubJubScalar, public: JubJubExtended, nonce: BlsScalar, message: &'a [BlsScalar], cipher: &'a [BlsScalar], - ) -> Self { - Self { - secret, - public, - nonce, - message, - cipher, + } + + impl<'a> TestCipherCircuit<'a> { + pub const fn new( + secret: JubJubScalar, + public: JubJubExtended, + nonce: BlsScalar, + message: &'a [BlsScalar], + cipher: &'a [BlsScalar], + ) -> Self { + Self { + secret, + public, + nonce, + message, + cipher, + } } } -} -impl<'a> Default for TestCipherCircuit<'a> { - fn default() -> Self { - let secret = Default::default(); - let public = Default::default(); - let nonce = Default::default(); + impl<'a> Default for TestCipherCircuit<'a> { + fn default() -> Self { + let secret = Default::default(); + let public = Default::default(); + let nonce = Default::default(); - const MESSAGE: [BlsScalar; PoseidonCipher::capacity()] = - [BlsScalar::zero(); PoseidonCipher::capacity()]; - const CIPHER: [BlsScalar; PoseidonCipher::cipher_size()] = - [BlsScalar::zero(); PoseidonCipher::cipher_size()]; + const MESSAGE: [BlsScalar; PoseidonCipher::capacity()] = + [BlsScalar::zero(); PoseidonCipher::capacity()]; + const CIPHER: [BlsScalar; PoseidonCipher::cipher_size()] = + [BlsScalar::zero(); PoseidonCipher::cipher_size()]; - Self::new(secret, public, nonce, &MESSAGE, &CIPHER) + Self::new(secret, public, nonce, &MESSAGE, &CIPHER) + } } -} -impl<'a> Circuit for TestCipherCircuit<'a> { - fn circuit(&self, composer: &mut Composer) -> Result<(), PlonkError> { - let nonce = composer.append_witness(self.nonce); + impl<'a> Circuit for TestCipherCircuit<'a> { + fn circuit(&self, composer: &mut Composer) -> Result<(), PlonkError> { + let nonce = composer.append_witness(self.nonce); - let secret = composer.append_witness(self.secret); - let public = composer.append_point(self.public); + let secret = composer.append_witness(self.secret); + let public = composer.append_point(self.public); - let shared = composer.component_mul_point(secret, public); + let shared = composer.component_mul_point(secret, public); - let mut message_circuit = [Composer::ZERO; PoseidonCipher::capacity()]; + let mut message_circuit = + [Composer::ZERO; PoseidonCipher::capacity()]; - self.message - .iter() - .zip(message_circuit.iter_mut()) - .for_each(|(m, v)| { - *v = composer.append_witness(*m); - }); + self.message + .iter() + .zip(message_circuit.iter_mut()) + .for_each(|(m, v)| { + *v = composer.append_witness(*m); + }); - let cipher_gadget = - cipher::encrypt(composer, &shared, nonce, &message_circuit); + let cipher_gadget = + cipher::encrypt(composer, &shared, nonce, &message_circuit); - self.cipher - .iter() - .zip(cipher_gadget.iter()) - .for_each(|(c, g)| { - let x = composer.append_witness(*c); - composer.assert_equal(x, *g); - }); + self.cipher + .iter() + .zip(cipher_gadget.iter()) + .for_each(|(c, g)| { + let x = composer.append_witness(*c); + composer.assert_equal(x, *g); + }); - let message_gadget = - cipher::decrypt(composer, &shared, nonce, &cipher_gadget); + let message_gadget = + cipher::decrypt(composer, &shared, nonce, &cipher_gadget); - self.message - .iter() - .zip(message_gadget.iter()) - .for_each(|(m, g)| { - let x = composer.append_witness(*m); - composer.assert_equal(x, *g); - }); + self.message.iter().zip(message_gadget.iter()).for_each( + |(m, g)| { + let x = composer.append_witness(*m); + composer.assert_equal(x, *g); + }, + ); - Ok(()) + Ok(()) + } } -} -#[test] -fn gadget() -> Result<(), PlonkError> { - // Generate a secret and a public key for Bob - let bob_secret = JubJubScalar::random(&mut OsRng); + #[test] + fn gadget() -> Result<(), PlonkError> { + // Generate a secret and a public key for Bob + let bob_secret = JubJubScalar::random(&mut OsRng); - // Generate a secret and a public key for Alice - let alice_secret = JubJubScalar::random(&mut OsRng); - let alice_public = GENERATOR_EXTENDED * alice_secret; + // Generate a secret and a public key for Alice + let alice_secret = JubJubScalar::random(&mut OsRng); + let alice_public = GENERATOR_EXTENDED * alice_secret; - // Generate a shared secret - let shared_secret = dhke(&bob_secret, &alice_public); + // Generate a shared secret + let shared_secret = dhke(&bob_secret, &alice_public); - // Generate a secret message - let a = BlsScalar::random(&mut OsRng); - let b = BlsScalar::random(&mut OsRng); - let message = [a, b]; + // Generate a secret message + let a = BlsScalar::random(&mut OsRng); + let b = BlsScalar::random(&mut OsRng); + let message = [a, b]; - // Perform the encryption - let nonce = BlsScalar::random(&mut OsRng); - let cipher = PoseidonCipher::encrypt(&message, &shared_secret, &nonce); - - let label = b"poseidon-cipher"; - let size = 13; - - let pp = PublicParameters::setup(1 << size, &mut OsRng)?; - let (prover, verifier) = - Compiler::compile::(&pp, label)?; - let mut rng = StdRng::seed_from_u64(0xbeef); - - let circuit = TestCipherCircuit::new( - bob_secret, - alice_public, - nonce, - &message, - cipher.cipher(), - ); + // Perform the encryption + let nonce = BlsScalar::random(&mut OsRng); + let cipher = PoseidonCipher::encrypt(&message, &shared_secret, &nonce); + + let label = b"poseidon-cipher"; + let size = 13; - let (proof, public_inputs) = prover.prove(&mut rng, &circuit)?; + let pp = PublicParameters::setup(1 << size, &mut OsRng)?; + let (prover, verifier) = + Compiler::compile::(&pp, label)?; + let mut rng = StdRng::seed_from_u64(0xbeef); - verifier.verify(&proof, &public_inputs) + let circuit = TestCipherCircuit::new( + bob_secret, + alice_public, + nonce, + &message, + cipher.cipher(), + ); + + let (proof, public_inputs) = prover.prove(&mut rng, &circuit)?; + + verifier.verify(&proof, &public_inputs) + } } diff --git a/tests/merkle.rs b/tests/merkle.rs index c3d3c26..c3be494 100644 --- a/tests/merkle.rs +++ b/tests/merkle.rs @@ -4,7 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -#![cfg(feature = "alloc")] +#![cfg(feature = "zk")] #![cfg(feature = "merkle")] use dusk_plonk::prelude::Error as PlonkError; diff --git a/tests/sponge.rs b/tests/sponge.rs index 35589e2..2e23b91 100644 --- a/tests/sponge.rs +++ b/tests/sponge.rs @@ -4,17 +4,9 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -#![cfg(feature = "alloc")] - use dusk_bls12_381::BlsScalar; use dusk_bytes::ParseHexStr; use dusk_poseidon::sponge; -use ff::Field; -use rand::rngs::{OsRng, StdRng}; -use rand::SeedableRng; - -use dusk_plonk::prelude::Error as PlonkError; -use dusk_plonk::prelude::*; const TEST_INPUTS: [&str; 32] = [ "bb67ed265bf1db490ded2e1ede55c0d14c55521509dc73f9c354e98ab76c9625", @@ -51,72 +43,6 @@ const TEST_INPUTS: [&str; 32] = [ "e111a0664ac113b960cd336643db4b34c5cd4f69de84d44be95cadaca4d19115", ]; -const CAPACITY: usize = 12; - -fn poseidon_sponge_params(n: usize) -> (Vec, BlsScalar) { - let mut input = vec![BlsScalar::zero(); n]; - - input - .iter_mut() - .for_each(|s| *s = BlsScalar::random(&mut OsRng)); - - let output = sponge::hash(&input); - - (input, output) -} - -#[derive(Default, Debug)] -pub struct TestSpongeCircuit { - input: Vec, - output: BlsScalar, -} - -impl TestSpongeCircuit { - pub fn new(input: Vec, output: BlsScalar) -> Self { - Self { input, output } - } -} - -impl Circuit for TestSpongeCircuit { - fn circuit(&self, composer: &mut Composer) -> Result<(), PlonkError> { - let mut i_var = vec![Composer::ZERO; self.input.len()]; - self.input.iter().zip(i_var.iter_mut()).for_each(|(i, v)| { - *v = composer.append_witness(*i); - }); - - // Apply Poseidon Sponge hash to the inputs - let computed_o_var = sponge::gadget(composer, i_var.as_slice()); - - // Check that the Gadget sponge hash result = Scalar sponge hash result - let o_var = composer.append_witness(self.output); - composer.assert_equal(o_var, computed_o_var); - - Ok(()) - } -} - -#[test] -fn sponge_gadget() -> Result<(), Error> { - let label = b"sponge-tester"; - let pp = PublicParameters::setup(1 << CAPACITY, &mut OsRng)?; - - let mut rng = StdRng::seed_from_u64(0xbeef); - - for w in [3, 5, 15] { - let (i, o) = poseidon_sponge_params(w); - let circuit = TestSpongeCircuit::new(i, o); - - let (prover, verifier) = - Compiler::compile_with_circuit(&pp, label, &circuit)?; - - let (proof, public_inputs) = prover.prove(&mut rng, &circuit)?; - - verifier.verify(&proof, &public_inputs)?; - } - - Ok(()) -} - #[test] fn sponge_hash_test() { let test_inputs: Vec = TEST_INPUTS @@ -155,69 +81,148 @@ fn sponge_hash_test() { ); } -#[derive(Debug, Default)] -pub struct TestTruncatedCircuit { - input: Vec, - output: JubJubScalar, -} +#[cfg(feature = "zk")] +mod zk { + use super::*; + + use rand::rngs::{OsRng, StdRng}; + use rand::SeedableRng; + + use dusk_plonk::prelude::Error as PlonkError; + use dusk_plonk::prelude::*; + use ff::Field; + + const CAPACITY: usize = 12; + + fn poseidon_sponge_params(n: usize) -> (Vec, BlsScalar) { + let mut input = vec![BlsScalar::zero(); n]; -const TRUNCATED_CAPACITY: usize = 17; + input + .iter_mut() + .for_each(|s| *s = BlsScalar::random(&mut OsRng)); -impl TestTruncatedCircuit { - pub fn new(input: Vec, output: JubJubScalar) -> Self { - Self { input, output } + let output = sponge::hash(&input); + + (input, output) } -} -impl Circuit for TestTruncatedCircuit { - fn circuit(&self, composer: &mut Composer) -> Result<(), PlonkError> { - let h = sponge::truncated::hash(self.input.as_slice()); - let p = JubJubAffine::from(dusk_jubjub::GENERATOR_EXTENDED * h); - let p = composer.append_point(p); + #[derive(Default, Debug)] + pub struct TestSpongeCircuit { + input: Vec, + output: BlsScalar, + } - let i: Vec = self - .input - .iter() - .map(|i| composer.append_witness(*i)) - .collect(); + impl TestSpongeCircuit { + pub fn new(input: Vec, output: BlsScalar) -> Self { + Self { input, output } + } + } + + impl Circuit for TestSpongeCircuit { + fn circuit(&self, composer: &mut Composer) -> Result<(), PlonkError> { + let mut i_var = vec![Composer::ZERO; self.input.len()]; + self.input.iter().zip(i_var.iter_mut()).for_each(|(i, v)| { + *v = composer.append_witness(*i); + }); - let o = composer.append_witness(self.output); + // Apply Poseidon Sponge hash to the inputs + let computed_o_var = sponge::gadget(composer, i_var.as_slice()); - let t = sponge::truncated::gadget(composer, i.as_slice()); - let p_p = composer - .component_mul_generator(t, dusk_jubjub::GENERATOR_EXTENDED) - .expect("Multiplying with the generator should succeed"); + // Check that the Gadget sponge hash result = Scalar sponge hash + // result + let o_var = composer.append_witness(self.output); + composer.assert_equal(o_var, computed_o_var); + + Ok(()) + } + } - composer.assert_equal(t, o); - composer.assert_equal_point(p, p_p); + #[test] + fn sponge_gadget() -> Result<(), Error> { + let label = b"sponge-tester"; + let pp = PublicParameters::setup(1 << CAPACITY, &mut OsRng)?; + + let mut rng = StdRng::seed_from_u64(0xbeef); + + for w in [3, 5, 15] { + let (i, o) = poseidon_sponge_params(w); + let circuit = TestSpongeCircuit::new(i, o); + + let (prover, verifier) = + Compiler::compile_with_circuit(&pp, label, &circuit)?; + + let (proof, public_inputs) = prover.prove(&mut rng, &circuit)?; + + verifier.verify(&proof, &public_inputs)?; + } Ok(()) } -} -#[test] -fn truncated_sponge() -> Result<(), PlonkError> { - let input: Vec = TEST_INPUTS - .iter() - .map(|input| BlsScalar::from_hex_str(input).unwrap()) - .collect(); + #[derive(Debug, Default)] + pub struct TestTruncatedCircuit { + input: Vec, + output: JubJubScalar, + } - let label = b"truncated-sponge-tester"; - let pp = PublicParameters::setup(1 << TRUNCATED_CAPACITY, &mut OsRng)?; - let mut rng = StdRng::seed_from_u64(0xbeef); + const TRUNCATED_CAPACITY: usize = 17; + + impl TestTruncatedCircuit { + pub fn new(input: Vec, output: JubJubScalar) -> Self { + Self { input, output } + } + } - for w in [3, 6, 9] { - let i = input[..w].to_vec(); - let o = sponge::truncated::hash(i.as_slice()); + impl Circuit for TestTruncatedCircuit { + fn circuit(&self, composer: &mut Composer) -> Result<(), PlonkError> { + let h = sponge::truncated::hash(self.input.as_slice()); + let p = JubJubAffine::from(dusk_jubjub::GENERATOR_EXTENDED * h); + let p = composer.append_point(p); - let circuit = TestTruncatedCircuit::new(i, o); - let (prover, verifier) = - Compiler::compile_with_circuit(&pp, label, &circuit)?; + let i: Vec = self + .input + .iter() + .map(|i| composer.append_witness(*i)) + .collect(); - let (proof, public_inputs) = prover.prove(&mut rng, &circuit)?; + let o = composer.append_witness(self.output); - verifier.verify(&proof, &public_inputs)?; + let t = sponge::truncated::gadget(composer, i.as_slice()); + let p_p = composer + .component_mul_generator(t, dusk_jubjub::GENERATOR_EXTENDED) + .expect("Multiplying with the generator should succeed"); + + composer.assert_equal(t, o); + composer.assert_equal_point(p, p_p); + + Ok(()) + } } - Ok(()) + #[test] + fn truncated_sponge() -> Result<(), PlonkError> { + let input: Vec = TEST_INPUTS + .iter() + .map(|input| BlsScalar::from_hex_str(input).unwrap()) + .collect(); + + let label = b"truncated-sponge-tester"; + let pp = PublicParameters::setup(1 << TRUNCATED_CAPACITY, &mut OsRng)?; + let mut rng = StdRng::seed_from_u64(0xbeef); + + for w in [3, 6, 9] { + let i = input[..w].to_vec(); + let o = sponge::truncated::hash(i.as_slice()); + + let circuit = TestTruncatedCircuit::new(i, o); + let (prover, verifier) = + Compiler::compile_with_circuit(&pp, label, &circuit)?; + + let (proof, public_inputs) = prover.prove(&mut rng, &circuit)?; + + verifier.verify(&proof, &public_inputs)?; + } + + Ok(()) + } }